X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=contrib%2Flibtests%2Fpngvalid.c;h=55d094592a4b4bd28f7fbf2eb0a6be844c034936;hb=7b7ccdda02217a442f273ef9684706ecdef2acc5;hp=e8401c93f8b06ed947a208e6844a784d531ccad6;hpb=361fb099aa967c1618d8071268abab395e67e86a;p=platform%2Fupstream%2Flibpng.git diff --git a/contrib/libtests/pngvalid.c b/contrib/libtests/pngvalid.c index e8401c9..55d0945 100644 --- a/contrib/libtests/pngvalid.c +++ b/contrib/libtests/pngvalid.c @@ -1,8 +1,8 @@ /* pngvalid.c - validate libpng by constructing then reading png files. * - * Last changed in libpng 1.6.10 [March 6, 2014] - * Copyright (c) 2014 Glenn Randers-Pehrson + * Last changed in libpng 1.6.21 [January 15, 2016] + * Copyright (c) 2014-2016 Glenn Randers-Pehrson * Written by John Cunningham Bowler * * This code is released under the libpng license. @@ -34,6 +34,16 @@ # include #endif +#ifndef FE_DIVBYZERO +# define FE_DIVBYZERO 0 +#endif +#ifndef FE_INVALID +# define FE_INVALID 0 +#endif +#ifndef FE_OVERFLOW +# define FE_OVERFLOW 0 +#endif + /* Define the following to use this test against your installed libpng, rather * than the one being built here: */ @@ -64,7 +74,7 @@ (defined(PNG_FIXED_POINT_SUPPORTED) || defined(PNG_FLOATING_POINT_SUPPORTED)) #if PNG_LIBPNG_VER < 10500 -/* This deliberately lacks the PNG_CONST. */ +/* This deliberately lacks the const. */ typedef png_byte *png_const_bytep; /* This is copied from 1.5.1 png.h: */ @@ -106,6 +116,16 @@ typedef png_byte *png_const_bytep; # define png_const_structp png_structp #endif +#ifndef RELEASE_BUILD + /* RELEASE_BUILD is true for releases and release candidates: */ +# define RELEASE_BUILD (PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC) +#endif +#if RELEASE_BUILD +# define debugonly(something) +#else /* !RELEASE_BUILD */ +# define debugonly(something) something +#endif /* !RELEASE_BUILD */ + #include /* For floating point constants */ #include /* For malloc */ #include /* For memcpy, memset */ @@ -147,6 +167,31 @@ define_exception_type(struct png_store*); &(ps)->exception_context #define context(ps,fault) anon_context(ps); png_store *fault +/* This macro returns the number of elements in an array as an (unsigned int), + * it is necessary to avoid the inability of certain versions of GCC to use + * the value of a compile-time constant when performing range checks. It must + * be passed an array name. + */ +#define ARRAY_SIZE(a) ((unsigned int)((sizeof (a))/(sizeof (a)[0]))) + +/* GCC BUG 66447 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66447) requires + * some broken GCC versions to be fixed up to avoid invalid whining about auto + * variables that are *not* changed within the scope of a setjmp being changed. + * + * Feel free to extend the list of broken versions. + */ +#define is_gnu(major,minor)\ + (defined __GNUC__) && __GNUC__ == (major) && __GNUC_MINOR__ == (minor) +#define is_gnu_patch(major,minor,patch)\ + is_gnu(major,minor) && __GNUC_PATCHLEVEL__ == 0 +/* For the moment just do it always; all versions of GCC seem to be broken: */ +#ifdef __GNUC__ + const void * volatile make_volatile_for_gnu; +# define gnu_volatile(x) make_volatile_for_gnu = &x; +#else /* !GNUC broken versions */ +# define gnu_volatile(x) +#endif /* !GNUC broken versions */ + /******************************* UTILITIES ************************************/ /* Error handling is particularly problematic in production code - error * handlers often themselves have bugs which lead to programs that detect @@ -155,7 +200,7 @@ define_exception_type(struct png_store*); * warning messages into buffers that are too small. */ static size_t safecat(char *buffer, size_t bufsize, size_t pos, - PNG_CONST char *cat) + const char *cat) { while (pos < bufsize && cat != NULL && *cat != 0) buffer[pos++] = *cat++; @@ -184,16 +229,16 @@ static size_t safecatd(char *buffer, size_t bufsize, size_t pos, double d, } #endif -static PNG_CONST char invalid[] = "invalid"; -static PNG_CONST char sep[] = ": "; +static const char invalid[] = "invalid"; +static const char sep[] = ": "; -static PNG_CONST char *colour_types[8] = +static const char *colour_types[8] = { "grayscale", invalid, "truecolour", "indexed-colour", "grayscale with alpha", invalid, "truecolour with alpha", invalid }; -#ifdef PNG_READ_SUPPORTED +#ifdef PNG_READ_TRANSFORMS_SUPPORTED /* Convert a double precision value to fixed point. */ static png_fixed_point fix(double d) @@ -241,7 +286,8 @@ make_four_random_bytes(png_uint_32* seed, png_bytep bytes) make_random_bytes(seed, bytes, 4); } -#ifdef PNG_READ_SUPPORTED +#if defined PNG_READ_SUPPORTED || defined PNG_WRITE_tRNS_SUPPORTED ||\ + defined PNG_WRITE_FILTER_SUPPORTED static void randomize(void *pv, size_t size) { @@ -249,35 +295,85 @@ randomize(void *pv, size_t size) make_random_bytes(random_seed, pv, size); } -#define RANDOMIZE(this) randomize(&(this), sizeof (this)) +#define R8(this) randomize(&(this), sizeof (this)) + +static void r16(png_uint_16p p16, size_t count) +{ + size_t i; + + for (i=0; i> 3) & 0x1fU)) #define PALETTE_FROM_ID(id) (((id) >> 8) & 0x1f) -#define INTERLACE_FROM_ID(id) ((int)(((id) >> 13) & 0x3)) +#define INTERLACE_FROM_ID(id) ((png_byte)(((id) >> 13) & 0x3)) #define DO_INTERLACE_FROM_ID(id) ((int)(((id)>>15) & 1)) #define WIDTH_FROM_ID(id) (((id)>>16) & 0xff) #define HEIGHT_FROM_ID(id) (((id)>>24) & 0xff) @@ -298,12 +394,16 @@ standard_name(char *buffer, size_t bufsize, size_t pos, png_byte colour_type, png_uint_32 w, png_uint_32 h, int do_interlace) { pos = safecat(buffer, bufsize, pos, colour_types[colour_type]); - if (npalette > 0) + if (colour_type == 3) /* must have a palette */ { pos = safecat(buffer, bufsize, pos, "["); pos = safecatn(buffer, bufsize, pos, npalette); pos = safecat(buffer, bufsize, pos, "]"); } + + else if (npalette != 0) + pos = safecat(buffer, bufsize, pos, "+tRNS"); + pos = safecat(buffer, bufsize, pos, " "); pos = safecatn(buffer, bufsize, pos, bit_depth); pos = safecat(buffer, bufsize, pos, " bit"); @@ -360,25 +460,32 @@ standard_name_from_id(char *buffer, size_t bufsize, size_t pos, png_uint_32 id) static int next_format(png_bytep colour_type, png_bytep bit_depth, - unsigned int* palette_number, int no_low_depth_gray) + unsigned int* palette_number, int low_depth_gray, int tRNS) { if (*bit_depth == 0) { *colour_type = 0; - if (no_low_depth_gray) - *bit_depth = 8; - else + if (low_depth_gray) *bit_depth = 1; + else + *bit_depth = 8; *palette_number = 0; return 1; } - if (*colour_type == 3) + if (*colour_type < 4/*no alpha channel*/) { - /* Add multiple palettes for colour type 3. */ - if (++*palette_number < PALETTE_COUNT(*bit_depth)) + /* Add multiple palettes for colour type 3, one image with tRNS + * and one without for other non-alpha formats: + */ + unsigned int pn = ++*palette_number; + png_byte ct = *colour_type; + + if (((ct == 0/*GRAY*/ || ct/*RGB*/ == 2) && tRNS && pn < 2) || + (ct == 3/*PALETTE*/ && pn < PALETTE_COUNT(*bit_depth))) return 1; + /* No: next bit depth */ *palette_number = 0; } @@ -423,7 +530,7 @@ next_format(png_bytep colour_type, png_bytep bit_depth, #ifdef PNG_READ_TRANSFORMS_SUPPORTED static unsigned int sample(png_const_bytep row, png_byte colour_type, png_byte bit_depth, - png_uint_32 x, unsigned int sample_index) + png_uint_32 x, unsigned int sample_index, int swap16, int littleendian) { png_uint_32 bit_index, result; @@ -452,11 +559,23 @@ sample(png_const_bytep row, png_byte colour_type, png_byte bit_depth, return result; else if (bit_depth > 8) - return (result << 8) + *++row; + { + if (swap16) + return (*++row << 8) + result; + else + return (result << 8) + *++row; + } - /* Less than 8 bits per sample. */ + /* Less than 8 bits per sample. By default PNG has the big end of + * the egg on the left of the screen, but if littleendian is set + * then the big end is on the right. + */ bit_index &= 7; - return (result >> (8-bit_index-bit_depth)) & ((1U<> bit_index) & ((1U<> 3] & ~destMask; unsigned int sourceByte = fromBuffer[fromIndex >> 3]; /* Don't rely on << or >> supporting '0' here, just in case: */ fromIndex &= 7; - if (fromIndex > 0) sourceByte <<= fromIndex; - if ((toIndex & 7) > 0) sourceByte >>= toIndex & 7; + if (littleendian) + { + if (fromIndex > 0) sourceByte >>= fromIndex; + if ((toIndex & 7) > 0) sourceByte <<= toIndex & 7; + } + + else + { + if (fromIndex > 0) sourceByte <<= fromIndex; + if ((toIndex & 7) > 0) sourceByte >>= toIndex & 7; + } toBuffer[toIndex >> 3] = (png_byte)(destByte | (sourceByte & destMask)); } @@ -501,7 +631,8 @@ pixel_copy(png_bytep toBuffer, png_uint_32 toIndex, * bytes at the end. */ static void -row_copy(png_bytep toBuffer, png_const_bytep fromBuffer, unsigned int bitWidth) +row_copy(png_bytep toBuffer, png_const_bytep fromBuffer, unsigned int bitWidth, + int littleendian) { memcpy(toBuffer, fromBuffer, bitWidth >> 3); @@ -511,10 +642,10 @@ row_copy(png_bytep toBuffer, png_const_bytep fromBuffer, unsigned int bitWidth) toBuffer += bitWidth >> 3; fromBuffer += bitWidth >> 3; - /* The remaining bits are in the top of the byte, the mask is the bits to - * retain. - */ - mask = 0xff >> (bitWidth & 7); + if (littleendian) + mask = 0xff << (bitWidth & 7); + else + mask = 0xff >> (bitWidth & 7); *toBuffer = (png_byte)((*toBuffer & mask) | (*fromBuffer & ~mask)); } } @@ -678,7 +809,7 @@ store_pool_mark(png_bytep mark) make_four_random_bytes(store_seed, mark); } -#ifdef PNG_READ_SUPPORTED +#ifdef PNG_READ_TRANSFORMS_SUPPORTED /* Use this for random 32 bit values; this function makes sure the result is * non-zero. */ @@ -686,7 +817,7 @@ static png_uint_32 random_32(void) { - for(;;) + for (;;) { png_byte mark[4]; png_uint_32 result; @@ -836,7 +967,7 @@ store_storefile(png_store *ps, png_uint_32 id) /* Generate an error message (in the given buffer) */ static size_t store_message(png_store *ps, png_const_structp pp, char *buffer, size_t bufsize, - size_t pos, PNG_CONST char *msg) + size_t pos, const char *msg) { if (pp != NULL && pp == ps->pread) { @@ -954,7 +1085,7 @@ store_warning(png_structp ppIn, png_const_charp message) */ /* Return a single row from the correct image. */ static png_bytep -store_image_row(PNG_CONST png_store* ps, png_const_structp pp, int nImage, +store_image_row(const png_store* ps, png_const_structp pp, int nImage, png_uint_32 y) { png_size_t coffset = (nImage * ps->image_h + y) * (ps->cb_row + 5) + 2; @@ -1058,7 +1189,7 @@ store_ensure_image(png_store *ps, png_const_structp pp, int nImages, #ifdef PNG_READ_SUPPORTED static void -store_image_check(PNG_CONST png_store* ps, png_const_structp pp, int iImage) +store_image_check(const png_store* ps, png_const_structp pp, int iImage) { png_const_bytep image = ps->image; @@ -1275,7 +1406,10 @@ store_current_palette(png_store *ps, int *npalette) * operation.) */ if (ps->current == NULL) + { store_log(ps, ps->pread, "no current stream for palette", 1); + return NULL; + } /* The result may be null if there is no palette. */ *npalette = ps->current->npalette; @@ -1305,7 +1439,7 @@ typedef struct store_memory * all the memory. */ static void -store_pool_error(png_store *ps, png_const_structp pp, PNG_CONST char *msg) +store_pool_error(png_store *ps, png_const_structp pp, const char *msg) { if (pp != NULL) png_error(pp, msg); @@ -1369,7 +1503,7 @@ store_pool_delete(png_store *ps, store_pool *pool) next->next = NULL; fprintf(stderr, "\t%lu bytes @ %p\n", - (unsigned long)next->size, (PNG_CONST void*)(next+1)); + (unsigned long)next->size, (const void*)(next+1)); /* The NULL means this will always return, even if the memory is * corrupted. */ @@ -1524,8 +1658,7 @@ store_write_reset(png_store *ps) * returned libpng structures as destroyed by store_write_reset above. */ static png_structp -set_store_for_write(png_store *ps, png_infopp ppi, - PNG_CONST char * volatile name) +set_store_for_write(png_store *ps, png_infopp ppi, const char *name) { anon_context(ps); @@ -1645,7 +1778,7 @@ store_read_set(png_store *ps, png_uint_32 id) */ static png_structp set_store_for_read(png_store *ps, png_infopp ppi, png_uint_32 id, - PNG_CONST char *name) + const char *name) { /* Set the name for png_error */ safecat(ps->test, sizeof ps->test, 0, name); @@ -1752,6 +1885,7 @@ typedef struct color_encoding } color_encoding; #ifdef PNG_READ_SUPPORTED +#if defined PNG_READ_TRANSFORMS_SUPPORTED && defined PNG_READ_cHRM_SUPPORTED static double chromaticity_x(CIE_color c) { @@ -1765,7 +1899,7 @@ chromaticity_y(CIE_color c) } static CIE_color -white_point(PNG_CONST color_encoding *encoding) +white_point(const color_encoding *encoding) { CIE_color white; @@ -1775,12 +1909,13 @@ white_point(PNG_CONST color_encoding *encoding) return white; } +#endif /* READ_TRANSFORMS && READ_cHRM */ #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED static void normalize_color_encoding(color_encoding *encoding) { - PNG_CONST double whiteY = encoding->red.Y + encoding->green.Y + + const double whiteY = encoding->red.Y + encoding->green.Y + encoding->blue.Y; if (whiteY != 1) @@ -1798,9 +1933,10 @@ normalize_color_encoding(color_encoding *encoding) } #endif +#ifdef PNG_READ_TRANSFORMS_SUPPORTED static size_t safecat_color_encoding(char *buffer, size_t bufsize, size_t pos, - PNG_CONST color_encoding *e, double encoding_gamma) + const color_encoding *e, double encoding_gamma) { if (e != 0) { @@ -1837,6 +1973,7 @@ safecat_color_encoding(char *buffer, size_t bufsize, size_t pos, return pos; } +#endif /* READ_TRANSFORMS */ #endif /* PNG_READ_SUPPORTED */ typedef struct png_modifier @@ -1861,9 +1998,9 @@ typedef struct png_modifier unsigned int ngammas; unsigned int ngamma_tests; /* Number of gamma tests to run*/ double current_gamma; /* 0 if not set */ - PNG_CONST color_encoding *encodings; + const color_encoding *encodings; unsigned int nencodings; - PNG_CONST color_encoding *current_encoding; /* If an encoding has been set */ + const color_encoding *current_encoding; /* If an encoding has been set */ unsigned int encoding_counter; /* For iteration */ int encoding_ignored; /* Something overwrote it */ @@ -1874,7 +2011,7 @@ typedef struct png_modifier unsigned int repeat :1; /* Repeat this transform test. */ unsigned int test_uses_encoding :1; - /* Lowest sbit to test (libpng fails for sbit < 8) */ + /* Lowest sbit to test (pre-1.7 libpng fails for sbit < 8) */ png_byte sbitlow; /* Error control - these are the limits on errors accepted by the gamma tests @@ -1894,6 +2031,8 @@ typedef struct png_modifier * internal check on pngvalid to ensure that the calculated error limits are * not ridiculous; without this it is too easy to make a mistake in pngvalid * that allows any value through. + * + * NOTE: this is not checked in release builds. */ double limit; /* limit on error values, normally 4E-3 */ @@ -1929,6 +2068,7 @@ typedef struct png_modifier /* Run tests on reading with a combination of transforms, */ unsigned int test_transform :1; + unsigned int test_tRNS :1; /* Includes tRNS images */ /* When to use the use_input_precision option, this controls the gamma * validation code checks. If set any value that is within the transformed @@ -1960,6 +2100,16 @@ typedef struct png_modifier unsigned int test_gamma_expand16 :1; unsigned int test_exhaustive :1; + /* Whether or not to run the low-bit-depth grayscale tests. This fails on + * gamma images in some cases because of gross inaccuracies in the grayscale + * gamma handling for low bit depth. + */ + unsigned int test_lbg :1; + unsigned int test_lbg_gamma_threshold :1; + unsigned int test_lbg_gamma_transform :1; + unsigned int test_lbg_gamma_sbit :1; + unsigned int test_lbg_gamma_composition :1; + unsigned int log :1; /* Log max error */ /* Buffer information, the buffer size limits the size of the chunks that can @@ -2012,6 +2162,11 @@ modifier_init(png_modifier *pm) pm->test_standard = 0; pm->test_size = 0; pm->test_transform = 0; +# ifdef PNG_WRITE_tRNS_SUPPORTED + pm->test_tRNS = 1; +# else + pm->test_tRNS = 0; +# endif pm->use_input_precision = 0; pm->use_input_precision_sbit = 0; pm->use_input_precision_16to8 = 0; @@ -2024,6 +2179,11 @@ modifier_init(png_modifier *pm) pm->test_gamma_background = 0; pm->test_gamma_alpha_mode = 0; pm->test_gamma_expand16 = 0; + pm->test_lbg = 1; + pm->test_lbg_gamma_threshold = 1; + pm->test_lbg_gamma_transform = 1; + pm->test_lbg_gamma_sbit = 1; + pm->test_lbg_gamma_composition = 1; pm->test_exhaustive = 0; pm->log = 0; @@ -2057,7 +2217,7 @@ static double digitize(double value, int depth, int do_round) * rounding and 'do_round' should be 1, if it is 0 the digitized value will * be truncated. */ - PNG_CONST unsigned int digitization_factor = (1U << depth) -1; + const unsigned int digitization_factor = (1U << depth) -1; /* Limiting the range is done as a convenience to the caller - it's easier to * do it once here than every time at the call site. @@ -2076,7 +2236,7 @@ static double digitize(double value, int depth, int do_round) #endif /* RGB_TO_GRAY */ #ifdef PNG_READ_GAMMA_SUPPORTED -static double abserr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) +static double abserr(const png_modifier *pm, int in_depth, int out_depth) { /* Absolute error permitted in linear values - affected by the bit depth of * the calculations. @@ -2088,7 +2248,7 @@ static double abserr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) return pm->maxabs8; } -static double calcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) +static double calcerr(const png_modifier *pm, int in_depth, int out_depth) { /* Error in the linear composition arithmetic - only relevant when * composition actually happens (0 < alpha < 1). @@ -2101,7 +2261,7 @@ static double calcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) return pm->maxcalc8; } -static double pcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) +static double pcerr(const png_modifier *pm, int in_depth, int out_depth) { /* Percentage error permitted in the linear values. Note that the specified * value is a percentage but this routine returns a simple number. @@ -2124,7 +2284,7 @@ static double pcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) * The specified parameter does *not* include the base .5 digitization error but * it is added here. */ -static double outerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) +static double outerr(const png_modifier *pm, int in_depth, int out_depth) { /* There is a serious error in the 2 and 4 bit grayscale transform because * the gamma table value (8 bits) is simply shifted, not rounded, so the @@ -2156,7 +2316,7 @@ static double outerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) * rather than raising a warning. This is useful for debugging to track down * exactly what set of parameters cause high error values. */ -static double outlog(PNG_CONST png_modifier *pm, int in_depth, int out_depth) +static double outlog(const png_modifier *pm, int in_depth, int out_depth) { /* The command line parameters are either 8 bit (0..255) or 16 bit (0..65535) * and so must be adjusted for low bit depth grayscale: @@ -2194,7 +2354,7 @@ static double outlog(PNG_CONST png_modifier *pm, int in_depth, int out_depth) * but in the 8 bit calculation case it's actually quantization to a multiple of * 257! */ -static int output_quantization_factor(PNG_CONST png_modifier *pm, int in_depth, +static int output_quantization_factor(const png_modifier *pm, int in_depth, int out_depth) { if (out_depth == 16 && in_depth != 16 && @@ -2258,7 +2418,7 @@ modification_init(png_modification *pmm) #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED static void -modifier_current_encoding(PNG_CONST png_modifier *pm, color_encoding *ce) +modifier_current_encoding(const png_modifier *pm, color_encoding *ce) { if (pm->current_encoding != 0) *ce = *pm->current_encoding; @@ -2270,9 +2430,10 @@ modifier_current_encoding(PNG_CONST png_modifier *pm, color_encoding *ce) } #endif +#ifdef PNG_READ_TRANSFORMS_SUPPORTED static size_t safecat_current_encoding(char *buffer, size_t bufsize, size_t pos, - PNG_CONST png_modifier *pm) + const png_modifier *pm) { pos = safecat_color_encoding(buffer, bufsize, pos, pm->current_encoding, pm->current_gamma); @@ -2282,6 +2443,7 @@ safecat_current_encoding(char *buffer, size_t bufsize, size_t pos, return pos; } +#endif /* Iterate through the usefully testable color encodings. An encoding is one * of: @@ -2301,7 +2463,7 @@ safecat_current_encoding(char *buffer, size_t bufsize, size_t pos, * caller of modifier_reset must reset it at the start of each run of the test! */ static unsigned int -modifier_total_encodings(PNG_CONST png_modifier *pm) +modifier_total_encodings(const png_modifier *pm) { return 1 + /* (1) nothing */ pm->ngammas + /* (2) gamma values to test */ @@ -2417,14 +2579,14 @@ modifier_set_encoding(png_modifier *pm) * assumption below that the first encoding in the list is the one for sRGB. */ static int -modifier_color_encoding_is_sRGB(PNG_CONST png_modifier *pm) +modifier_color_encoding_is_sRGB(const png_modifier *pm) { return pm->current_encoding != 0 && pm->current_encoding == pm->encodings && pm->current_encoding->gamma == pm->current_gamma; } static int -modifier_color_encoding_is_set(PNG_CONST png_modifier *pm) +modifier_color_encoding_is_set(const png_modifier *pm) { return pm->current_gamma != 0; } @@ -2743,7 +2905,7 @@ modifier_progressive_read(png_modifier *pm, png_structp pp, png_infop pi) /* Set up a modifier. */ static png_structp set_modifier_for_read(png_modifier *pm, png_infopp ppi, png_uint_32 id, - PNG_CONST char *name) + const char *name) { /* Do this first so that the modifier fields are cleared even if an error * happens allocating the png_struct. No allocation is done here so no @@ -2803,7 +2965,7 @@ gama_modification_init(gama_modification *me, png_modifier *pm, double gammad) typedef struct chrm_modification { png_modification this; - PNG_CONST color_encoding *encoding; + const color_encoding *encoding; png_fixed_point wx, wy, rx, ry, gx, gy, bx, by; } chrm_modification; @@ -2827,7 +2989,7 @@ chrm_modify(png_modifier *pm, png_modification *me, int add) static void chrm_modification_init(chrm_modification *me, png_modifier *pm, - PNG_CONST color_encoding *encoding) + const color_encoding *encoding) { CIE_color white = white_point(encoding); @@ -3155,12 +3317,57 @@ init_standard_palette(png_store *ps, png_structp pp, png_infop pi, int npalette, for (; i<256; ++i) tRNS[i] = 24; -# ifdef PNG_WRITE_tRNS_SUPPORTED +# ifdef PNG_WRITE_tRNS_SUPPORTED if (j > 0) png_set_tRNS(pp, pi, tRNS, j, 0/*color*/); -# endif +# endif + } +} + +#ifdef PNG_WRITE_tRNS_SUPPORTED +static void +set_random_tRNS(png_structp pp, png_infop pi, const png_byte colour_type, + const int bit_depth) +{ + /* To make this useful the tRNS color needs to match at least one pixel. + * Random values are fine for gray, including the 16-bit case where we know + * that the test image contains all the gray values. For RGB we need more + * method as only 65536 different RGB values are generated. + */ + png_color_16 tRNS; + const png_uint_16 mask = (png_uint_16)((1U << bit_depth)-1); + + R8(tRNS); /* makes unset fields random */ + + if (colour_type & 2/*RGB*/) + { + if (bit_depth == 8) + { + R16(tRNS.red); + R16(tRNS.green); + tRNS.blue = tRNS.red ^ tRNS.green; + tRNS.red &= mask; + tRNS.green &= mask; + tRNS.blue &= mask; + } + + else /* bit_depth == 16 */ + { + R16(tRNS.red); + tRNS.green = (png_uint_16)(tRNS.red * 257); + tRNS.blue = (png_uint_16)(tRNS.green * 17); + } + } + + else + { + R16(tRNS.gray); + tRNS.gray &= mask; } + + png_set_tRNS(pp, pi, NULL, 0, &tRNS); } +#endif /* The number of passes is related to the interlace type. There was no libpng * API to determine this prior to 1.5, so we need an inquiry function: @@ -3411,13 +3618,17 @@ transform_row(png_const_structp pp, png_byte buffer[TRANSFORM_ROWMAX], #ifdef PNG_WRITE_INTERLACING_SUPPORTED # define INTERLACE_LAST PNG_INTERLACE_LAST # define check_interlace_type(type) ((void)(type)) -#else -# define INTERLACE_LAST (PNG_INTERLACE_NONE+1) -# define png_set_interlace_handling(a) (1) - +# define set_write_interlace_handling(pp,type) png_set_interlace_handling(pp) +# define do_own_interlace 0 +#elif PNG_LIBPNG_VER < 10700 +# define set_write_interlace_handling(pp,type) (1) static void -check_interlace_type(int PNG_CONST interlace_type) +check_interlace_type(int const interlace_type) { + /* Prior to 1.7.0 libpng does not support the write of an interlaced image + * unless PNG_WRITE_INTERLACING_SUPPORTED, even with do_interlace so the + * code here does the pixel interlace itself, so: + */ if (interlace_type != PNG_INTERLACE_NONE) { /* This is an internal error - --interlace tests should be skipped, not @@ -3427,17 +3638,107 @@ check_interlace_type(int PNG_CONST interlace_type) exit(99); } } +# define INTERLACE_LAST (PNG_INTERLACE_NONE+1) +# define do_own_interlace 0 +#else /* libpng 1.7+ */ +# define set_write_interlace_handling(pp,type)\ + npasses_from_interlace_type(pp,type) +# define check_interlace_type(type) ((void)(type)) +# define INTERLACE_LAST PNG_INTERLACE_LAST +# define do_own_interlace 1 +#endif /* WRITE_INTERLACING tests */ + +#define CAN_WRITE_INTERLACE\ + PNG_LIBPNG_VER >= 10700 || defined PNG_WRITE_INTERLACING_SUPPORTED + +/* Do the same thing for read interlacing; this controls whether read tests do + * their own de-interlace or use libpng. + */ +#ifdef PNG_READ_INTERLACING_SUPPORTED +# define do_read_interlace 0 +#else /* no libpng read interlace support */ +# define do_read_interlace 1 #endif +/* The following two routines use the PNG interlace support macros from + * png.h to interlace or deinterlace rows. + */ +static void +interlace_row(png_bytep buffer, png_const_bytep imageRow, + unsigned int pixel_size, png_uint_32 w, int pass, int littleendian) +{ + png_uint_32 xin, xout, xstep; + + /* Note that this can, trivially, be optimized to a memcpy on pass 7, the + * code is presented this way to make it easier to understand. In practice + * consult the code in the libpng source to see other ways of doing this. + * + * It is OK for buffer and imageRow to be identical, because 'xin' moves + * faster than 'xout' and we copy up. + */ + xin = PNG_PASS_START_COL(pass); + xstep = 1U< 0) + interlace_row(buffer, buffer, + bit_size(pp, colour_type, bit_depth), w, pass, + 0/*data always bigendian*/); + else + continue; + } +# endif /* do_own_interlace */ + + choose_random_filter(pp, pass == 0 && y == 0); png_write_row(pp, buffer); } } @@ -3568,19 +3901,20 @@ make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, } static void -make_transform_images(png_store *ps) +make_transform_images(png_modifier *pm) { png_byte colour_type = 0; png_byte bit_depth = 0; unsigned int palette_number = 0; /* This is in case of errors. */ - safecat(ps->test, sizeof ps->test, 0, "make standard images"); + safecat(pm->this.test, sizeof pm->this.test, 0, "make standard images"); /* Use next_format to enumerate all the combinations we test, including - * generating multiple low bit depth palette images. + * generating multiple low bit depth palette images. Non-A images (palette + * and direct) are created with and without tRNS chunks. */ - while (next_format(&colour_type, &bit_depth, &palette_number, 0)) + while (next_format(&colour_type, &bit_depth, &palette_number, 1, 1)) { int interlace_type; @@ -3590,59 +3924,13 @@ make_transform_images(png_store *ps) char name[FILE_NAME_SIZE]; standard_name(name, sizeof name, 0, colour_type, bit_depth, - palette_number, interlace_type, 0, 0, 0); - make_transform_image(ps, colour_type, bit_depth, palette_number, + palette_number, interlace_type, 0, 0, do_own_interlace); + make_transform_image(&pm->this, colour_type, bit_depth, palette_number, interlace_type, name); } } } -/* The following two routines use the PNG interlace support macros from - * png.h to interlace or deinterlace rows. - */ -static void -interlace_row(png_bytep buffer, png_const_bytep imageRow, - unsigned int pixel_size, png_uint_32 w, int pass) -{ - png_uint_32 xin, xout, xstep; - - /* Note that this can, trivially, be optimized to a memcpy on pass 7, the - * code is presented this way to make it easier to understand. In practice - * consult the code in the libpng source to see other ways of doing this. - */ - xin = PNG_PASS_START_COL(pass); - xstep = 1U<= PNG_FILTER_VALUE_LAST ? PNG_ALL_FILTERS : nfilter); + { + int filters = 8 << random_mod(PNG_FILTER_VALUE_LAST); + + if (pass == 0 && y == 0 && + (filters < PNG_FILTER_UP || w == 1U)) + filters |= PNG_FILTER_UP; - if (nfilter-- == 0) - nfilter = PNG_FILTER_VALUE_LAST-1; + png_set_filter(pp, 0/*method*/, filters); + } # endif png_write_row(pp, row); @@ -3866,8 +4153,8 @@ make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, } static void -make_size(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo, - int PNG_CONST bdhi) +make_size(png_store* const ps, png_byte const colour_type, int bdlo, + int const bdhi) { for (; bdlo <= bdhi; ++bdlo) { @@ -3890,6 +4177,11 @@ make_size(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo, # ifdef PNG_WRITE_INTERLACING_SUPPORTED make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7, width, height, 0); +# endif +# if CAN_WRITE_INTERLACE + /* 1.7.0 removes the hack that prevented app write of an interlaced + * image if WRITE_INTERLACE was not supported + */ make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7, width, height, 1); # endif @@ -3963,41 +4255,45 @@ sBIT_error_fn(png_structp pp, png_infop pi) png_set_sBIT(pp, pi, &bad); } -static PNG_CONST struct +static const struct { void (*fn)(png_structp, png_infop); - PNG_CONST char *msg; + const char *msg; unsigned int warning :1; /* the error is a warning... */ } error_test[] = { - /* no warnings makes these errors undetectable. */ - { sBIT0_error_fn, "sBIT(0): failed to detect error", 1 }, - { sBIT_error_fn, "sBIT(too big): failed to detect error", 1 }, + /* no warnings makes these errors undetectable prior to 1.7.0 */ + { sBIT0_error_fn, "sBIT(0): failed to detect error", + PNG_LIBPNG_VER < 10700 }, + + { sBIT_error_fn, "sBIT(too big): failed to detect error", + PNG_LIBPNG_VER < 10700 }, }; static void -make_error(png_store* volatile psIn, png_byte PNG_CONST colour_type, +make_error(png_store* const ps, png_byte const colour_type, png_byte bit_depth, int interlace_type, int test, png_const_charp name) { - png_store * volatile ps = psIn; - context(ps, fault); check_interlace_type(interlace_type); Try { - png_structp pp; png_infop pi; - - pp = set_store_for_write(ps, &pi, name); + const png_structp pp = set_store_for_write(ps, &pi, name); + png_uint_32 w, h; + gnu_volatile(pp) if (pp == NULL) Throw ps; - png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth), - transform_height(pp, colour_type, bit_depth), bit_depth, colour_type, - interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + w = transform_width(pp, colour_type, bit_depth); + gnu_volatile(w) + h = transform_height(pp, colour_type, bit_depth); + gnu_volatile(h) + png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (colour_type == 3) /* palette */ init_standard_palette(ps, pp, pi, 1U << bit_depth, 0/*do tRNS*/); @@ -4009,6 +4305,8 @@ make_error(png_store* volatile psIn, png_byte PNG_CONST colour_type, # define exception__env exception_env_1 Try { + gnu_volatile(exception__prev) + /* Expect this to throw: */ ps->expect_error = !error_test[test].warning; ps->expect_warning = error_test[test].warning; @@ -4029,49 +4327,81 @@ make_error(png_store* volatile psIn, png_byte PNG_CONST colour_type, } Catch (fault) - ps = fault; /* expected exit, make sure ps is not clobbered */ + { /* expected exit */ + } #undef exception__prev #undef exception__env /* And clear these flags */ - ps->expect_error = 0; ps->expect_warning = 0; - /* Now write the whole image, just to make sure that the detected, or - * undetected, errro has not created problems inside libpng. - */ - if (png_get_rowbytes(pp, pi) != - transform_rowsize(pp, colour_type, bit_depth)) - png_error(pp, "row size incorrect"); + if (ps->expect_error) + ps->expect_error = 0; else { - png_uint_32 h = transform_height(pp, colour_type, bit_depth); - int npasses = png_set_interlace_handling(pp); - int pass; - - if (npasses != npasses_from_interlace_type(pp, interlace_type)) - png_error(pp, "write: png_set_interlace_handling failed"); + /* Now write the whole image, just to make sure that the detected, or + * undetected, errro has not created problems inside libpng. This + * doesn't work if there was a png_error in png_write_info because that + * can abort before PLTE was written. + */ + if (png_get_rowbytes(pp, pi) != + transform_rowsize(pp, colour_type, bit_depth)) + png_error(pp, "row size incorrect"); - for (pass=0; pass 0) + interlace_row(buffer, buffer, + bit_size(pp, colour_type, bit_depth), w, pass, + 0/*data always bigendian*/); + else + continue; + } +# endif /* do_own_interlace */ + + png_write_row(pp, buffer); + } + } + } /* image writing */ + + png_write_end(pp, pi); + } + + /* The following deletes the file that was just written. */ + store_write_reset(ps); + } Catch(fault) { @@ -4080,8 +4410,8 @@ make_error(png_store* volatile psIn, png_byte PNG_CONST colour_type, } static int -make_errors(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type, - int bdlo, int PNG_CONST bdhi) +make_errors(png_modifier* const pm, png_byte const colour_type, + int bdlo, int const bdhi) { for (; bdlo <= bdhi; ++bdlo) { @@ -4094,9 +4424,9 @@ make_errors(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type, char name[FILE_NAME_SIZE]; standard_name(name, sizeof name, 0, colour_type, 1<this, colour_type, DEPTH(bdlo), interlace_type, test, name); @@ -4141,7 +4471,7 @@ perform_error_test(png_modifier *pm) * then the warning messages the library outputs will probably be garbage. */ static void -perform_formatting_test(png_store *volatile ps) +perform_formatting_test(png_store *ps) { #ifdef PNG_TIME_RFC1123_SUPPORTED /* The handle into the formatting code is the RFC1123 support; this test does @@ -4246,7 +4576,8 @@ typedef struct standard_display png_byte green_sBIT; png_byte blue_sBIT; png_byte alpha_sBIT; - int interlace_type; + png_byte interlace_type; + png_byte filler; /* Output has a filler */ png_uint_32 id; /* Calculated file ID */ png_uint_32 w; /* Width of image */ png_uint_32 h; /* Height of image */ @@ -4255,7 +4586,9 @@ typedef struct standard_display png_uint_32 bit_width; /* Width of output row in bits */ size_t cbRow; /* Bytes in a row of the output image */ int do_interlace; /* Do interlacing internally */ + int littleendian; /* App (row) data is little endian */ int is_transparent; /* Transparency information was present. */ + int has_tRNS; /* color type GRAY or RGB with a tRNS chunk. */ int speed; /* Doing a speed test */ int use_update_info;/* Call update_info, not start_image */ struct @@ -4296,6 +4629,7 @@ standard_display_init(standard_display *dp, png_store* ps, png_uint_32 id, dp->bit_width = 0; dp->cbRow = 0; dp->do_interlace = do_interlace; + dp->littleendian = 0; dp->is_transparent = 0; dp->speed = ps->speed; dp->use_update_info = use_update_info; @@ -4588,14 +4922,14 @@ standard_info_part1(standard_display *dp, png_structp pp, png_infop pi) case 0: dp->transparent.red = dp->transparent.green = dp->transparent.blue = trans_color->gray; - dp->is_transparent = 1; + dp->has_tRNS = 1; break; case 2: dp->transparent.red = trans_color->red; dp->transparent.green = trans_color->green; dp->transparent.blue = trans_color->blue; - dp->is_transparent = 1; + dp->has_tRNS = 1; break; case 3: @@ -4616,8 +4950,19 @@ standard_info_part1(standard_display *dp, png_structp pp, png_infop pi) * turning on interlace handling (if do_interlace is not set.) */ dp->npasses = npasses_from_interlace_type(pp, dp->interlace_type); - if (!dp->do_interlace && dp->npasses != png_set_interlace_handling(pp)) - png_error(pp, "validate: file changed interlace type"); + if (!dp->do_interlace) + { +# ifdef PNG_READ_INTERLACING_SUPPORTED + if (dp->npasses != png_set_interlace_handling(pp)) + png_error(pp, "validate: file changed interlace type"); +# else /* !READ_INTERLACING */ + /* This should never happen: the relevant tests (!do_interlace) should + * not be run. + */ + if (dp->npasses > 1) + png_error(pp, "validate: no libpng interlace support"); +# endif /* !READ_INTERLACING */ + } /* Caller calls png_read_update_info or png_start_read_image now, then calls * part2. @@ -4633,8 +4978,16 @@ standard_info_part2(standard_display *dp, png_const_structp pp, png_const_infop pi, int nImages) { /* Record cbRow now that it can be found. */ - dp->pixel_size = bit_size(pp, png_get_color_type(pp, pi), - png_get_bit_depth(pp, pi)); + { + png_byte ct = png_get_color_type(pp, pi); + png_byte bd = png_get_bit_depth(pp, pi); + + if (bd >= 8 && (ct == PNG_COLOR_TYPE_RGB || ct == PNG_COLOR_TYPE_GRAY) && + dp->filler) + ct |= 4; /* handle filler as faked alpha channel */ + + dp->pixel_size = bit_size(pp, ct, bd); + } dp->bit_width = png_get_image_width(pp, pi) * dp->pixel_size; dp->cbRow = png_get_rowbytes(pp, pi); @@ -4691,7 +5044,7 @@ static void PNGCBAPI progressive_row(png_structp ppIn, png_bytep new_row, png_uint_32 y, int pass) { png_const_structp pp = ppIn; - PNG_CONST standard_display *dp = voidcast(standard_display*, + const standard_display *dp = voidcast(standard_display*, png_get_progressive_ptr(pp)); /* When handling interlacing some rows will be absent in each pass, the @@ -4715,7 +5068,7 @@ progressive_row(png_structp ppIn, png_bytep new_row, png_uint_32 y, int pass) if (pass != png_get_current_pass_number(pp)) png_error(pp, "png_get_current_pass_number is broken"); -#endif +#endif /* USER_TRANSFORM_INFO */ y = PNG_ROW_FROM_PASS_ROW(y, pass); } @@ -4726,38 +5079,39 @@ progressive_row(png_structp ppIn, png_bytep new_row, png_uint_32 y, int pass) row = store_image_row(dp->ps, pp, 0, y); -#ifdef PNG_READ_INTERLACING_SUPPORTED /* Combine the new row into the old: */ +#ifdef PNG_READ_INTERLACING_SUPPORTED if (dp->do_interlace) +#endif /* READ_INTERLACING */ { if (dp->interlace_type == PNG_INTERLACE_ADAM7) - deinterlace_row(row, new_row, dp->pixel_size, dp->w, pass); + deinterlace_row(row, new_row, dp->pixel_size, dp->w, pass, + dp->littleendian); else - row_copy(row, new_row, dp->pixel_size * dp->w); + row_copy(row, new_row, dp->pixel_size * dp->w, dp->littleendian); } +#ifdef PNG_READ_INTERLACING_SUPPORTED else png_progressive_combine_row(pp, row, new_row); #endif /* PNG_READ_INTERLACING_SUPPORTED */ } -#ifdef PNG_READ_INTERLACING_SUPPORTED else if (dp->interlace_type == PNG_INTERLACE_ADAM7 && PNG_ROW_IN_INTERLACE_PASS(y, pass) && PNG_PASS_COLS(dp->w, pass) > 0) png_error(pp, "missing row in progressive de-interlacing"); -#endif /* PNG_READ_INTERLACING_SUPPORTED */ } static void sequential_row(standard_display *dp, png_structp pp, png_infop pi, - PNG_CONST int iImage, PNG_CONST int iDisplay) + const int iImage, const int iDisplay) { - PNG_CONST int npasses = dp->npasses; - PNG_CONST int do_interlace = dp->do_interlace && + const int npasses = dp->npasses; + const int do_interlace = dp->do_interlace && dp->interlace_type == PNG_INTERLACE_ADAM7; - PNG_CONST png_uint_32 height = standard_height(pp, dp->id); - PNG_CONST png_uint_32 width = standard_width(pp, dp->id); - PNG_CONST png_store* ps = dp->ps; + const png_uint_32 height = standard_height(pp, dp->id); + const png_uint_32 width = standard_width(pp, dp->id); + const png_store* ps = dp->ps; int pass; for (pass=0; pass= 0) deinterlace_row(store_image_row(ps, pp, iImage, y), row, - dp->pixel_size, dp->w, pass); + dp->pixel_size, dp->w, pass, dp->littleendian); if (iDisplay >= 0) deinterlace_row(store_image_row(ps, pp, iDisplay, y), display, - dp->pixel_size, dp->w, pass); + dp->pixel_size, dp->w, pass, dp->littleendian); } } else @@ -4943,14 +5297,6 @@ standard_row_validate(standard_display *dp, png_const_structp pp, * In earlier passes 'row' will be partially filled in, with only the pixels * that have been read so far, but 'display' will have those pixels * replicated to fill the unread pixels while reading an interlaced image. -#if PNG_LIBPNG_VER < 10506 - * The side effect inside the libpng sequential reader is that the 'row' - * array retains the correct values for unwritten pixels within the row - * bytes, while the 'display' array gets bits off the end of the image (in - * the last byte) trashed. Unfortunately in the progressive reader the - * row bytes are always trashed, so we always do a pixel_cmp here even though - * a memcmp of all cbRow bytes will succeed for the sequential reader. -#endif */ if (iImage >= 0 && (where = pixel_cmp(std, store_image_row(dp->ps, pp, iImage, y), @@ -4963,19 +5309,12 @@ standard_row_validate(standard_display *dp, png_const_structp pp, png_error(pp, msg); } -#if PNG_LIBPNG_VER < 10506 - /* In this case use pixel_cmp because we need to compare a partial - * byte at the end of the row if the row is not an exact multiple - * of 8 bits wide. (This is fixed in libpng-1.5.6 and pixel_cmp is - * changed to match!) - */ -#endif if (iDisplay >= 0 && (where = pixel_cmp(std, store_image_row(dp->ps, pp, iDisplay, y), dp->bit_width)) != 0) { char msg[64]; - sprintf(msg, "display row[%lu][%d] changed from %.2x to %.2x", + sprintf(msg, "display row[%lu][%d] changed from %.2x to %.2x", (unsigned long)y, where-1, std[where-1], store_image_row(dp->ps, pp, iDisplay, y)[where-1]); png_error(pp, msg); @@ -5020,7 +5359,7 @@ standard_end(png_structp ppIn, png_infop pi) /* A single test run checking the standard image to ensure it is not damaged. */ static void -standard_test(png_store* PNG_CONST psIn, png_uint_32 PNG_CONST id, +standard_test(png_store* const psIn, png_uint_32 const id, int do_interlace, int use_update_info) { standard_display d; @@ -5108,8 +5447,8 @@ standard_test(png_store* PNG_CONST psIn, png_uint_32 PNG_CONST id, } static int -test_standard(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type, - int bdlo, int PNG_CONST bdhi) +test_standard(png_modifier* const pm, png_byte const colour_type, + int bdlo, int const bdhi) { for (; bdlo <= bdhi; ++bdlo) { @@ -5119,7 +5458,7 @@ test_standard(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type, interlace_type < INTERLACE_LAST; ++interlace_type) { standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/, - interlace_type, 0, 0, 0), 0/*do_interlace*/, pm->use_update_info); + interlace_type, 0, 0, 0), do_read_interlace, pm->use_update_info); if (fail(pm)) return 0; @@ -5154,8 +5493,8 @@ perform_standard_test(png_modifier *pm) /********************************** SIZE TESTS ********************************/ static int -test_size(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type, - int bdlo, int PNG_CONST bdhi) +test_size(png_modifier* const pm, png_byte const colour_type, + int bdlo, int const bdhi) { /* Run the tests on each combination. * @@ -5164,8 +5503,10 @@ test_size(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type, * width and height. This is a waste of time in practice, hence the * hinc and winc stuff: */ - static PNG_CONST png_byte hinc[] = {1, 3, 11, 1, 5}; - static PNG_CONST png_byte winc[] = {1, 9, 5, 7, 1}; + static const png_byte hinc[] = {1, 3, 11, 1, 5}; + static const png_byte winc[] = {1, 9, 5, 7, 1}; + const int save_bdlo = bdlo; + for (; bdlo <= bdhi; ++bdlo) { png_uint_32 h, w; @@ -5191,34 +5532,56 @@ test_size(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type, if (fail(pm)) return 0; -# ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Now validate the interlaced read side - do_interlace true, + * in the progressive case this does actually make a difference + * to the code used in the non-interlaced case too. + */ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/, - PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/, + PNG_INTERLACE_NONE, w, h, 0), 1/*do_interlace*/, pm->use_update_info); if (fail(pm)) return 0; +# if CAN_WRITE_INTERLACE + /* Validate the pngvalid code itself: */ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/, - PNG_INTERLACE_ADAM7, w, h, 1), 0/*do_interlace*/, + PNG_INTERLACE_ADAM7, w, h, 1), 1/*do_interlace*/, pm->use_update_info); if (fail(pm)) return 0; # endif + } + } - /* Now validate the interlaced read side - do_interlace true, - * in the progressive case this does actually make a difference - * to the code used in the non-interlaced case too. + /* Now do the tests of libpng interlace handling, after we have made sure + * that the pngvalid version works: + */ + for (bdlo = save_bdlo; bdlo <= bdhi; ++bdlo) + { + png_uint_32 h, w; + + for (h=1; h<=16; h+=hinc[bdlo]) for (w=1; w<=16; w+=winc[bdlo]) + { +# ifdef PNG_READ_INTERLACING_SUPPORTED + /* Test with pngvalid generated interlaced images first; we have + * already verify these are ok (unless pngvalid has self-consistent + * read/write errors, which is unlikely), so this detects errors in the + * read side first: */ +# if CAN_WRITE_INTERLACE standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/, - PNG_INTERLACE_NONE, w, h, 0), 1/*do_interlace*/, + PNG_INTERLACE_ADAM7, w, h, 1), 0/*do_interlace*/, pm->use_update_info); if (fail(pm)) return 0; +# endif +# endif /* READ_INTERLACING */ # ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Test the libpng write side against the pngvalid read side: */ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/, PNG_INTERLACE_ADAM7, w, h, 0), 1/*do_interlace*/, pm->use_update_info); @@ -5226,6 +5589,18 @@ test_size(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type, if (fail(pm)) return 0; # endif + +# ifdef PNG_READ_INTERLACING_SUPPORTED +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Test both together: */ + standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/, + PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/, + pm->use_update_info); + + if (fail(pm)) + return 0; +# endif +# endif /* READ_INTERLACING */ } } @@ -5274,10 +5649,17 @@ typedef struct image_pixel */ unsigned int red, green, blue, alpha; /* For non-palette images. */ unsigned int palette_index; /* For a palette image. */ - png_byte colour_type; /* As in the spec. */ - png_byte bit_depth; /* Defines bit size in row */ - png_byte sample_depth; /* Scale of samples */ - int have_tRNS; /* tRNS chunk may need processing */ + png_byte colour_type; /* As in the spec. */ + png_byte bit_depth; /* Defines bit size in row */ + png_byte sample_depth; /* Scale of samples */ + unsigned int have_tRNS :1; /* tRNS chunk may need processing */ + unsigned int swap_rgb :1; /* RGB swapped to BGR */ + unsigned int alpha_first :1; /* Alpha at start, not end */ + unsigned int alpha_inverted :1; /* Alpha channel inverted */ + unsigned int mono_inverted :1; /* Gray channel inverted */ + unsigned int swap16 :1; /* Byte swap 16-bit components */ + unsigned int littleendian :1; /* High bits on right */ + unsigned int sig_bits :1; /* Pixel shifted (sig bits only) */ /* For checking the code calculates double precision floating point values * along with an error value, accumulated from the transforms. Because an @@ -5285,6 +5667,9 @@ typedef struct image_pixel * up to just less than +/-1 in the scaled value) the *lowest* sBIT for each * channel is stored. This sBIT value is folded in to the stored error value * at the end of the application of the transforms to the pixel. + * + * If sig_bits is set above the red, green, blue and alpha values have been + * scaled so they only contain the significant bits of the component values. */ double redf, greenf, bluef, alphaf; double rede, greene, bluee, alphae; @@ -5293,26 +5678,27 @@ typedef struct image_pixel /* Shared utility function, see below. */ static void -image_pixel_setf(image_pixel *this, unsigned int max) +image_pixel_setf(image_pixel *this, unsigned int rMax, unsigned int gMax, + unsigned int bMax, unsigned int aMax) { - this->redf = this->red / (double)max; - this->greenf = this->green / (double)max; - this->bluef = this->blue / (double)max; - this->alphaf = this->alpha / (double)max; + this->redf = this->red / (double)rMax; + this->greenf = this->green / (double)gMax; + this->bluef = this->blue / (double)bMax; + this->alphaf = this->alpha / (double)aMax; - if (this->red < max) + if (this->red < rMax) this->rede = this->redf * DBL_EPSILON; else this->rede = 0; - if (this->green < max) + if (this->green < gMax) this->greene = this->greenf * DBL_EPSILON; else this->greene = 0; - if (this->blue < max) + if (this->blue < bMax) this->bluee = this->bluef * DBL_EPSILON; else this->bluee = 0; - if (this->alpha < max) + if (this->alpha < aMax) this->alphae = this->alphaf * DBL_EPSILON; else this->alphae = 0; @@ -5324,18 +5710,22 @@ image_pixel_setf(image_pixel *this, unsigned int max) */ static void image_pixel_init(image_pixel *this, png_const_bytep row, png_byte colour_type, - png_byte bit_depth, png_uint_32 x, store_palette palette) + png_byte bit_depth, png_uint_32 x, store_palette palette, + const image_pixel *format /*from pngvalid transform of input*/) { - PNG_CONST png_byte sample_depth = (png_byte)(colour_type == + const png_byte sample_depth = (png_byte)(colour_type == PNG_COLOR_TYPE_PALETTE ? 8 : bit_depth); - PNG_CONST unsigned int max = (1U<swap16); + const int littleendian = (format != 0 && format->littleendian); + const int sig_bits = (format != 0 && format->sig_bits); /* Initially just set everything to the same number and the alpha to opaque. * Note that this currently assumes a simple palette where entry x has colour * rgb(x,x,x)! */ this->palette_index = this->red = this->green = this->blue = - sample(row, colour_type, bit_depth, x, 0); + sample(row, colour_type, bit_depth, x, 0, swap16, littleendian); this->alpha = max; this->red_sBIT = this->green_sBIT = this->blue_sBIT = this->alpha_sBIT = sample_depth; @@ -5346,7 +5736,7 @@ image_pixel_init(image_pixel *this, png_const_bytep row, png_byte colour_type, /* This permits the caller to default to the sample value. */ if (palette != 0) { - PNG_CONST unsigned int i = this->palette_index; + const unsigned int i = this->palette_index; this->red = palette[i].red; this->green = palette[i].green; @@ -5359,21 +5749,52 @@ image_pixel_init(image_pixel *this, png_const_bytep row, png_byte colour_type, { unsigned int i = 0; + if ((colour_type & 4) != 0 && format != 0 && format->alpha_first) + { + this->alpha = this->red; + /* This handles the gray case for 'AG' pixels */ + this->palette_index = this->red = this->green = this->blue = + sample(row, colour_type, bit_depth, x, 1, swap16, littleendian); + i = 1; + } + if (colour_type & 2) { - this->green = sample(row, colour_type, bit_depth, x, 1); - this->blue = sample(row, colour_type, bit_depth, x, 2); - i = 2; + /* Green is second for both BGR and RGB: */ + this->green = sample(row, colour_type, bit_depth, x, ++i, swap16, + littleendian); + + if (format != 0 && format->swap_rgb) /* BGR */ + this->red = sample(row, colour_type, bit_depth, x, ++i, swap16, + littleendian); + else + this->blue = sample(row, colour_type, bit_depth, x, ++i, swap16, + littleendian); + } + + else /* grayscale */ if (format != 0 && format->mono_inverted) + this->red = this->green = this->blue = this->red ^ max; + + if ((colour_type & 4) != 0) /* alpha */ + { + if (format == 0 || !format->alpha_first) + this->alpha = sample(row, colour_type, bit_depth, x, ++i, swap16, + littleendian); + + if (format != 0 && format->alpha_inverted) + this->alpha ^= max; } - if (colour_type & 4) - this->alpha = sample(row, colour_type, bit_depth, x, ++i); } /* Calculate the scaled values, these are simply the values divided by * 'max' and the error is initialized to the double precision epsilon value * from the header file. */ - image_pixel_setf(this, max); + image_pixel_setf(this, + sig_bits ? (1U << format->red_sBIT)-1 : max, + sig_bits ? (1U << format->green_sBIT)-1 : max, + sig_bits ? (1U << format->blue_sBIT)-1 : max, + sig_bits ? (1U << format->alpha_sBIT)-1 : max); /* Store the input information for use in the transforms - these will * modify the information. @@ -5382,8 +5803,18 @@ image_pixel_init(image_pixel *this, png_const_bytep row, png_byte colour_type, this->bit_depth = bit_depth; this->sample_depth = sample_depth; this->have_tRNS = 0; -} - + this->swap_rgb = 0; + this->alpha_first = 0; + this->alpha_inverted = 0; + this->mono_inverted = 0; + this->swap16 = 0; + this->littleendian = 0; + this->sig_bits = 0; +} + +#if defined PNG_READ_EXPAND_SUPPORTED || defined PNG_READ_GRAY_TO_RGB_SUPPORTED\ + || defined PNG_READ_EXPAND_SUPPORTED || defined PNG_READ_EXPAND_16_SUPPORTED\ + || defined PNG_READ_BACKGROUND_SUPPORTED /* Convert a palette image to an rgb image. This necessarily converts the tRNS * chunk at the same time, because the tRNS will be in palette form. The way * palette validation works means that the original palette is never updated, @@ -5413,10 +5844,14 @@ image_pixel_convert_PLTE(image_pixel *this) /* Add an alpha channel; this will import the tRNS information because tRNS is * not valid in an alpha image. The bit depth will invariably be set to at - * least 8. Palette images will be converted to alpha (using the above API). + * least 8 prior to 1.7.0. Palette images will be converted to alpha (using + * the above API). With png_set_background the alpha channel is never expanded + * but this routine is used by pngvalid to simplify code; 'for_background' + * records this. */ static void -image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display) +image_pixel_add_alpha(image_pixel *this, const standard_display *display, + int for_background) { if (this->colour_type == PNG_COLOR_TYPE_PALETTE) image_pixel_convert_PLTE(this); @@ -5425,11 +5860,21 @@ image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display) { if (this->colour_type == PNG_COLOR_TYPE_GRAY) { - if (this->bit_depth < 8) - this->bit_depth = 8; +# if PNG_LIBPNG_VER < 10700 + if (!for_background && this->bit_depth < 8) + this->bit_depth = this->sample_depth = 8; +# endif if (this->have_tRNS) { + /* After 1.7 the expansion of bit depth only happens if there is a + * tRNS chunk to expand at this point. + */ +# if PNG_LIBPNG_VER >= 10700 + if (!for_background && this->bit_depth < 8) + this->bit_depth = this->sample_depth = 8; +# endif + this->have_tRNS = 0; /* Check the input, original, channel value here against the @@ -5461,9 +5906,11 @@ image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display) this->alphaf = 0; else this->alphaf = 1; - - this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA; } + else + this->alphaf = 1; + + this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA; } /* The error in the alpha is zero and the sBIT value comes from the @@ -5473,18 +5920,19 @@ image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display) this->alpha_sBIT = display->alpha_sBIT; } } +#endif /* transforms that need image_pixel_add_alpha */ struct transform_display; typedef struct image_transform { /* The name of this transform: a string. */ - PNG_CONST char *name; + const char *name; /* Each transform can be disabled from the command line: */ int enable; /* The global list of transforms; read only. */ - struct image_transform *PNG_CONST list; + struct image_transform *const list; /* The global count of the number of times this transform has been set on an * image. @@ -5497,7 +5945,7 @@ typedef struct image_transform /* The next transform in the list, each transform must call its own next * transform after it has processed the pixel successfully. */ - PNG_CONST struct image_transform *next; + const struct image_transform *next; /* A single transform for the image, expressed as a series of function * callbacks and some space for values. @@ -5505,12 +5953,12 @@ typedef struct image_transform * First a callback to add any required modifications to the png_modifier; * this gets called just before the modifier is set up for read. */ - void (*ini)(PNG_CONST struct image_transform *this, + void (*ini)(const struct image_transform *this, struct transform_display *that); /* And a callback to set the transform on the current png_read_struct: */ - void (*set)(PNG_CONST struct image_transform *this, + void (*set)(const struct image_transform *this, struct transform_display *that, png_structp pp, png_infop pi); /* Then a transform that takes an input pixel in one PNG format or another @@ -5519,8 +5967,8 @@ typedef struct image_transform * in the libpng implementation!) The png_structp is solely to allow error * reporting via png_error and png_warning. */ - void (*mod)(PNG_CONST struct image_transform *this, image_pixel *that, - png_const_structp pp, PNG_CONST struct transform_display *display); + void (*mod)(const struct image_transform *this, image_pixel *that, + png_const_structp pp, const struct transform_display *display); /* Add this transform to the list and return true if the transform is * meaningful for this colour type and bit depth - if false then the @@ -5528,7 +5976,7 @@ typedef struct image_transform * point running it. */ int (*add)(struct image_transform *this, - PNG_CONST struct image_transform **that, png_byte colour_type, + const struct image_transform **that, png_byte colour_type, png_byte bit_depth); } image_transform; @@ -5538,11 +5986,13 @@ typedef struct transform_display /* Parameters */ png_modifier* pm; - PNG_CONST image_transform* transform_list; + const image_transform* transform_list; + unsigned int max_gamma_8; /* Local variables */ png_byte output_colour_type; png_byte output_bit_depth; + png_byte unpacked; /* Modifications (not necessarily used.) */ gama_modification gama_mod; @@ -5579,7 +6029,7 @@ transform_set_encoding(transform_display *this) /* Three functions to end the list: */ static void -image_transform_ini_end(PNG_CONST image_transform *this, +image_transform_ini_end(const image_transform *this, transform_display *that) { UNUSED(this) @@ -5587,7 +6037,7 @@ image_transform_ini_end(PNG_CONST image_transform *this, } static void -image_transform_set_end(PNG_CONST image_transform *this, +image_transform_set_end(const image_transform *this, transform_display *that, png_structp pp, png_infop pi) { UNUSED(this) @@ -5614,10 +6064,11 @@ sample_scale(double sample_value, unsigned int scale) } static void -image_transform_mod_end(PNG_CONST image_transform *this, image_pixel *that, - png_const_structp pp, PNG_CONST transform_display *display) +image_transform_mod_end(const image_transform *this, image_pixel *that, + png_const_structp pp, const transform_display *display) { - PNG_CONST unsigned int scale = (1U<sample_depth)-1; + const unsigned int scale = (1U<sample_depth)-1; + const int sig_bits = that->sig_bits; UNUSED(this) UNUSED(pp) @@ -5632,6 +6083,13 @@ image_transform_mod_end(PNG_CONST image_transform *this, image_pixel *that, */ that->red = sample_scale(that->redf, scale); + /* This is a bit bogus; really the above calculation should use the red_sBIT + * value, not sample_depth, but because libpng does png_set_shift by just + * shifting the bits we get errors if we don't do it the same way. + */ + if (sig_bits && that->red_sBIT < that->sample_depth) + that->red >>= that->sample_depth - that->red_sBIT; + /* The error value is increased, at the end, according to the lowest sBIT * value seen. Common sense tells us that the intermediate integer * representations are no more accurate than +/- 0.5 in the integral values, @@ -5647,7 +6105,13 @@ image_transform_mod_end(PNG_CONST image_transform *this, image_pixel *that, if (that->colour_type & PNG_COLOR_MASK_COLOR) { that->green = sample_scale(that->greenf, scale); + if (sig_bits && that->green_sBIT < that->sample_depth) + that->green >>= that->sample_depth - that->green_sBIT; + that->blue = sample_scale(that->bluef, scale); + if (sig_bits && that->blue_sBIT < that->sample_depth) + that->blue >>= that->sample_depth - that->blue_sBIT; + that->greene += 1./(2*((1U<green_sBIT)-1)); that->bluee += 1./(2*((1U<blue_sBIT)-1)); } @@ -5667,9 +6131,12 @@ image_transform_mod_end(PNG_CONST image_transform *this, image_pixel *that, else { that->alpha = scale; /* opaque */ - that->alpha = 1; /* Override this. */ + that->alphaf = 1; /* Override this. */ that->alphae = 0; /* It's exact ;-) */ } + + if (sig_bits && that->alpha_sBIT < that->sample_depth) + that->alpha >>= that->sample_depth - that->alpha_sBIT; } /* Static 'end' structure: */ @@ -5692,21 +6159,23 @@ static image_transform image_transform_end = */ static void transform_display_init(transform_display *dp, png_modifier *pm, png_uint_32 id, - PNG_CONST image_transform *transform_list) + const image_transform *transform_list) { memset(dp, 0, sizeof *dp); /* Standard fields */ - standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/, + standard_display_init(&dp->this, &pm->this, id, do_read_interlace, pm->use_update_info); /* Parameter fields */ dp->pm = pm; dp->transform_list = transform_list; + dp->max_gamma_8 = 16; /* Local variable fields */ dp->output_colour_type = 255; /* invalid */ dp->output_bit_depth = 255; /* invalid */ + dp->unpacked = 0; /* not unpacked */ } static void @@ -5734,6 +6203,14 @@ transform_info_imp(transform_display *dp, png_structp pp, png_infop pi) dp->output_colour_type = png_get_color_type(pp, pi); dp->output_bit_depth = png_get_bit_depth(pp, pi); + /* If png_set_filler is in action then fake the output color type to include + * an alpha channel where appropriate. + */ + if (dp->output_bit_depth >= 8 && + (dp->output_colour_type == PNG_COLOR_TYPE_RGB || + dp->output_colour_type == PNG_COLOR_TYPE_GRAY) && dp->this.filler) + dp->output_colour_type |= 4; + /* Validate the combination of colour type and bit depth that we are getting * out of libpng; the semantics of something not in the PNG spec are, at * best, unclear. @@ -5768,7 +6245,8 @@ transform_info_imp(transform_display *dp, png_structp pp, png_infop pi) } /* Use a test pixel to check that the output agrees with what we expect - - * this avoids running the whole test if the output is unexpected. + * this avoids running the whole test if the output is unexpected. This also + * checks for internal errors. */ { image_pixel test_pixel; @@ -5783,7 +6261,7 @@ transform_info_imp(transform_display *dp, png_structp pp, png_infop pi) /* Don't need sBIT here, but it must be set to non-zero to avoid * arithmetic overflows. */ - test_pixel.have_tRNS = dp->this.is_transparent; + test_pixel.have_tRNS = dp->this.is_transparent != 0; test_pixel.red_sBIT = test_pixel.green_sBIT = test_pixel.blue_sBIT = test_pixel.alpha_sBIT = test_pixel.sample_depth; @@ -5814,22 +6292,41 @@ transform_info_imp(transform_display *dp, png_structp pp, png_infop pi) } /* If both bit depth and colour type are correct check the sample depth. - * I believe these are both internal errors. */ - if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE) + if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE && + test_pixel.sample_depth != 8) /* oops - internal error! */ + png_error(pp, "pngvalid: internal: palette sample depth not 8"); + else if (dp->unpacked && test_pixel.bit_depth != 8) + png_error(pp, "pngvalid: internal: bad unpacked pixel depth"); + else if (!dp->unpacked && test_pixel.colour_type != PNG_COLOR_TYPE_PALETTE + && test_pixel.bit_depth != test_pixel.sample_depth) { - if (test_pixel.sample_depth != 8) /* oops - internal error! */ - png_error(pp, "pngvalid: internal: palette sample depth not 8"); + char message[128]; + size_t pos = safecat(message, sizeof message, 0, + "internal: sample depth "); + + /* Because unless something has set 'unpacked' or the image is palette + * mapped we expect the transform to keep sample depth and bit depth + * the same. + */ + pos = safecatn(message, sizeof message, pos, test_pixel.sample_depth); + pos = safecat(message, sizeof message, pos, " expected "); + pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth); + + png_error(pp, message); } - else if (test_pixel.sample_depth != dp->output_bit_depth) + else if (test_pixel.bit_depth != dp->output_bit_depth) { + /* This could be a libpng error too; libpng has not produced what we + * expect for the output bit depth. + */ char message[128]; size_t pos = safecat(message, sizeof message, 0, - "internal: sample depth "); + "internal: bit depth "); pos = safecatn(message, sizeof message, pos, dp->output_bit_depth); pos = safecat(message, sizeof message, pos, " expected "); - pos = safecatn(message, sizeof message, pos, test_pixel.sample_depth); + pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth); png_error(pp, message); } @@ -5847,7 +6344,7 @@ static void transform_range_check(png_const_structp pp, unsigned int r, unsigned int g, unsigned int b, unsigned int a, unsigned int in_digitized, double in, unsigned int out, png_byte sample_depth, double err, double limit, - PNG_CONST char *name, double digitization_error) + const char *name, double digitization_error) { /* Compare the scaled, digitzed, values of our local calculation (in+-err) * with the digitized values libpng produced; 'sample_depth' is the actual @@ -5858,7 +6355,7 @@ transform_range_check(png_const_structp pp, unsigned int r, unsigned int g, unsigned int max = (1U< limit || !(out >= in_min && out <= in_max)) + if (debugonly(err > limit ||) !(out >= in_min && out <= in_max)) { char message[256]; size_t pos; @@ -5884,6 +6381,8 @@ transform_range_check(png_const_structp pp, unsigned int r, unsigned int g, png_error(pp, message); } + + UNUSED(limit) } static void @@ -5891,20 +6390,20 @@ transform_image_validate(transform_display *dp, png_const_structp pp, png_infop pi) { /* Constants for the loop below: */ - PNG_CONST png_store* PNG_CONST ps = dp->this.ps; - PNG_CONST png_byte in_ct = dp->this.colour_type; - PNG_CONST png_byte in_bd = dp->this.bit_depth; - PNG_CONST png_uint_32 w = dp->this.w; - PNG_CONST png_uint_32 h = dp->this.h; - PNG_CONST png_byte out_ct = dp->output_colour_type; - PNG_CONST png_byte out_bd = dp->output_bit_depth; - PNG_CONST png_byte sample_depth = (png_byte)(out_ct == + const png_store* const ps = dp->this.ps; + const png_byte in_ct = dp->this.colour_type; + const png_byte in_bd = dp->this.bit_depth; + const png_uint_32 w = dp->this.w; + const png_uint_32 h = dp->this.h; + const png_byte out_ct = dp->output_colour_type; + const png_byte out_bd = dp->output_bit_depth; + const png_byte sample_depth = (png_byte)(out_ct == PNG_COLOR_TYPE_PALETTE ? 8 : out_bd); - PNG_CONST png_byte red_sBIT = dp->this.red_sBIT; - PNG_CONST png_byte green_sBIT = dp->this.green_sBIT; - PNG_CONST png_byte blue_sBIT = dp->this.blue_sBIT; - PNG_CONST png_byte alpha_sBIT = dp->this.alpha_sBIT; - PNG_CONST int have_tRNS = dp->this.is_transparent; + const png_byte red_sBIT = dp->this.red_sBIT; + const png_byte green_sBIT = dp->this.green_sBIT; + const png_byte blue_sBIT = dp->this.blue_sBIT; + const png_byte alpha_sBIT = dp->this.alpha_sBIT; + const int have_tRNS = dp->this.is_transparent; double digitization_error; store_palette out_palette; @@ -5959,7 +6458,7 @@ transform_image_validate(transform_display *dp, png_const_structp pp, for (y=0; ythis.palette); + image_pixel_init(&in_pixel, std, in_ct, in_bd, x, dp->this.palette, + NULL); in_pixel.red_sBIT = red_sBIT; in_pixel.green_sBIT = green_sBIT; in_pixel.blue_sBIT = blue_sBIT; in_pixel.alpha_sBIT = alpha_sBIT; - in_pixel.have_tRNS = have_tRNS; + in_pixel.have_tRNS = have_tRNS != 0; /* For error detection, below. */ r = in_pixel.red; @@ -5990,12 +6490,18 @@ transform_image_validate(transform_display *dp, png_const_structp pp, b = in_pixel.blue; a = in_pixel.alpha; + /* This applies the transforms to the input data, including output + * format operations which must be used when reading the output + * pixel that libpng produces. + */ dp->transform_list->mod(dp->transform_list, &in_pixel, pp, dp); /* Read the output pixel and compare it to what we got, we don't - * use the error field here, so no need to update sBIT. + * use the error field here, so no need to update sBIT. in_pixel + * says whether we expect libpng to change the output format. */ - image_pixel_init(&out_pixel, pRow, out_ct, out_bd, x, out_palette); + image_pixel_init(&out_pixel, pRow, out_ct, out_bd, x, out_palette, + &in_pixel); /* We don't expect changes to the index here even if the bit depth is * changed. @@ -6058,8 +6564,8 @@ transform_end(png_structp ppIn, png_infop pi) /* A single test run. */ static void -transform_test(png_modifier *pmIn, PNG_CONST png_uint_32 idIn, - PNG_CONST image_transform* transform_listIn, PNG_CONST char * volatile name) +transform_test(png_modifier *pmIn, const png_uint_32 idIn, + const image_transform* transform_listIn, const char * const name) { transform_display d; context(&pmIn->this, fault); @@ -6160,8 +6666,11 @@ static image_transform ITSTRUCT(name) =\ #define PT ITSTRUCT(end) /* stores the previous transform */ /* To save code: */ -static void -image_transform_default_ini(PNG_CONST image_transform *this, +extern void image_transform_default_ini(const image_transform *this, + transform_display *that); /* silence GCC warnings */ + +void /* private, but almost always needed */ +image_transform_default_ini(const image_transform *this, transform_display *that) { this->next->ini(this->next, that); @@ -6170,7 +6679,7 @@ image_transform_default_ini(PNG_CONST image_transform *this, #ifdef PNG_READ_BACKGROUND_SUPPORTED static int image_transform_default_add(image_transform *this, - PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth) + const image_transform **that, png_byte colour_type, png_byte bit_depth) { UNUSED(colour_type) UNUSED(bit_depth) @@ -6185,7 +6694,7 @@ image_transform_default_add(image_transform *this, #ifdef PNG_READ_EXPAND_SUPPORTED /* png_set_palette_to_rgb */ static void -image_transform_png_set_palette_to_rgb_set(PNG_CONST image_transform *this, +image_transform_png_set_palette_to_rgb_set(const image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_palette_to_rgb(pp); @@ -6193,9 +6702,9 @@ image_transform_png_set_palette_to_rgb_set(PNG_CONST image_transform *this, } static void -image_transform_png_set_palette_to_rgb_mod(PNG_CONST image_transform *this, +image_transform_png_set_palette_to_rgb_mod(const image_transform *this, image_pixel *that, png_const_structp pp, - PNG_CONST transform_display *display) + const transform_display *display) { if (that->colour_type == PNG_COLOR_TYPE_PALETTE) image_pixel_convert_PLTE(that); @@ -6205,7 +6714,7 @@ image_transform_png_set_palette_to_rgb_mod(PNG_CONST image_transform *this, static int image_transform_png_set_palette_to_rgb_add(image_transform *this, - PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth) + const image_transform **that, png_byte colour_type, png_byte bit_depth) { UNUSED(bit_depth) @@ -6223,28 +6732,42 @@ IT(palette_to_rgb); #ifdef PNG_READ_EXPAND_SUPPORTED /* png_set_tRNS_to_alpha */ static void -image_transform_png_set_tRNS_to_alpha_set(PNG_CONST image_transform *this, +image_transform_png_set_tRNS_to_alpha_set(const image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_tRNS_to_alpha(pp); + + /* If there was a tRNS chunk that would get expanded and add an alpha + * channel is_transparent must be updated: + */ + if (that->this.has_tRNS) + that->this.is_transparent = 1; + this->next->set(this->next, that, pp, pi); } static void -image_transform_png_set_tRNS_to_alpha_mod(PNG_CONST image_transform *this, +image_transform_png_set_tRNS_to_alpha_mod(const image_transform *this, image_pixel *that, png_const_structp pp, - PNG_CONST transform_display *display) + const transform_display *display) { +#if PNG_LIBPNG_VER < 10700 /* LIBPNG BUG: this always forces palette images to RGB. */ if (that->colour_type == PNG_COLOR_TYPE_PALETTE) image_pixel_convert_PLTE(that); +#endif /* This effectively does an 'expand' only if there is some transparency to * convert to an alpha channel. */ if (that->have_tRNS) - image_pixel_add_alpha(that, &display->this); +# if PNG_LIBPNG_VER >= 10700 + if (that->colour_type != PNG_COLOR_TYPE_PALETTE && + (that->colour_type & PNG_COLOR_MASK_ALPHA) == 0) +# endif + image_pixel_add_alpha(that, &display->this, 0/*!for background*/); +#if PNG_LIBPNG_VER < 10700 /* LIBPNG BUG: otherwise libpng still expands to 8 bits! */ else { @@ -6253,13 +6776,14 @@ image_transform_png_set_tRNS_to_alpha_mod(PNG_CONST image_transform *this, if (that->sample_depth < 8) that->sample_depth = 8; } +#endif this->next->mod(this->next, that, pp, display); } static int image_transform_png_set_tRNS_to_alpha_add(image_transform *this, - PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth) + const image_transform **that, png_byte colour_type, png_byte bit_depth) { UNUSED(bit_depth) @@ -6268,9 +6792,14 @@ image_transform_png_set_tRNS_to_alpha_add(image_transform *this, /* We don't know yet whether there will be a tRNS chunk, but we know that * this transformation should do nothing if there already is an alpha - * channel. + * channel. In addition, after the bug fix in 1.7.0, there is no longer + * any action on a palette image. */ - return (colour_type & PNG_COLOR_MASK_ALPHA) == 0; + return +# if PNG_LIBPNG_VER >= 10700 + colour_type != PNG_COLOR_TYPE_PALETTE && +# endif + (colour_type & PNG_COLOR_MASK_ALPHA) == 0; } IT(tRNS_to_alpha); @@ -6281,17 +6810,18 @@ IT(tRNS_to_alpha); #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED /* png_set_gray_to_rgb */ static void -image_transform_png_set_gray_to_rgb_set(PNG_CONST image_transform *this, +image_transform_png_set_gray_to_rgb_set(const image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_gray_to_rgb(pp); + /* NOTE: this doesn't result in tRNS expansion. */ this->next->set(this->next, that, pp, pi); } static void -image_transform_png_set_gray_to_rgb_mod(PNG_CONST image_transform *this, +image_transform_png_set_gray_to_rgb_mod(const image_transform *this, image_pixel *that, png_const_structp pp, - PNG_CONST transform_display *display) + const transform_display *display) { /* NOTE: we can actually pend the tRNS processing at this point because we * can correctly recognize the original pixel value even though we have @@ -6299,7 +6829,7 @@ image_transform_png_set_gray_to_rgb_mod(PNG_CONST image_transform *this, * doesn't do this, so we don't either. */ if ((that->colour_type & PNG_COLOR_MASK_COLOR) == 0 && that->have_tRNS) - image_pixel_add_alpha(that, &display->this); + image_pixel_add_alpha(that, &display->this, 0/*!for background*/); /* Simply expand the bit depth and alter the colour type as required. */ if (that->colour_type == PNG_COLOR_TYPE_GRAY) @@ -6322,7 +6852,7 @@ image_transform_png_set_gray_to_rgb_mod(PNG_CONST image_transform *this, static int image_transform_png_set_gray_to_rgb_add(image_transform *this, - PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth) + const image_transform **that, png_byte colour_type, png_byte bit_depth) { UNUSED(bit_depth) @@ -6340,17 +6870,21 @@ IT(gray_to_rgb); #ifdef PNG_READ_EXPAND_SUPPORTED /* png_set_expand */ static void -image_transform_png_set_expand_set(PNG_CONST image_transform *this, +image_transform_png_set_expand_set(const image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_expand(pp); + + if (that->this.has_tRNS) + that->this.is_transparent = 1; + this->next->set(this->next, that, pp, pi); } static void -image_transform_png_set_expand_mod(PNG_CONST image_transform *this, +image_transform_png_set_expand_mod(const image_transform *this, image_pixel *that, png_const_structp pp, - PNG_CONST transform_display *display) + const transform_display *display) { /* The general expand case depends on what the colour type is: */ if (that->colour_type == PNG_COLOR_TYPE_PALETTE) @@ -6359,14 +6893,14 @@ image_transform_png_set_expand_mod(PNG_CONST image_transform *this, that->sample_depth = that->bit_depth = 8; if (that->have_tRNS) - image_pixel_add_alpha(that, &display->this); + image_pixel_add_alpha(that, &display->this, 0/*!for background*/); this->next->mod(this->next, that, pp, display); } static int image_transform_png_set_expand_add(image_transform *this, - PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth) + const image_transform **that, png_byte colour_type, png_byte bit_depth) { UNUSED(bit_depth) @@ -6386,31 +6920,53 @@ IT(expand); #ifdef PNG_READ_EXPAND_SUPPORTED /* png_set_expand_gray_1_2_4_to_8 - * LIBPNG BUG: this just does an 'expand' + * Pre 1.7.0 LIBPNG BUG: this just does an 'expand' */ static void image_transform_png_set_expand_gray_1_2_4_to_8_set( - PNG_CONST image_transform *this, transform_display *that, png_structp pp, + const image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_expand_gray_1_2_4_to_8(pp); + /* NOTE: don't expect this to expand tRNS */ this->next->set(this->next, that, pp, pi); } static void image_transform_png_set_expand_gray_1_2_4_to_8_mod( - PNG_CONST image_transform *this, image_pixel *that, png_const_structp pp, - PNG_CONST transform_display *display) + const image_transform *this, image_pixel *that, png_const_structp pp, + const transform_display *display) { +#if PNG_LIBPNG_VER < 10700 image_transform_png_set_expand_mod(this, that, pp, display); +#else + /* Only expand grayscale of bit depth less than 8: */ + if (that->colour_type == PNG_COLOR_TYPE_GRAY && + that->bit_depth < 8) + that->sample_depth = that->bit_depth = 8; + + this->next->mod(this->next, that, pp, display); +#endif /* 1.7 or later */ } static int image_transform_png_set_expand_gray_1_2_4_to_8_add(image_transform *this, - PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth) + const image_transform **that, png_byte colour_type, png_byte bit_depth) { +#if PNG_LIBPNG_VER < 10700 return image_transform_png_set_expand_add(this, that, colour_type, bit_depth); +#else + UNUSED(bit_depth) + + this->next = *that; + *that = this; + + /* This should do nothing unless the color type is gray and the bit depth is + * less than 8: + */ + return colour_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8; +#endif /* 1.7 or later */ } IT(expand_gray_1_2_4_to_8); @@ -6421,17 +6977,24 @@ IT(expand_gray_1_2_4_to_8); #ifdef PNG_READ_EXPAND_16_SUPPORTED /* png_set_expand_16 */ static void -image_transform_png_set_expand_16_set(PNG_CONST image_transform *this, +image_transform_png_set_expand_16_set(const image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_expand_16(pp); + + /* NOTE: prior to 1.7 libpng does SET_EXPAND as well, so tRNS is expanded. */ +# if PNG_LIBPNG_VER < 10700 + if (that->this.has_tRNS) + that->this.is_transparent = 1; +# endif + this->next->set(this->next, that, pp, pi); } static void -image_transform_png_set_expand_16_mod(PNG_CONST image_transform *this, +image_transform_png_set_expand_16_mod(const image_transform *this, image_pixel *that, png_const_structp pp, - PNG_CONST transform_display *display) + const transform_display *display) { /* Expect expand_16 to expand everything to 16 bits as a result of also * causing 'expand' to happen. @@ -6440,7 +7003,7 @@ image_transform_png_set_expand_16_mod(PNG_CONST image_transform *this, image_pixel_convert_PLTE(that); if (that->have_tRNS) - image_pixel_add_alpha(that, &display->this); + image_pixel_add_alpha(that, &display->this, 0/*!for background*/); if (that->bit_depth < 16) that->sample_depth = that->bit_depth = 16; @@ -6450,7 +7013,7 @@ image_transform_png_set_expand_16_mod(PNG_CONST image_transform *this, static int image_transform_png_set_expand_16_add(image_transform *this, - PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth) + const image_transform **that, png_byte colour_type, png_byte bit_depth) { UNUSED(colour_type) @@ -6469,17 +7032,21 @@ IT(expand_16); #ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED /* API added in 1.5.4 */ /* png_set_scale_16 */ static void -image_transform_png_set_scale_16_set(PNG_CONST image_transform *this, +image_transform_png_set_scale_16_set(const image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_scale_16(pp); +# if PNG_LIBPNG_VER < 10700 + /* libpng will limit the gamma table size: */ + that->max_gamma_8 = PNG_MAX_GAMMA_8; +# endif this->next->set(this->next, that, pp, pi); } static void -image_transform_png_set_scale_16_mod(PNG_CONST image_transform *this, +image_transform_png_set_scale_16_mod(const image_transform *this, image_pixel *that, png_const_structp pp, - PNG_CONST transform_display *display) + const transform_display *display) { if (that->bit_depth == 16) { @@ -6495,7 +7062,7 @@ image_transform_png_set_scale_16_mod(PNG_CONST image_transform *this, static int image_transform_png_set_scale_16_add(image_transform *this, - PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth) + const image_transform **that, png_byte colour_type, png_byte bit_depth) { UNUSED(colour_type) @@ -6513,17 +7080,21 @@ IT(scale_16); #ifdef PNG_READ_16_TO_8_SUPPORTED /* the default before 1.5.4 */ /* png_set_strip_16 */ static void -image_transform_png_set_strip_16_set(PNG_CONST image_transform *this, +image_transform_png_set_strip_16_set(const image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_strip_16(pp); +# if PNG_LIBPNG_VER < 10700 + /* libpng will limit the gamma table size: */ + that->max_gamma_8 = PNG_MAX_GAMMA_8; +# endif this->next->set(this->next, that, pp, pi); } static void -image_transform_png_set_strip_16_mod(PNG_CONST image_transform *this, +image_transform_png_set_strip_16_mod(const image_transform *this, image_pixel *that, png_const_structp pp, - PNG_CONST transform_display *display) + const transform_display *display) { if (that->bit_depth == 16) { @@ -6548,7 +7119,7 @@ image_transform_png_set_strip_16_mod(PNG_CONST image_transform *this, * png_set_scale_16 API in 1.5.4 (but 1.5.4+ always defines the above!) */ { - PNG_CONST double d = (255-128.5)/65535; + const double d = (255-128.5)/65535; that->rede += d; that->greene += d; that->bluee += d; @@ -6562,7 +7133,7 @@ image_transform_png_set_strip_16_mod(PNG_CONST image_transform *this, static int image_transform_png_set_strip_16_add(image_transform *this, - PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth) + const image_transform **that, png_byte colour_type, png_byte bit_depth) { UNUSED(colour_type) @@ -6580,7 +7151,7 @@ IT(strip_16); #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED /* png_set_strip_alpha */ static void -image_transform_png_set_strip_alpha_set(PNG_CONST image_transform *this, +image_transform_png_set_strip_alpha_set(const image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_strip_alpha(pp); @@ -6588,9 +7159,9 @@ image_transform_png_set_strip_alpha_set(PNG_CONST image_transform *this, } static void -image_transform_png_set_strip_alpha_mod(PNG_CONST image_transform *this, +image_transform_png_set_strip_alpha_mod(const image_transform *this, image_pixel *that, png_const_structp pp, - PNG_CONST transform_display *display) + const transform_display *display) { if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA) that->colour_type = PNG_COLOR_TYPE_GRAY; @@ -6605,7 +7176,7 @@ image_transform_png_set_strip_alpha_mod(PNG_CONST image_transform *this, static int image_transform_png_set_strip_alpha_add(image_transform *this, - PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth) + const image_transform **that, png_byte colour_type, png_byte bit_depth) { UNUSED(bit_depth) @@ -6626,7 +7197,8 @@ IT(strip_alpha); * png_fixed_point green) * png_get_rgb_to_gray_status * - * The 'default' test here uses values known to be used inside libpng: + * The 'default' test here uses values known to be used inside libpng prior to + * 1.7.0: * * red: 6968 * green: 23434 @@ -6663,11 +7235,11 @@ static struct #undef image_transform_ini #define image_transform_ini image_transform_png_set_rgb_to_gray_ini static void -image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this, +image_transform_png_set_rgb_to_gray_ini(const image_transform *this, transform_display *that) { png_modifier *pm = that->pm; - PNG_CONST color_encoding *e = pm->current_encoding; + const color_encoding *e = pm->current_encoding; UNUSED(this) @@ -6682,7 +7254,7 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this, /* Coefficients come from the encoding, but may need to be normalized to a * white point Y of 1.0 */ - PNG_CONST double whiteY = e->red.Y + e->green.Y + e->blue.Y; + const double whiteY = e->red.Y + e->green.Y + e->blue.Y; data.red_coefficient = e->red.Y; data.green_coefficient = e->green.Y; @@ -6699,9 +7271,15 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this, else { /* The default (built in) coeffcients, as above: */ - data.red_coefficient = 6968 / 32768.; - data.green_coefficient = 23434 / 32768.; - data.blue_coefficient = 2366 / 32768.; +# if PNG_LIBPNG_VER < 10700 + data.red_coefficient = 6968 / 32768.; + data.green_coefficient = 23434 / 32768.; + data.blue_coefficient = 2366 / 32768.; +# else + data.red_coefficient = .2126; + data.green_coefficient = .7152; + data.blue_coefficient = .0722; +# endif } data.gamma = pm->current_gamma; @@ -6735,7 +7313,7 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this, png_uint_32 ru; double total; - RANDOMIZE(ru); + R32(ru); data.green_coefficient = total = (ru & 0xffff) / 65535.; ru >>= 16; data.red_coefficient = (1 - total) * (ru & 0xffff) / 65535.; @@ -6776,14 +7354,15 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this, * conversion adds another +/-2 in the 16-bit case and * +/-(1<<(15-PNG_MAX_GAMMA_8)) in the 8-bit case. */ - that->pm->limit += -# if PNG_MAX_GAMMA_8 < 14 - pow((that->this.bit_depth == 16 ? - 8. : 6. + (1<<(15-PNG_MAX_GAMMA_8)))/65535, data.gamma); -# else - pow((that->this.bit_depth == 16 ? - 8. : 8. + (1<<(15-PNG_MAX_GAMMA_8)))/65535, data.gamma); +# if PNG_LIBPNG_VER < 10700 + if (that->this.bit_depth < 16) + that->max_gamma_8 = PNG_MAX_GAMMA_8; # endif + that->pm->limit += pow( + (that->this.bit_depth == 16 || that->max_gamma_8 > 14 ? + 8. : + 6. + (1<<(15-that->max_gamma_8)) + )/65535, data.gamma); } else @@ -6795,18 +7374,16 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this, * When DIGITIZE is set because a pre-1.7 version of libpng is being * tested allow a bigger slack. * - * NOTE: this magic number was determined by experiment to be 1.1 (when - * using fixed point arithmetic). There's no great merit to the value - * below, however it only affects the limit used for checking for - * internal calculation errors, not the actual limit imposed by - * pngvalid on the output errors. + * NOTE: this number only affects the internal limit check in pngvalid, + * it has no effect on the limits applied to the libpng values. */ - that->pm->limit += + that->pm->limit += pow( # if DIGITIZE - pow(1.1 /255, data.gamma); + 2.0 # else - pow(1.0 /255, data.gamma); + 1.0 # endif + /255, data.gamma); } } @@ -6821,10 +7398,10 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this, } static void -image_transform_png_set_rgb_to_gray_set(PNG_CONST image_transform *this, +image_transform_png_set_rgb_to_gray_set(const image_transform *this, transform_display *that, png_structp pp, png_infop pi) { - PNG_CONST int error_action = 1; /* no error, no defines in png.h */ + const int error_action = 1; /* no error, no defines in png.h */ # ifdef PNG_FLOATING_POINT_SUPPORTED png_set_rgb_to_gray(pp, error_action, data.red_to_set, data.green_to_set); @@ -6861,7 +7438,7 @@ image_transform_png_set_rgb_to_gray_set(PNG_CONST image_transform *this, & PNG_INFO_cHRM) != 0) { double maxe; - PNG_CONST char *el; + const char *el; color_encoding e, o; /* Expect libpng to return a normalized result, but the original @@ -6948,26 +7525,32 @@ image_transform_png_set_rgb_to_gray_set(PNG_CONST image_transform *this, } static void -image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, +image_transform_png_set_rgb_to_gray_mod(const image_transform *this, image_pixel *that, png_const_structp pp, - PNG_CONST transform_display *display) + const transform_display *display) { if ((that->colour_type & PNG_COLOR_MASK_COLOR) != 0) { double gray, err; - if (that->colour_type == PNG_COLOR_TYPE_PALETTE) - image_pixel_convert_PLTE(that); +# if PNG_LIBPNG_VER < 10700 + if (that->colour_type == PNG_COLOR_TYPE_PALETTE) + image_pixel_convert_PLTE(that); +# endif /* Image now has RGB channels... */ # if DIGITIZE { - PNG_CONST png_modifier *pm = display->pm; + png_modifier *pm = display->pm; const unsigned int sample_depth = that->sample_depth; const unsigned int calc_depth = (pm->assume_16_bit_calculations ? 16 : sample_depth); - const unsigned int gamma_depth = (sample_depth == 16 ? 16 : - (pm->assume_16_bit_calculations ? PNG_MAX_GAMMA_8 : sample_depth)); + const unsigned int gamma_depth = + (sample_depth == 16 ? + display->max_gamma_8 : + (pm->assume_16_bit_calculations ? + display->max_gamma_8 : + sample_depth)); int isgray; double r, g, b; double rlo, rhi, glo, ghi, blo, bhi, graylo, grayhi; @@ -6980,56 +7563,73 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, * will be identical after this operation if there is only one * transform, feel free to delete the png_error checks on this below in * the future (this is just me trying to ensure it works!) + * + * Interval arithmetic is exact, but to implement it it must be + * possible to control the floating point implementation rounding mode. + * This cannot be done in ANSI-C, so instead I reduce the 'lo' values + * by DBL_EPSILON and increase the 'hi' values by the same. */ +# define DD(v,d,r) (digitize(v*(1-DBL_EPSILON), d, r) * (1-DBL_EPSILON)) +# define DU(v,d,r) (digitize(v*(1+DBL_EPSILON), d, r) * (1+DBL_EPSILON)) + r = rlo = rhi = that->redf; rlo -= that->rede; - rlo = digitize(rlo, calc_depth, 1/*round*/); + rlo = DD(rlo, calc_depth, 1/*round*/); rhi += that->rede; - rhi = digitize(rhi, calc_depth, 1/*round*/); + rhi = DU(rhi, calc_depth, 1/*round*/); g = glo = ghi = that->greenf; glo -= that->greene; - glo = digitize(glo, calc_depth, 1/*round*/); + glo = DD(glo, calc_depth, 1/*round*/); ghi += that->greene; - ghi = digitize(ghi, calc_depth, 1/*round*/); + ghi = DU(ghi, calc_depth, 1/*round*/); b = blo = bhi = that->bluef; blo -= that->bluee; - blo = digitize(blo, calc_depth, 1/*round*/); - bhi += that->greene; - bhi = digitize(bhi, calc_depth, 1/*round*/); + blo = DD(blo, calc_depth, 1/*round*/); + bhi += that->bluee; + bhi = DU(bhi, calc_depth, 1/*round*/); isgray = r==g && g==b; if (data.gamma != 1) { - PNG_CONST double power = 1/data.gamma; - PNG_CONST double abse = calc_depth == 16 ? .5/65535 : .5/255; - - /* 'abse' is the absolute error permitted in linear calculations. It - * is used here to capture the error permitted in the handling - * (undoing) of the gamma encoding. Once again digitization occurs - * to handle the upper and lower bounds of the values. This is - * where the real errors are introduced. + const double power = 1/data.gamma; + const double abse = .5/(sample_depth == 16 ? 65535 : 255); + + /* If a gamma calculation is done it is done using lookup tables of + * precision gamma_depth, so the already digitized value above may + * need to be further digitized here. */ + if (gamma_depth != calc_depth) + { + rlo = DD(rlo, gamma_depth, 0/*truncate*/); + rhi = DU(rhi, gamma_depth, 0/*truncate*/); + glo = DD(glo, gamma_depth, 0/*truncate*/); + ghi = DU(ghi, gamma_depth, 0/*truncate*/); + blo = DD(blo, gamma_depth, 0/*truncate*/); + bhi = DU(bhi, gamma_depth, 0/*truncate*/); + } + + /* 'abse' is the error in the gamma table calculation itself. */ r = pow(r, power); - rlo = digitize(pow(rlo, power)-abse, calc_depth, 1); - rhi = digitize(pow(rhi, power)+abse, calc_depth, 1); + rlo = DD(pow(rlo, power)-abse, calc_depth, 1); + rhi = DU(pow(rhi, power)+abse, calc_depth, 1); g = pow(g, power); - glo = digitize(pow(glo, power)-abse, calc_depth, 1); - ghi = digitize(pow(ghi, power)+abse, calc_depth, 1); + glo = DD(pow(glo, power)-abse, calc_depth, 1); + ghi = DU(pow(ghi, power)+abse, calc_depth, 1); b = pow(b, power); - blo = digitize(pow(blo, power)-abse, calc_depth, 1); - bhi = digitize(pow(bhi, power)+abse, calc_depth, 1); + blo = DD(pow(blo, power)-abse, calc_depth, 1); + bhi = DU(pow(bhi, power)+abse, calc_depth, 1); } /* Now calculate the actual gray values. Although the error in the * coefficients depends on whether they were specified on the command * line (in which case truncation to 15 bits happened) or not (rounding * was used) the maxium error in an individual coefficient is always - * 1/32768, because even in the rounding case the requirement that + * 2/32768, because even in the rounding case the requirement that * coefficients add up to 32768 can cause a larger rounding error. * * The only time when rounding doesn't occur in 1.5.5 and later is when @@ -7039,32 +7639,46 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, b * data.blue_coefficient; { - PNG_CONST int do_round = data.gamma != 1 || calc_depth == 16; - PNG_CONST double ce = 1. / 32768; + const int do_round = data.gamma != 1 || calc_depth == 16; + const double ce = 2. / 32768; - graylo = digitize(rlo * (data.red_coefficient-ce) + + graylo = DD(rlo * (data.red_coefficient-ce) + glo * (data.green_coefficient-ce) + - blo * (data.blue_coefficient-ce), gamma_depth, do_round); - if (graylo <= 0) - graylo = 0; + blo * (data.blue_coefficient-ce), calc_depth, do_round); + if (graylo > gray) /* always accept the right answer */ + graylo = gray; - grayhi = digitize(rhi * (data.red_coefficient+ce) + + grayhi = DU(rhi * (data.red_coefficient+ce) + ghi * (data.green_coefficient+ce) + - bhi * (data.blue_coefficient+ce), gamma_depth, do_round); - if (grayhi >= 1) - grayhi = 1; + bhi * (data.blue_coefficient+ce), calc_depth, do_round); + if (grayhi < gray) + grayhi = gray; } /* And invert the gamma. */ if (data.gamma != 1) { - PNG_CONST double power = data.gamma; + const double power = data.gamma; + + /* And this happens yet again, shifting the values once more. */ + if (gamma_depth != sample_depth) + { + rlo = DD(rlo, gamma_depth, 0/*truncate*/); + rhi = DU(rhi, gamma_depth, 0/*truncate*/); + glo = DD(glo, gamma_depth, 0/*truncate*/); + ghi = DU(ghi, gamma_depth, 0/*truncate*/); + blo = DD(blo, gamma_depth, 0/*truncate*/); + bhi = DU(bhi, gamma_depth, 0/*truncate*/); + } gray = pow(gray, power); - graylo = digitize(pow(graylo, power), sample_depth, 1); - grayhi = digitize(pow(grayhi, power), sample_depth, 1); + graylo = DD(pow(graylo, power), sample_depth, 1); + grayhi = DU(pow(grayhi, power), sample_depth, 1); } +# undef DD +# undef DU + /* Now the error can be calculated. * * If r==g==b because there is no overall gamma correction libpng @@ -7076,9 +7690,11 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, else { err = fabs(grayhi-gray); + if (fabs(gray - graylo) > err) err = fabs(graylo-gray); +#if !RELEASE_BUILD /* Check that this worked: */ if (err > pm->limit) { @@ -7089,11 +7705,13 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, pos = safecatd(buffer, sizeof buffer, pos, err, 6); pos = safecat(buffer, sizeof buffer, pos, " exceeds limit "); pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6); - png_error(pp, buffer); + png_warning(pp, buffer); + pm->limit = err; } +#endif /* !RELEASE_BUILD */ } } -# else /* DIGITIZE */ +# else /* !DIGITIZE */ { double r = that->redf; double re = that->rede; @@ -7102,29 +7720,46 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, double b = that->bluef; double be = that->bluee; - /* The true gray case involves no math. */ - if (r == g && r == b) - { - gray = r; - err = re; - if (err < ge) err = ge; - if (err < be) err = be; - } +# if PNG_LIBPNG_VER < 10700 + /* The true gray case involves no math in earlier versions (not + * true, there was some if gamma correction was happening too.) + */ + if (r == g && r == b) + { + gray = r; + err = re; + if (err < ge) err = ge; + if (err < be) err = be; + } - else if (data.gamma == 1) + else +# endif /* before 1.7 */ + if (data.gamma == 1) { /* There is no need to do the conversions to and from linear space, * so the calculation should be a lot more accurate. There is a - * built in 1/32768 error in the coefficients because they only have - * 15 bits and are adjusted to make sure they add up to 32768, so - * the result may have an additional error up to 1/32768. (Note - * that adding the 1/32768 here avoids needing to increase the - * global error limits to take this into account.) + * built in error in the coefficients because they only have 15 bits + * and are adjusted to make sure they add up to 32768. This + * involves a integer calculation with truncation of the form: + * + * ((int)(coefficient * 100000) * 32768)/100000 + * + * This is done to the red and green coefficients (the ones + * provided to the API) then blue is calculated from them so the + * result adds up to 32768. In the worst case this can result in + * a -1 error in red and green and a +2 error in blue. Consequently + * the worst case in the calculation below is 2/32768 error. + * + * TODO: consider fixing this in libpng by rounding the calculation + * limiting the error to 1/32768. + * + * Handling this by adding 2/32768 here avoids needing to increase + * the global error limits to take this into account.) */ gray = r * data.red_coefficient + g * data.green_coefficient + b * data.blue_coefficient; err = re * data.red_coefficient + ge * data.green_coefficient + - be * data.blue_coefficient + 1./32768 + gray * 5 * DBL_EPSILON; + be * data.blue_coefficient + 2./32768 + gray * 5 * DBL_EPSILON; } else @@ -7135,10 +7770,10 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, * lookups in the calculation and each introduces a quantization * error defined by the table size. */ - PNG_CONST png_modifier *pm = display->pm; + png_modifier *pm = display->pm; double in_qe = (that->sample_depth > 8 ? .5/65535 : .5/255); double out_qe = (that->sample_depth > 8 ? .5/65535 : - (pm->assume_16_bit_calculations ? .5/(1<assume_16_bit_calculations ? .5/(1<max_gamma_8) : .5/255)); double rhi, ghi, bhi, grayhi; double g1 = 1/data.gamma; @@ -7159,7 +7794,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, * previously added input quantization error at this point. */ gray = r * data.red_coefficient + g * data.green_coefficient + - b * data.blue_coefficient - 1./32768 - out_qe; + b * data.blue_coefficient - 2./32768 - out_qe; if (gray <= 0) gray = 0; else @@ -7169,7 +7804,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, } grayhi = rhi * data.red_coefficient + ghi * data.green_coefficient + - bhi * data.blue_coefficient + 1./32768 + out_qe; + bhi * data.blue_coefficient + 2./32768 + out_qe; grayhi *= (1 + 6 * DBL_EPSILON); if (grayhi >= 1) grayhi = 1; @@ -7185,6 +7820,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, else err -= in_qe; +#if !RELEASE_BUILD /* Validate that the error is within limits (this has caused * problems before, it's much easier to detect them here.) */ @@ -7197,8 +7833,10 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, pos = safecatd(buffer, sizeof buffer, pos, err, 6); pos = safecat(buffer, sizeof buffer, pos, " exceeds limit "); pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6); - png_error(pp, buffer); + png_warning(pp, buffer); + pm->limit = err; } +#endif /* !RELEASE_BUILD */ } } # endif /* !DIGITIZE */ @@ -7225,7 +7863,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, static int image_transform_png_set_rgb_to_gray_add(image_transform *this, - PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth) + const image_transform **that, png_byte colour_type, png_byte bit_depth) { UNUSED(bit_depth) @@ -7256,7 +7894,7 @@ IT(rgb_to_gray); static image_pixel data; static void -image_transform_png_set_background_set(PNG_CONST image_transform *this, +image_transform_png_set_background_set(const image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_byte colour_type, bit_depth; @@ -7269,7 +7907,7 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this, * so we need to know what that is! The background colour is stored in the * transform_display. */ - RANDOMIZE(random_bytes); + R8(random_bytes); /* Read the random value, for colour type 3 the background colour is actually * expressed as a 24bit rgb, not an index. @@ -7284,17 +7922,20 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this, else { + if (that->this.has_tRNS) + that->this.is_transparent = 1; + bit_depth = that->this.bit_depth; expand = 1; } image_pixel_init(&data, random_bytes, colour_type, - bit_depth, 0/*x*/, 0/*unused: palette*/); + bit_depth, 0/*x*/, 0/*unused: palette*/, NULL/*format*/); /* Extract the background colour from this image_pixel, but make sure the * unused fields of 'back' are garbage. */ - RANDOMIZE(back); + R8(back); if (colour_type & PNG_COLOR_MASK_COLOR) { @@ -7316,13 +7957,13 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this, } static void -image_transform_png_set_background_mod(PNG_CONST image_transform *this, +image_transform_png_set_background_mod(const image_transform *this, image_pixel *that, png_const_structp pp, - PNG_CONST transform_display *display) + const transform_display *display) { /* Check for tRNS first: */ if (that->have_tRNS && that->colour_type != PNG_COLOR_TYPE_PALETTE) - image_pixel_add_alpha(that, &display->this); + image_pixel_add_alpha(that, &display->this, 1/*for background*/); /* This is only necessary if the alpha value is less than 1. */ if (that->alphaf < 1) @@ -7361,14 +8002,14 @@ image_transform_png_set_background_mod(PNG_CONST image_transform *this, /* Remove the alpha type and set the alpha (not in that order.) */ that->alphaf = 1; that->alphae = 0; - - if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA) - that->colour_type = PNG_COLOR_TYPE_RGB; - else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA) - that->colour_type = PNG_COLOR_TYPE_GRAY; - /* PNG_COLOR_TYPE_PALETTE is not changed */ } + if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA) + that->colour_type = PNG_COLOR_TYPE_RGB; + else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA) + that->colour_type = PNG_COLOR_TYPE_GRAY; + /* PNG_COLOR_TYPE_PALETTE is not changed */ + this->next->mod(this->next, that, pp, display); } @@ -7380,68 +8021,661 @@ IT(background); #define PT ITSTRUCT(background) #endif /* PNG_READ_BACKGROUND_SUPPORTED */ -/* This may just be 'end' if all the transforms are disabled! */ -static image_transform *PNG_CONST image_transform_first = &PT; +/* png_set_quantize(png_structp, png_colorp palette, int num_palette, + * int maximum_colors, png_const_uint_16p histogram, int full_quantize) + * + * Very difficult to validate this! + */ +/*NOTE: TBD NYI */ +/* The data layout transforms are handled by swapping our own channel data, + * necessarily these need to happen at the end of the transform list because the + * semantic of the channels changes after these are executed. Some of these, + * like set_shift and set_packing, can't be done at present because they change + * the layout of the data at the sub-sample level so sample() won't get the + * right answer. + */ +/* png_set_invert_alpha */ +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED +/* Invert the alpha channel + * + * png_set_invert_alpha(png_structrp png_ptr) + */ static void -transform_enable(PNG_CONST char *name) +image_transform_png_set_invert_alpha_set(const image_transform *this, + transform_display *that, png_structp pp, png_infop pi) { - /* Everything starts out enabled, so if we see an 'enable' disabled - * everything else the first time round. - */ - static int all_disabled = 0; - int found_it = 0; - image_transform *list = image_transform_first; - - while (list != &image_transform_end) - { - if (strcmp(list->name, name) == 0) - { - list->enable = 1; - found_it = 1; - } - else if (!all_disabled) - list->enable = 0; - - list = list->list; - } - - all_disabled = 1; - - if (!found_it) - { - fprintf(stderr, "pngvalid: --transform-enable=%s: unknown transform\n", - name); - exit(99); - } + png_set_invert_alpha(pp); + this->next->set(this->next, that, pp, pi); } static void -transform_disable(PNG_CONST char *name) +image_transform_png_set_invert_alpha_mod(const image_transform *this, + image_pixel *that, png_const_structp pp, + const transform_display *display) { - image_transform *list = image_transform_first; + if (that->colour_type & 4) + that->alpha_inverted = 1; - while (list != &image_transform_end) - { - if (strcmp(list->name, name) == 0) - { - list->enable = 0; - return; - } + this->next->mod(this->next, that, pp, display); +} - list = list->list; - } +static int +image_transform_png_set_invert_alpha_add(image_transform *this, + const image_transform **that, png_byte colour_type, png_byte bit_depth) +{ + UNUSED(bit_depth) - fprintf(stderr, "pngvalid: --transform-disable=%s: unknown transform\n", - name); - exit(99); + this->next = *that; + *that = this; + + /* Only has an effect on pixels with alpha: */ + return (colour_type & 4) != 0; } +IT(invert_alpha); +#undef PT +#define PT ITSTRUCT(invert_alpha) + +#endif /* PNG_READ_INVERT_ALPHA_SUPPORTED */ + +/* png_set_bgr */ +#ifdef PNG_READ_BGR_SUPPORTED +/* Swap R,G,B channels to order B,G,R. + * + * png_set_bgr(png_structrp png_ptr) + * + * This only has an effect on RGB and RGBA pixels. + */ static void -image_transform_reset_count(void) +image_transform_png_set_bgr_set(const image_transform *this, + transform_display *that, png_structp pp, png_infop pi) { - image_transform *next = image_transform_first; - int count = 0; + png_set_bgr(pp); + this->next->set(this->next, that, pp, pi); +} + +static void +image_transform_png_set_bgr_mod(const image_transform *this, + image_pixel *that, png_const_structp pp, + const transform_display *display) +{ + if (that->colour_type == PNG_COLOR_TYPE_RGB || + that->colour_type == PNG_COLOR_TYPE_RGBA) + that->swap_rgb = 1; + + this->next->mod(this->next, that, pp, display); +} + +static int +image_transform_png_set_bgr_add(image_transform *this, + const image_transform **that, png_byte colour_type, png_byte bit_depth) +{ + UNUSED(bit_depth) + + this->next = *that; + *that = this; + + return colour_type == PNG_COLOR_TYPE_RGB || + colour_type == PNG_COLOR_TYPE_RGBA; +} + +IT(bgr); +#undef PT +#define PT ITSTRUCT(bgr) + +#endif /* PNG_READ_BGR_SUPPORTED */ + +/* png_set_swap_alpha */ +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED +/* Put the alpha channel first. + * + * png_set_swap_alpha(png_structrp png_ptr) + * + * This only has an effect on GA and RGBA pixels. + */ +static void +image_transform_png_set_swap_alpha_set(const image_transform *this, + transform_display *that, png_structp pp, png_infop pi) +{ + png_set_swap_alpha(pp); + this->next->set(this->next, that, pp, pi); +} + +static void +image_transform_png_set_swap_alpha_mod(const image_transform *this, + image_pixel *that, png_const_structp pp, + const transform_display *display) +{ + if (that->colour_type == PNG_COLOR_TYPE_GA || + that->colour_type == PNG_COLOR_TYPE_RGBA) + that->alpha_first = 1; + + this->next->mod(this->next, that, pp, display); +} + +static int +image_transform_png_set_swap_alpha_add(image_transform *this, + const image_transform **that, png_byte colour_type, png_byte bit_depth) +{ + UNUSED(bit_depth) + + this->next = *that; + *that = this; + + return colour_type == PNG_COLOR_TYPE_GA || + colour_type == PNG_COLOR_TYPE_RGBA; +} + +IT(swap_alpha); +#undef PT +#define PT ITSTRUCT(swap_alpha) + +#endif /* PNG_READ_SWAP_ALPHA_SUPPORTED */ + +/* png_set_swap */ +#ifdef PNG_READ_SWAP_SUPPORTED +/* Byte swap 16-bit components. + * + * png_set_swap(png_structrp png_ptr) + */ +static void +image_transform_png_set_swap_set(const image_transform *this, + transform_display *that, png_structp pp, png_infop pi) +{ + png_set_swap(pp); + this->next->set(this->next, that, pp, pi); +} + +static void +image_transform_png_set_swap_mod(const image_transform *this, + image_pixel *that, png_const_structp pp, + const transform_display *display) +{ + if (that->bit_depth == 16) + that->swap16 = 1; + + this->next->mod(this->next, that, pp, display); +} + +static int +image_transform_png_set_swap_add(image_transform *this, + const image_transform **that, png_byte colour_type, png_byte bit_depth) +{ + UNUSED(colour_type) + + this->next = *that; + *that = this; + + return bit_depth == 16; +} + +IT(swap); +#undef PT +#define PT ITSTRUCT(swap) + +#endif /* PNG_READ_SWAP_SUPPORTED */ + +#ifdef PNG_READ_FILLER_SUPPORTED +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. + * + * png_set_filler, (png_structp png_ptr, png_uint_32 filler, int flags)); + * + * Flags: + * + * PNG_FILLER_BEFORE + * PNG_FILLER_AFTER + */ +#define data ITDATA(filler) +static struct +{ + png_uint_32 filler; + int flags; +} data; + +static void +image_transform_png_set_filler_set(const image_transform *this, + transform_display *that, png_structp pp, png_infop pi) +{ + /* Need a random choice for 'before' and 'after' as well as for the + * filler. The 'filler' value has all 32 bits set, but only bit_depth + * will be used. At this point we don't know bit_depth. + */ + R32(data.filler); + data.flags = random_choice(); + + png_set_filler(pp, data.filler, data.flags); + + /* The standard display handling stuff also needs to know that + * there is a filler, so set that here. + */ + that->this.filler = 1; + + this->next->set(this->next, that, pp, pi); +} + +static void +image_transform_png_set_filler_mod(const image_transform *this, + image_pixel *that, png_const_structp pp, + const transform_display *display) +{ + if (that->bit_depth >= 8 && + (that->colour_type == PNG_COLOR_TYPE_RGB || + that->colour_type == PNG_COLOR_TYPE_GRAY)) + { + const unsigned int max = (1U << that->bit_depth)-1; + that->alpha = data.filler & max; + that->alphaf = ((double)that->alpha) / max; + that->alphae = 0; + + /* The filler has been stored in the alpha channel, we must record + * that this has been done for the checking later on, the color + * type is faked to have an alpha channel, but libpng won't report + * this; the app has to know the extra channel is there and this + * was recording in standard_display::filler above. + */ + that->colour_type |= 4; /* alpha added */ + that->alpha_first = data.flags == PNG_FILLER_BEFORE; + } + + this->next->mod(this->next, that, pp, display); +} + +static int +image_transform_png_set_filler_add(image_transform *this, + const image_transform **that, png_byte colour_type, png_byte bit_depth) +{ + this->next = *that; + *that = this; + + return bit_depth >= 8 && (colour_type == PNG_COLOR_TYPE_RGB || + colour_type == PNG_COLOR_TYPE_GRAY); +} + +#undef data +IT(filler); +#undef PT +#define PT ITSTRUCT(filler) + +/* png_set_add_alpha, (png_structp png_ptr, png_uint_32 filler, int flags)); */ +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +#define data ITDATA(add_alpha) +static struct +{ + png_uint_32 filler; + int flags; +} data; + +static void +image_transform_png_set_add_alpha_set(const image_transform *this, + transform_display *that, png_structp pp, png_infop pi) +{ + /* Need a random choice for 'before' and 'after' as well as for the + * filler. The 'filler' value has all 32 bits set, but only bit_depth + * will be used. At this point we don't know bit_depth. + */ + R32(data.filler); + data.flags = random_choice(); + + png_set_add_alpha(pp, data.filler, data.flags); + this->next->set(this->next, that, pp, pi); +} + +static void +image_transform_png_set_add_alpha_mod(const image_transform *this, + image_pixel *that, png_const_structp pp, + const transform_display *display) +{ + if (that->bit_depth >= 8 && + (that->colour_type == PNG_COLOR_TYPE_RGB || + that->colour_type == PNG_COLOR_TYPE_GRAY)) + { + const unsigned int max = (1U << that->bit_depth)-1; + that->alpha = data.filler & max; + that->alphaf = ((double)that->alpha) / max; + that->alphae = 0; + + that->colour_type |= 4; /* alpha added */ + that->alpha_first = data.flags == PNG_FILLER_BEFORE; + } + + this->next->mod(this->next, that, pp, display); +} + +static int +image_transform_png_set_add_alpha_add(image_transform *this, + const image_transform **that, png_byte colour_type, png_byte bit_depth) +{ + this->next = *that; + *that = this; + + return bit_depth >= 8 && (colour_type == PNG_COLOR_TYPE_RGB || + colour_type == PNG_COLOR_TYPE_GRAY); +} + +#undef data +IT(add_alpha); +#undef PT +#define PT ITSTRUCT(add_alpha) + +#endif /* PNG_READ_FILLER_SUPPORTED */ + +/* png_set_packing */ +#ifdef PNG_READ_PACK_SUPPORTED +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. + * + * png_set_packing(png_structrp png_ptr) + * + * This should only affect grayscale and palette images with less than 8 bits + * per pixel. + */ +static void +image_transform_png_set_packing_set(const image_transform *this, + transform_display *that, png_structp pp, png_infop pi) +{ + png_set_packing(pp); + that->unpacked = 1; + this->next->set(this->next, that, pp, pi); +} + +static void +image_transform_png_set_packing_mod(const image_transform *this, + image_pixel *that, png_const_structp pp, + const transform_display *display) +{ + /* The general expand case depends on what the colour type is, + * low bit-depth pixel values are unpacked into bytes without + * scaling, so sample_depth is not changed. + */ + if (that->bit_depth < 8) /* grayscale or palette */ + that->bit_depth = 8; + + this->next->mod(this->next, that, pp, display); +} + +static int +image_transform_png_set_packing_add(image_transform *this, + const image_transform **that, png_byte colour_type, png_byte bit_depth) +{ + UNUSED(colour_type) + + this->next = *that; + *that = this; + + /* Nothing should happen unless the bit depth is less than 8: */ + return bit_depth < 8; +} + +IT(packing); +#undef PT +#define PT ITSTRUCT(packing) + +#endif /* PNG_READ_PACK_SUPPORTED */ + +/* png_set_packswap */ +#ifdef PNG_READ_PACKSWAP_SUPPORTED +/* Swap pixels packed into bytes; reverses the order on screen so that + * the high order bits correspond to the rightmost pixels. + * + * png_set_packswap(png_structrp png_ptr) + */ +static void +image_transform_png_set_packswap_set(const image_transform *this, + transform_display *that, png_structp pp, png_infop pi) +{ + png_set_packswap(pp); + that->this.littleendian = 1; + this->next->set(this->next, that, pp, pi); +} + +static void +image_transform_png_set_packswap_mod(const image_transform *this, + image_pixel *that, png_const_structp pp, + const transform_display *display) +{ + if (that->bit_depth < 8) + that->littleendian = 1; + + this->next->mod(this->next, that, pp, display); +} + +static int +image_transform_png_set_packswap_add(image_transform *this, + const image_transform **that, png_byte colour_type, png_byte bit_depth) +{ + UNUSED(colour_type) + + this->next = *that; + *that = this; + + return bit_depth < 8; +} + +IT(packswap); +#undef PT +#define PT ITSTRUCT(packswap) + +#endif /* PNG_READ_PACKSWAP_SUPPORTED */ + + +/* png_set_invert_mono */ +#ifdef PNG_READ_INVERT_MONO_SUPPORTED +/* Invert the gray channel + * + * png_set_invert_mono(png_structrp png_ptr) + */ +static void +image_transform_png_set_invert_mono_set(const image_transform *this, + transform_display *that, png_structp pp, png_infop pi) +{ + png_set_invert_mono(pp); + this->next->set(this->next, that, pp, pi); +} + +static void +image_transform_png_set_invert_mono_mod(const image_transform *this, + image_pixel *that, png_const_structp pp, + const transform_display *display) +{ + if (that->colour_type & 4) + that->mono_inverted = 1; + + this->next->mod(this->next, that, pp, display); +} + +static int +image_transform_png_set_invert_mono_add(image_transform *this, + const image_transform **that, png_byte colour_type, png_byte bit_depth) +{ + UNUSED(bit_depth) + + this->next = *that; + *that = this; + + /* Only has an effect on pixels with no colour: */ + return (colour_type & 2) == 0; +} + +IT(invert_mono); +#undef PT +#define PT ITSTRUCT(invert_mono) + +#endif /* PNG_READ_INVERT_MONO_SUPPORTED */ + +#ifdef PNG_READ_SHIFT_SUPPORTED +/* png_set_shift(png_structp, png_const_color_8p true_bits) + * + * The output pixels will be shifted by the given true_bits + * values. + */ +#define data ITDATA(shift) +static png_color_8 data; + +static void +image_transform_png_set_shift_set(const image_transform *this, + transform_display *that, png_structp pp, png_infop pi) +{ + /* Get a random set of shifts. The shifts need to do something + * to test the transform, so they are limited to the bit depth + * of the input image. Notice that in the following the 'gray' + * field is randomized independently. This acts as a check that + * libpng does use the correct field. + */ + const unsigned int depth = that->this.bit_depth; + + data.red = (png_byte)/*SAFE*/(random_mod(depth)+1); + data.green = (png_byte)/*SAFE*/(random_mod(depth)+1); + data.blue = (png_byte)/*SAFE*/(random_mod(depth)+1); + data.gray = (png_byte)/*SAFE*/(random_mod(depth)+1); + data.alpha = (png_byte)/*SAFE*/(random_mod(depth)+1); + + png_set_shift(pp, &data); + this->next->set(this->next, that, pp, pi); +} + +static void +image_transform_png_set_shift_mod(const image_transform *this, + image_pixel *that, png_const_structp pp, + const transform_display *display) +{ + /* Copy the correct values into the sBIT fields, libpng does not do + * anything to palette data: + */ + if (that->colour_type != PNG_COLOR_TYPE_PALETTE) + { + that->sig_bits = 1; + + /* The sBIT fields are reset to the values previously sent to + * png_set_shift according to the colour type. + * does. + */ + if (that->colour_type & 2) /* RGB channels */ + { + that->red_sBIT = data.red; + that->green_sBIT = data.green; + that->blue_sBIT = data.blue; + } + + else /* One grey channel */ + that->red_sBIT = that->green_sBIT = that->blue_sBIT = data.gray; + + that->alpha_sBIT = data.alpha; + } + + this->next->mod(this->next, that, pp, display); +} + +static int +image_transform_png_set_shift_add(image_transform *this, + const image_transform **that, png_byte colour_type, png_byte bit_depth) +{ + UNUSED(bit_depth) + + this->next = *that; + *that = this; + + return colour_type != PNG_COLOR_TYPE_PALETTE; +} + +IT(shift); +#undef PT +#define PT ITSTRUCT(shift) + +#endif /* PNG_READ_SHIFT_SUPPORTED */ + +#ifdef THIS_IS_THE_PROFORMA +static void +image_transform_png_set_@_set(const image_transform *this, + transform_display *that, png_structp pp, png_infop pi) +{ + png_set_@(pp); + this->next->set(this->next, that, pp, pi); +} + +static void +image_transform_png_set_@_mod(const image_transform *this, + image_pixel *that, png_const_structp pp, + const transform_display *display) +{ + this->next->mod(this->next, that, pp, display); +} + +static int +image_transform_png_set_@_add(image_transform *this, + const image_transform **that, png_byte colour_type, png_byte bit_depth) +{ + this->next = *that; + *that = this; + + return 1; +} + +IT(@); +#endif + + +/* This may just be 'end' if all the transforms are disabled! */ +static image_transform *const image_transform_first = &PT; + +static void +transform_enable(const char *name) +{ + /* Everything starts out enabled, so if we see an 'enable' disabled + * everything else the first time round. + */ + static int all_disabled = 0; + int found_it = 0; + image_transform *list = image_transform_first; + + while (list != &image_transform_end) + { + if (strcmp(list->name, name) == 0) + { + list->enable = 1; + found_it = 1; + } + else if (!all_disabled) + list->enable = 0; + + list = list->list; + } + + all_disabled = 1; + + if (!found_it) + { + fprintf(stderr, "pngvalid: --transform-enable=%s: unknown transform\n", + name); + exit(99); + } +} + +static void +transform_disable(const char *name) +{ + image_transform *list = image_transform_first; + + while (list != &image_transform_end) + { + if (strcmp(list->name, name) == 0) + { + list->enable = 0; + return; + } + + list = list->list; + } + + fprintf(stderr, "pngvalid: --transform-disable=%s: unknown transform\n", + name); + exit(99); +} + +static void +image_transform_reset_count(void) +{ + image_transform *next = image_transform_first; + int count = 0; while (next != &image_transform_end) { @@ -7480,7 +8714,7 @@ image_transform_test_counter(png_uint_32 counter, unsigned int max) } static png_uint_32 -image_transform_add(PNG_CONST image_transform **this, unsigned int max, +image_transform_add(const image_transform **this, unsigned int max, png_uint_32 counter, char *name, size_t sizeof_name, size_t *pos, png_byte colour_type, png_byte bit_depth) { @@ -7557,83 +8791,6 @@ image_transform_add(PNG_CONST image_transform **this, unsigned int max, } } -#ifdef THIS_IS_THE_PROFORMA -static void -image_transform_png_set_@_set(PNG_CONST image_transform *this, - transform_display *that, png_structp pp, png_infop pi) -{ - png_set_@(pp); - this->next->set(this->next, that, pp, pi); -} - -static void -image_transform_png_set_@_mod(PNG_CONST image_transform *this, - image_pixel *that, png_const_structp pp, - PNG_CONST transform_display *display) -{ - this->next->mod(this->next, that, pp, display); -} - -static int -image_transform_png_set_@_add(image_transform *this, - PNG_CONST image_transform **that, char *name, size_t sizeof_name, - size_t *pos, png_byte colour_type, png_byte bit_depth) -{ - this->next = *that; - *that = this; - - *pos = safecat(name, sizeof_name, *pos, " +@"); - - return 1; -} - -IT(@); -#endif - -/* png_set_quantize(png_structp, png_colorp palette, int num_palette, - * int maximum_colors, png_const_uint_16p histogram, int full_quantize) - * - * Very difficult to validate this! - */ -/*NOTE: TBD NYI */ - -/* The data layout transforms are handled by swapping our own channel data, - * necessarily these need to happen at the end of the transform list because the - * semantic of the channels changes after these are executed. Some of these, - * like set_shift and set_packing, can't be done at present because they change - * the layout of the data at the sub-sample level so sample() won't get the - * right answer. - */ -/* png_set_invert_alpha */ -/*NOTE: TBD NYI */ - -/* png_set_bgr */ -/*NOTE: TBD NYI */ - -/* png_set_swap_alpha */ -/*NOTE: TBD NYI */ - -/* png_set_swap */ -/*NOTE: TBD NYI */ - -/* png_set_filler, (png_structp png_ptr, png_uint_32 filler, int flags)); */ -/*NOTE: TBD NYI */ - -/* png_set_add_alpha, (png_structp png_ptr, png_uint_32 filler, int flags)); */ -/*NOTE: TBD NYI */ - -/* png_set_packing */ -/*NOTE: TBD NYI */ - -/* png_set_packswap */ -/*NOTE: TBD NYI */ - -/* png_set_invert_mono */ -/*NOTE: TBD NYI */ - -/* png_set_shift(png_structp, png_const_color_8p true_bits) */ -/*NOTE: TBD NYI */ - static void perform_transform_test(png_modifier *pm) { @@ -7641,7 +8798,8 @@ perform_transform_test(png_modifier *pm) png_byte bit_depth = 0; unsigned int palette_number = 0; - while (next_format(&colour_type, &bit_depth, &palette_number, 0)) + while (next_format(&colour_type, &bit_depth, &palette_number, pm->test_lbg, + pm->test_tRNS)) { png_uint_32 counter = 0; size_t base_pos; @@ -7652,7 +8810,7 @@ perform_transform_test(png_modifier *pm) for (;;) { size_t pos = base_pos; - PNG_CONST image_transform *list = 0; + const image_transform *list = 0; /* 'max' is currently hardwired to '1'; this should be settable on the * command line. @@ -7713,11 +8871,11 @@ static void gamma_display_init(gamma_display *dp, png_modifier *pm, png_uint_32 id, double file_gamma, double screen_gamma, png_byte sbit, int threshold_test, int use_input_precision, int scale16, int expand16, - int do_background, PNG_CONST png_color_16 *pointer_to_the_background_color, + int do_background, const png_color_16 *pointer_to_the_background_color, double background_gamma) { /* Standard fields */ - standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/, + standard_display_init(&dp->this, &pm->this, id, do_read_interlace, pm->use_update_info); /* Parameter fields */ @@ -7749,7 +8907,7 @@ gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi) /* If requested strip 16 to 8 bits - this is handled automagically below * because the output bit depth is read from the library. Note that there * are interactions with sBIT but, internally, libpng makes sbit at most - * PNG_MAX_GAMMA_8 when doing the following. + * PNG_MAX_GAMMA_8 prior to 1.7 when doing the following. */ if (dp->scale16) # ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED @@ -7781,9 +8939,9 @@ gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi) * non-inverted, represenation. It provides a default for the PNG file * gamma, but since the file has a gAMA chunk this does not matter. */ - PNG_CONST double sg = dp->screen_gamma; + const double sg = dp->screen_gamma; # ifndef PNG_FLOATING_POINT_SUPPORTED - PNG_CONST png_fixed_point g = fix(sg); + const png_fixed_point g = fix(sg); # endif # ifdef PNG_FLOATING_POINT_SUPPORTED @@ -7829,9 +8987,9 @@ gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi) # ifdef PNG_READ_BACKGROUND_SUPPORTED /* NOTE: this assumes the caller provided the correct background gamma! */ - PNG_CONST double bg = dp->background_gamma; + const double bg = dp->background_gamma; # ifndef PNG_FLOATING_POINT_SUPPORTED - PNG_CONST png_fixed_point g = fix(bg); + const png_fixed_point g = fix(bg); # endif # ifdef PNG_FLOATING_POINT_SUPPORTED @@ -7905,7 +9063,7 @@ static void init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp, int in_depth, int out_depth) { - PNG_CONST unsigned int outmax = (1U<pp = pp; vi->dp = dp; @@ -7944,13 +9102,15 @@ init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp, vi->outlog = outlog(dp->pm, in_depth, out_depth); if ((dp->this.colour_type & PNG_COLOR_MASK_ALPHA) != 0 || - (dp->this.colour_type == 3 && dp->this.is_transparent)) + (dp->this.colour_type == 3 && dp->this.is_transparent) || + ((dp->this.colour_type == 0 || dp->this.colour_type == 2) && + dp->this.has_tRNS)) { vi->do_background = dp->do_background; if (vi->do_background != 0) { - PNG_CONST double bg_inverse = 1/dp->background_gamma; + const double bg_inverse = 1/dp->background_gamma; double r, g, b; /* Caller must at least put the gray value into the red channel */ @@ -7974,7 +9134,7 @@ init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp, vi->background_blue = b; } } - else + else /* Do not expect any background processing */ vi->do_background = 0; if (vi->do_background == 0) @@ -8064,15 +9224,15 @@ gamma_component_compose(int do_background, double input_sample, double alpha, /* This API returns the encoded *input* component, in the range 0..1 */ static double -gamma_component_validate(PNG_CONST char *name, PNG_CONST validate_info *vi, - PNG_CONST unsigned int id, PNG_CONST unsigned int od, - PNG_CONST double alpha /* <0 for the alpha channel itself */, - PNG_CONST double background /* component background value */) +gamma_component_validate(const char *name, const validate_info *vi, + const unsigned int id, const unsigned int od, + const double alpha /* <0 for the alpha channel itself */, + const double background /* component background value */) { - PNG_CONST unsigned int isbit = id >> vi->isbit_shift; - PNG_CONST unsigned int sbit_max = vi->sbit_max; - PNG_CONST unsigned int outmax = vi->outmax; - PNG_CONST int do_background = vi->do_background; + const unsigned int isbit = id >> vi->isbit_shift; + const unsigned int sbit_max = vi->sbit_max; + const unsigned int outmax = vi->outmax; + const int do_background = vi->do_background; double i; @@ -8637,14 +9797,14 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp, png_infop pi) { /* Get some constants derived from the input and output file formats: */ - PNG_CONST png_store* PNG_CONST ps = dp->this.ps; - PNG_CONST png_byte in_ct = dp->this.colour_type; - PNG_CONST png_byte in_bd = dp->this.bit_depth; - PNG_CONST png_uint_32 w = dp->this.w; - PNG_CONST png_uint_32 h = dp->this.h; - PNG_CONST size_t cbRow = dp->this.cbRow; - PNG_CONST png_byte out_ct = png_get_color_type(pp, pi); - PNG_CONST png_byte out_bd = png_get_bit_depth(pp, pi); + const png_store* const ps = dp->this.ps; + const png_byte in_ct = dp->this.colour_type; + const png_byte in_bd = dp->this.bit_depth; + const png_uint_32 w = dp->this.w; + const png_uint_32 h = dp->this.h; + const size_t cbRow = dp->this.cbRow; + const png_byte out_ct = png_get_color_type(pp, pi); + const png_byte out_bd = png_get_bit_depth(pp, pi); /* There are three sources of error, firstly the quantization in the * file encoding, determined by sbit and/or the file depth, secondly @@ -8685,11 +9845,12 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp, * The basic tests below do not do this, however if 'use_input_precision' * is set a subsequent test is performed above. */ - PNG_CONST unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U; + const unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U; int processing; png_uint_32 y; - PNG_CONST store_palette_entry *in_palette = dp->this.palette; - PNG_CONST int in_is_transparent = dp->this.is_transparent; + const store_palette_entry *in_palette = dp->this.palette; + const int in_is_transparent = dp->this.is_transparent; + int process_tRNS; int out_npalette = -1; int out_is_transparent = 0; /* Just refers to the palette case */ store_palette out_palette; @@ -8705,6 +9866,7 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp, processing = (vi.gamma_correction > 0 && !dp->threshold_test) || in_bd != out_bd || in_ct != out_ct || vi.do_background; + process_tRNS = dp->this.has_tRNS && vi.do_background; /* TODO: FIX THIS: MAJOR BUG! If the transformations all happen inside * the palette there is no way of finding out, because libpng fails to @@ -8735,20 +9897,20 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp, double alpha = 1; /* serves as a flag value */ /* Record the palette index for index images. */ - PNG_CONST unsigned int in_index = - in_ct == 3 ? sample(std, 3, in_bd, x, 0) : 256; - PNG_CONST unsigned int out_index = - out_ct == 3 ? sample(std, 3, out_bd, x, 0) : 256; + const unsigned int in_index = + in_ct == 3 ? sample(std, 3, in_bd, x, 0, 0, 0) : 256; + const unsigned int out_index = + out_ct == 3 ? sample(std, 3, out_bd, x, 0, 0, 0) : 256; /* Handle input alpha - png_set_background will cause the output * alpha to disappear so there is nothing to check. */ - if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 || (in_ct == 3 && - in_is_transparent)) + if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 || + (in_ct == 3 && in_is_transparent)) { - PNG_CONST unsigned int input_alpha = in_ct == 3 ? + const unsigned int input_alpha = in_ct == 3 ? dp->this.palette[in_index].alpha : - sample(std, in_ct, in_bd, x, samples_per_pixel); + sample(std, in_ct, in_bd, x, samples_per_pixel, 0, 0); unsigned int output_alpha = 65536 /* as a flag value */; @@ -8760,7 +9922,7 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp, else if ((out_ct & PNG_COLOR_MASK_ALPHA) != 0) output_alpha = sample(pRow, out_ct, out_bd, x, - samples_per_pixel); + samples_per_pixel, 0, 0); if (output_alpha != 65536) alpha = gamma_component_validate("alpha", &vi, input_alpha, @@ -8776,33 +9938,62 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp, } } + else if (process_tRNS) + { + /* alpha needs to be set appropriately for this pixel, it is + * currently 1 and needs to be 0 for an input pixel which matches + * the values in tRNS. + */ + switch (in_ct) + { + case 0: /* gray */ + if (sample(std, in_ct, in_bd, x, 0, 0, 0) == + dp->this.transparent.red) + alpha = 0; + break; + + case 2: /* RGB */ + if (sample(std, in_ct, in_bd, x, 0, 0, 0) == + dp->this.transparent.red && + sample(std, in_ct, in_bd, x, 1, 0, 0) == + dp->this.transparent.green && + sample(std, in_ct, in_bd, x, 2, 0, 0) == + dp->this.transparent.blue) + alpha = 0; + break; + + default: + break; + } + } + /* Handle grayscale or RGB components. */ if ((in_ct & PNG_COLOR_MASK_COLOR) == 0) /* grayscale */ (void)gamma_component_validate("gray", &vi, - sample(std, in_ct, in_bd, x, 0), - sample(pRow, out_ct, out_bd, x, 0), alpha/*component*/, - vi.background_red); + sample(std, in_ct, in_bd, x, 0, 0, 0), + sample(pRow, out_ct, out_bd, x, 0, 0, 0), + alpha/*component*/, vi.background_red); else /* RGB or palette */ { (void)gamma_component_validate("red", &vi, in_ct == 3 ? in_palette[in_index].red : - sample(std, in_ct, in_bd, x, 0), + sample(std, in_ct, in_bd, x, 0, 0, 0), out_ct == 3 ? out_palette[out_index].red : - sample(pRow, out_ct, out_bd, x, 0), + sample(pRow, out_ct, out_bd, x, 0, 0, 0), alpha/*component*/, vi.background_red); (void)gamma_component_validate("green", &vi, in_ct == 3 ? in_palette[in_index].green : - sample(std, in_ct, in_bd, x, 1), + sample(std, in_ct, in_bd, x, 1, 0, 0), out_ct == 3 ? out_palette[out_index].green : - sample(pRow, out_ct, out_bd, x, 1), + sample(pRow, out_ct, out_bd, x, 1, 0, 0), alpha/*component*/, vi.background_green); (void)gamma_component_validate("blue", &vi, in_ct == 3 ? in_palette[in_index].blue : - sample(std, in_ct, in_bd, x, 2), + sample(std, in_ct, in_bd, x, 2, 0, 0), out_ct == 3 ? out_palette[out_index].blue : - sample(pRow, out_ct, out_bd, x, 2), + sample(pRow, out_ct, out_bd, x, 2, 0, 0), alpha/*component*/, vi.background_blue); } } @@ -8842,15 +10033,15 @@ gamma_end(png_structp ppIn, png_infop pi) * maxpc: maximum percentage error (as a percentage) */ static void -gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn, - PNG_CONST png_byte bit_depthIn, PNG_CONST int palette_numberIn, - PNG_CONST int interlace_typeIn, - PNG_CONST double file_gammaIn, PNG_CONST double screen_gammaIn, - PNG_CONST png_byte sbitIn, PNG_CONST int threshold_testIn, - PNG_CONST char *name, - PNG_CONST int use_input_precisionIn, PNG_CONST int scale16In, - PNG_CONST int expand16In, PNG_CONST int do_backgroundIn, - PNG_CONST png_color_16 *bkgd_colorIn, double bkgd_gammaIn) +gamma_test(png_modifier *pmIn, const png_byte colour_typeIn, + const png_byte bit_depthIn, const int palette_numberIn, + const int interlace_typeIn, + const double file_gammaIn, const double screen_gammaIn, + const png_byte sbitIn, const int threshold_testIn, + const char *name, + const int use_input_precisionIn, const int scale16In, + const int expand16In, const int do_backgroundIn, + const png_color_16 *bkgd_colorIn, double bkgd_gammaIn) { gamma_display d; context(&pmIn->this, fault); @@ -8885,7 +10076,7 @@ gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn, modification_reset(d.pm->modifications); - /* Get a png_struct for writing the image. */ + /* Get a png_struct for reading the image. */ pp = set_modifier_for_read(d.pm, &pi, d.this.id, name); standard_palette_init(&d.this); @@ -9024,9 +10215,13 @@ perform_gamma_threshold_tests(png_modifier *pm) /* Don't test more than one instance of each palette - it's pointless, in * fact this test is somewhat excessive since libpng doesn't make this * decision based on colour type or bit depth! + * + * CHANGED: now test two palettes and, as a side effect, images with and + * without tRNS. */ - while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/)) - if (palette_number == 0) + while (next_format(&colour_type, &bit_depth, &palette_number, + pm->test_lbg_gamma_threshold, pm->test_tRNS)) + if (palette_number < 2) { double test_gamma = 1.0; while (test_gamma >= .4) @@ -9049,11 +10244,11 @@ perform_gamma_threshold_tests(png_modifier *pm) } static void gamma_transform_test(png_modifier *pm, - PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth, - PNG_CONST int palette_number, - PNG_CONST int interlace_type, PNG_CONST double file_gamma, - PNG_CONST double screen_gamma, PNG_CONST png_byte sbit, - PNG_CONST int use_input_precision, PNG_CONST int scale16) + const png_byte colour_type, const png_byte bit_depth, + const int palette_number, + const int interlace_type, const double file_gamma, + const double screen_gamma, const png_byte sbit, + const int use_input_precision, const int scale16) { size_t pos = 0; char name[64]; @@ -9086,7 +10281,8 @@ static void perform_gamma_transform_tests(png_modifier *pm) png_byte bit_depth = 0; unsigned int palette_number = 0; - while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/)) + while (next_format(&colour_type, &bit_depth, &palette_number, + pm->test_lbg_gamma_transform, pm->test_tRNS)) { unsigned int i, j; @@ -9116,7 +10312,8 @@ static void perform_gamma_sbit_tests(png_modifier *pm) png_byte colour_type = 0, bit_depth = 0; unsigned int npalette = 0; - while (next_format(&colour_type, &bit_depth, &npalette, 1/*gamma*/)) + while (next_format(&colour_type, &bit_depth, &npalette, + pm->test_lbg_gamma_sbit, pm->test_tRNS)) if ((colour_type & PNG_COLOR_MASK_ALPHA) == 0 && ((colour_type == 3 && sbit < 8) || (colour_type != 3 && sbit < bit_depth))) @@ -9151,7 +10348,11 @@ static void perform_gamma_scale16_tests(png_modifier *pm) # ifndef PNG_MAX_GAMMA_8 # define PNG_MAX_GAMMA_8 11 # endif -# define SBIT_16_TO_8 PNG_MAX_GAMMA_8 +# if defined PNG_MAX_GAMMA_8 || PNG_LIBPNG_VER < 10700 +# define SBIT_16_TO_8 PNG_MAX_GAMMA_8 +# else +# define SBIT_16_TO_8 16 +# endif /* Include the alpha cases here. Note that sbit matches the internal value * used by the library - otherwise we will get spurious errors from the * internal sbit style approximation. @@ -9204,12 +10405,12 @@ static void perform_gamma_scale16_tests(png_modifier *pm) #if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ defined(PNG_READ_ALPHA_MODE_SUPPORTED) static void gamma_composition_test(png_modifier *pm, - PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth, - PNG_CONST int palette_number, - PNG_CONST int interlace_type, PNG_CONST double file_gamma, - PNG_CONST double screen_gamma, - PNG_CONST int use_input_precision, PNG_CONST int do_background, - PNG_CONST int expand_16) + const png_byte colour_type, const png_byte bit_depth, + const int palette_number, + const int interlace_type, const double file_gamma, + const double screen_gamma, + const int use_input_precision, const int do_background, + const int expand_16) { size_t pos = 0; png_const_charp base; @@ -9307,8 +10508,17 @@ static void gamma_composition_test(png_modifier *pm, } background.index = 193; /* rgb(193,193,193) to detect errors */ + if (!(colour_type & PNG_COLOR_MASK_COLOR)) { + /* Because, currently, png_set_background is always called with + * 'need_expand' false in this case and because the gamma test itself + * doesn't cause an expand to 8-bit for lower bit depths the colour must + * be reduced to the correct range. + */ + if (bit_depth < 8) + background.gray &= (png_uint_16)((1U << bit_depth)-1); + /* Grayscale input, we do not convert to RGB (TBD), so we must set the * background to gray - else libpng seems to fail. */ @@ -9357,9 +10567,18 @@ perform_gamma_composition_tests(png_modifier *pm, int do_background, /* Skip the non-alpha cases - there is no setting of a transparency colour at * present. - */ - while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/)) - if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0) + * + * TODO: incorrect; the palette case sets tRNS and, now RGB and gray do, + * however the palette case fails miserably so is commented out below. + */ + while (next_format(&colour_type, &bit_depth, &palette_number, + pm->test_lbg_gamma_composition, pm->test_tRNS)) + if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0 +#if 0 /* TODO: FIXME */ + /*TODO: FIXME: this should work */ + || colour_type == 3 +#endif + || (colour_type != 3 && palette_number != 0)) { unsigned int i, j; @@ -9578,7 +10797,7 @@ perform_gamma_test(png_modifier *pm, int summary) * be indexed adam7[y][x] and notice that the pass numbers are based at * 1, not 0 - the base libpng uses. */ -static PNG_CONST +static const png_byte adam7[8][8] = { { 1,6,4,6,2,6,4,6 }, @@ -9929,7 +11148,7 @@ perform_interlace_macro_validation(void) * The png_modifier code assumes that encodings[0] is sRGB and treats it * specially: do not change the first entry in this list! */ -static PNG_CONST color_encoding test_encodings[] = +static const color_encoding test_encodings[] = { /* sRGB: must be first in this list! */ /*gamma:*/ { 1/2.2, @@ -9951,6 +11170,11 @@ static PNG_CONST color_encoding test_encodings[] = /*red: */ { 0.716500716779386, 0.258728243040113, 0.000000000000000 }, /*green:*/ { 0.101020574397477, 0.724682314948566, 0.051211818965388 }, /*blue: */ { 0.146774385252705, 0.016589442011321, 0.773892783545073} }, +/* Fake encoding which selects just the green channel */ +/*gamma:*/ { 1.45/2.2, /* the 'Mac' gamma */ +/*red: */ { 0.716500716779386, 0.000000000000000, 0.000000000000000 }, +/*green:*/ { 0.101020574397477, 1.000000000000000, 0.051211818965388 }, +/*blue: */ { 0.146774385252705, 0.000000000000000, 0.773892783545073} }, }; /* signal handler @@ -10022,11 +11246,11 @@ static void signal_handler(int signum) /* main program */ int main(int argc, char **argv) { - volatile int summary = 1; /* Print the error summary at the end */ - volatile int memstats = 0; /* Print memory statistics at the end */ + int summary = 1; /* Print the error summary at the end */ + int memstats = 0; /* Print memory statistics at the end */ /* Create the given output file on success: */ - PNG_CONST char *volatile touch = NULL; + const char *touch = NULL; /* This is an array of standard gamma values (believe it or not I've seen * every one of these mentioned somewhere.) @@ -10042,6 +11266,10 @@ int main(int argc, char **argv) anon_context(&pm.this); + gnu_volatile(summary) + gnu_volatile(memstats) + gnu_volatile(touch) + /* Add appropriate signal handlers, just the ANSI specified ones: */ signal(SIGABRT, signal_handler); signal(SIGFPE, signal_handler); @@ -10088,14 +11316,30 @@ int main(int argc, char **argv) /* Store the test gammas */ pm.gammas = gammas; - pm.ngammas = (sizeof gammas) / (sizeof gammas[0]); + pm.ngammas = ARRAY_SIZE(gammas); pm.ngamma_tests = 0; /* default to off */ + /* Low bit depth gray images don't do well in the gamma tests, until + * this is fixed turn them off for some gamma cases: + */ +# ifdef PNG_WRITE_tRNS_SUPPORTED + pm.test_tRNS = 1; +# endif + pm.test_lbg = PNG_LIBPNG_VER >= 10600; + pm.test_lbg_gamma_threshold = 1; + pm.test_lbg_gamma_transform = PNG_LIBPNG_VER >= 10600; + pm.test_lbg_gamma_sbit = 1; + pm.test_lbg_gamma_composition = PNG_LIBPNG_VER >= 10700; + /* And the test encodings */ pm.encodings = test_encodings; - pm.nencodings = (sizeof test_encodings) / (sizeof test_encodings[0]); + pm.nencodings = ARRAY_SIZE(test_encodings); - pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */ +# if PNG_LIBPNG_VER < 10700 + pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */ +# else + pm.sbitlow = 1U; +# endif /* The following allows results to pass if they correspond to anything in the * transformed range [input-.5,input+.5]; this is is required because of the @@ -10121,7 +11365,11 @@ int main(int argc, char **argv) pm.maxout16 = .499; /* Error in *encoded* value */ pm.maxabs16 = .00005;/* 1/20000 */ pm.maxcalc16 =1./65535;/* +/-1 in 16 bits for compose errors */ - pm.maxcalcG = 1./((1<