/* png.c - location for general purpose libpng functions * * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h
*/
#include"pngpriv.h"
/* Generate a compiler error if there is an old png.h in the search path. */ typedef png_libpng_version_1_6_45 Your_png_h_is_not_version_1_6_45;
/* Tells libpng that we have already handled the first "num_bytes" bytes * of the PNG file signature. If the PNG data is embedded into another * stream we can set num_bytes = 8 so that libpng will not attempt to read * or write any of the magic bytes before it starts on the IHDR.
*/
#ifdef PNG_READ_SUPPORTED void PNGAPI
png_set_sig_bytes(png_structrp png_ptr, int num_bytes)
{ unsignedint nb = (unsignedint)num_bytes;
png_debug(1, "in png_set_sig_bytes");
if (png_ptr == NULL) return;
if (num_bytes < 0)
nb = 0;
if (nb > 8)
png_error(png_ptr, "Too many bytes for PNG signature");
png_ptr->sig_bytes = (png_byte)nb;
}
/* Checks whether the supplied bytes match the PNG signature. We allow * checking less than the full 8-byte signature so that those apps that * already read the first few bytes of a file to determine the file type * can simply check the remaining bytes for extra assurance. Returns * an integer less than, equal to, or greater than zero if sig is found, * respectively, to be less than, to match, or be greater than the correct * PNG signature (this is the same behavior as strcmp, memcmp, etc).
*/ int PNGAPI
png_sig_cmp(png_const_bytep sig, size_t start, size_t num_to_check)
{ staticconst png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
/* Function to free memory for zlib */ void/* PRIVATE */
png_zfree(voidpf png_ptr, voidpf ptr)
{
png_free(png_voidcast(png_const_structrp,png_ptr), ptr);
}
/* Reset the CRC variable to 32 bits of 1's. Care must be taken * in case CRC is > 32 bits to leave the top bits 0.
*/ void/* PRIVATE */
png_reset_crc(png_structrp png_ptr)
{ /* The cast is safe because the crc is a 32-bit value. */
png_ptr->crc = (png_uint_32)crc32(0, Z_NULL, 0);
}
/* Calculate the CRC over a section of data. We can only pass as * much data to this routine as the largest single buffer size. We * also check that this data will actually be used before going to the * trouble of calculating it.
*/ void/* PRIVATE */
png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, size_t length)
{ int need_crc = 1;
if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0)
{ if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
(PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
need_crc = 0;
}
/* 'uLong' is defined in zlib.h as unsigned long; this means that on some * systems it is a 64-bit value. crc32, however, returns 32 bits so the * following cast is safe. 'uInt' may be no more than 16 bits, so it is * necessary to perform a loop here.
*/ if (need_crc != 0 && length > 0)
{
uLong crc = png_ptr->crc; /* Should never issue a warning */
do
{
uInt safe_length = (uInt)length; #ifndef __COVERITY__ if (safe_length == 0)
safe_length = (uInt)-1; /* evil, but safe */ #endif
crc = crc32(crc, ptr, safe_length);
/* The following should never issue compiler warnings; if they do the * target system has characteristics that will probably violate other * assumptions within the libpng code.
*/
ptr += safe_length;
length -= safe_length;
} while (length > 0);
/* And the following is always safe because the crc is only 32 bits. */
png_ptr->crc = (png_uint_32)crc;
}
}
/* Check a user supplied version number, called from both read and write * functions that create a png_struct.
*/ int
png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver)
{ /* Libpng versions 1.0.0 and later are binary compatible if the version * string matches through the second '.'; we must recompile any * applications that use any older library version.
*/
if (user_png_ver != NULL)
{ int i = -1; int found_dots = 0;
do
{
i++; if (user_png_ver[i] != PNG_LIBPNG_VER_STRING[i])
png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; if (user_png_ver[i] == '.')
found_dots++;
} while (found_dots < 2 && user_png_ver[i] != 0 &&
PNG_LIBPNG_VER_STRING[i] != 0);
}
pos = png_safecat(m, (sizeof m), pos, "Application built with libpng-");
pos = png_safecat(m, (sizeof m), pos, user_png_ver);
pos = png_safecat(m, (sizeof m), pos, " but running with ");
pos = png_safecat(m, (sizeof m), pos, PNG_LIBPNG_VER_STRING);
PNG_UNUSED(pos)
/* Generic function to create a png_struct for either read or write - this * contains the common initialization.
*/
PNG_FUNCTION(png_structp /* PRIVATE */,
png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
{
png_struct create_struct; # ifdef PNG_SETJMP_SUPPORTED
jmp_buf create_jmp_buf; # endif
/* This temporary stack-allocated structure is used to provide a place to * build enough context to allow the user provided memory allocator (if any) * to be called.
*/
memset(&create_struct, 0, (sizeof create_struct));
# ifdef PNG_USER_CHUNK_CACHE_MAX /* Added at libpng-1.2.43 and 1.4.0 */
create_struct.user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX; # endif
# ifdef PNG_USER_CHUNK_MALLOC_MAX /* Added at libpng-1.2.43 and 1.4.1, required only for read but exists * in png_struct regardless.
*/
create_struct.user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX; # endif # endif
/* The following two API calls simply set fields in png_struct, so it is safe * to do them now even though error handling is not yet set up.
*/ # ifdef PNG_USER_MEM_SUPPORTED
png_set_mem_fn(&create_struct, mem_ptr, malloc_fn, free_fn); # else
PNG_UNUSED(mem_ptr)
PNG_UNUSED(malloc_fn)
PNG_UNUSED(free_fn) # endif
/* (*error_fn) can return control to the caller after the error_ptr is set, * this will result in a memory leak unless the error_fn does something * extremely sophisticated. The design lacks merit but is implicit in the * API.
*/
png_set_error_fn(&create_struct, error_ptr, error_fn, warn_fn);
# ifdef PNG_SETJMP_SUPPORTED if (!setjmp(create_jmp_buf)) # endif
{ # ifdef PNG_SETJMP_SUPPORTED /* Temporarily fake out the longjmp information until we have * successfully completed this function. This only works if we have * setjmp() support compiled in, but it is safe - this stuff should * never happen.
*/
create_struct.jmp_buf_ptr = &create_jmp_buf;
create_struct.jmp_buf_size = 0; /*stack allocation*/
create_struct.longjmp_fn = longjmp; # endif /* Call the general version checker (shared with read and write code):
*/ if (png_user_version_check(&create_struct, user_png_ver) != 0)
{
png_structrp png_ptr = png_voidcast(png_structrp,
png_malloc_warn(&create_struct, (sizeof *png_ptr)));
if (png_ptr != NULL)
{ /* png_ptr->zstream holds a back-pointer to the png_struct, so * this can only be done now:
*/
create_struct.zstream.zalloc = png_zalloc;
create_struct.zstream.zfree = png_zfree;
create_struct.zstream.opaque = png_ptr;
/* This is the successful return point */ return png_ptr;
}
}
}
/* A longjmp because of a bug in the application storage allocator or a * simple failure to allocate the png_struct.
*/ return NULL;
}
/* Allocate the memory for an info_struct for the application. */
PNG_FUNCTION(png_infop,PNGAPI
png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED)
{
png_inforp info_ptr;
png_debug(1, "in png_create_info_struct");
if (png_ptr == NULL) return NULL;
/* Use the internal API that does not (or at least should not) error out, so * that this call always returns ok. The application typically sets up the * error handling *after* creating the info_struct because this is the way it * has always been done in 'example.c'.
*/
info_ptr = png_voidcast(png_inforp, png_malloc_base(png_ptr,
(sizeof *info_ptr)));
if (info_ptr != NULL)
memset(info_ptr, 0, (sizeof *info_ptr));
return info_ptr;
}
/* This function frees the memory associated with a single info struct. * Normally, one would use either png_destroy_read_struct() or * png_destroy_write_struct() to free an info struct, but this may be * useful for some applications. From libpng 1.6.0 this function is also used * internally to implement the png_info release part of the 'struct' destroy * APIs. This ensures that all possible approaches free the same data (all of * it).
*/ void PNGAPI
png_destroy_info_struct(png_const_structrp png_ptr, png_infopp info_ptr_ptr)
{
png_inforp info_ptr = NULL;
png_debug(1, "in png_destroy_info_struct");
if (png_ptr == NULL) return;
if (info_ptr_ptr != NULL)
info_ptr = *info_ptr_ptr;
if (info_ptr != NULL)
{ /* Do this first in case of an error below; if the app implements its own * memory management this can lead to png_free calling png_error, which * will abort this routine and return control to the app error handler. * An infinite loop may result if it then tries to free the same info * ptr.
*/
*info_ptr_ptr = NULL;
/* Initialize the info structure. This is now an internal function (0.89) * and applications using it are urged to use png_create_info_struct() * instead. Use deprecated in 1.6.0, internal use removed (used internally it * is just a memset). * * NOTE: it is almost inconceivable that this API is used because it bypasses * the user-memory mechanism and the user error handling/warning mechanisms in * those cases where it does anything other than a memset.
*/
PNG_FUNCTION(void,PNGAPI
png_info_init_3,(png_infopp ptr_ptr, size_t png_info_struct_size),
PNG_DEPRECATED)
{
png_inforp info_ptr = *ptr_ptr;
png_debug(1, "in png_info_init_3");
if (info_ptr == NULL) return;
if ((sizeof (png_info)) > png_info_struct_size)
{
*ptr_ptr = NULL; /* The following line is why this API should not be used: */
free(info_ptr);
info_ptr = png_voidcast(png_inforp, png_malloc_base(NULL,
(sizeof *info_ptr))); if (info_ptr == NULL) return;
*ptr_ptr = info_ptr;
}
/* Set everything to 0 */
memset(info_ptr, 0, (sizeof *info_ptr));
}
void PNGAPI
png_data_freer(png_const_structrp png_ptr, png_inforp info_ptr, int freer, png_uint_32 mask)
{
png_debug(1, "in png_data_freer");
if (png_ptr == NULL || info_ptr == NULL) return;
if (freer == PNG_DESTROY_WILL_FREE_DATA)
info_ptr->free_me |= mask;
else
png_error(png_ptr, "Unknown freer parameter in png_data_freer");
}
void PNGAPI
png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, int num)
{
png_debug(1, "in png_free_data");
if (png_ptr == NULL || info_ptr == NULL) return;
#ifdef PNG_TEXT_SUPPORTED /* Free text item num or (if num == -1) all text items */ if (info_ptr->text != NULL &&
((mask & PNG_FREE_TEXT) & info_ptr->free_me) != 0)
{ if (num != -1)
{
png_free(png_ptr, info_ptr->text[num].key);
info_ptr->text[num].key = NULL;
}
else
{ int i;
for (i = 0; i < info_ptr->num_text; i++)
png_free(png_ptr, info_ptr->text[i].key);
#ifdef PNG_sPLT_SUPPORTED /* Free a given sPLT entry, or (if num == -1) all sPLT entries */ if (info_ptr->splt_palettes != NULL &&
((mask & PNG_FREE_SPLT) & info_ptr->free_me) != 0)
{ if (num != -1)
{
png_free(png_ptr, info_ptr->splt_palettes[num].name);
png_free(png_ptr, info_ptr->splt_palettes[num].entries);
info_ptr->splt_palettes[num].name = NULL;
info_ptr->splt_palettes[num].entries = NULL;
}
else
{ int i;
for (i = 0; i < info_ptr->splt_palettes_num; i++)
{
png_free(png_ptr, info_ptr->splt_palettes[i].name);
png_free(png_ptr, info_ptr->splt_palettes[i].entries);
}
/* This function returns a pointer to the io_ptr associated with the user * functions. The application should free any memory associated with this * pointer before png_write_destroy() or png_read_destroy() are called.
*/
png_voidp PNGAPI
png_get_io_ptr(png_const_structrp png_ptr)
{ if (png_ptr == NULL) return NULL;
return png_ptr->io_ptr;
}
#ifdefined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) # ifdef PNG_STDIO_SUPPORTED /* Initialize the default input/output functions for the PNG file. If you * use your own read or write routines, you can call either png_set_read_fn() * or png_set_write_fn() instead of png_init_io(). If you have defined * PNG_NO_STDIO or otherwise disabled PNG_STDIO_SUPPORTED, you must use a * function of your own because "FILE *" isn't necessarily available.
*/ void PNGAPI
png_init_io(png_structrp png_ptr, png_FILE_p fp)
{
png_debug(1, "in png_init_io");
if (png_ptr == NULL) return;
png_ptr->io_ptr = (png_voidp)fp;
} # endif
# ifdef PNG_SAVE_INT_32_SUPPORTED /* PNG signed integers are saved in 32-bit 2's complement format. ANSI C-90 * defines a cast of a signed integer to an unsigned integer either to preserve * the value, if it is positive, or to calculate: * * (UNSIGNED_MAX+1) + integer * * Where UNSIGNED_MAX is the appropriate maximum unsigned value, so when the * negative integral value is added the result will be an unsigned value * corresponding to the 2's complement representation.
*/ void PNGAPI
png_save_int_32(png_bytep buf, png_int_32 i)
{
png_save_uint_32(buf, (png_uint_32)i);
} # endif
# ifdef PNG_TIME_RFC1123_SUPPORTED /* Convert the supplied time into an RFC 1123 string suitable for use in * a "Creation Time" or other text-based time string.
*/ int PNGAPI
png_convert_to_rfc1123_buffer(char out[29], png_const_timep ptime)
{ staticconstchar short_months[12][4] =
{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
# if PNG_LIBPNG_VER < 10700 /* To do: remove the following from libpng-1.7 */ /* Original API that uses a private buffer in png_struct. * Deprecated because it causes png_struct to carry a spurious temporary * buffer (png_struct::time_buffer), better to have the caller pass this in.
*/
png_const_charp PNGAPI
png_convert_to_rfc1123(png_structrp png_ptr, png_const_timep ptime)
{ if (png_ptr != NULL)
{ /* The only failure above if png_ptr != NULL is from an invalid ptime */ if (png_convert_to_rfc1123_buffer(png_ptr->time_buffer, ptime) == 0)
png_warning(png_ptr, "Ignoring invalid time value");
/* The following return the library version as a short string in the * format 1.0.0 through 99.99.99zz. To get the version of *.h files * used with your application, print out PNG_LIBPNG_VER_STRING, which * is defined in png.h. * Note: now there is no difference between png_get_libpng_ver() and * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard, * it is guaranteed that png.c uses the correct version of png.h.
*/
png_const_charp PNGAPI
png_get_libpng_ver(png_const_structrp png_ptr)
{ /* Version of *.c files used when building libpng */ return png_get_header_ver(png_ptr);
}
png_const_charp PNGAPI
png_get_header_ver(png_const_structrp png_ptr)
{ /* Version of *.h files used when building libpng */
PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ return PNG_LIBPNG_VER_STRING;
}
png_const_charp PNGAPI
png_get_header_version(png_const_structrp png_ptr)
{ /* Returns longer string containing both version and date */
PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ #ifdef __STDC__ return PNG_HEADER_VERSION_STRING # ifndef PNG_READ_SUPPORTED " (NO READ SUPPORT)" # endif
PNG_STRING_NEWLINE; #else return PNG_HEADER_VERSION_STRING; #endif
}
#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED /* NOTE: this routine is not used internally! */ /* Build a grayscale palette. Palette is assumed to be 1 << bit_depth * large of png_color. This lets grayscale images be treated as * paletted. Most useful for gamma correction and simplification * of code. This API is not used internally.
*/ void PNGAPI
png_build_grayscale_palette(int bit_depth, png_colorp palette)
{ int num_palette; int color_inc; int i; int v;
png_debug(1, "in png_do_build_grayscale_palette");
case 4:
num_palette = 16;
color_inc = 0x11; break;
case 8:
num_palette = 256;
color_inc = 1; break;
default:
num_palette = 0;
color_inc = 0; break;
}
for (i = 0, v = 0; i < num_palette; i++, v += color_inc)
{
palette[i].red = (png_byte)(v & 0xff);
palette[i].green = (png_byte)(v & 0xff);
palette[i].blue = (png_byte)(v & 0xff);
}
} #endif
#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED int PNGAPI
png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name)
{ /* Check chunk_name and return "keep" value if it's on the list, else 0 */
png_const_bytep p, p_end;
p_end = png_ptr->chunk_list;
p = p_end + png_ptr->num_chunk_list*5; /* beyond end */
/* The code is the fifth byte after each four byte string. Historically this * code was always searched from the end of the list, this is no longer * necessary because the 'set' routine handles duplicate entries correctly.
*/ do/* num_chunk_list > 0, so at least one */
{
p -= 5;
if (memcmp(chunk_name, p, 4) == 0) return p[4];
} while (p > p_end);
/* This means that known chunks should be processed and unknown chunks should * be handled according to the value of png_ptr->unknown_default; this can be * confusing because, as a result, there are two levels of defaulting for * unknown chunks.
*/ return PNG_HANDLE_CHUNK_AS_DEFAULT;
}
#ifdef PNG_READ_SUPPORTED /* This function, added to libpng-1.0.6g, is untested. */ int PNGAPI
png_reset_zstream(png_structrp png_ptr)
{ if (png_ptr == NULL) return Z_STREAM_ERROR;
/* WARNING: this resets the window bits to the maximum! */ return inflateReset(&png_ptr->zstream);
} #endif/* READ */
/* This function was added to libpng-1.0.7 */
png_uint_32 PNGAPI
png_access_version_number(void)
{ /* Version of *.c files used when building libpng */ return (png_uint_32)PNG_LIBPNG_VER;
}
#ifdefined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) /* Ensure that png_ptr->zstream.msg holds some appropriate error message string. * If it doesn't 'ret' is used to set it to something appropriate, even in cases * like Z_OK or Z_STREAM_END where the error code is apparently a success code.
*/ void/* PRIVATE */
png_zstream_error(png_structrp png_ptr, int ret)
{ /* Translate 'ret' into an appropriate error string, priority is given to the * one in zstream if set. This always returns a string, even in cases like * Z_OK or Z_STREAM_END where the error code is a success code.
*/ if (png_ptr->zstream.msg == NULL) switch (ret)
{ default: case Z_OK:
png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return code"); break;
case Z_STREAM_END: /* Normal exit */
png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected end of LZ stream"); break;
case Z_NEED_DICT: /* This means the deflate stream did not have a dictionary; this * indicates a bogus PNG.
*/
png_ptr->zstream.msg = PNGZ_MSG_CAST("missing LZ dictionary"); break;
case Z_ERRNO: /* gz APIs only: should not happen */
png_ptr->zstream.msg = PNGZ_MSG_CAST("zlib IO error"); break;
case Z_STREAM_ERROR: /* internal libpng error */
png_ptr->zstream.msg = PNGZ_MSG_CAST("bad parameters to zlib"); break;
case Z_DATA_ERROR:
png_ptr->zstream.msg = PNGZ_MSG_CAST("damaged LZ stream"); break;
case Z_MEM_ERROR:
png_ptr->zstream.msg = PNGZ_MSG_CAST("insufficient memory"); break;
case Z_BUF_ERROR: /* End of input or output; not a problem if the caller is doing * incremental read or write.
*/
png_ptr->zstream.msg = PNGZ_MSG_CAST("truncated"); break;
case Z_VERSION_ERROR:
png_ptr->zstream.msg = PNGZ_MSG_CAST("unsupported zlib version"); break;
case PNG_UNEXPECTED_ZLIB_RETURN: /* Compile errors here mean that zlib now uses the value co-opted in * pngpriv.h for PNG_UNEXPECTED_ZLIB_RETURN; update the switch above * and change pngpriv.h. Note that this message is "... return", * whereas the default/Z_OK one is "... return code".
*/
png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return"); break;
}
}
/* 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_GAMMA_SUPPORTED /* always set if COLORSPACE */ staticint
png_colorspace_check_gamma(png_const_structrp png_ptr,
png_colorspacerp colorspace, png_fixed_point gAMA, int from) /* This is called to check a new gamma value against an existing one. The * routine returns false if the new gamma value should not be written. * * 'from' says where the new gamma value comes from: * * 0: the new gamma value is the libpng estimate for an ICC profile * 1: the new gamma value comes from a gAMA chunk * 2: the new gamma value comes from an sRGB chunk
*/
{
png_fixed_point gtest;
if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
(png_muldiv(>est, colorspace->gamma, PNG_FP_1, gAMA) == 0 ||
png_gamma_significant(gtest) != 0))
{ /* Either this is an sRGB image, in which case the calculated gamma * approximation should match, or this is an image with a profile and the * value libpng calculates for the gamma of the profile does not match the * value recorded in the file. The former, sRGB, case is an error, the * latter is just a warning.
*/ if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2)
{
png_chunk_report(png_ptr, "gamma value does not match sRGB",
PNG_CHUNK_ERROR); /* Do not overwrite an sRGB value */ return from == 2;
}
else/* sRGB tag not involved */
{
png_chunk_report(png_ptr, "gamma value does not match libpng estimate",
PNG_CHUNK_WARNING); return from == 1;
}
}
return 1;
}
void/* PRIVATE */
png_colorspace_set_gamma(png_const_structrp png_ptr,
png_colorspacerp colorspace, png_fixed_point gAMA)
{ /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't * occur. Since the fixed point representation is asymmetrical it is * possible for 1/gamma to overflow the limit of 21474 and this means the * gamma value must be at least 5/100000 and hence at most 20000.0. For * safety the limits here are a little narrower. The values are 0.00016 to * 6250.0, which are truly ridiculous gamma values (and will produce * displays that are all black or all white.) * * In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk * handling code, which only required the value to be >0.
*/
png_const_charp errmsg;
if (gAMA < 16 || gAMA > 625000000)
errmsg = "gamma value out of range";
# ifdef PNG_READ_gAMA_SUPPORTED /* Allow the application to set the gamma value more than once */ elseif ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
(colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0)
errmsg = "duplicate"; # endif
/* Do nothing if the colorspace is already invalid */ elseif ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) return;
else
{ if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA,
1/*from gAMA*/) != 0)
{ /* Store this gamma value. */
colorspace->gamma = gAMA;
colorspace->flags |=
(PNG_COLORSPACE_HAVE_GAMMA | PNG_COLORSPACE_FROM_gAMA);
}
/* At present if the check_gamma test fails the gamma of the colorspace is * not updated however the colorspace is not invalidated. This * corresponds to the case where the existing gamma comes from an sRGB * chunk or profile. An error message has already been output.
*/ return;
}
/* Error exit - errmsg has been set. */
colorspace->flags |= PNG_COLORSPACE_INVALID;
png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR);
}
# 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) != 0)
info_ptr->valid |= PNG_INFO_sRGB;
else
info_ptr->valid &= ~PNG_INFO_sRGB;
if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
info_ptr->valid |= PNG_INFO_cHRM;
else
info_ptr->valid &= ~PNG_INFO_cHRM; # endif
if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0)
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;
#ifdef PNG_COLORSPACE_SUPPORTED static png_int_32
png_fp_add(png_int_32 addend0, png_int_32 addend1, int *error)
{ /* Safely add two fixed point values setting an error flag and returning 0.5 * on overflow. * IMPLEMENTATION NOTE: ANSI requires signed overflow not to occur, therefore * relying on addition of two positive values producing a negative one is not * safe.
*/ if (addend0 > 0)
{ if (0x7fffffff - addend0 >= addend1) return addend0+addend1;
} elseif (addend0 < 0)
{ if (-0x7fffffff - addend0 <= addend1) return addend0+addend1;
} else return addend1;
*error = 1; return PNG_FP_1/2;
}
static png_int_32
png_fp_sub(png_int_32 addend0, png_int_32 addend1, int *error)
{ /* As above but calculate addend0-addend1. */ if (addend1 > 0)
{ if (-0x7fffffff + addend1 <= addend0) return addend0-addend1;
} elseif (addend1 < 0)
{ if (0x7fffffff + addend1 >= addend0) return addend0-addend1;
} else return addend0;
*error = 1; return PNG_FP_1/2;
}
staticint
png_safe_add(png_int_32 *addend0_and_result, png_int_32 addend1,
png_int_32 addend2)
{ /* Safely add three integers. Returns 0 on success, 1 on overflow. Does not * set the result on overflow.
*/ int error = 0; int result = png_fp_add(*addend0_and_result,
png_fp_add(addend1, addend2, &error),
&error); if (!error) *addend0_and_result = result; return error;
}
/* 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.
*/ staticint
png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ)
{
png_int_32 d, dred, dgreen, dblue, dwhite, whiteX, whiteY;
/* 'd' in each of the blocks below is just X+Y+Z for each component, * x, y and z are X,Y,Z/(X+Y+Z).
*/
d = XYZ->red_X; if (png_safe_add(&d, XYZ->red_Y, XYZ->red_Z)) return 1;
dred = d; if (png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, dred) == 0) return 1; if (png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, dred) == 0) return 1;
d = XYZ->green_X; if (png_safe_add(&d, XYZ->green_Y, XYZ->green_Z)) return 1;
dgreen = d; if (png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, dgreen) == 0) return 1; if (png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, dgreen) == 0) return 1;
d = XYZ->blue_X; if (png_safe_add(&d, XYZ->blue_Y, XYZ->blue_Z)) return 1;
dblue = d; if (png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, dblue) == 0) return 1; if (png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, dblue) == 0) return 1;
/* The reference white is simply the sum of the end-point (X,Y,Z) vectors so * the fillowing calculates (X+Y+Z) of the reference white (media white, * encoding white) itself:
*/
d = dblue; if (png_safe_add(&d, dred, dgreen)) return 1;
dwhite = d;
/* Find the white X,Y values from the sum of the red, green and blue X,Y * values.
*/
d = XYZ->red_X; if (png_safe_add(&d, XYZ->green_X, XYZ->blue_X)) return 1;
whiteX = d;
d = XYZ->red_Y; if (png_safe_add(&d, XYZ->green_Y, XYZ->blue_Y)) return 1;
whiteY = d;
if (png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite) == 0) return 1; if (png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite) == 0) return 1;
/* 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). We check * xy->whitey against 5, not 0, to avoid a possible integer overflow. * * The limits here will *not* accept ACES AP0, where bluey is -7700 * (-0.0770) because the PNG spec itself requires the xy values to be * unsigned. whitey is also required to be 5 or more to avoid overflow. * * Instead the upper limits have been relaxed to accomodate ACES AP1 where * redz ends up as -600 (-0.006). ProPhotoRGB was already "in range." * The new limit accomodates the AP0 and AP1 ranges for z but not AP0 redy.
*/ const png_fixed_point fpLimit = PNG_FP_1+(PNG_FP_1/10); if (xy->redx < 0 || xy->redx > fpLimit) return 1; if (xy->redy < 0 || xy->redy > fpLimit-xy->redx) return 1; if (xy->greenx < 0 || xy->greenx > fpLimit) return 1; if (xy->greeny < 0 || xy->greeny > fpLimit-xy->greenx) return 1; if (xy->bluex < 0 || xy->bluex > fpLimit) return 1; if (xy->bluey < 0 || xy->bluey > fpLimit-xy->bluex) return 1; if (xy->whitex < 0 || xy->whitex > fpLimit) return 1; if (xy->whitey < 5 || xy->whitey > fpLimit-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 slightly 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 inversion 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. * * In the previous implementation the values were 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. * * This range has now been extended to allow values up to 1.1, or 110,000 in * fixed point. * * 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..+1.1 it is sufficient to divide the product by 8; * ceil(121,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
*/ int error = 0;
/* By the argument above overflow should be impossible here, however the * code now simply returns a failure code. The xy subtracts in the arguments * to png_muldiv are *not* checked for overflow because the checks at the * start guarantee they are in the range 0..110000 and png_fixed_point is a * 32-bit signed number.
*/ if (png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 8) == 0) return 1; if (png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 8) == 0) return 1;
denominator = png_fp_sub(left, right, &error); if (error) return 1;
/* Now find the red numerator. */ if (png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 8) == 0) return 1; if (png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 8) == 0) return 1;
/* 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,
png_fp_sub(left, right, &error)) == 0 || error ||
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, 8) == 0) return 1; if (png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 8) == 0) return 1; if (png_muldiv(&green_inverse, xy->whitey, denominator,
png_fp_sub(left, right, &error)) == 0 || error ||
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_fp_sub(png_fp_sub(png_reciprocal(xy->whitey),
png_reciprocal(red_inverse), &error),
png_reciprocal(green_inverse), &error); if (error || blue_scale <= 0) return 1;
/* And fill in the png_XYZ. Again the subtracts are safe because of the * checks on the xy values at the start (the subtracts just calculate the * corresponding z values.)
*/ if (png_muldiv(&XYZ->red_X, xy->redx, PNG_FP_1, red_inverse) == 0) return 1; if (png_muldiv(&XYZ->red_Y, xy->redy, PNG_FP_1, red_inverse) == 0) return 1; if (png_muldiv(&XYZ->red_Z, PNG_FP_1 - xy->redx - xy->redy, PNG_FP_1,
red_inverse) == 0) return 1;
staticint
png_XYZ_normalize(png_XYZ *XYZ)
{
png_int_32 Y, Ytemp;
/* Normalize by scaling so the sum of the end-point Y values is PNG_FP_1. */
Ytemp = XYZ->red_Y; if (png_safe_add(&Ytemp, XYZ->green_Y, XYZ->blue_Y)) return 1;
Y = Ytemp;
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;
}
staticint
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.
*/ staticint
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.
*/ staticint
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;
/* Used to check for an endpoint match against sRGB */ staticconst 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
};
/* 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 */
}
/* 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;
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;
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");
}
/* This is recoverable, but make it unconditionally an app_error on write to * avoid writing invalid ICC profiles into PNG files (i.e., we handle them * on read, with a warning, but on write unless the app turns off * application errors the PNG won't be written.)
*/
png_chunk_report(png_ptr, message,
(colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR);
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)
{ /* 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.)
*/ staticconst 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.)
*/
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.