diff --git a/ANNOUNCE b/ANNOUNCE index 226fafeca..959868b39 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -45,6 +45,8 @@ Version 1.6.9beta01 [December 26, 2013] Version 1.6.9beta02 [December 27, 2013] Added checks for libpng 1.5 to pngvalid.c. This supports the use of this version of pngvalid in libpng 1.5 + Merged with pngvalid.c from libpng-1.7 changes to create a single + pngvalid.c Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/CHANGES b/CHANGES index a8f60945b..4e62d7602 100644 --- a/CHANGES +++ b/CHANGES @@ -4770,6 +4770,8 @@ Version 1.6.9beta01 [December 26, 2013] Version 1.6.9beta02 [December 27, 2013] Added checks for libpng 1.5 to pngvalid.c. This supports the use of this version of pngvalid in libpng 1.5 + Merged with pngvalid.c from libpng-1.7 changes to create a single + pngvalid.c Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/contrib/libtests/pngvalid.c b/contrib/libtests/pngvalid.c index b67450e28..3e0459761 100644 --- a/contrib/libtests/pngvalid.c +++ b/contrib/libtests/pngvalid.c @@ -43,6 +43,12 @@ # include "../../png.h" #endif +#ifdef PNG_ZLIB_HEADER +# include PNG_ZLIB_HEADER +#else +# include /* For crc32 */ +#endif + /* pngvalid requires write support and one of the fixed or floating point APIs. */ #if defined(PNG_WRITE_SUPPORTED) &&\ @@ -91,8 +97,6 @@ typedef png_byte *png_const_bytep; # define png_const_structp png_structp #endif -#include /* For crc32 */ - #include /* For floating point constants */ #include /* For malloc */ #include /* For memcpy, memset */ @@ -352,11 +356,16 @@ 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) + unsigned int* palette_number, int no_low_depth_gray) { if (*bit_depth == 0) { - *colour_type = 0, *bit_depth = 1, *palette_number = 0; + *colour_type = 0; + if (no_low_depth_gray) + *bit_depth = 8; + else + *bit_depth = 1; + *palette_number = 0; return 1; } @@ -1874,6 +1883,7 @@ typedef struct png_modifier double maxout16; /* Maximum output value error */ double maxabs16; /* Absolute sample error 0..1 */ double maxcalc16;/* Absolute sample error 0..1 */ + double maxcalcG; /* Absolute sample error 0..1 */ double maxpc16; /* Percentage sample error 0..100% */ /* This is set by transforms that need to allow a higher limit, it is an @@ -1913,10 +1923,15 @@ typedef struct png_modifier /* Run the odd-sized image and interlace read/write tests? */ unsigned int test_size :1; - /* Run tests on reading with a combiniation of transforms, */ + /* Run tests on reading with a combination of transforms, */ unsigned int test_transform :1; - /* When to use the use_input_precision option: */ + /* When to use the use_input_precision option, this controls the gamma + * validation code checks. If set any value that is within the transformed + * range input-.5 to input+.5 will be accepted, otherwise the value must be + * within the normal limits. It should not be necessary to set this; the + * result should always be exact within the permitted error limits. + */ unsigned int use_input_precision :1; unsigned int use_input_precision_sbit :1; unsigned int use_input_precision_16to8 :1; @@ -1926,8 +1941,8 @@ typedef struct png_modifier */ unsigned int calculations_use_input_precision :1; - /* If set assume that the calculations are done in 16 bits even if both input - * and output are 8 bit or less. + /* If set assume that the calculations are done in 16 bits even if the sample + * depth is 8 bits. */ unsigned int assume_16_bit_calculations :1; @@ -1982,6 +1997,7 @@ modifier_init(png_modifier *pm) pm->test_uses_encoding = 0; pm->maxout8 = pm->maxpc8 = pm->maxabs8 = pm->maxcalc8 = 0; pm->maxout16 = pm->maxpc16 = pm->maxabs16 = pm->maxcalc16 = 0; + pm->maxcalcG = 0; pm->limit = 4E-3; pm->log8 = pm->log16 = 0; /* Means 'off' */ pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0; @@ -1996,6 +2012,7 @@ modifier_init(png_modifier *pm) pm->use_input_precision_sbit = 0; pm->use_input_precision_16to8 = 0; pm->calculations_use_input_precision = 0; + pm->assume_16_bit_calculations = 0; pm->test_gamma_threshold = 0; pm->test_gamma_transform = 0; pm->test_gamma_sbit = 0; @@ -2010,8 +2027,16 @@ modifier_init(png_modifier *pm) } #ifdef PNG_READ_TRANSFORMS_SUPPORTED + +/* This controls use of checks that explicitly know how libpng digitizes the + * samples in calculations; setting this circumvents simple error limit checking + * in the rgb_to_gray check, replacing it with an exact copy of the libpng 1.5 + * algorithm. + */ +#define DIGITIZE PNG_LIBPNG_VER < 10700 + /* If pm->calculations_use_input_precision is set then operations will happen - * with only 8 bit precision unless both the input and output bit depth are 16. + * with the precision of the input, not the precision of the output depth. * * If pm->assume_16_bit_calculations is set then even 8 bit calculations use 16 * bit precision. This only affects those of the following limits that pertain @@ -2019,8 +2044,8 @@ modifier_init(png_modifier *pm) * called directly. */ #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -static double digitize(PNG_CONST png_modifier *pm, double value, - int sample_depth, int do_round) +#if DIGITIZE +static double digitize(double value, int depth, int do_round) { /* 'value' is in the range 0 to 1, the result is the same value rounded to a * multiple of the digitization factor - 8 or 16 bits depending on both the @@ -2028,14 +2053,14 @@ static double digitize(PNG_CONST png_modifier *pm, double value, * rounding and 'do_round' should be 1, if it is 0 the digitized value will * be truncated. */ - PNG_CONST unsigned int digitization_factor = - (pm->assume_16_bit_calculations || sample_depth == 16) ? 65535 : 255; + PNG_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. */ if (value <= 0) value = 0; + else if (value >= 1) value = 1; @@ -2044,31 +2069,30 @@ static double digitize(PNG_CONST png_modifier *pm, double value, return floor(value)/digitization_factor; } #endif +#endif /* RGB_TO_GRAY */ -#if defined(PNG_READ_GAMMA_SUPPORTED) ||\ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED static double abserr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) { /* Absolute error permitted in linear values - affected by the bit depth of * the calculations. */ - if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 || - !pm->calculations_use_input_precision))) + if (pm->assume_16_bit_calculations || + (pm->calculations_use_input_precision ? in_depth : out_depth) == 16) return pm->maxabs16; else return pm->maxabs8; } -#endif -#ifdef PNG_READ_GAMMA_SUPPORTED static double calcerr(PNG_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). */ - if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 || - !pm->calculations_use_input_precision))) + if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16) return pm->maxcalc16; + else if (pm->assume_16_bit_calculations) + return pm->maxcalcG; else return pm->maxcalc8; } @@ -2078,8 +2102,8 @@ static double pcerr(PNG_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. */ - if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 || - !pm->calculations_use_input_precision))) + if (pm->assume_16_bit_calculations || + (pm->calculations_use_input_precision ? in_depth : out_depth) == 16) return pm->maxpc16 * .01; else return pm->maxpc8 * .01; @@ -2111,8 +2135,7 @@ static double outerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) if (out_depth == 4) return .90644-.5; - if (out_depth == 16 && (in_depth == 16 || - !pm->calculations_use_input_precision)) + if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16) return pm->maxout16; /* This is the case where the value was calculated at 8-bit precision then @@ -2145,8 +2168,7 @@ static double outlog(PNG_CONST png_modifier *pm, int in_depth, int out_depth) return pm->log8; } - if (out_depth == 16 && (in_depth == 16 || - !pm->calculations_use_input_precision)) + if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16) { if (pm->log16 == 0) return 65536; @@ -2171,8 +2193,8 @@ static double outlog(PNG_CONST png_modifier *pm, int in_depth, int out_depth) static int output_quantization_factor(PNG_CONST png_modifier *pm, int in_depth, int out_depth) { - if (out_depth == 16 && in_depth != 16 - && pm->calculations_use_input_precision) + if (out_depth == 16 && in_depth != 16 && + pm->calculations_use_input_precision) return 257; else return 1; @@ -3534,7 +3556,7 @@ make_transform_images(png_store *ps) /* Use next_format to enumerate all the combinations we test, including * generating multiple low bit depth palette images. */ - while (next_format(&colour_type, &bit_depth, &palette_number)) + while (next_format(&colour_type, &bit_depth, &palette_number, 0)) { int interlace_type; @@ -3843,8 +3865,15 @@ make_size(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo, make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7, width, height, 0); # endif +# if defined(PNG_WRITE_INTERLACING_SUPPORTED) || PNG_LIBPNG_VER > 10518 + /* This fails in 1.5.8 with a zlib stream error writing the rows of + * the internally generated interlaced images, but only when the + * read code is disabled: to be investigated. Probably an erroneous + * #define out of the zlib deflate reset. + */ make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7, width, height, 1); +# endif } } } @@ -5886,12 +5915,9 @@ transform_image_validate(transform_display *dp, png_const_structp pp, memset(out_palette, 0x5e, sizeof out_palette); - /* assume-8-bit-calculations means assume that if the input has 8 bit - * (or less) samples and the output has 16 bit samples the calculations - * will be done with 8 bit precision, not 16. - * - * TODO: fix this in libpng; png_set_expand_16 should cause 16 bit - * calculations to be used throughout. + /* use-input-precision means assume that if the input has 8 bit (or less) + * samples and the output has 16 bit samples the calculations will be done + * with 8 bit precision, not 16. */ if (in_ct == PNG_COLOR_TYPE_PALETTE || in_bd < 16) in_sample_depth = 8; @@ -5902,7 +5928,8 @@ transform_image_validate(transform_display *dp, png_const_structp pp, !dp->pm->calculations_use_input_precision) digitization_error = .5; - /* Else errors are at 8 bit precision, scale .5 in 8 bits to the 16 bits: + /* Else calculations are at 8 bit precision, and the output actually + * consists of scaled 8-bit values, so scale .5 in 8 bits to the 16 bits: */ else digitization_error = .5 * 257; @@ -6718,10 +6745,23 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this, { if (that->this.bit_depth == 16 || pm->assume_16_bit_calculations) { - /* The 16 bit case ends up producing a maximum error of about - * +/-5 in 65535, allow for +/-8 with the given gamma. + /* The computations have the form: + * + * r * rc + g * gc + b * bc + * + * Each component of which is +/-1/65535 from the gamma_to_1 table + * lookup, resulting in a base error of +/-6. The gamma_from_1 + * conversion adds another +/-2 in the 16-bit case and + * +/-(1<<(15-PNG_MAX_GAMMA_8)) in the 8-bit case. */ - that->pm->limit += pow(8./65535, data.gamma); + that->pm->limit += pow( +# if PNG_MAX_GAMMA_8 < 14 + (that->this.bit_depth == 16 ? 8. : + 6. + (1<<(15-PNG_MAX_GAMMA_8))) +# else + 8. +# endif + /65535, data.gamma); } else @@ -6729,8 +6769,23 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this, /* Rounding to 8 bits in the linear space causes massive errors which * will trigger the error check in transform_range_check. Fix that * here by taking the gamma encoding into account. + * + * 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. */ - that->pm->limit += pow(1./255, data.gamma); + that->pm->limit += pow( +# if DIGITIZE + 1.1 +# else + 1. +# endif + /255, data.gamma); } } @@ -6739,7 +6794,7 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this, /* With no gamma correction a large error comes from the truncation of the * calculation in the 8 bit case, allow for that here. */ - if (that->this.bit_depth != 16) + if (that->this.bit_depth != 16 && !pm->assume_16_bit_calculations) that->pm->limit += 4E-3; } } @@ -6884,9 +6939,14 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, image_pixel_convert_PLTE(that); /* Image now has RGB channels... */ +# if DIGITIZE { PNG_CONST png_modifier *pm = display->pm; - PNG_CONST unsigned int sample_depth = that->sample_depth; + 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)); int isgray; double r, g, b; double rlo, rhi, glo, ghi, blo, bhi, graylo, grayhi; @@ -6902,28 +6962,28 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, */ r = rlo = rhi = that->redf; rlo -= that->rede; - rlo = digitize(pm, rlo, sample_depth, 1/*round*/); + rlo = digitize(rlo, calc_depth, 1/*round*/); rhi += that->rede; - rhi = digitize(pm, rhi, sample_depth, 1/*round*/); + rhi = digitize(rhi, calc_depth, 1/*round*/); g = glo = ghi = that->greenf; glo -= that->greene; - glo = digitize(pm, glo, sample_depth, 1/*round*/); + glo = digitize(glo, calc_depth, 1/*round*/); ghi += that->greene; - ghi = digitize(pm, ghi, sample_depth, 1/*round*/); + ghi = digitize(ghi, calc_depth, 1/*round*/); b = blo = bhi = that->bluef; blo -= that->bluee; - blo = digitize(pm, blo, sample_depth, 1/*round*/); + blo = digitize(blo, calc_depth, 1/*round*/); bhi += that->greene; - bhi = digitize(pm, bhi, sample_depth, 1/*round*/); + bhi = digitize(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 = abserr(pm, sample_depth, sample_depth); + 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 @@ -6932,16 +6992,16 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, * where the real errors are introduced. */ r = pow(r, power); - rlo = digitize(pm, pow(rlo, power)-abse, sample_depth, 1); - rhi = digitize(pm, pow(rhi, power)+abse, sample_depth, 1); + rlo = digitize(pow(rlo, power)-abse, calc_depth, 1); + rhi = digitize(pow(rhi, power)+abse, calc_depth, 1); g = pow(g, power); - glo = digitize(pm, pow(glo, power)-abse, sample_depth, 1); - ghi = digitize(pm, pow(ghi, power)+abse, sample_depth, 1); + glo = digitize(pow(glo, power)-abse, calc_depth, 1); + ghi = digitize(pow(ghi, power)+abse, calc_depth, 1); b = pow(b, power); - blo = digitize(pm, pow(blo, power)-abse, sample_depth, 1); - bhi = digitize(pm, pow(bhi, power)+abse, sample_depth, 1); + blo = digitize(pow(blo, power)-abse, calc_depth, 1); + bhi = digitize(pow(bhi, power)+abse, calc_depth, 1); } /* Now calculate the actual gray values. Although the error in the @@ -6958,18 +7018,18 @@ 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 || sample_depth == 16; + PNG_CONST int do_round = data.gamma != 1 || calc_depth == 16; PNG_CONST double ce = 1. / 32768; - graylo = digitize(pm, rlo * (data.red_coefficient-ce) + + graylo = digitize(rlo * (data.red_coefficient-ce) + glo * (data.green_coefficient-ce) + - blo * (data.blue_coefficient-ce), sample_depth, do_round); + blo * (data.blue_coefficient-ce), gamma_depth, do_round); if (graylo <= 0) graylo = 0; - grayhi = digitize(pm, rhi * (data.red_coefficient+ce) + + grayhi = digitize(rhi * (data.red_coefficient+ce) + ghi * (data.green_coefficient+ce) + - bhi * (data.blue_coefficient+ce), sample_depth, do_round); + bhi * (data.blue_coefficient+ce), gamma_depth, do_round); if (grayhi >= 1) grayhi = 1; } @@ -6980,8 +7040,8 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, PNG_CONST double power = data.gamma; gray = pow(gray, power); - graylo = digitize(pm, pow(graylo, power), sample_depth, 1); - grayhi = digitize(pm, pow(grayhi, power), sample_depth, 1); + graylo = digitize(pow(graylo, power), sample_depth, 1); + grayhi = digitize(pow(grayhi, power), sample_depth, 1); } /* Now the error can be calculated. @@ -6999,7 +7059,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, err = fabs(graylo-gray); /* Check that this worked: */ - if (err > display->pm->limit) + if (err > pm->limit) { size_t pos = 0; char buffer[128]; @@ -7007,12 +7067,120 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, pos = safecat(buffer, sizeof buffer, pos, "rgb_to_gray error "); pos = safecatd(buffer, sizeof buffer, pos, err, 6); pos = safecat(buffer, sizeof buffer, pos, " exceeds limit "); - pos = safecatd(buffer, sizeof buffer, pos, - display->pm->limit, 6); + pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6); png_error(pp, buffer); } } } +# else /* DIGITIZE */ + { + double r = that->redf; + double re = that->rede; + double g = that->greenf; + double ge = that->greene; + 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; + } + + else 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.) + */ + 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; + } + + else + { + /* The calculation happens in linear space, and this produces much + * wider errors in the encoded space. These are handled here by + * factoring the errors in to the calculation. There are two table + * lookups in the calculation and each introduces a quantization + * error defined by the table size. + */ + PNG_CONST 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< 1) rhi = 1; + r -= re + in_qe; if (r < 0) r = 0; + ghi = g + ge + in_qe; if (ghi > 1) ghi = 1; + g -= ge + in_qe; if (g < 0) g = 0; + bhi = b + be + in_qe; if (bhi > 1) bhi = 1; + b -= be + in_qe; if (b < 0) b = 0; + + r = pow(r, g1)*(1-DBL_EPSILON); rhi = pow(rhi, g1)*(1+DBL_EPSILON); + g = pow(g, g1)*(1-DBL_EPSILON); ghi = pow(ghi, g1)*(1+DBL_EPSILON); + b = pow(b, g1)*(1-DBL_EPSILON); bhi = pow(bhi, g1)*(1+DBL_EPSILON); + + /* Work out the lower and upper bounds for the gray value in the + * encoded space, then work out an average and error. Remove the + * 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; + if (gray <= 0) + gray = 0; + else + { + gray *= (1 - 6 * DBL_EPSILON); + gray = pow(gray, data.gamma) * (1-DBL_EPSILON); + } + + grayhi = rhi * data.red_coefficient + ghi * data.green_coefficient + + bhi * data.blue_coefficient + 1./32768 + out_qe; + grayhi *= (1 + 6 * DBL_EPSILON); + if (grayhi >= 1) + grayhi = 1; + else + grayhi = pow(grayhi, data.gamma) * (1+DBL_EPSILON); + + err = (grayhi - gray) / 2; + gray = (grayhi + gray) / 2; + + if (err <= in_qe) + err = gray * DBL_EPSILON; + + else + err -= in_qe; + + /* Validate that the error is within limits (this has caused + * problems before, it's much easier to detect them here.) + */ + if (err > pm->limit) + { + size_t pos = 0; + char buffer[128]; + + pos = safecat(buffer, sizeof buffer, pos, "rgb_to_gray error "); + 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); + } + } + } +# endif /* !DIGITIZE */ that->bluef = that->greenf = that->redf = gray; that->bluee = that->greene = that->rede = err; @@ -7061,7 +7229,7 @@ IT(rgb_to_gray); * int background_gamma_code, int need_expand, * png_fixed_point background_gamma) * - * As with rgb_to_gray this ignores the gamma (at present.) + * This ignores the gamma (at present.) */ #define data ITDATA(background) static image_pixel data; @@ -7072,6 +7240,7 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this, { png_byte colour_type, bit_depth; png_byte random_bytes[8]; /* 8 bytes - 64 bits - the biggest pixel */ + int expand; png_color_16 back; /* We need a background colour, because we don't know exactly what transforms @@ -7089,10 +7258,14 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this, { colour_type = PNG_COLOR_TYPE_RGB; bit_depth = 8; + expand = 0; /* passing in an RGB not a pixel index */ } else + { bit_depth = that->this.bit_depth; + expand = 1; + } image_pixel_init(&data, random_bytes, colour_type, bit_depth, 0/*x*/, 0/*unused: palette*/); @@ -7113,11 +7286,9 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this, back.gray = (png_uint_16)data.red; # ifdef PNG_FLOATING_POINT_SUPPORTED - png_set_background(pp, &back, PNG_BACKGROUND_GAMMA_FILE, 1/*need expand*/, - 0); + png_set_background(pp, &back, PNG_BACKGROUND_GAMMA_FILE, expand, 0); # else - png_set_background_fixed(pp, &back, PNG_BACKGROUND_GAMMA_FILE, - 1/*need expand*/, 0); + png_set_background_fixed(pp, &back, PNG_BACKGROUND_GAMMA_FILE, expand, 0); # endif this->next->set(this->next, that, pp, pi); @@ -7449,7 +7620,7 @@ 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)) + while (next_format(&colour_type, &bit_depth, &palette_number, 0)) { png_uint_32 counter = 0; size_t base_pos; @@ -8134,14 +8305,25 @@ gamma_component_validate(PNG_CONST char *name, PNG_CONST validate_info *vi, * passed. Don't do these additional tests here - just log the * original [es_lo..es_hi] values. */ - if (pass == 0 && vi->use_input_precision) + if (pass == 0 && vi->use_input_precision && vi->dp->sbit) { /* Ok, something is wrong - this actually happens in current libpng * 16-to-8 processing. Assume that the input value (id, adjusted * for sbit) can be anywhere between value-.5 and value+.5 - quite a * large range if sbit is low. + * + * NOTE: at present because the libpng gamma table stuff has been + * changed to use a rounding algorithm to correct errors in 8-bit + * calculations the precise sbit calculation (a shift) has been + * lost. This can result in up to a +/-1 error in the presence of + * an sbit less than the bit depth. */ - double tmp = (isbit - .5)/sbit_max; +# if PNG_LIBPNG_VER < 10700 +# define SBIT_ERROR .5 +# else +# define SBIT_ERROR 1. +# endif + double tmp = (isbit - SBIT_ERROR)/sbit_max; if (tmp <= 0) tmp = 0; @@ -8160,10 +8342,10 @@ gamma_component_validate(PNG_CONST char *name, PNG_CONST validate_info *vi, if (is_lo < 0) is_lo = 0; - tmp = (isbit + .5)/sbit_max; + tmp = (isbit + SBIT_ERROR)/sbit_max; - if (tmp <= 0) - tmp = 0; + if (tmp >= 1) + tmp = 1; else if (alpha >= 0 && vi->file_inverse > 0 && tmp < 1) tmp = pow(tmp, vi->file_inverse); @@ -8480,7 +8662,7 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp, * Because there is limited precision in the input it is arguable that * an acceptable result is any valid result from input-.5 to input+.5. * The basic tests below do not do this, however if 'use_input_precision' - * is set a subsequent test is performed below. + * is set a subsequent test is performed above. */ PNG_CONST unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U; int processing; @@ -8822,7 +9004,7 @@ perform_gamma_threshold_tests(png_modifier *pm) * fact this test is somewhat excessive since libpng doesn't make this * decision based on colour type or bit depth! */ - while (next_format(&colour_type, &bit_depth, &palette_number)) + while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/)) if (palette_number == 0) { double test_gamma = 1.0; @@ -8883,7 +9065,7 @@ 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)) + while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/)) { unsigned int i, j; @@ -8913,7 +9095,7 @@ 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)) + while (next_format(&colour_type, &bit_depth, &npalette, 1/*gamma*/)) if ((colour_type & PNG_COLOR_MASK_ALPHA) == 0 && ((colour_type == 3 && sbit < 8) || (colour_type != 3 && sbit < bit_depth))) @@ -8948,6 +9130,7 @@ 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 /* 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. @@ -8965,28 +9148,28 @@ static void perform_gamma_scale16_tests(png_modifier *pm) fabs(pm->gammas[j]/pm->gammas[i]-1) >= PNG_GAMMA_THRESHOLD) { gamma_transform_test(pm, 0, 16, 0, pm->interlace_type, - 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8, + 1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8, pm->use_input_precision_16to8, 1 /*scale16*/); if (fail(pm)) return; gamma_transform_test(pm, 2, 16, 0, pm->interlace_type, - 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8, + 1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8, pm->use_input_precision_16to8, 1 /*scale16*/); if (fail(pm)) return; gamma_transform_test(pm, 4, 16, 0, pm->interlace_type, - 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8, + 1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8, pm->use_input_precision_16to8, 1 /*scale16*/); if (fail(pm)) return; gamma_transform_test(pm, 6, 16, 0, pm->interlace_type, - 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8, + 1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8, pm->use_input_precision_16to8, 1 /*scale16*/); if (fail(pm)) @@ -9132,7 +9315,7 @@ 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)) + while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/)) if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0) { unsigned int i, j; @@ -9154,31 +9337,46 @@ perform_gamma_composition_tests(png_modifier *pm, int do_background, static void init_gamma_errors(png_modifier *pm) { - pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0; - pm->error_color_8 = 0; - pm->error_indexed = 0; - pm->error_gray_16 = pm->error_color_16 = 0; + /* Use -1 to catch tests that were not actually run */ + pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = -1.; + pm->error_color_8 = -1.; + pm->error_indexed = -1.; + pm->error_gray_16 = pm->error_color_16 = -1.; } static void -summarize_gamma_errors(png_modifier *pm, png_const_charp who, int low_bit_depth) +print_one(const char *leader, double err) { + if (err != -1.) + printf(" %s %.5f\n", leader, err); +} + +static void +summarize_gamma_errors(png_modifier *pm, png_const_charp who, int low_bit_depth, + int indexed) +{ + fflush(stderr); + if (who) - printf("Gamma correction with %s:\n", who); + printf("\nGamma correction with %s:\n", who); + + else + printf("\nBasic gamma correction:\n"); if (low_bit_depth) { - printf(" 2 bit gray: %.5f\n", pm->error_gray_2); - printf(" 4 bit gray: %.5f\n", pm->error_gray_4); - printf(" 8 bit gray: %.5f\n", pm->error_gray_8); - printf(" 8 bit color: %.5f\n", pm->error_color_8); - printf(" indexed: %.5f\n", pm->error_indexed); + print_one(" 2 bit gray: ", pm->error_gray_2); + print_one(" 4 bit gray: ", pm->error_gray_4); + print_one(" 8 bit gray: ", pm->error_gray_8); + print_one(" 8 bit color:", pm->error_color_8); + if (indexed) + print_one(" indexed: ", pm->error_indexed); } -#ifdef DO_16BIT - printf(" 16 bit gray: %.5f\n", pm->error_gray_16); - printf(" 16 bit color: %.5f\n", pm->error_color_16); -#endif + print_one("16 bit gray: ", pm->error_gray_16); + print_one("16 bit color:", pm->error_color_16); + + fflush(stdout); } static void @@ -9204,18 +9402,9 @@ perform_gamma_test(png_modifier *pm, int summary) /* Now some real transforms. */ if (pm->test_gamma_transform) { - init_gamma_errors(pm); - /*TODO: remove this. Necessary because the current libpng - * implementation works in 8 bits: - */ - if (pm->test_gamma_expand16) - pm->calculations_use_input_precision = 1; - perform_gamma_transform_tests(pm); - if (!calculations_use_input_precision) - pm->calculations_use_input_precision = 0; - if (summary) { + fflush(stderr); printf("Gamma correction error summary\n\n"); printf("The printed value is the maximum error in the pixel values\n"); printf("calculated by the libpng gamma correction code. The error\n"); @@ -9227,10 +9416,25 @@ perform_gamma_test(png_modifier *pm, int summary) printf("less than 1 for formats with fewer than 8 bits and a small\n"); printf("number (typically less than 5) for the 16 bit formats.\n"); printf("For performance reasons the value for 16 bit formats\n"); - printf("increases when the image file includes an sBIT chunk.\n\n"); - - summarize_gamma_errors(pm, 0/*who*/, 1); + printf("increases when the image file includes an sBIT chunk.\n"); + fflush(stdout); } + + init_gamma_errors(pm); + /*TODO: remove this. Necessary because the current libpng + * implementation works in 8 bits: + */ + if (pm->test_gamma_expand16) + pm->calculations_use_input_precision = 1; + perform_gamma_transform_tests(pm); + if (!calculations_use_input_precision) + pm->calculations_use_input_precision = 0; + + if (summary) + summarize_gamma_errors(pm, 0/*who*/, 1/*low bit depth*/, 1/*indexed*/); + + if (fail(pm)) + return; } /* The sbit tests produce much larger errors: */ @@ -9240,7 +9444,10 @@ perform_gamma_test(png_modifier *pm, int summary) perform_gamma_sbit_tests(pm); if (summary) - summarize_gamma_errors(pm, "sBIT", pm->sbitlow < 8U); + summarize_gamma_errors(pm, "sBIT", pm->sbitlow < 8U, 1/*indexed*/); + + if (fail(pm)) + return; } #ifdef DO_16BIT /* Should be READ_16BIT_SUPPORTED */ @@ -9252,10 +9459,15 @@ perform_gamma_test(png_modifier *pm, int summary) if (summary) { - printf("Gamma correction with 16 to 8 bit reduction:\n"); + fflush(stderr); + printf("\nGamma correction with 16 to 8 bit reduction:\n"); printf(" 16 bit gray: %.5f\n", pm->error_gray_16); printf(" 16 bit color: %.5f\n", pm->error_color_16); + fflush(stdout); } + + if (fail(pm)) + return; } #endif @@ -9279,7 +9491,10 @@ perform_gamma_test(png_modifier *pm, int summary) pm->maxout8 = maxout8; if (summary) - summarize_gamma_errors(pm, "background", 1); + summarize_gamma_errors(pm, "background", 1, 0/*indexed*/); + + if (fail(pm)) + return; } #endif @@ -9304,7 +9519,10 @@ perform_gamma_test(png_modifier *pm, int summary) pm->calculations_use_input_precision = 0; if (summary) - summarize_gamma_errors(pm, "alpha mode", 1); + summarize_gamma_errors(pm, "alpha mode", 1, 0/*indexed*/); + + if (fail(pm)) + return; } #endif } @@ -9812,6 +10030,19 @@ int main(int argc, char **argv) /* Default to error on warning: */ pm.this.treat_warnings_as_errors = 1; + /* Default assume_16_bit_calculations appropriately; this tells the checking + * code that 16-bit arithmetic is used for 8-bit samples when it would make a + * difference. + */ + pm.assume_16_bit_calculations = PNG_LIBPNG_VER >= 10700; + + /* Currently 16 bit expansion happens at the end of the pipeline, so the + * calculations are done in the input bit depth not the output. + * + * TODO: fix this + */ + pm.calculations_use_input_precision = 1U; + /* Store the test gammas */ pm.gammas = gammas; pm.ngammas = (sizeof gammas) / (sizeof gammas[0]); @@ -9822,13 +10053,16 @@ int main(int argc, char **argv) pm.nencodings = (sizeof test_encodings) / (sizeof test_encodings[0]); pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */ + /* 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 - * way libpng treates the 16_TO_8 flag when building the gamma tables. + * way libpng treates the 16_TO_8 flag when building the gamma tables in + * releases up to 1.6.0. * * TODO: review this */ pm.use_input_precision_16to8 = 1U; + pm.use_input_precision_sbit = 1U; /* because libpng now rounds sBIT */ /* Some default values (set the behavior for 'make check' here). * These values simply control the maximum error permitted in the gamma @@ -9839,11 +10073,12 @@ int main(int argc, char **argv) */ pm.maxout8 = .1; /* Arithmetic error in *encoded* value */ pm.maxabs8 = .00005; /* 1/20000 */ - pm.maxcalc8 = .004; /* +/-1 in 8 bits for compose errors */ + pm.maxcalc8 = 1./255; /* +/-1 in 8 bits for compose errors */ pm.maxpc8 = .499; /* I.e., .499% fractional error */ pm.maxout16 = .499; /* Error in *encoded* value */ pm.maxabs16 = .00005;/* 1/20000 */ - pm.maxcalc16 =.000015;/* +/-1 in 16 bits for compose errors */ + pm.maxcalc16 =1./65535;/* +/-1 in 16 bits for compose errors */ + pm.maxcalcG = 1./((1<