diff --git a/ANNOUNCE b/ANNOUNCE index 4c10106cd..c5021fd48 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -28,6 +28,23 @@ Changes since the last public release (1.6.0): Version 1.7.0alpha01 [December 10, 2012] Started 1.7.0 branch from libpng-1.6.0beta33. + Made 8-bit compose and rgb_to_grayscale accuracy improvements. These + changes cause 16-bit arithmetic to be used for 8-bit data in the gamma + corrected compose and grayscale operations. The arithmetic errors have + three sources all of which are fixed in this commit: + 1) 8-bit linear calculations produce massive errors for lower intensity + values. + 2) The old 16-bit "16 to 8" gamma table code erroneously wrote the lowest + output value into a table entry which corresponded to multiple output + values (so where the value written should have been the closest to the + transformed input value.) + 3) In a number of cases the code to access the 16-bit table did not round; + it did a simple shift, which was wrong and made the side effects of (2) + even worse. + The new gamma code does not have the 16-to-8 problem at the cost of slighly + more calculations and the algorithm used to minimize the number of + calculations has been extended to all the 16-bit tables; it has advantages + for any significant gamma correction. Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/CHANGES b/CHANGES index 72735b07f..e1d2dfcc3 100644 --- a/CHANGES +++ b/CHANGES @@ -4313,6 +4313,23 @@ Version 1.6.0beta33 [December 10, 2012] Version 1.7.0alpha01 [December 10, 2012] Started 1.7.0 branch from libpng-1.6.0beta33. + Made 8-bit compose and rgb_to_grayscale accuracy improvements. These + changes cause 16-bit arithmetic to be used for 8-bit data in the gamma + corrected compose and grayscale operations. The arithmetic errors have + three sources all of which are fixed in this commit: + 1) 8-bit linear calculations produce massive errors for lower intensity + values. + 2) The old 16-bit "16 to 8" gamma table code erroneously wrote the lowest + output value into a table entry which corresponded to multiple output + values (so where the value written should have been the closest to the + transformed input value.) + 3) In a number of cases the code to access the 16-bit table did not round; + it did a simple shift, which was wrong and made the side effects of (2) + even worse. + The new gamma code does not have the 16-to-8 problem at the cost of slighly + more calculations and the algorithm used to minimize the number of + calculations has been extended to all the 16-bit tables; it has advantages + for any significant gamma correction. 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 a5fa68d0b..8890bd929 100644 --- a/contrib/libtests/pngvalid.c +++ b/contrib/libtests/pngvalid.c @@ -344,11 +344,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; } @@ -1828,6 +1833,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 @@ -1870,7 +1876,12 @@ typedef struct png_modifier /* Run tests on reading with a combiniation 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; @@ -1880,8 +1891,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; @@ -1936,6 +1947,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; @@ -1950,6 +1962,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; @@ -1964,8 +1977,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 < 10600 + /* 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 @@ -1973,8 +1994,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 @@ -1982,14 +2003,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; @@ -1998,31 +2019,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; } @@ -2032,8 +2052,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; @@ -2065,8 +2085,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 @@ -2099,8 +2118,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; @@ -2125,8 +2143,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; @@ -3473,7 +3491,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; @@ -5784,12 +5802,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; @@ -5800,7 +5815,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; @@ -6616,10 +6632,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 + * convertion 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 @@ -6637,7 +6666,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; } } @@ -6782,9 +6811,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; @@ -6800,28 +6834,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 @@ -6830,16 +6864,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 @@ -6856,18 +6890,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; } @@ -6878,8 +6912,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. @@ -6897,7 +6931,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]; @@ -6905,12 +6939,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 convertions 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; @@ -6959,7 +7101,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; @@ -6970,6 +7112,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 @@ -6987,10 +7130,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*/); @@ -7011,11 +7158,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); @@ -7347,7 +7492,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; @@ -8032,14 +8177,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 < 10600 +# define SBIT_ERROR .5 +# else +# define SBIT_ERROR 1. +# endif + double tmp = (isbit - SBIT_ERROR)/sbit_max; if (tmp <= 0) tmp = 0; @@ -8058,10 +8214,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); @@ -8378,7 +8534,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; @@ -8720,7 +8876,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; @@ -8781,7 +8937,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; @@ -8811,7 +8967,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))) @@ -8846,6 +9002,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. @@ -8863,28 +9020,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)) @@ -9030,7 +9187,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; @@ -9052,31 +9209,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 @@ -9102,18 +9274,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"); @@ -9125,10 +9288,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: */ @@ -9138,7 +9316,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 */ @@ -9150,10 +9331,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 @@ -9177,7 +9363,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 @@ -9202,7 +9391,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 } @@ -9710,6 +9902,22 @@ 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. + * + * TODO: IMPORTANT; set to '>= 10600' below to enable development, change + * this to the correct value before release! + */ + pm.assume_16_bit_calculations = PNG_LIBPNG_VER >= 10600; + + /* 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]); @@ -9720,13 +9928,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 @@ -9737,11 +9948,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<>16 + * + * The command arguments are: + * + * scale target source + * + * and the program works out a pair of numbers, mult and add, that evaluate: + * + * number * target + * round( --------------- ) + * source + * + * exactly for number in the range 0..source + */ +#define _ISOC99_SOURCE 1 + +#include +#include +#include +#include + +static double minerr; +static unsigned long minmult, minadd, minshift; +static long mindelta; + +static int +test(unsigned long target, unsigned long source, unsigned long mult, + long add, unsigned long shift, long delta) +{ + unsigned long i; + double maxerr = 0; + double rs = (double)target/source; + + for (i=0; i<=source; ++i) + { + unsigned long t = i*mult+add; + double err = fabs((t >> shift) - i*rs); + + if (err > minerr) + return 0; + + if (err > maxerr) + maxerr = err; + } + + if (maxerr < minerr) + { + minerr = maxerr; + minmult = mult; + minadd = add; + minshift = shift; + mindelta = delta; + } + + return maxerr < .5; +} + +static int +dotest(unsigned long target, unsigned long source, unsigned long mult, + long add, unsigned long shift, long delta, int print) +{ + if (test(target, source, mult, add, shift, delta)) + { + if (print & 4) + printf(" {%11lu,%6ld /* >>%lu */ }, /* %lu/%lu */\n", + mult, add, shift, target, source); + + else if (print & 2) + printf(" {%11lu,%6ld,%3lu }, /* %lu/%lu */\n", + mult, add, shift, target, source); + + else if (print) + printf("number * %lu/%lu = (number * %lu + %ld) >> %lu [delta %ld]\n", + target, source, mult, add, shift, delta); + + return 1; + } + + return 0; +} + +static int +find(unsigned long target, unsigned long source, int print, int fixshift) +{ + unsigned long shift = 0; + unsigned long shiftlim = 0; + + /* In the final math the sum is at most (source*mult+add) >> shift, so: + * + * source*mult+add < 1<<32 + * mult < (1<<32)/source + * + * but: + * + * mult = (target<>1)) / source; + long delta; + long limit = 1; /* seems to be sufficient */ + long add, start, end; + + end = 1<>%lu */ }, /* %lu/%lu ERROR: .5+%g*/\n", + minmult, minadd, minshift, target, source, minerr-.5); + + else if (print & 2) + printf(" {%11lu,%6ld,%3lu }, /* %lu/%lu ERROR: .5+%g*/\n", + minmult, minadd, minshift, target, source, minerr-.5); + + else if (print) + printf( + "number * %lu/%lu ~= (number * %lu + %ld) >> %lu +/-.5+%g [delta %ld]\n", + target, source, minmult, minadd, minshift, minerr-.5, mindelta); + + return 0; +} + +static void +usage(const char *prog) +{ + fprintf(stderr, + "usage: %s {--denominator|--maxshift|--code} target {source}\n" + " For each 'source' prints 'mult' and 'add' such that:\n\n" + " (number * mult + add) >> 16 = round(number*target/source)\n\n" + " for all integer values of number in the range 0..source.\n\n" + " --denominator: swap target and source (specify a single source first\n" + " and follow with multiple targets.)\n" + " --maxshift: find the lowest shift value that works for all the\n" + " repeated 'source' values\n" + " --code: output C code for array/structure initialization\n", + prog); + exit(1); +} + +int +main(int argc, const char **argv) +{ + int i, err = 0, maxshift = 0, firstsrc = 1, code = 0, denominator = 0; + unsigned long target, shift = 0; + + while (argc > 1) + { + if (strcmp(argv[firstsrc], "--maxshift") == 0) + { + maxshift = 1; + ++firstsrc; + } + + else if (strcmp(argv[firstsrc], "--code") == 0) + { + code = 1; + ++firstsrc; + } + + else if (strcmp(argv[firstsrc], "--denominator") == 0) + { + denominator = 1; + ++firstsrc; + } + + else + break; + } + + + if (argc < 2+firstsrc) + usage(argv[0]); + + target = strtoul(argv[firstsrc++], 0, 0); + if (target == 0) usage(argv[0]); + + for (i=firstsrc; i shift) shift = minshift; + } + + if (maxshift) for (i=firstsrc; ifree_me) { + info_ptr->valid &= ~PNG_INFO_tRNS; png_free(png_ptr, info_ptr->trans_alpha); info_ptr->trans_alpha = NULL; - info_ptr->valid &= ~PNG_INFO_tRNS; + info_ptr->num_trans = 0; } #endif @@ -732,29 +731,6 @@ png_convert_to_rfc1123_buffer(char out[29], png_const_timep ptime) return 1; } - -# if PNG_LIBPNG_VER < 10700 -/* To do: remove the following from libpng-1.7 */ -/* Original API that uses a private buffer in png_struct. - * Deprecated because it causes png_struct to carry a spurious temporary - * buffer (png_struct::time_buffer), better to have the caller pass this in. - */ -png_const_charp PNGAPI -png_convert_to_rfc1123(png_structrp png_ptr, png_const_timep ptime) -{ - if (png_ptr != NULL) - { - /* The only failure above if png_ptr != NULL is from an invalid ptime */ - if (!png_convert_to_rfc1123_buffer(png_ptr->time_buffer, ptime)) - png_warning(png_ptr, "Ignoring invalid time value"); - - else - return png_ptr->time_buffer; - } - - return NULL; -} -# endif # endif /* PNG_TIME_RFC1123_SUPPORTED */ #endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ @@ -2287,7 +2263,7 @@ void /* PRIVATE */ png_colorspace_set_rgb_coefficients(png_structrp png_ptr) { /* Set the rgb_to_gray coefficients from the colorspace. */ - if (!png_ptr->rgb_to_gray_coefficients_set && + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_RGB_TO_GRAY_SET) == 0 && (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) { /* png_set_background has not been called, get the coefficients from the Y @@ -3263,6 +3239,7 @@ png_gamma_significant(png_fixed_point gamma_val) #ifdef PNG_READ_GAMMA_SUPPORTED /* A local convenience routine. */ +#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED static png_fixed_point png_product2(png_fixed_point a, png_fixed_point b) { @@ -3283,6 +3260,7 @@ png_product2(png_fixed_point a, png_fixed_point b) return 0; /* overflow */ } +#endif /* The inverse of the above. */ png_fixed_point @@ -3661,6 +3639,7 @@ png_gamma_correct(png_structrp png_ptr, unsigned int value, return png_gamma_16bit_correct(value, gamma_val); } +#if 0 /* Internal function to build a single 16-bit table - the table consists of * 'num' 256 entry subtables, where 'num' is determined by 'shift' - the amount * to shift the input values right (or 16-number_of_signifiant_bits). @@ -3670,7 +3649,7 @@ png_gamma_correct(png_structrp png_ptr, unsigned int value, * should be somewhere that will be cleaned. */ static void -png_build_16bit_table(png_structrp png_ptr, png_uint_16pp *ptable, +png_build_16bit_table(png_structrp png_ptr, png_uint_16p *ptable, PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val) { /* Various values derived from 'shift': */ @@ -3751,7 +3730,7 @@ png_build_16to8_table(png_structrp png_ptr, png_uint_16pp *ptable, /* 'num' is the number of tables and also the number of low bits of low * bits of the input 16-bit value used to select a table. Each table is - * itself index by the high 8 bits of the value. + * itself indexed by the high 8 bits of the value. */ for (i = 0; i < num; i++) table[i] = (png_uint_16p)png_malloc(png_ptr, @@ -3818,6 +3797,504 @@ png_build_8bit_table(png_structrp png_ptr, png_bytepp ptable, table[i] = (png_byte)i; } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +static void +png_build_8to16_table(png_structrp png_ptr, png_uint_16pp ptable, + PNG_CONST png_fixed_point gamma_val) +{ + unsigned int i; + png_uint_16p table = *ptable = (png_uint_16p)png_malloc(png_ptr, + sizeof (png_uint_16[256])); + + if (png_gamma_significant(gamma_val)) for (i=0; i<256; i++) + table[i] = png_gamma_16bit_correct(i*257, gamma_val); + + else for (i=0; i<256; ++i) + table[i] = (png_uint_16)(i*257); +} + +static void +png_build_16to8_table(png_structrp png_ptr, png_bytepp ptable8, + png_uint_16pp ptable16, png_fixed_point gamma_val, int gamma_shift) + /* Build a table of (1<<(16-gamma_shift))+1 entries to find 8-bit values + * encoded by gamma_val. The table is to be indexed with a 16-bit value + * scaled down by gamma_shift: + * + * table[(value + (1<<(gamma_shift-1)) >> gamma_shift] + * + * Because of the rounding the table is 2^(16-gamma_shift)+1 entries. + * Each table entry must contain value^gamma_val, rounded to 8 bits. + */ +{ + unsigned int size = 1U << (16-gamma_shift); + unsigned int add = (gamma_shift > 0 ? 1U<<(gamma_shift-1) : 0U); + png_bytep table8; + png_uint_16p table16; + unsigned int i; + + /* If 'table8' it gets the output values as 8-bit bytes, if 'table16' is set + * the same values are scaled by 257 to give 16-bit values. + */ + if (ptable8) + { + table8 = png_voidcast(png_bytep, png_malloc(png_ptr, size+1)); + *ptable8 = table8; + table8[0] = 0; + } + + else + table8 = NULL; + + if (ptable16) + { + table16 = png_voidcast(png_uint_16p, png_malloc(png_ptr, + sizeof (png_uint_16) * (size+1))); + *ptable16 = table16; + table16[0] = 0; + } + + else + table16 = NULL; + + i = 1; + +# define FILL(val) do {\ + if (table8) table8[i] = (val);\ + if (table16) table16[i] = (png_uint_16)((val)*257U);\ + ++i; + } while (0) + + if (gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED) + { + /* gamma < 1; the output values will jump by more than one for each table + * entry initially. + */ + png_byte last_out = 0; + + while (i < size) + { + png_byte out = (png_byte)PNG_DIV257(png_gamma_16bit_correct( + i << gamma_shift, gamma_val)); + + FILL(out); + + if (out <= last_out) + { + /* The table values are no longer advancing at each step, swap to + * finding the *last* table index corresponding to this value + * of 'out' - this is the one that corresponds to out+.5, below this + * is calculated as out*257+128; the exact value would be + * out*257+128.5, so we get a lower value, rounding ensures that we + * fill all the table entries corresponding to 'out' (with some very + * small chance of error at the boundaries.) + */ + png_fixed_point gamma_inv = png_reciprocal(gamma_val); + + while (out < 255 && i < size) + { + unsigned int next_index = (png_gamma_16bit_correct(out*257+128, + gamma_inv) + add) >> gamma_shift; + + while (i <= next_index && i < size) + FILL(out); + + ++out; + } + + break; /* fill remaining entries with 255 */ + } + } + } + + else if (gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED) + { + /* gamma > 1; initially multiple table entries will correspond to the same + * value, invert the above loops. + */ + png_fixed_point gamma_inv = png_reciprocal(gamma_val); + png_byte out = 0; + + while (out < 255) + { + unsigned int next_index = (png_gamma_16bit_correct(out*257+128, + gamma_inv) + add) >> gamma_shift; + + /* If this 'out' value falls in the same slot as the last one + * recalculate the value for this slot. + */ + if (next_index <= i) + { + i = next_index; + break; + } + + while (i <= next_index && i < size) + FILL(out); + + ++out; + } + + while (i < size && out < 255) + { + out = (png_byte)PNG_DIV257(png_gamma_16bit_correct(i << gamma_shift, + gamma_val)); + FILL(out); + } + } + + else /* gamma_val not significant */ + { + png_byte out = 0; /* current output value */ + + add += 128; + + while (out < 255 && i < size) { + unsigned int next_index = (out*257+add) >> gamma_shift; + + while (i <= next_index && i < size) + FILL(out); + + ++out; + } + } + + /* Fill in the remainder of the table */ + while (i <= size) + FILL(255); +} +#endif /* BACKGROUND || ALPHA_MODE || RGB_TO_GRAY */ +#endif /* UNUSED */ + +#define PNG_GAMMA_TABLE_8 0 /* 8-bit entries in png_byte */ +#define PNG_GAMMA_TABLE_8_IN_16 1 /* 8-bit entries * 257 in png_uint_16 */ +#define PNG_GAMMA_TABLE_16 2 /* 16-bit entries in png_uint_16 */ + +typedef struct +{ + png_fixed_point gamma; + png_uint_32 mult; + unsigned int add; + unsigned int shift; /* input value is (i * mult + add) >> shift */ + unsigned int output; /* One of the above values */ + int adjust; /* Divide or multiple output by 257 */ + png_voidp table; /* Lookup table */ +} gamma_table_data; + +static unsigned int +write_gamma_table_entry(const gamma_table_data *data, png_uint_32 i) + /* Calculate and write a single entry into table[i], the value of the entry + * written is returned. + */ +{ + png_uint_32 in = (i * data->mult + data->add) >> data->shift; + unsigned int out; + + /* If the output is TABLE_8 with no adjust, or the output is not with an + * adjust, use 8-bit correction. + */ + if ((data->output == PNG_GAMMA_TABLE_8) != (data->adjust != 0)) + { + out = png_gamma_8bit_correct((unsigned int)in, data->gamma); + + if (data->adjust != 0) + out *= 257U; + } + + else /* 16-bit correction */ + { + out = png_gamma_16bit_correct((unsigned int)in, data->gamma); + + if (data->adjust != 0) + out = PNG_DIV257(out); + } + + if (data->output == PNG_GAMMA_TABLE_8) + ((png_bytep)data->table)[i] = (png_byte)out; + + else + ((png_uint_16p)data->table)[i] = (png_uint_16)out; + + return out; +} + +static void +write_gamma_table(const gamma_table_data *data, png_uint_32 lo, + unsigned int loval, png_uint_32 hi, unsigned int hival) + /* Fill in gamma table entries between lo and hi, exclusive. The entries at + * table[lo] and table[hi] have already been written, the intervening entries + * are written. + */ +{ + if (hi > lo+1) /* Else nothing to fill in */ + { + if (hival == loval) + { + /* All intervening entries must be the same. */ + if (data->output == PNG_GAMMA_TABLE_8) + { + png_bytep table8 = ((png_bytep)data->table); + + while (++lo < hi) + table8[lo] = (png_byte)loval; + } + + else + { + png_uint_16p table16 = ((png_uint_16p)data->table); + + while (++lo < hi) + table16[lo] = (png_uint_16)loval; + } + } + + else + { + png_uint_32 mid = (lo+hi) >> 1; + unsigned int midval = write_gamma_table_entry(data, mid); + + /* The algorithm used is to divide the entries to be written in half + * and fill in the middle. For all practical tables with significant + * gamma this will result in a performance gain because the expensive + * gamma correction arithmetic is avoided for some entries. + */ + write_gamma_table(data, lo, loval, mid, midval); + write_gamma_table(data, mid, midval, hi, hival); + } + } +} + +static void * +png_build_gamma_table(png_structrp png_ptr, png_fixed_point gamma_val, + unsigned int output/*as above*/, unsigned int input_depth, int use_shift) + /* Build a gamma lookup table to encode input_depth bit input values. + * The table will have 2^input_depth entries plus an extra one if use_shift + * is specified. With shift the table is accessed: + * + * table[(original-value + rounding) >> shift] + * + * And an extra entry exists to accomodate overflow of original-value on + * rounding. If use_shift is not specified the table is accessed with an + * input_depth bit value and the original values must have been correctly + * scaled to this range (not using a shift!) + * + * Each table entry contains input-value^gamma_val rounded to the output + * precision. This is 8 bit precision unless output is specified as + * PNG_GAMMA_TABLE_16, in which case it is 16-bit precision. For + * PNG_GAMMA_TABLE_8_IN_16 the 8-bit value is scaled to 16-bits by + * multiplying by 257. + */ +{ + png_uint_32 size; + unsigned int hival; + gamma_table_data data; + + /* If use_shift is true or if the input or output is not 8-bit the gamma + * correction will use the 16-bit correction code. This requires a value in + * the range 0..65535. For use_shift the value is simply: + * + * input << shift + * + * For the scaling case the value is: + * + * round(input * 65535 / ((1<> shift; + * + * With 'mult' and 'add' chosen to minimize the error for all input values + * in the range 0..((1< PNG_GAMMA_TABLE_8; + + if (use_shift) + { + /* The multiplier does the shift: */ + data.mult = 1U << (8-input_depth); + data.add = 0; + data.shift = 0; + if (input_depth < 8) ++size; + } + + else + { + data.mult = multadd255[input_depth-1].mult; + data.add = multadd255[input_depth-1].add; + data.shift = multadd255[input_depth-1].shift; + } + } + + else + { + /* 16-bit correction is used for cases where input or output require more + * than 8 bits. + */ + data.adjust = output == PNG_GAMMA_TABLE_8; + + if (use_shift) + { + data.mult = 1U << (16-input_depth); + data.add = 0; + data.shift = 0; + if (input_depth < 16) ++size; + } + + else + { + data.mult = multadd65535[input_depth-1].mult; + data.add = multadd65535[input_depth-1].add; + data.shift = multadd65535[input_depth-1].shift; + } + } + + if (output == PNG_GAMMA_TABLE_8) + { + data.table = png_malloc(png_ptr, size * sizeof (png_byte)); + ((png_bytep)data.table)[0] = 0; + hival = ((png_bytep)data.table)[size-1] = 255; + } + + else + { + /* Output is 16 bits, although it may only have 8 bits of precision */ + data.table = png_malloc(png_ptr, size * sizeof (png_uint_16)); + ((png_uint_16p)data.table)[0] = 0; + hival = ((png_uint_16p)data.table)[size-1] = 65535; + } + + if (png_gamma_significant(gamma_val)) + write_gamma_table(&data, 0, 0, size-1, hival); + + else /* gamma_val not significant */ + { + if (output == PNG_GAMMA_TABLE_8) + { + png_uint_32 i; + png_bytep table8 = ((png_bytep)data.table); + + if (data.adjust) + for (i=1; i> + data.shift); + + else + for (i=1; i> data.shift); + } + + else + { + png_uint_32 i; + png_uint_16p table16 = ((png_uint_16p)data.table); + + if (data.adjust) + for (i=1; i> + data.shift) * 257U); + + else + for (i=1; i> + data.shift); + } + } + + return data.table; +} + /* Used from png_read_destroy and below to release the memory used by the gamma * tables. */ @@ -3827,17 +4304,8 @@ png_destroy_gamma_table(png_structrp png_ptr) png_free(png_ptr, png_ptr->gamma_table); png_ptr->gamma_table = NULL; - if (png_ptr->gamma_16_table != NULL) - { - int i; - int istop = (1 << (8 - png_ptr->gamma_shift)); - for (i = 0; i < istop; i++) - { - png_free(png_ptr, png_ptr->gamma_16_table[i]); - } png_free(png_ptr, png_ptr->gamma_16_table); png_ptr->gamma_16_table = NULL; - } #if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ @@ -3847,28 +4315,10 @@ png_destroy_gamma_table(png_structrp png_ptr) png_free(png_ptr, png_ptr->gamma_to_1); png_ptr->gamma_to_1 = NULL; - if (png_ptr->gamma_16_from_1 != NULL) - { - int i; - int istop = (1 << (8 - png_ptr->gamma_shift)); - for (i = 0; i < istop; i++) - { - png_free(png_ptr, png_ptr->gamma_16_from_1[i]); - } png_free(png_ptr, png_ptr->gamma_16_from_1); png_ptr->gamma_16_from_1 = NULL; - } - if (png_ptr->gamma_16_to_1 != NULL) - { - int i; - int istop = (1 << (8 - png_ptr->gamma_shift)); - for (i = 0; i < istop; i++) - { - png_free(png_ptr, png_ptr->gamma_16_to_1[i]); - } png_free(png_ptr, png_ptr->gamma_16_to_1); png_ptr->gamma_16_to_1 = NULL; - } #endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ } @@ -3878,7 +4328,7 @@ png_destroy_gamma_table(png_structrp png_ptr) * we don't need to allocate > 64K chunks for a full 16-bit table. */ void /* PRIVATE */ -png_build_gamma_table(png_structrp png_ptr, int bit_depth) +png_build_gamma_tables(png_structrp png_ptr, int bit_depth) { png_debug(1, "in png_build_gamma_table"); @@ -3896,27 +4346,44 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth) if (bit_depth <= 8) { - png_build_8bit_table(png_ptr, &png_ptr->gamma_table, - png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma) : PNG_FP_1); + png_ptr->gamma_table = png_voidcast(png_bytep, png_build_gamma_table( + png_ptr, png_ptr->screen_gamma > 0 ? + png_reciprocal2(png_ptr->colorspace.gamma, png_ptr->screen_gamma) : + PNG_FP_1, PNG_GAMMA_TABLE_8, 8/*input depth*/, 0/*scale*/)); #if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) { - png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1, - png_reciprocal(png_ptr->colorspace.gamma)); + /* This sets the accuracy of 8-bit composition and the 8-bit RGB to gray + * conversion - PNG_MAX_GAMMA_8 (the number of bits in the sixteen bit + * value that are considered significant.) + */ + png_ptr->gamma_to_1 = png_voidcast(png_uint_16p, png_build_gamma_table( + png_ptr, png_reciprocal(png_ptr->colorspace.gamma), + PNG_GAMMA_TABLE_16, 8/*input depth*/, 0/*scale*/)); - png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1, - png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) : - png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); + png_ptr->gamma_from_1 = png_voidcast(png_bytep, png_build_gamma_table( + png_ptr, png_ptr->screen_gamma > 0 ? + png_reciprocal(png_ptr->screen_gamma) : + png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */, + PNG_GAMMA_TABLE_8, PNG_MAX_GAMMA_8/*input depth*/, 1/*shift*/)); + + png_ptr->gamma_shift = 16-PNG_MAX_GAMMA_8; } #endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ } else { png_byte shift, sig_bit; + int table_type; + +# ifdef PNG_16BIT_SUPPORTED + table_type = PNG_GAMMA_TABLE_16; +# else + table_type = PNG_GAMMA_TABLE_8_IN_16; +# endif if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) { @@ -3931,24 +4398,6 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth) else sig_bit = png_ptr->sig_bit.gray; - /* 16-bit gamma code uses this equation: - * - * ov = table[(iv & 0xff) >> gamma_shift][iv >> 8] - * - * Where 'iv' is the input color value and 'ov' is the output value - - * pow(iv, gamma). - * - * Thus the gamma table consists of up to 256 256 entry tables. The table - * is selected by the (8-gamma_shift) most significant of the low 8 bits of - * the color value then indexed by the upper 8 bits: - * - * table[low bits][high 8 bits] - * - * So the table 'n' corresponds to all those 'iv' of: - * - * ..<(n+1 << gamma_shift)-1> - * - */ if (sig_bit > 0 && sig_bit < 16U) shift = (png_byte)(16U - sig_bit); /* shift == insignificant bits */ @@ -3959,51 +4408,40 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth) { /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively * the significant bits in the *input* when the output will - * eventually be 8 bits. By default it is 11. + * eventually be 8 bits. */ if (shift < (16U - PNG_MAX_GAMMA_8)) shift = (16U - PNG_MAX_GAMMA_8); - } - if (shift > 8U) - shift = 8U; /* Guarantees at least one table! */ + table_type = PNG_GAMMA_TABLE_8_IN_16; + } png_ptr->gamma_shift = shift; -#ifdef PNG_16BIT_SUPPORTED - /* NOTE: prior to 1.5.4 this test used to include PNG_BACKGROUND (now - * PNG_COMPOSE). This effectively smashed the background calculation for - * 16-bit output because the 8-bit table assumes the result will be reduced - * to 8 bits. - */ - if (png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) -#endif - png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift, - png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma) : PNG_FP_1); - -#ifdef PNG_16BIT_SUPPORTED - else - png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift, - png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma) : PNG_FP_1); -#endif + png_ptr->gamma_16_table = png_voidcast(png_uint_16p, png_build_gamma_table( + png_ptr, png_ptr->screen_gamma > 0 ? png_reciprocal2( + png_ptr->colorspace.gamma, png_ptr->screen_gamma) : PNG_FP_1, + table_type, (16-shift)/*input depth*/, 1/*shift*/)); #if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) { - png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift, - png_reciprocal(png_ptr->colorspace.gamma)); + png_ptr->gamma_16_to_1 = png_voidcast(png_uint_16p, + png_build_gamma_table(png_ptr, + png_reciprocal(png_ptr->colorspace.gamma), PNG_GAMMA_TABLE_16, + (16-shift)/*input depth*/, 1/*shift*/)); /* Notice that the '16 from 1' table should be full precision, however * the lookup on this table still uses gamma_shift, so it can't be. * TODO: fix this. */ - png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift, - png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) : - png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); + png_ptr->gamma_16_from_1 = png_voidcast(png_uint_16p, + png_build_gamma_table(png_ptr, png_ptr->screen_gamma > 0 ? + png_reciprocal(png_ptr->screen_gamma) : + png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */, + PNG_GAMMA_TABLE_16, (16-shift)/*input depth*/, 1/*shift*/)); } #endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ } diff --git a/png.h b/png.h index 2dc5e0f60..77619edbe 100644 --- a/png.h +++ b/png.h @@ -1116,13 +1116,10 @@ PNG_EXPORT(22, void, png_read_info, #ifdef PNG_TIME_RFC1123_SUPPORTED /* Convert to a US string format: there is no localization support in this * routine. The original implementation used a 29 character buffer in - * png_struct, this will be removed in future versions. + * png_struct, this has been removed. */ -#if PNG_LIBPNG_VER < 10700 -/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ -PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, - png_const_timep ptime),PNG_DEPRECATED); -#endif +PNG_REMOVED(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED) PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], png_const_timep ptime)); #endif @@ -1619,22 +1616,8 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, int filters)); -/* Flags for png_set_filter() to say which filters to use. The flags - * are chosen so that they don't conflict with real filter types - * below, in case they are supplied instead of the #defined constants. - * These values should NOT be changed. - */ -#define PNG_NO_FILTERS 0x00 -#define PNG_FILTER_NONE 0x08 -#define PNG_FILTER_SUB 0x10 -#define PNG_FILTER_UP 0x20 -#define PNG_FILTER_AVG 0x40 -#define PNG_FILTER_PAETH 0x80 -#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ - PNG_FILTER_AVG | PNG_FILTER_PAETH) - /* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. - * These defines should NOT be changed. + * These defines match the values in the PNG specification. */ #define PNG_FILTER_VALUE_NONE 0 #define PNG_FILTER_VALUE_SUB 1 @@ -1643,6 +1626,26 @@ PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, #define PNG_FILTER_VALUE_PAETH 4 #define PNG_FILTER_VALUE_LAST 5 +/* The above values are valid arguments to png_set_filter() if only a single + * filter is to be used. If multiple filters are to be allowed (the default is + * to allow any of them) then a combination of the following masks must be used + * and the low three bits of the argument to png_set_filter must be 0. + * + * The resultant argument fits in a single byte. + */ +#define PNG_FILTER_NONE (0x08 << PNG_FILTER_VALUE_NONE) +#define PNG_FILTER_SUB (0x08 << PNG_FILTER_VALUE_SUB) +#define PNG_FILTER_UP (0x08 << PNG_FILTER_VALUE_UP) +#define PNG_FILTER_AVG (0x08 << PNG_FILTER_VALUE_AVG) +#define PNG_FILTER_PAETH (0x08 << PNG_FILTER_VALUE_PAETH) + +/* Then two convenience values. PNG_NO_FILTERS is the same as + * PNG_FILTER_VALUE_NONE, but this is harmless because they mean the same thing. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* EXPERIMENTAL */ /* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ * defines, either the default (minimum-sum-of-absolute-differences), or diff --git a/pngpriv.h b/pngpriv.h index 1a5636389..6b0809e33 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -458,7 +458,7 @@ typedef const png_uint_16p * png_const_uint_16pp; #define PNG_HAVE_CHUNK_HEADER 0x100 #define PNG_WROTE_tIME 0x200 #define PNG_WROTE_INFO_BEFORE_PLTE 0x400 -#define PNG_BACKGROUND_IS_GRAY 0x800 + /* 0x800 (unused) */ #define PNG_HAVE_PNG_SIGNATURE 0x1000 #define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ /* 0x4000 (unused) */ @@ -472,10 +472,10 @@ typedef const png_uint_16p * png_const_uint_16pp; #define PNG_SWAP_BYTES 0x0010 #define PNG_INVERT_MONO 0x0020 #define PNG_QUANTIZE 0x0040 -#define PNG_COMPOSE 0x0080 /* Was PNG_BACKGROUND */ -#define PNG_BACKGROUND_EXPAND 0x0100 -#define PNG_EXPAND_16 0x0200 /* Added to libpng 1.5.2 */ -#define PNG_16_TO_8 0x0400 /* Becomes 'chop' in 1.5.4 */ +#define PNG_COMPOSE 0x0080 /* Was PNG_BACKGROUND */ + /* 0x0100 unused */ +#define PNG_EXPAND_16 0x0200 /* Added to libpng 1.5.2 */ +#define PNG_16_TO_8 0x0400 /* Becomes 'chop' in 1.5.4 */ #define PNG_RGBA 0x0800 #define PNG_EXPAND 0x1000 #define PNG_GAMMA 0x2000 @@ -490,13 +490,13 @@ typedef const png_uint_16p * png_const_uint_16pp; #define PNG_RGB_TO_GRAY_WARN 0x400000 #define PNG_RGB_TO_GRAY 0x600000 /* two bits, RGB_TO_GRAY_ERR|WARN */ #define PNG_ENCODE_ALPHA 0x800000 /* Added to libpng-1.5.4 */ -#define PNG_ADD_ALPHA 0x1000000 /* Added to libpng-1.2.7 */ -#define PNG_EXPAND_tRNS 0x2000000 /* Added to libpng-1.2.9 */ -#define PNG_SCALE_16_TO_8 0x4000000 /* Added to libpng-1.5.4 */ - /* 0x8000000 unused */ - /* 0x10000000 unused */ - /* 0x20000000 unused */ - /* 0x40000000 unused */ +#define PNG_ADD_ALPHA 0x1000000 /* Added to libpng-1.2.7 */ +#define PNG_EXPAND_tRNS 0x2000000 /* Added to libpng-1.2.9 */ +#define PNG_SCALE_16_TO_8 0x4000000 /* Added to libpng-1.5.4 */ + /* 0x8000000 unused */ + /* 0x10000000 unused */ + /* 0x20000000 unused */ + /* 0x40000000 unused */ /* Flags for png_create_struct */ #define PNG_STRUCT_PNG 0x0001 #define PNG_STRUCT_INFO 0x0002 @@ -529,8 +529,8 @@ typedef const png_uint_16p * png_const_uint_16pp; #define PNG_FLAG_BENIGN_ERRORS_WARN 0x100000 /* Added to libpng-1.4.0 */ #define PNG_FLAG_APP_WARNINGS_WARN 0x200000 /* Added to libpng-1.6.0 */ #define PNG_FLAG_APP_ERRORS_WARN 0x400000 /* Added to libpng-1.6.0 */ - /* 0x800000 unused */ - /* 0x1000000 unused */ +#define PNG_FLAG_BACKGROUND_IS_GRAY 0x800000 +#define PNG_FLAG_BACKGROUND_EXPAND 0x1000000 /* 0x2000000 unused */ /* 0x4000000 unused */ /* 0x8000000 unused */ @@ -733,6 +733,16 @@ PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr, /* Free the buffer list used by the compressed write code. */ #endif +#ifdef PNG_WRITE_FILTER_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_alloc_filter_row_buffers, + (png_structrp png_ptr, int filters),PNG_EMPTY); + /* Allocate pixel row buffers to cache filtered rows while testing candidate + * filters. + * TODO: avoid this, only one spare row buffer (at most) is required, this + * wastes a lot of memory for large images. + */ +#endif + #if defined(PNG_FLOATING_POINT_SUPPORTED) && \ !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \ @@ -1802,7 +1812,7 @@ PNG_INTERNAL_FUNCTION(png_byte,png_gamma_8bit_correct,(unsigned int value, png_fixed_point gamma_value),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr), PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_build_gamma_table,(png_structrp png_ptr, +PNG_INTERNAL_FUNCTION(void,png_build_gamma_tables,(png_structrp png_ptr, int bit_depth),PNG_EMPTY); #endif diff --git a/pngrio.c b/pngrio.c index 4dce51219..f9c842f4b 100644 --- a/pngrio.c +++ b/pngrio.c @@ -102,6 +102,7 @@ png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr, png_ptr->read_data_fn = read_data_fn; #endif +#ifdef PNG_WRITE_SUPPORTED /* It is an error to write to a read device */ if (png_ptr->write_data_fn != NULL) { @@ -110,6 +111,7 @@ png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr, "Can't set both read_data_fn and write_data_fn in the" " same structure"); } +#endif #ifdef PNG_WRITE_FLUSH_SUPPORTED png_ptr->output_flush_fn = NULL; diff --git a/pngrtran.c b/pngrtran.c index 09be05c0e..9d77457dd 100644 --- a/pngrtran.c +++ b/pngrtran.c @@ -130,9 +130,11 @@ png_set_background_fixed(png_structrp png_ptr, if (!png_rtran_ok(png_ptr, 0) || background_color == NULL) return; - if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN) + if (background_gamma_code != PNG_BACKGROUND_GAMMA_SCREEN && + background_gamma_code != PNG_BACKGROUND_GAMMA_FILE && + background_gamma_code != PNG_BACKGROUND_GAMMA_UNIQUE) { - png_warning(png_ptr, "Application must supply a known background gamma"); + png_app_error(png_ptr, "invalid gamma type"); return; } @@ -143,10 +145,12 @@ png_set_background_fixed(png_structrp png_ptr, png_ptr->background = *background_color; png_ptr->background_gamma = background_gamma; png_ptr->background_gamma_type = (png_byte)(background_gamma_code); + if (need_expand) - png_ptr->transformations |= PNG_BACKGROUND_EXPAND; + png_ptr->flags |= PNG_FLAG_BACKGROUND_EXPAND; + else - png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND; + png_ptr->flags &= ~PNG_FLAG_BACKGROUND_EXPAND; } # ifdef PNG_FLOATING_POINT_SUPPORTED @@ -367,7 +371,7 @@ png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, memset(&png_ptr->background, 0, (sizeof png_ptr->background)); png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */ png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE; - png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND; + png_ptr->flags &= ~PNG_FLAG_BACKGROUND_EXPAND; if (png_ptr->transformations & PNG_COMPOSE) png_error(png_ptr, @@ -1091,23 +1095,263 @@ png_gamma_threshold(png_fixed_point screen_gamma, png_fixed_point file_gamma) /* Initialize everything needed for the read. This includes modifying * the palette. */ +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) +static void +gamma_correct_background(unsigned int value, unsigned int depth, + png_uint_16p backgroundp, png_uint_16p background_1p, + png_fixed_point gamma_correct, png_fixed_point gamma_to_1) +{ + switch (depth) + { + case 8: + if (gamma_correct != PNG_FP_1) + *backgroundp = png_gamma_8bit_correct(value, gamma_correct); + + else + *backgroundp = (png_uint_16)value; + + if (gamma_to_1 != PNG_FP_1) + *background_1p = png_gamma_16bit_correct(value*257, gamma_to_1); + + else + *background_1p = (png_uint_16)(value*257); + + return; + + case 16: + if (gamma_correct != PNG_FP_1) + *backgroundp = png_gamma_16bit_correct(value, gamma_correct); + + else + *backgroundp = (png_uint_16)value; + + if (gamma_to_1 != PNG_FP_1) + *background_1p = png_gamma_16bit_correct(value, gamma_to_1); + + else + *background_1p = (png_uint_16)value; + + return; + + default: + /* Low bit depth gray levels; do no harm. */ + break; + } + + *backgroundp = (png_uint_16)value; + *background_1p = 0; /* should not be used */ +} + +static void /* PRIVATE */ +png_init_background_transformations(png_structrp png_ptr) + /* Set the png_ptr->background and png_ptr->background_1 members correctly + * for the bit depth and format. + */ +{ + /* png_ptr->background is only assigned by png_set_background and + * png_set_alpha_mode (which just zeros out the fields.) png_set_background + * can set the PNG_FLAG_BACKGROUND_EXPAND flag if the input value is in the + * file format, for example if it comes from a bKGD chunk. + * + * Under some circumstances deficiencies in the current libpng code mean that + * the bit depth of the values must differ from the final bit depth; the bit + * depth has to match that at which the processing of the image pixels + * happens and this is not always the final bit depth. This is fixed up + * here. + * + * First find the required depth. + */ + unsigned int bit_depth, required_bit_depth; + unsigned int color_type = png_ptr->color_type; + const png_uint_32 transform = png_ptr->transformations; + const int expand = (png_ptr->flags & PNG_FLAG_BACKGROUND_EXPAND) != 0; + + if (color_type & PNG_COLOR_MASK_PALETTE) + required_bit_depth = bit_depth = 8; + + else + { + required_bit_depth = bit_depth = png_ptr->bit_depth; + + /* But not PNG_EXPAND_16 at present because it happens after the compose + * operation where the background is used! + */ + if (bit_depth < 8 && (transform & PNG_EXPAND) != 0) + required_bit_depth = 8; + } + + /* bit_depth and color_type now refer to the original file data and + * required_bit_depth is correct for the processing libpng does, however it + * does not necessarily match the output the application gets, fix that and + * the color type here: + */ + if (!expand) + { + /* The background bit_depth and color_type need correcting */ + if ((transform & PNG_EXPAND) != 0) + color_type &= ~PNG_COLOR_MASK_PALETTE; + + /* The RGB<->gray transformations do the to gray operation first, then the + * from gray. + */ + if ((transform & PNG_RGB_TO_GRAY) != 0) + color_type &= ~PNG_COLOR_MASK_COLOR; + + if ((transform & PNG_GRAY_TO_RGB) != 0) + color_type |= PNG_COLOR_MASK_COLOR; + + bit_depth = required_bit_depth; + + /* The expansion to 16 bits and the scaling back from 16 bits per + * component to only 8 happens after the background processing (at + * present) so these transforms only affect the screen value, not the + * required value. Note that the 16_TO_8 conversions happen before the 8 + * to 16 one, so in theory both could occur - the order of the tests below + * must be correct! + * + * TODO: Note that the second of these changes cause an input 16-bit + * background value to be temporarily crushed to 8-bits per component, + * losing precision. This is a bug and should be fixed. + */ + if (bit_depth == 16 && + (transform & (PNG_16_TO_8|PNG_SCALE_16_TO_8)) != 0) + bit_depth = 8; + + if (bit_depth == 8 && (color_type & PNG_COLOR_MASK_PALETTE) == 0 && + (transform & PNG_EXPAND_16) != 0) + bit_depth = 16; + } + + /* Now make the background have the correct format, this involves reading the + * correct fields from png_ptr->background, adjusting the bit depth of the + * result and potentially gamma correcting the value then calculating the + * png_ptr->background_1 values too. + */ + { + unsigned int mult = 1; + png_fixed_point gamma_to_1, gamma_correct; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + gamma_to_1 = png_ptr->screen_gamma; + gamma_correct = PNG_FP_1; + break; + + case PNG_BACKGROUND_GAMMA_FILE: + gamma_to_1 = png_reciprocal(png_ptr->colorspace.gamma); + gamma_correct = png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma); + break; + + case PNG_BACKGROUND_GAMMA_UNIQUE: + gamma_to_1 = png_reciprocal(png_ptr->background_gamma); + gamma_correct = png_reciprocal2(png_ptr->background_gamma, + png_ptr->screen_gamma); + break; + + default: + gamma_to_1 = PNG_FP_1; + gamma_correct = PNG_FP_1; + break; + } + +# define CORRECT(v, c)\ + gamma_correct_background((v)*mult, bit_depth,\ + &png_ptr->background.c, &png_ptr->background_1.c,\ + gamma_correct, gamma_to_1);\ + if (bit_depth > required_bit_depth)\ + png_ptr->background.c =\ + (png_uint_16)PNG_DIV257(png_ptr->background.c) + + /* The multiplier 'mult' scales the values to 'required_depth', + * 'bit_depth' is the depth of the resultant values. + */ + while (bit_depth < required_bit_depth) + mult += mult << bit_depth, bit_depth <<= 1; + + /* In the event that this still leaves the background bit depth greater + * than the libpng required depth scale the values back to the 8-bit + * range, the test below verifies that this is correct. + */ + if (bit_depth > required_bit_depth) + { + if (bit_depth != 16 || required_bit_depth != 8) + png_error(png_ptr, "internal error handling background depth"); + } + + if ((color_type & PNG_COLOR_MASK_COLOR) != 0) + { + png_ptr->flags &= ~PNG_FLAG_BACKGROUND_IS_GRAY; /* checked below */ + + /* If need_expand was passed to png_set_background the background value + * was in the file format, therefore if the file is a palette file the + * background will have been an index into the palette. Notice that if + * need_expand was false then the color is RGB even if the output still + * has a palette. + */ + if (expand && (color_type & PNG_COLOR_MASK_PALETTE) != 0) + { + unsigned int index = png_ptr->background.index; + + if (index < png_ptr->num_palette && png_ptr->palette != NULL) + { + /* In fact 'mult' is always 1 at present in this case */ + CORRECT(png_ptr->palette[index].red, red); + CORRECT(png_ptr->palette[index].green, green); + CORRECT(png_ptr->palette[index].blue, blue); + } + + else + { + png_app_error(png_ptr, "out of range background index"); + memset(&png_ptr->background, 0, sizeof png_ptr->background); + memset(&png_ptr->background_1, 0, sizeof png_ptr->background_1); + } + } + + else + { + CORRECT(png_ptr->background.red, red); + CORRECT(png_ptr->background.green, green); + CORRECT(png_ptr->background.blue, blue); + } + + if (png_ptr->background.red == png_ptr->background.blue && + png_ptr->background.red == png_ptr->background.green) + { + png_ptr->flags |= PNG_FLAG_BACKGROUND_IS_GRAY; + png_ptr->background.gray = png_ptr->background.red; + png_ptr->background_1.gray = png_ptr->background_1.red; + } + + else + png_ptr->background.gray = png_ptr->background_1.gray = 0; + } + + else + { + png_ptr->flags |= PNG_FLAG_BACKGROUND_IS_GRAY; + + CORRECT(png_ptr->background.gray, gray); + + png_ptr->background.red = + png_ptr->background.green = + png_ptr->background.blue = png_ptr->background.gray; + + png_ptr->background_1.red = + png_ptr->background_1.green = + png_ptr->background_1.blue = png_ptr->background_1.gray; + } +# undef CORRECT + } +} +#endif /* READ_BACKGROUND || READ_ALPHA_MODE */ -/*For the moment 'png_init_palette_transformations' and - * 'png_init_rgb_transformations' only do some flag canceling optimizations. - * The intent is that these two routines should have palette or rgb operations - * extracted from 'png_init_read_transformations'. - */ static void /* PRIVATE */ png_init_palette_transformations(png_structrp png_ptr) { - /* Called to handle the (input) palette case. In png_do_read_transformations - * the first step is to expand the palette if requested, so this code must - * take care to only make changes that are invariant with respect to the - * palette expansion, or only do them if there is no expansion. - * - * STRIP_ALPHA has already been handled in the caller (by setting num_trans - * to 0.) - */ int input_has_alpha = 0; int input_has_transparency = 0; @@ -1129,55 +1373,15 @@ png_init_palette_transformations(png_structrp png_ptr) if (!input_has_alpha) { /* Any alpha means background and associative alpha processing is - * required, however if the alpha is 0 or 1 throughout OPTIIMIZE_ALPHA + * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA * and ENCODE_ALPHA are irrelevant. */ png_ptr->transformations &= ~PNG_ENCODE_ALPHA; png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; if (!input_has_transparency) - png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND); + png_ptr->transformations &= ~PNG_COMPOSE; } - -#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) - /* png_set_background handling - deals with the complexity of whether the - * background color is in the file format or the screen format in the case - * where an 'expand' will happen. - */ - - /* The following code cannot be entered in the alpha pre-multiplication case - * because PNG_BACKGROUND_EXPAND is cancelled below. - */ - if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && - (png_ptr->transformations & PNG_EXPAND)) - { - { - png_ptr->background.red = - png_ptr->palette[png_ptr->background.index].red; - png_ptr->background.green = - png_ptr->palette[png_ptr->background.index].green; - png_ptr->background.blue = - png_ptr->palette[png_ptr->background.index].blue; - -#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED - if (png_ptr->transformations & PNG_INVERT_ALPHA) - { - if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) - { - /* Invert the alpha channel (in tRNS) unless the pixels are - * going to be expanded, in which case leave it for later - */ - int i, istop = png_ptr->num_trans; - - for (i=0; itrans_alpha[i] = (png_byte)(255 - - png_ptr->trans_alpha[i]); - } - } -#endif /* PNG_READ_INVERT_ALPHA_SUPPORTED */ - } - } /* background expand and (therefore) no alpha association. */ -#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */ } static void /* PRIVATE */ @@ -1203,66 +1407,8 @@ png_init_rgb_transformations(png_structrp png_ptr) # endif if (!input_has_transparency) - png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND); + png_ptr->transformations &= ~PNG_COMPOSE; } - -#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) - /* png_set_background handling - deals with the complexity of whether the - * background color is in the file format or the screen format in the case - * where an 'expand' will happen. - */ - - /* The following code cannot be entered in the alpha pre-multiplication case - * because PNG_BACKGROUND_EXPAND is cancelled below. - */ - if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && - (png_ptr->transformations & PNG_EXPAND) && - !(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) - /* i.e., GRAY or GRAY_ALPHA */ - { - { - /* Expand background and tRNS chunks */ - int gray = png_ptr->background.gray; - int trans_gray = png_ptr->trans_color.gray; - - switch (png_ptr->bit_depth) - { - case 1: - gray *= 0xff; - trans_gray *= 0xff; - break; - - case 2: - gray *= 0x55; - trans_gray *= 0x55; - break; - - case 4: - gray *= 0x11; - trans_gray *= 0x11; - break; - - default: - - case 8: - /* FALL THROUGH (Already 8 bits) */ - - case 16: - /* Already a full 16 bits */ - break; - } - - png_ptr->background.red = png_ptr->background.green = - png_ptr->background.blue = (png_uint_16)gray; - - if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) - { - png_ptr->trans_color.red = png_ptr->trans_color.green = - png_ptr->trans_color.blue = (png_uint_16)trans_gray; - } - } - } /* background expand and (therefore) no alpha association. */ -#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */ } void /* PRIVATE */ @@ -1347,7 +1493,7 @@ png_init_read_transformations(png_structrp png_ptr) * 1) PNG_EXPAND (including PNG_EXPAND_tRNS) * 2) PNG_STRIP_ALPHA (if no compose) * 3) PNG_RGB_TO_GRAY - * 4) PNG_GRAY_TO_RGB iff !PNG_BACKGROUND_IS_GRAY + * 4) PNG_GRAY_TO_RGB iff !PNG_FLAG_BACKGROUND_IS_GRAY * 5) PNG_COMPOSE * 6) PNG_GAMMA * 7) PNG_STRIP_ALPHA (if compose) @@ -1356,7 +1502,7 @@ png_init_read_transformations(png_structrp png_ptr) * 10) PNG_16_TO_8 * 11) PNG_QUANTIZE (converts to palette) * 12) PNG_EXPAND_16 - * 13) PNG_GRAY_TO_RGB iff PNG_BACKGROUND_IS_GRAY + * 13) PNG_GRAY_TO_RGB iff PNG_FLAG_BACKGROUND_IS_GRAY * 14) PNG_INVERT_MONO * 15) PNG_SHIFT * 16) PNG_PACK @@ -1369,26 +1515,30 @@ png_init_read_transformations(png_structrp png_ptr) * 23) PNG_USER_TRANSFORM [must be last] */ #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED - if ((png_ptr->transformations & PNG_STRIP_ALPHA) && - !(png_ptr->transformations & PNG_COMPOSE)) + if (png_ptr->transformations & PNG_STRIP_ALPHA) { - /* Stripping the alpha channel happens immediately after the 'expand' - * transformations, before all other transformation, so it cancels out - * the alpha handling. It has the side effect negating the effect of - * PNG_EXPAND_tRNS too: - */ - png_ptr->transformations &= ~(PNG_BACKGROUND_EXPAND | PNG_ENCODE_ALPHA | - PNG_EXPAND_tRNS); - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + if (!(png_ptr->transformations & PNG_FILLER)) + png_ptr->transformations &= ~(PNG_INVERT_ALPHA|PNG_SWAP_ALPHA); - /* Kill the tRNS chunk itself too. Prior to 1.5.4 this did not happen - * so transparency information would remain just so long as it wasn't - * expanded. This produces unexpected API changes if the set of things - * that do PNG_EXPAND_tRNS changes (perfectly possible given the - * documentation - which says ask for what you want, accept what you - * get.) This makes the behavior consistent from 1.5.4: - */ - png_ptr->num_trans = 0; + if (!(png_ptr->transformations & PNG_COMPOSE)) + { + /* Stripping the alpha channel happens immediately after the 'expand' + * transformations, before all other transformations, so it cancels out + * the alpha handling. It has the side effect negating the effect of + * PNG_EXPAND_tRNS too: + */ + png_ptr->transformations &= ~(PNG_ENCODE_ALPHA | PNG_EXPAND_tRNS); + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + + /* Kill the tRNS chunk itself too. Prior to 1.5.4 this did not happen + * so transparency information would remain just so long as it wasn't + * expanded. This produces unexpected API changes if the set of things + * that do PNG_EXPAND_tRNS changes (perfectly possible given the + * documentation - which says ask for what you want, accept what you + * get.) This makes the behavior consistent from 1.5.4: + */ + png_ptr->num_trans = 0; + } } #endif /* STRIP_ALPHA supported, no COMPOSE */ @@ -1411,51 +1561,19 @@ png_init_read_transformations(png_structrp png_ptr) png_colorspace_set_rgb_coefficients(png_ptr); #endif -#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED -#if defined PNG_READ_EXPAND_SUPPORTED && defined PNG_READ_BACKGROUND_SUPPORTED - /* Detect gray background and attempt to enable optimization for - * gray --> RGB case. - * - * Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or - * RGB_ALPHA (in which case need_expand is superfluous anyway), the - * background color might actually be gray yet not be flagged as such. - * This is not a problem for the current code, which uses - * PNG_BACKGROUND_IS_GRAY only to decide when to do the - * png_do_gray_to_rgb() transformation. - * - * TODO: this code needs to be revised to avoid the complexity and - * interdependencies. The color type of the background should be recorded in - * png_set_background, along with the bit depth, then the code has a record - * of exactly what color space the background is currently in. - */ - if (png_ptr->transformations & PNG_BACKGROUND_EXPAND) - { - /* PNG_BACKGROUND_EXPAND: the background is in the file color space, so if - * the file was grayscale the background value is gray. - */ - if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) - png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; - } + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_init_palette_transformations(png_ptr); - else if (png_ptr->transformations & PNG_COMPOSE) - { - /* PNG_COMPOSE: png_set_background was called with need_expand false, - * so the color is in the color space of the output or png_set_alpha_mode - * was called and the color is black. Ignore RGB_TO_GRAY because that - * happens before GRAY_TO_RGB. - */ - if (png_ptr->transformations & PNG_GRAY_TO_RGB) - { - if (png_ptr->background.red == png_ptr->background.green && - png_ptr->background.red == png_ptr->background.blue) - { - png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; - png_ptr->background.gray = png_ptr->background.red; - } - } - } -#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */ -#endif /* PNG_READ_GRAY_TO_RGB_SUPPORTED */ + else + png_init_rgb_transformations(png_ptr); + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + /* Set up the background information if required, it is only used if + * PNG_COMPOSE is specified. + */ + if (png_ptr->transformations & PNG_COMPOSE) + png_init_background_transformations(png_ptr); +#endif /* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations * can be performed directly on the palette, and some (such as rgb to gray) @@ -1468,74 +1586,11 @@ png_init_read_transformations(png_structrp png_ptr) * leads to the reported bug that the palette returned by png_get_PLTE is not * updated. */ - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - png_init_palette_transformations(png_ptr); - - else - png_init_rgb_transformations(png_ptr); - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ - defined(PNG_READ_EXPAND_16_SUPPORTED) - if ((png_ptr->transformations & PNG_EXPAND_16) && - (png_ptr->transformations & PNG_COMPOSE) && - !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && - png_ptr->bit_depth != 16) - { - /* TODO: fix this. Because the expand_16 operation is after the compose - * handling the background color must be 8, not 16, bits deep, but the - * application will supply a 16-bit value so reduce it here. - * - * The PNG_BACKGROUND_EXPAND code above does not expand to 16 bits at - * present, so that case is ok (until do_expand_16 is moved.) - * - * NOTE: this discards the low 16 bits of the user supplied background - * color, but until expand_16 works properly there is no choice! - */ -# define CHOP(x) (x)=((png_uint_16)PNG_DIV257(x)) - CHOP(png_ptr->background.red); - CHOP(png_ptr->background.green); - CHOP(png_ptr->background.blue); - CHOP(png_ptr->background.gray); -# undef CHOP - } -#endif /* PNG_READ_BACKGROUND_SUPPORTED && PNG_READ_EXPAND_16_SUPPORTED */ - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ - (defined(PNG_READ_SCALE_16_TO_8_SUPPORTED) || \ - defined(PNG_READ_STRIP_16_TO_8_SUPPORTED)) - if ((png_ptr->transformations & (PNG_16_TO_8|PNG_SCALE_16_TO_8)) && - (png_ptr->transformations & PNG_COMPOSE) && - !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && - png_ptr->bit_depth == 16) - { - /* On the other hand, if a 16-bit file is to be reduced to 8-bits per - * component this will also happen after PNG_COMPOSE and so the background - * color must be pre-expanded here. - * - * TODO: fix this too. - */ - png_ptr->background.red = (png_uint_16)(png_ptr->background.red * 257); - png_ptr->background.green = - (png_uint_16)(png_ptr->background.green * 257); - png_ptr->background.blue = (png_uint_16)(png_ptr->background.blue * 257); - png_ptr->background.gray = (png_uint_16)(png_ptr->background.gray * 257); - } +#if 0 /* NYI */ + png_do_palette_transformations(png_ptr); #endif - /* NOTE: below 'PNG_READ_ALPHA_MODE_SUPPORTED' is presumed to also enable the - * background support (see the comments in scripts/pnglibconf.dfa), this - * allows pre-multiplication of the alpha channel to be implemented as - * compositing on black. This is probably sub-optimal and has been done in - * 1.5.4 betas simply to enable external critique and testing (i.e. to - * implement the new API quickly, without lots of internal changes.) - */ - #ifdef PNG_READ_GAMMA_SUPPORTED -# ifdef PNG_READ_BACKGROUND_SUPPORTED - /* Includes ALPHA_MODE */ - png_ptr->background_1 = png_ptr->background; -# endif - /* This needs to change - in the palette image case a whole set of tables are * built when it would be quicker to just calculate the correct value for * each palette entry directly. Also, the test is too tricky - why check @@ -1564,7 +1619,7 @@ png_init_read_transformations(png_structrp png_ptr) && png_gamma_significant(png_ptr->screen_gamma)) ) { - png_build_gamma_table(png_ptr, png_ptr->bit_depth); + png_build_gamma_tables(png_ptr, png_ptr->bit_depth); #ifdef PNG_READ_BACKGROUND_SUPPORTED if (png_ptr->transformations & PNG_COMPOSE) @@ -1581,91 +1636,18 @@ png_init_read_transformations(png_structrp png_ptr) if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - /* We don't get to here unless there is a tRNS chunk with non-opaque - * entries - see the checking code at the start of this function. - */ - png_color back, back_1; + unsigned int i, num_palette = png_ptr->num_palette; + png_color back; + png_color_16 back_1 = png_ptr->background_1; png_colorp palette = png_ptr->palette; - int num_palette = png_ptr->num_palette; - int i; - if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) - { - back.red = png_ptr->gamma_table[png_ptr->background.red]; - back.green = png_ptr->gamma_table[png_ptr->background.green]; - back.blue = png_ptr->gamma_table[png_ptr->background.blue]; - - back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; - back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; - back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; - } - else - { - png_fixed_point g, gs; - - switch (png_ptr->background_gamma_type) - { - case PNG_BACKGROUND_GAMMA_SCREEN: - g = (png_ptr->screen_gamma); - gs = PNG_FP_1; - break; - - case PNG_BACKGROUND_GAMMA_FILE: - g = png_reciprocal(png_ptr->colorspace.gamma); - gs = png_reciprocal2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma); - break; - - case PNG_BACKGROUND_GAMMA_UNIQUE: - g = png_reciprocal(png_ptr->background_gamma); - gs = png_reciprocal2(png_ptr->background_gamma, - png_ptr->screen_gamma); - break; - default: - g = PNG_FP_1; /* back_1 */ - gs = PNG_FP_1; /* back */ - break; - } - - if (png_gamma_significant(gs)) - { - back.red = png_gamma_8bit_correct(png_ptr->background.red, - gs); - back.green = png_gamma_8bit_correct(png_ptr->background.green, - gs); - back.blue = png_gamma_8bit_correct(png_ptr->background.blue, - gs); - } - - else - { - back.red = (png_byte)png_ptr->background.red; - back.green = (png_byte)png_ptr->background.green; - back.blue = (png_byte)png_ptr->background.blue; - } - - if (png_gamma_significant(g)) - { - back_1.red = png_gamma_8bit_correct(png_ptr->background.red, - g); - back_1.green = png_gamma_8bit_correct( - png_ptr->background.green, g); - back_1.blue = png_gamma_8bit_correct(png_ptr->background.blue, - g); - } - - else - { - back_1.red = (png_byte)png_ptr->background.red; - back_1.green = (png_byte)png_ptr->background.green; - back_1.blue = (png_byte)png_ptr->background.blue; - } - } + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; for (i = 0; i < num_palette; i++) { - if (i < (int)png_ptr->num_trans && - png_ptr->trans_alpha[i] != 0xff) + if (i < png_ptr->num_trans && png_ptr->trans_alpha[i] != 0xff) { if (png_ptr->trans_alpha[i] == 0) { @@ -1673,19 +1655,22 @@ png_init_read_transformations(png_structrp png_ptr) } else /* if (png_ptr->trans_alpha[i] != 0xff) */ { - png_byte v, w; + png_uint_16 v, w; + unsigned int alpha = png_ptr->trans_alpha[i] * 257U; + unsigned int shift = png_ptr->gamma_shift; + unsigned int add = (shift > 0 ? 1U<<(shift-1) : 0); v = png_ptr->gamma_to_1[palette[i].red]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.red); - palette[i].red = png_ptr->gamma_from_1[w]; + png_composite_16(w, v, alpha, back_1.red); + palette[i].red = png_ptr->gamma_from_1[(w+add)>>shift]; v = png_ptr->gamma_to_1[palette[i].green]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.green); - palette[i].green = png_ptr->gamma_from_1[w]; + png_composite_16(w, v, alpha, back_1.green); + palette[i].green = png_ptr->gamma_from_1[(w+add)>>shift]; v = png_ptr->gamma_to_1[palette[i].blue]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue); - palette[i].blue = png_ptr->gamma_from_1[w]; + png_composite_16(w, v, alpha, back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[(w+add)>>shift]; } } else @@ -1704,91 +1689,6 @@ png_init_read_transformations(png_structrp png_ptr) */ png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA); } /* color_type == PNG_COLOR_TYPE_PALETTE */ - - /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ - else /* color_type != PNG_COLOR_TYPE_PALETTE */ - { - int gs_sig, g_sig; - png_fixed_point g = PNG_FP_1; /* Correction to linear */ - png_fixed_point gs = PNG_FP_1; /* Correction to screen */ - - switch (png_ptr->background_gamma_type) - { - case PNG_BACKGROUND_GAMMA_SCREEN: - g = png_ptr->screen_gamma; - /* gs = PNG_FP_1; */ - break; - - case PNG_BACKGROUND_GAMMA_FILE: - g = png_reciprocal(png_ptr->colorspace.gamma); - gs = png_reciprocal2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma); - break; - - case PNG_BACKGROUND_GAMMA_UNIQUE: - g = png_reciprocal(png_ptr->background_gamma); - gs = png_reciprocal2(png_ptr->background_gamma, - png_ptr->screen_gamma); - break; - - default: - png_error(png_ptr, "invalid background gamma type"); - } - - g_sig = png_gamma_significant(g); - gs_sig = png_gamma_significant(gs); - - if (g_sig) - png_ptr->background_1.gray = png_gamma_correct(png_ptr, - png_ptr->background.gray, g); - - if (gs_sig) - png_ptr->background.gray = png_gamma_correct(png_ptr, - png_ptr->background.gray, gs); - - if ((png_ptr->background.red != png_ptr->background.green) || - (png_ptr->background.red != png_ptr->background.blue) || - (png_ptr->background.red != png_ptr->background.gray)) - { - /* RGB or RGBA with color background */ - if (g_sig) - { - png_ptr->background_1.red = png_gamma_correct(png_ptr, - png_ptr->background.red, g); - - png_ptr->background_1.green = png_gamma_correct(png_ptr, - png_ptr->background.green, g); - - png_ptr->background_1.blue = png_gamma_correct(png_ptr, - png_ptr->background.blue, g); - } - - if (gs_sig) - { - png_ptr->background.red = png_gamma_correct(png_ptr, - png_ptr->background.red, gs); - - png_ptr->background.green = png_gamma_correct(png_ptr, - png_ptr->background.green, gs); - - png_ptr->background.blue = png_gamma_correct(png_ptr, - png_ptr->background.blue, gs); - } - } - - else - { - /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */ - png_ptr->background_1.red = png_ptr->background_1.green - = png_ptr->background_1.blue = png_ptr->background_1.gray; - - png_ptr->background.red = png_ptr->background.green - = png_ptr->background.blue = png_ptr->background.gray; - } - - /* The background is now in screen gamma: */ - png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_SCREEN; - } /* color_type != PNG_COLOR_TYPE_PALETTE */ }/* png_ptr->transformations & PNG_BACKGROUND */ else @@ -2235,7 +2135,7 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) * for performance reasons */ if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && - !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + !(png_ptr->flags & PNG_FLAG_BACKGROUND_IS_GRAY)) png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1); #endif @@ -2320,7 +2220,7 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED /* NOTE: moved here in 1.5.4 (from much later in this list.) */ if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && - (png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + (png_ptr->flags & PNG_FLAG_BACKGROUND_IS_GRAY)) png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1); #endif @@ -3284,7 +3184,6 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) if (row_info->bit_depth == 8) { -#ifdef PNG_READ_GAMMA_SUPPORTED /* Notice that gamma to/from 1 are not necessarily inverses (if * there is an overall gamma correction). Prior to 1.5.5 this code * checked the linearized values for equality; this doesn't match @@ -3292,15 +3191,17 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) */ if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) { + PNG_CONST unsigned int shift = 15 + png_ptr->gamma_shift; + PNG_CONST png_uint_32 add = 1U << (shift-1); png_bytep sp = row; png_bytep dp = row; png_uint_32 i; for (i = 0; i < row_width; i++) { - png_byte red = *(sp++); - png_byte green = *(sp++); - png_byte blue = *(sp++); + unsigned int red = *(sp++); + unsigned int green = *(sp++); + unsigned int blue = *(sp++); if (red != green || red != blue) { @@ -3310,7 +3211,7 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) rgb_error |= 1; *(dp++) = png_ptr->gamma_from_1[ - (rc*red + gc*green + bc*blue + 16384)>>15]; + (rc*red + gc*green + bc*blue + add)>>shift]; } else @@ -3321,15 +3222,15 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) if (png_ptr->gamma_table != NULL) red = png_ptr->gamma_table[red]; - *(dp++) = red; + *(dp++) = (png_byte)red; } if (have_alpha) *(dp++) = *(sp++); } } + else -#endif { png_bytep sp = row; png_bytep dp = row; @@ -3344,10 +3245,7 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) if (red != green || red != blue) { rgb_error |= 1; - /* NOTE: this is the historical approach which simply - * truncates the results. - */ - *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((rc*red+gc*green+bc*blue+16384)>>15); } else @@ -3361,9 +3259,10 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) else /* RGB bit_depth == 16 */ { -#ifdef PNG_READ_GAMMA_SUPPORTED if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL) { + unsigned int shift = png_ptr->gamma_shift; + unsigned int add = (shift > 0 ? (1U << (shift-1)) : 0); png_bytep sp = row; png_bytep dp = row; png_uint_32 i; @@ -3379,8 +3278,7 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) if (red == green && red == blue) { if (png_ptr->gamma_16_table != NULL) - w = png_ptr->gamma_16_table[(red&0xff) - >> png_ptr->gamma_shift][red>>8]; + w = png_ptr->gamma_16_table[(red+add) >> shift]; else w = red; @@ -3388,17 +3286,15 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) else { - png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) - >> png_ptr->gamma_shift][red>>8]; - png_uint_16 green_1 = - png_ptr->gamma_16_to_1[(green&0xff) >> - png_ptr->gamma_shift][green>>8]; - png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) - >> png_ptr->gamma_shift][blue>>8]; + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red+add) + >> shift]; + png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green+add) + >> shift]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue+add) + >> shift]; png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + bc*blue_1 + 16384)>>15); - w = png_ptr->gamma_16_from_1[(gray16&0xff) >> - png_ptr->gamma_shift][gray16 >> 8]; + w = png_ptr->gamma_16_from_1[(gray16+add) >> shift]; rgb_error |= 1; } @@ -3412,8 +3308,8 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) } } } + else -#endif { png_bytep sp = row; png_bytep dp = row; @@ -3434,8 +3330,7 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) * in the 'fast' case - this is because this is where the code * ends up when handling linear 16 bit data. */ - gray16 = (png_uint_16)((rc*red + gc*green + bc*blue + 16384) >> - 15); + gray16 = (png_uint_16)((rc*red+gc*green+bc*blue+16384) >> 15); *(dp++) = (png_byte)((gray16>>8) & 0xff); *(dp++) = (png_byte)(gray16 & 0xff); @@ -3527,21 +3422,19 @@ png_build_grayscale_palette(int bit_depth, png_colorp palette) void /* PRIVATE */ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { -#ifdef PNG_READ_GAMMA_SUPPORTED png_const_bytep gamma_table = png_ptr->gamma_table; png_const_bytep gamma_from_1 = png_ptr->gamma_from_1; - png_const_bytep gamma_to_1 = png_ptr->gamma_to_1; - png_const_uint_16pp gamma_16 = png_ptr->gamma_16_table; - png_const_uint_16pp gamma_16_from_1 = png_ptr->gamma_16_from_1; - png_const_uint_16pp gamma_16_to_1 = png_ptr->gamma_16_to_1; - int gamma_shift = png_ptr->gamma_shift; - int optimize = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0; -#endif + png_const_uint_16p gamma_to_1 = png_ptr->gamma_to_1; + png_const_uint_16p gamma_16 = png_ptr->gamma_16_table; + png_const_uint_16p gamma_16_from_1 = png_ptr->gamma_16_from_1; + png_const_uint_16p gamma_16_to_1 = png_ptr->gamma_16_to_1; + PNG_CONST unsigned int shift = png_ptr->gamma_shift; + PNG_CONST unsigned int add = (shift > 0 ? 1U<<(shift-1) : 0); + PNG_CONST int optimize = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0; png_bytep sp; png_uint_32 i; png_uint_32 row_width = row_info->width; - int shift; png_debug(1, "in png_do_compose"); @@ -3554,91 +3447,91 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { case 1: { + int bit_shift = 7; sp = row; - shift = 7; for (i = 0; i < row_width; i++) { - if ((png_uint_16)((*sp >> shift) & 0x01) + if ((png_uint_16)((*sp >> bit_shift) & 0x01) == png_ptr->trans_color.gray) { - unsigned int tmp = *sp & (0x7f7f >> (7 - shift)); - tmp |= png_ptr->background.gray << shift; + unsigned int tmp = *sp & (0x7f7f >> (7 - bit_shift)); + tmp |= png_ptr->background.gray << bit_shift; *sp = (png_byte)(tmp & 0xff); } - if (!shift) + if (!bit_shift) { - shift = 7; + bit_shift = 7; sp++; } else - shift--; + bit_shift--; } break; } case 2: { -#ifdef PNG_READ_GAMMA_SUPPORTED +#if 0 if (gamma_table != NULL) { + int bit_shift = 6; sp = row; - shift = 6; for (i = 0; i < row_width; i++) { - if ((png_uint_16)((*sp >> shift) & 0x03) + if ((png_uint_16)((*sp >> bit_shift) & 0x03) == png_ptr->trans_color.gray) { - unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); - tmp |= png_ptr->background.gray << shift; + unsigned int tmp = *sp & (0x3f3f >> (6 - bit_shift)); + tmp |= png_ptr->background.gray << bit_shift; *sp = (png_byte)(tmp & 0xff); } else { - unsigned int p = (*sp >> shift) & 0x03; + unsigned int p = (*sp >> bit_shift) & 0x03; unsigned int g = (gamma_table [p | (p << 2) | (p << 4) | (p << 6)] >> 6) & 0x03; - unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); - tmp |= g << shift; + unsigned int tmp = *sp & (0x3f3f >> (6 - bit_shift)); + tmp |= g << bit_shift; *sp = (png_byte)(tmp & 0xff); } - if (!shift) + if (!bit_shift) { - shift = 6; + bit_shift = 6; sp++; } else - shift -= 2; + bit_shift -= 2; } } else #endif { + int bit_shift = 6; sp = row; - shift = 6; for (i = 0; i < row_width; i++) { - if ((png_uint_16)((*sp >> shift) & 0x03) + if ((png_uint_16)((*sp >> bit_shift) & 0x03) == png_ptr->trans_color.gray) { - unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); - tmp |= png_ptr->background.gray << shift; + unsigned int tmp = *sp & (0x3f3f >> (6 - bit_shift)); + tmp |= png_ptr->background.gray << bit_shift; *sp = (png_byte)(tmp & 0xff); } - if (!shift) + if (!bit_shift) { - shift = 6; + bit_shift = 6; sp++; } else - shift -= 2; + bit_shift -= 2; } } break; @@ -3646,65 +3539,65 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) case 4: { -#ifdef PNG_READ_GAMMA_SUPPORTED +#if 0 if (gamma_table != NULL) { + int bit_shift = 4; sp = row; - shift = 4; for (i = 0; i < row_width; i++) { - if ((png_uint_16)((*sp >> shift) & 0x0f) + if ((png_uint_16)((*sp >> bit_shift) & 0x0f) == png_ptr->trans_color.gray) { - unsigned int tmp = *sp & (0xf0f >> (4 - shift)); - tmp |= png_ptr->background.gray << shift; + unsigned int tmp = *sp & (0xf0f >> (4 - bit_shift)); + tmp |= png_ptr->background.gray << bit_shift; *sp = (png_byte)(tmp & 0xff); } else { - unsigned int p = (*sp >> shift) & 0x0f; + unsigned int p = (*sp >> bit_shift) & 0x0f; unsigned int g = (gamma_table[p | (p << 4)] >> 4) & 0x0f; - unsigned int tmp = *sp & (0xf0f >> (4 - shift)); - tmp |= g << shift; + unsigned int tmp = *sp & (0xf0f >> (4 - bit_shift)); + tmp |= g << bit_shift; *sp = (png_byte)(tmp & 0xff); } - if (!shift) + if (!bit_shift) { - shift = 4; + bit_shift = 4; sp++; } else - shift -= 4; + bit_shift -= 4; } } else #endif { + int bit_shift = 4; sp = row; - shift = 4; for (i = 0; i < row_width; i++) { - if ((png_uint_16)((*sp >> shift) & 0x0f) + if ((png_uint_16)((*sp >> bit_shift) & 0x0f) == png_ptr->trans_color.gray) { - unsigned int tmp = *sp & (0xf0f >> (4 - shift)); - tmp |= png_ptr->background.gray << shift; + unsigned int tmp = *sp & (0xf0f >> (4 - bit_shift)); + tmp |= png_ptr->background.gray << bit_shift; *sp = (png_byte)(tmp & 0xff); } - if (!shift) + if (!bit_shift) { - shift = 4; + bit_shift = 4; sp++; } else - shift -= 4; + bit_shift -= 4; } } break; @@ -3712,7 +3605,6 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) case 8: { -#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_table != NULL) { sp = row; @@ -3725,8 +3617,8 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) *sp = gamma_table[*sp]; } } + else -#endif { sp = row; for (i = 0; i < row_width; i++, sp++) @@ -3740,7 +3632,6 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) case 16: { -#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL) { sp = row; @@ -3761,14 +3652,14 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) else { - v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + v = gamma_16[(v+add) >> shift]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); } } } + else -#endif { sp = row; for (i = 0; i < row_width; i++, sp += 2) @@ -3799,7 +3690,6 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { if (row_info->bit_depth == 8) { -#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_table != NULL) { sp = row; @@ -3822,8 +3712,8 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) } } } + else -#endif { sp = row; for (i = 0; i < row_width; i++, sp += 3) @@ -3841,7 +3731,6 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) } else /* if (row_info->bit_depth == 16) */ { -#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL) { sp = row; @@ -3873,15 +3762,15 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) else { - png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + png_uint_16 v = gamma_16[(r+add) >> shift]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); - v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + v = gamma_16[(g+add) >> shift]; *(sp + 2) = (png_byte)((v >> 8) & 0xff); *(sp + 3) = (png_byte)(v & 0xff); - v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + v = gamma_16[(b+add) >> shift]; *(sp + 4) = (png_byte)((v >> 8) & 0xff); *(sp + 5) = (png_byte)(v & 0xff); } @@ -3889,7 +3778,6 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) } else -#endif { sp = row; for (i = 0; i < row_width; i++, sp += 6) @@ -3926,7 +3814,6 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { if (row_info->bit_depth == 8) { -#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_to_1 != NULL && gamma_from_1 != NULL && gamma_table != NULL) { @@ -3946,18 +3833,24 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) else { - png_byte v, w; + unsigned int v, w; v = gamma_to_1[*sp]; - png_composite(w, v, a, png_ptr->background_1.gray); + png_composite_16(w, v, 257*a, + png_ptr->background_1.gray); + if (!optimize) - w = gamma_from_1[w]; - *sp = w; + w = gamma_from_1[(w+add)>>shift]; + + else /* alpha pixels linear and approximate */ + w = PNG_DIV257(w); + + *sp = (png_byte)w; } } } + else -#endif { sp = row; for (i = 0; i < row_width; i++, sp += 2) @@ -3972,9 +3865,9 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) } } } + else /* if (png_ptr->bit_depth == 16) */ { -#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL && gamma_16_from_1 != NULL && gamma_16_to_1 != NULL) { @@ -3988,7 +3881,7 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { png_uint_16 v; - v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + v = gamma_16[((sp[0]<<8)+sp[1]+add) >> shift]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); } @@ -4005,19 +3898,22 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { png_uint_16 g, v, w; - g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + g = gamma_16_to_1[((sp[0]<<8)+sp[1]+add) >> shift]; png_composite_16(v, g, a, png_ptr->background_1.gray); - if (optimize) - w = v; + + if (!optimize) + w = gamma_16_from_1[(v+add) >> shift]; + else - w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8]; + w = v; + *sp = (png_byte)((w >> 8) & 0xff); *(sp + 1) = (png_byte)(w & 0xff); } } } + else -#endif { sp = row; for (i = 0; i < row_width; i++, sp += 4) @@ -4051,7 +3947,6 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { if (row_info->bit_depth == 8) { -#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_to_1 != NULL && gamma_from_1 != NULL && gamma_table != NULL) { @@ -4077,27 +3972,49 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) else { - png_byte v, w; + unsigned int v, w; + unsigned int alpha = a * 257U; v = gamma_to_1[*sp]; - png_composite(w, v, a, png_ptr->background_1.red); - if (!optimize) w = gamma_from_1[w]; - *sp = w; + png_composite_16(w, v, alpha, + png_ptr->background_1.red); + + if (!optimize) + w = gamma_from_1[(w+add)>>shift]; + + else + w = PNG_DIV257(w); + + *sp = (png_byte)w; v = gamma_to_1[*(sp + 1)]; - png_composite(w, v, a, png_ptr->background_1.green); - if (!optimize) w = gamma_from_1[w]; - *(sp + 1) = w; + png_composite_16(w, v, alpha, + png_ptr->background_1.green); + + if (!optimize) + w = gamma_from_1[(w+add)>>shift]; + + else + w = PNG_DIV257(w); + + *(sp + 1) = (png_byte)w; v = gamma_to_1[*(sp + 2)]; - png_composite(w, v, a, png_ptr->background_1.blue); - if (!optimize) w = gamma_from_1[w]; - *(sp + 2) = w; + png_composite_16(w, v, alpha, + png_ptr->background_1.blue); + + if (!optimize) + w = gamma_from_1[(w+add)>>shift]; + + else + w = PNG_DIV257(w); + + *(sp + 2) = (png_byte)w; } } } + else -#endif { sp = row; for (i = 0; i < row_width; i++, sp += 4) @@ -4124,9 +4041,9 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) } } } + else /* if (row_info->bit_depth == 16) */ { -#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL && gamma_16_from_1 != NULL && gamma_16_to_1 != NULL) { @@ -4140,15 +4057,15 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { png_uint_16 v; - v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + v = gamma_16[((sp[0]<<8)+sp[1]+add) >> shift]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); - v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + v = gamma_16[((sp[2]<<8)+sp[3]+add) >> shift]; *(sp + 2) = (png_byte)((v >> 8) & 0xff); *(sp + 3) = (png_byte)(v & 0xff); - v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + v = gamma_16[((sp[4]<<8)+sp[5]+add) >> shift]; *(sp + 4) = (png_byte)((v >> 8) & 0xff); *(sp + 5) = (png_byte)(v & 0xff); } @@ -4171,28 +4088,29 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { png_uint_16 v, w; - v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + v = gamma_16_to_1[((sp[0]<<8)+sp[1]+add) >> shift]; png_composite_16(w, v, a, png_ptr->background_1.red); + if (!optimize) - w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> - 8]; + w = gamma_16_from_1[(w+add) >> shift]; + *sp = (png_byte)((w >> 8) & 0xff); *(sp + 1) = (png_byte)(w & 0xff); - v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; + v = gamma_16_to_1[((sp[2]<<8)+sp[3]+add) >> shift]; png_composite_16(w, v, a, png_ptr->background_1.green); + if (!optimize) - w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> - 8]; + w = gamma_16_from_1[(w+add) >> shift]; *(sp + 2) = (png_byte)((w >> 8) & 0xff); *(sp + 3) = (png_byte)(w & 0xff); - v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; + v = gamma_16_to_1[((sp[4]<<8)+sp[5]+add) >> shift]; png_composite_16(w, v, a, png_ptr->background_1.blue); + if (!optimize) - w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> - 8]; + w = gamma_16_from_1[(w+add) >> shift]; *(sp + 4) = (png_byte)((w >> 8) & 0xff); *(sp + 5) = (png_byte)(w & 0xff); @@ -4201,7 +4119,6 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) } else -#endif { sp = row; for (i = 0; i < row_width; i++, sp += 8) @@ -4268,8 +4185,9 @@ void /* PRIVATE */ png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { png_const_bytep gamma_table = png_ptr->gamma_table; - png_const_uint_16pp gamma_16_table = png_ptr->gamma_16_table; - int gamma_shift = png_ptr->gamma_shift; + png_const_uint_16p gamma_16_table = png_ptr->gamma_16_table; + int shift = png_ptr->gamma_shift; + int add = (shift > 0 ? 1U << (shift-1) : 0); png_bytep sp; png_uint_32 i; @@ -4277,7 +4195,12 @@ png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) png_debug(1, "in png_do_gamma"); - if (((row_info->bit_depth <= 8 && gamma_table != NULL) || + /* Prior to libpng 1.7.0 this code would attempt to gamma correct 2 and 4 bit + * gray level values, the results are ridiculously inaccurate. In 1.7.0 the + * code is removed and a warning is introduced to catch cases where an + * application might actually try it. + */ + if (((row_info->bit_depth == 8 && gamma_table != NULL) || (row_info->bit_depth == 16 && gamma_16_table != NULL))) { switch (row_info->color_type) @@ -4305,17 +4228,17 @@ png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { png_uint_16 v; - v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; - v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; - v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; @@ -4349,17 +4272,19 @@ png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) sp = row; for (i = 0; i < row_width; i++) { - png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + png_uint_16 v; + + v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; - v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; - v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 4; @@ -4385,7 +4310,9 @@ png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) sp = row; for (i = 0; i < row_width; i++) { - png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + png_uint_16 v; + + v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 4; @@ -4396,40 +4323,7 @@ png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) case PNG_COLOR_TYPE_GRAY: { - if (row_info->bit_depth == 2) - { - sp = row; - for (i = 0; i < row_width; i += 4) - { - int a = *sp & 0xc0; - int b = *sp & 0x30; - int c = *sp & 0x0c; - int d = *sp & 0x03; - - *sp = (png_byte)( - ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| - ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| - ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| - ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); - sp++; - } - } - - if (row_info->bit_depth == 4) - { - sp = row; - for (i = 0; i < row_width; i += 2) - { - int msb = *sp & 0xf0; - int lsb = *sp & 0x0f; - - *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0) - | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); - sp++; - } - } - - else if (row_info->bit_depth == 8) + if (row_info->bit_depth == 8) { sp = row; for (i = 0; i < row_width; i++) @@ -4439,12 +4333,14 @@ png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) } } - else if (row_info->bit_depth == 16) + else /*row_info->bit_depth == 16 */ { sp = row; for (i = 0; i < row_width; i++) { - png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + png_uint_16 v; + + v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; @@ -4474,11 +4370,14 @@ png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr) if (row_info->color_type & PNG_COLOR_MASK_ALPHA) { + PNG_CONST unsigned int shift = png_ptr->gamma_shift; + PNG_CONST unsigned int add = (shift > 0 ? 1U<<(shift-1) : 0); + if (row_info->bit_depth == 8) { - PNG_CONST png_bytep table = png_ptr->gamma_from_1; + PNG_CONST png_bytep gamma_from_1 = png_ptr->gamma_from_1; - if (table != NULL) + if (gamma_from_1 != NULL) { PNG_CONST int step = (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 4 : 2; @@ -4487,7 +4386,7 @@ png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr) row += step - 1; for (; row_width > 0; --row_width, row += step) - *row = table[*row]; + *row = gamma_from_1[(257U**row+add)>>shift]; return; } @@ -4495,10 +4394,9 @@ png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr) else if (row_info->bit_depth == 16) { - PNG_CONST png_uint_16pp table = png_ptr->gamma_16_from_1; - PNG_CONST int gamma_shift = png_ptr->gamma_shift; + PNG_CONST png_uint_16p gamma_16_from_1 = png_ptr->gamma_16_from_1; - if (table != NULL) + if (gamma_16_from_1 != NULL) { PNG_CONST int step = (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 8 : 4; @@ -4510,7 +4408,7 @@ png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { png_uint_16 v; - v = table[*(row + 1) >> gamma_shift][*row]; + v = gamma_16_from_1[((row[0]<<8)+row[1]+add) >> shift]; *row = (png_byte)((v >> 8) & 0xff); *(row + 1) = (png_byte)(v & 0xff); } diff --git a/pngrutil.c b/pngrutil.c index aa5cd0cfb..72d01bda1 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -1832,6 +1832,8 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, &(png_ptr->trans_color)); + + png_ptr->trans_alpha = info_ptr->trans_alpha; } #endif @@ -4410,7 +4412,7 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) png_error(png_ptr, "This image requires a row greater than 64KB"); #endif - if (row_bytes + 48 > png_ptr->old_big_row_buf_size) + if (row_bytes + 48 > png_ptr->big_row_buf_size) { png_free(png_ptr, png_ptr->big_row_buf); png_free(png_ptr, png_ptr->big_prev_row); @@ -4447,7 +4449,7 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) png_ptr->row_buf = png_ptr->big_row_buf + 31; png_ptr->prev_row = png_ptr->big_prev_row + 31; #endif - png_ptr->old_big_row_buf_size = row_bytes + 48; + png_ptr->big_row_buf_size = row_bytes + 48; } #ifdef PNG_MAX_MALLOC_64K diff --git a/pngset.c b/pngset.c index c57dcef79..ee8d44e94 100644 --- a/pngset.c +++ b/pngset.c @@ -910,52 +910,89 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, if (png_ptr == NULL || info_ptr == NULL) return; - if (trans_alpha != NULL) + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + png_chunk_report(png_ptr, + "png_set_tRNS: invalid on PNG with alpha channel", PNG_CHUNK_ERROR); + + else if (info_ptr->color_type & PNG_COLOR_MASK_PALETTE) { - /* It may not actually be necessary to set png_ptr->trans_alpha here; - * we do it for backward compatibility with the way the png_handle_tRNS - * function used to do the allocation. - * - * 1.6.0: The above statement is incorrect; png_handle_tRNS effectively - * relies on png_set_tRNS storing the information in png_struct - * (otherwise it won't be there for the code in pngrtran.c). - */ + int max_num; - png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); + /* Free the old data; num_trans 0 can be used to kill the tRNS */ + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); - /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ - png_ptr->trans_alpha = info_ptr->trans_alpha = png_voidcast(png_bytep, - png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); + /* Do this just in case the old data was not owned by libpng: */ + info_ptr->valid &= ~PNG_INFO_tRNS; + info_ptr->trans_alpha = NULL; + info_ptr->num_trans = 0; - if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) - memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans); + /* Expect png_set_PLTE to happen before png_set_tRNS, so num_palette will + * be set, but this is not a requirement of the API. + */ + if (png_ptr->num_palette) + max_num = png_ptr->num_palette; + + else + max_num = 1 << png_ptr->bit_depth; + + if (num_trans > max_num) + { + png_chunk_report(png_ptr, "png_set_tRNS: num_trans too large", + PNG_CHUNK_ERROR); + /* If control returns simply limit it; the behavior prior to 1.7 was to + * issue a warning and skip the palette in png_write_tRNS. + */ + num_trans = max_num; + } + + /* But only attempt a malloc if there is something to do; so the app can + * set a tRNS array then later delete it. + */ + if (num_trans > 0 && trans_alpha != NULL) + { + /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1, + * this avoids issues where a palette image contains out of range + * indices. + */ + info_ptr->trans_alpha = png_voidcast(png_bytep, png_malloc(png_ptr, + PNG_MAX_PALETTE_LENGTH)); + info_ptr->free_me |= PNG_FREE_TRNS; + + memcpy(info_ptr->trans_alpha, trans_alpha, num_trans); + info_ptr->valid |= PNG_INFO_tRNS; + info_ptr->num_trans = (png_uint_16)num_trans; /* SAFE */ + } } - if (trans_color != NULL) + else /* not a PALETTE image */ { - int sample_max = (1 << info_ptr->bit_depth); + /* Invalidate any prior transparent color, set num_trans too, it is not + * used internally in this case but png_get_tRNS still returns it. + */ + info_ptr->valid &= ~PNG_INFO_tRNS; + info_ptr->num_trans = 0; /* for png_get_tRNS */ - if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && - trans_color->gray > sample_max) || - (info_ptr->color_type == PNG_COLOR_TYPE_RGB && - (trans_color->red > sample_max || - trans_color->green > sample_max || - trans_color->blue > sample_max))) - png_warning(png_ptr, - "tRNS chunk has out-of-range samples for bit_depth"); + if (trans_color != NULL) + { + int sample_max = (1 << info_ptr->bit_depth); - info_ptr->trans_color = *trans_color; + if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && + trans_color->gray <= sample_max) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + trans_color->red <= sample_max && + trans_color->green <= sample_max && + trans_color->blue <= sample_max)) + { + info_ptr->trans_color = *trans_color; + info_ptr->valid |= PNG_INFO_tRNS; + info_ptr->num_trans = 1; /* for png_get_tRNS */ + } - if (num_trans == 0) - num_trans = 1; - } - - info_ptr->num_trans = (png_uint_16)num_trans; - - if (num_trans != 0) - { - info_ptr->valid |= PNG_INFO_tRNS; - info_ptr->free_me |= PNG_FREE_TRNS; + else + png_chunk_report(png_ptr, + "tRNS chunk has out-of-range samples for bit_depth", + PNG_CHUNK_ERROR); + } } } #endif @@ -1498,8 +1535,13 @@ png_set_user_limits (png_structrp png_ptr, png_uint_32 user_width_max, void PNGAPI png_set_chunk_cache_max (png_structrp png_ptr, png_uint_32 user_chunk_cache_max) { - if (png_ptr) - png_ptr->user_chunk_cache_max = user_chunk_cache_max; +# ifdef PNG_READ_SUPPORTED + if (png_ptr) + png_ptr->user_chunk_cache_max = user_chunk_cache_max; +# else + PNG_UNUSED(png_ptr) + PNG_UNUSED(user_chunk_cache_max) +# endif } /* This function was added to libpng 1.4.1 */ @@ -1507,8 +1549,13 @@ void PNGAPI png_set_chunk_malloc_max (png_structrp png_ptr, png_alloc_size_t user_chunk_malloc_max) { - if (png_ptr) - png_ptr->user_chunk_malloc_max = user_chunk_malloc_max; +# ifdef PNG_READ_SUPPORTED + if (png_ptr) + png_ptr->user_chunk_malloc_max = user_chunk_malloc_max; +# else + PNG_UNUSED(png_ptr) + PNG_UNUSED(user_chunk_malloc_max) +# endif } #endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ diff --git a/pngstruct.h b/pngstruct.h index 3179165f1..87b8c1564 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -48,10 +48,10 @@ /* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib * can handle at once. This type need be no larger than 16 bits (so maximum of * 65535), this define allows us to discover how big it is, but limited by the - * maximuum for png_size_t. The value can be overriden in a library build - * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably - * lower value (e.g. 255 works). A lower value may help memory usage (slightly) - * and may even improve performance on some systems (and degrade it on others.) + * maximum for size_t. The value can be overriden in a library build (pngusr.h, + * or set it in CPPFLAGS) and it works to set it to a considerably lower value + * (e.g. 255 works). A lower value may help memory usage (slightly) and may + * even improve performance on some systems (and degrade it on others.) */ #ifndef ZLIB_IO_MAX # define ZLIB_IO_MAX ((uInt)-1) @@ -137,313 +137,232 @@ typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp; #define PNG_COLORSPACE_FROM_sRGB 0x0020 #define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040 #define PNG_COLORSPACE_MATCHES_sRGB 0x0080 /* exact match on profile */ +#define PNG_COLORSPACE_RGB_TO_GRAY_SET 0x0100 /* user specified coeffs */ #define PNG_COLORSPACE_INVALID 0x8000 #define PNG_COLORSPACE_CANCEL(flags) (0xffff ^ (flags)) #endif /* COLORSPACE || GAMMA */ struct png_struct_def { -#ifdef PNG_SETJMP_SUPPORTED - jmp_buf jmp_buf_local; /* New name in 1.6.0 for jmp_buf in png_struct */ - png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */ - jmp_buf *jmp_buf_ptr; /* passed to longjmp_fn */ - size_t jmp_buf_size; /* size of the above, if allocated */ -#endif - png_error_ptr error_fn; /* function for printing errors and aborting */ -#ifdef PNG_WARNINGS_SUPPORTED - png_error_ptr warning_fn; /* function for printing warnings */ -#endif - png_voidp error_ptr; /* user supplied struct for error functions */ - png_rw_ptr write_data_fn; /* function for writing output data */ - png_rw_ptr read_data_fn; /* function for reading input data */ - png_voidp io_ptr; /* ptr to application struct for I/O functions */ + /* Rearranged in libpng 1.7 to attempt to lessen padding; in general + * (char), (short), (int) and pointer types are kept separate, however + * associated members under the control of the same #define are still + * together. + * + * First the frequently accessed fields. Many processors perform arithmetic + * in the address pipeline, but frequently the amount of addition or + * subtraction is limited. By putting these fields at the head of png_struct + * the hope is that such processors will generate code that is both smaller + * and faster. + */ + png_colorp palette; /* palette from the input file */ + size_t rowbytes; /* size of row in bytes */ + size_t info_rowbytes; /* cache of updated row bytes */ + png_uint_32 width; /* width of image in pixels */ + png_uint_32 height; /* height of image in pixels */ + png_uint_32 num_rows; /* number of rows in current pass */ + png_uint_32 usr_width; /* width of row at start of write */ + png_uint_32 iwidth; /* width of current interlaced row in pixels */ + png_uint_32 row_number; /* current row in interlace pass */ + png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */ + png_uint_32 crc; /* current chunk CRC value */ + png_uint_32 mode; /* tells us where we are in the PNG file */ + png_uint_32 flags; /* flags indicating various things to libpng */ + png_uint_32 transformations;/* which transformations to perform */ + png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */ + png_uint_32 free_me; /* items libpng is responsible for freeing */ -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - png_user_transform_ptr read_user_transform_fn; /* user read transform */ -#endif - -#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED - png_user_transform_ptr write_user_transform_fn; /* user write transform */ -#endif - -/* These were added in libpng-1.0.2 */ -#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) - png_voidp user_transform_ptr; /* user supplied struct for user transform */ - png_byte user_transform_depth; /* bit depth of user transformed pixels */ - png_byte user_transform_channels; /* channels in user transformed pixels */ -#endif -#endif - - png_uint_32 mode; /* tells us where we are in the PNG file */ - png_uint_32 flags; /* flags indicating various things to libpng */ - png_uint_32 transformations; /* which transformations to perform */ - - png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */ - z_stream zstream; /* decompression structure */ - -#ifdef PNG_WRITE_SUPPORTED - png_compression_bufferp zbuffer_list; /* Created on demand during write */ - uInt zbuffer_size; /* size of the actual buffer */ - - int zlib_level; /* holds zlib compression level */ - int zlib_method; /* holds zlib compression method */ - int zlib_window_bits; /* holds zlib compression window bits */ - int zlib_mem_level; /* holds zlib compression memory level */ - int zlib_strategy; /* holds zlib compression strategy */ -#endif -/* Added at libpng 1.5.4 */ -#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED - int zlib_text_level; /* holds zlib compression level */ - int zlib_text_method; /* holds zlib compression method */ - int zlib_text_window_bits; /* holds zlib compression window bits */ - int zlib_text_mem_level; /* holds zlib compression memory level */ - int zlib_text_strategy; /* holds zlib compression strategy */ -#endif -/* End of material added at libpng 1.5.4 */ -/* Added at libpng 1.6.0 */ -#ifdef PNG_WRITE_SUPPORTED - int zlib_set_level; /* Actual values set into the zstream on write */ - int zlib_set_method; - int zlib_set_window_bits; - int zlib_set_mem_level; - int zlib_set_strategy; -#endif - - png_uint_32 width; /* width of image in pixels */ - png_uint_32 height; /* height of image in pixels */ - png_uint_32 num_rows; /* number of rows in current pass */ - png_uint_32 usr_width; /* width of row at start of write */ - png_size_t rowbytes; /* size of row in bytes */ - png_uint_32 iwidth; /* width of current interlaced row in pixels */ - png_uint_32 row_number; /* current row in interlace pass */ - png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */ - png_bytep prev_row; /* buffer to save previous (unfiltered) row. - * This is a pointer into big_prev_row - */ - png_bytep row_buf; /* buffer to save current (unfiltered) row. - * This is a pointer into big_row_buf - */ -#ifdef PNG_WRITE_SUPPORTED - png_bytep sub_row; /* buffer to save "sub" row when filtering */ - png_bytep up_row; /* buffer to save "up" row when filtering */ - png_bytep avg_row; /* buffer to save "avg" row when filtering */ - png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ -#endif - png_size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */ - - png_uint_32 idat_size; /* current IDAT size for read */ - png_uint_32 crc; /* current chunk CRC value */ - png_colorp palette; /* palette from the input file */ - png_uint_16 num_palette; /* number of color entries in palette */ - -/* Added at libpng-1.5.10 */ + int maximum_pixel_depth; /* pixel depth used for the row buffers */ #ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED - int num_palette_max; /* maximum palette index found in IDAT */ + int num_palette_max; /* maximum palette index found in IDAT */ #endif + png_uint_16 num_palette; /* number of color entries in palette */ png_uint_16 num_trans; /* number of transparency values */ - png_byte compression; /* file compression type (always 0) */ + + /* Single byte values, typically used either to save space or to hold 1-byte + * values from the PNG chunk specifications. + */ + png_byte compression_type; /* file compression type (always 0) */ png_byte filter; /* file filter type (always 0) */ png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ png_byte pass; /* current interlace pass (0 - 6) */ png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ png_byte color_type; /* color type of file */ png_byte bit_depth; /* bit depth of file */ - png_byte usr_bit_depth; /* bit depth of users row: write only */ png_byte pixel_depth; /* number of bits per pixel */ png_byte channels; /* number of channels in file */ -#ifdef PNG_WRITE_SUPPORTED - png_byte usr_channels; /* channels at start of write: write only */ -#endif png_byte sig_bytes; /* magic bytes read/written from start of file */ - png_byte maximum_pixel_depth; - /* pixel depth used for the row buffers */ png_byte transformed_pixel_depth; /* pixel depth after read/write transforms */ -#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) - png_uint_16 filler; /* filler bytes for pixel expansion */ + + /* ERROR HANDLING */ +#ifdef PNG_SETJMP_SUPPORTED + /* jmp_buf can have very high alignment requirements on some systems, so put + * it first. + */ + jmp_buf jmp_buf_local; + jmp_buf *jmp_buf_ptr; /* passed to longjmp_fn */ + png_longjmp_ptr longjmp_fn; /* setjmp non-local goto function. */ + size_t jmp_buf_size; /* size of *jmp_buf_ptr, if allocated */ #endif -#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) - png_byte background_gamma_type; - png_fixed_point background_gamma; - png_color_16 background; /* background color in screen gamma space */ -#ifdef PNG_READ_GAMMA_SUPPORTED - png_color_16 background_1; /* background normalized to gamma 1.0 */ + /* Error/warning callbacks */ + png_error_ptr error_fn; /* print an error message and abort */ +#ifdef PNG_WARNINGS_SUPPORTED + png_error_ptr warning_fn; /* print a warning and continue */ #endif -#endif /* PNG_bKGD_SUPPORTED */ + png_voidp error_ptr; /* user supplied data for the above */ -#ifdef PNG_WRITE_FLUSH_SUPPORTED - png_flush_ptr output_flush_fn; /* Function for flushing output */ - png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ - png_uint_32 flush_rows; /* number of rows written since last flush */ -#endif - -#ifdef PNG_READ_GAMMA_SUPPORTED - int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */ - png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */ - - png_bytep gamma_table; /* gamma table for 8-bit depth files */ - png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - png_bytep gamma_from_1; /* converts from 1.0 to screen */ - png_bytep gamma_to_1; /* converts from file to 1.0 */ - png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ - png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ -#endif - -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) - png_color_8 sig_bit; /* significant bits in each available channel */ -#endif - -#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) - png_color_8 shift; /* shift for significant bit tranformation */ -#endif - -#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ - || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - png_bytep trans_alpha; /* alpha values for paletted files */ - png_color_16 trans_color; /* transparent color for non-paletted files */ -#endif - - png_read_status_ptr read_row_fn; /* called after each row is decoded */ - png_write_status_ptr write_row_fn; /* called after each row is encoded */ -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED - png_progressive_info_ptr info_fn; /* called after header data fully read */ - png_progressive_row_ptr row_fn; /* called after a prog. row is decoded */ - png_progressive_end_ptr end_fn; /* called after image is complete */ - png_bytep save_buffer_ptr; /* current location in save_buffer */ - png_bytep save_buffer; /* buffer for previously read data */ - png_bytep current_buffer_ptr; /* current location in current_buffer */ - png_bytep current_buffer; /* buffer for recently used data */ - png_uint_32 push_length; /* size of current input chunk */ - png_uint_32 skip_length; /* bytes to skip in input data */ - png_size_t save_buffer_size; /* amount of data now in save_buffer */ - png_size_t save_buffer_max; /* total size of save_buffer */ - png_size_t buffer_size; /* total amount of available input data */ - png_size_t current_buffer_size; /* amount of data now in current_buffer */ - int process_mode; /* what push library is currently doing */ - int cur_palette; /* current push library palette index */ - -#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ - -#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) -/* For the Borland special 64K segment handler */ - png_bytepp offset_table_ptr; - png_bytep offset_table; - png_uint_16 offset_table_number; - png_uint_16 offset_table_count; - png_uint_16 offset_table_count_free; -#endif - -#ifdef PNG_READ_QUANTIZE_SUPPORTED - png_bytep palette_lookup; /* lookup table for quantizing */ - png_bytep quantize_index; /* index translation for palette files */ -#endif - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - png_byte heuristic_method; /* heuristic for row filter selection */ - png_byte num_prev_filters; /* number of weights for previous rows */ - png_bytep prev_filters; /* filter type(s) of previous row(s) */ - png_uint_16p filter_weights; /* weight(s) for previous line(s) */ - png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ - png_uint_16p filter_costs; /* relative filter calculation cost */ - png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ -#endif - -#if PNG_LIBPNG_VER < 10700 -/* To do: remove this from libpng-1.7 */ -#ifdef PNG_TIME_RFC1123_SUPPORTED - char time_buffer[29]; /* String to hold RFC 1123 time text */ -#endif -#endif - -/* New members added in libpng-1.0.6 */ - - png_uint_32 free_me; /* flags items libpng is responsible for freeing */ - -#ifdef PNG_USER_CHUNKS_SUPPORTED - png_voidp user_chunk_ptr; -#ifdef PNG_READ_USER_CHUNKS_SUPPORTED - png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ -#endif -#endif - -#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED - int unknown_default; /* As PNG_HANDLE_* */ - unsigned int num_chunk_list; /* Number of entries in the list */ - png_bytep chunk_list; /* List of png_byte[5]; the textual chunk name - * followed by a PNG_HANDLE_* byte */ -#endif - -/* New members added in libpng-1.0.3 */ -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - png_byte rgb_to_gray_status; - /* Added in libpng 1.5.5 to record setting of coefficients: */ - png_byte rgb_to_gray_coefficients_set; - /* These were changed from png_byte in libpng-1.0.6 */ - png_uint_16 rgb_to_gray_red_coeff; - png_uint_16 rgb_to_gray_green_coeff; - /* deleted in 1.5.5: rgb_to_gray_blue_coeff; */ -#endif - -/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ -#if defined(PNG_MNG_FEATURES_SUPPORTED) -/* Changed from png_byte to png_uint_32 at version 1.2.0 */ - png_uint_32 mng_features_permitted; -#endif - -/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ -#ifdef PNG_MNG_FEATURES_SUPPORTED - png_byte filter_type; -#endif - -/* New members added in libpng-1.2.0 */ - -/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ + /* MEMORY ALLOCATION */ #ifdef PNG_USER_MEM_SUPPORTED - png_voidp mem_ptr; /* user supplied struct for mem functions */ - png_malloc_ptr malloc_fn; /* function for allocating memory */ - png_free_ptr free_fn; /* function for freeing memory */ + png_malloc_ptr malloc_fn; /* allocate memory */ + png_free_ptr free_fn; /* free memory */ + png_voidp mem_ptr; /* user supplied data for the above */ #endif -/* New member added in libpng-1.0.13 and 1.2.0 */ - png_bytep big_row_buf; /* buffer to save current (unfiltered) row */ + /* IO and BASIC READ/WRITE SUPPORT */ + png_voidp io_ptr; /* user supplied data for IO callbacks */ -#ifdef PNG_READ_QUANTIZE_SUPPORTED -/* The following three members were added at version 1.0.14 and 1.2.4 */ - png_bytep quantize_sort; /* working sort array */ - png_bytep index_to_palette; /* where the original index currently is - in the palette */ - png_bytep palette_to_index; /* which original index points to this - palette color */ +#ifdef PNG_READ_SUPPORTED + png_rw_ptr read_data_fn; /* read some bytes (must succeed) */ + png_read_status_ptr read_row_fn; /* called after each row is decoded */ + png_bytep read_buffer; /* buffer for reading chunk data */ + + /* During read the following array is set up to point to the appropriate + * un-filter function, this allows per-image and per-processor optimization. + */ + void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info, + png_bytep row, png_const_bytep prev_row); + +#if (defined PNG_COLORSPACE_SUPPORTED || defined PNG_GAMMA_SUPPORTED) + /* The png_struct colorspace structure is only required on read - on write it + * is in (just) the info_struct. + */ + png_colorspace colorspace; #endif - -/* New members added in libpng-1.0.16 and 1.2.6 */ - png_byte compression_type; +#endif /* PNG_READ_SUPPORTED */ #ifdef PNG_USER_LIMITS_SUPPORTED - png_uint_32 user_width_max; - png_uint_32 user_height_max; - - /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown - * chunks that can be stored (0 means unlimited). - */ - png_uint_32 user_chunk_cache_max; - + /* The limits only affect read from libpng 1.7 */ /* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk * can occupy when decompressed. 0 means unlimited. */ png_alloc_size_t user_chunk_malloc_max; + /* limit on total *number* of sPLT, text and unknown chunks that can be + * stored. 0 means unlimited. + */ + png_uint_32 user_chunk_cache_max; + png_uint_32 user_width_max; + png_uint_32 user_height_max; #endif -/* New member added in libpng-1.0.25 and 1.2.17 */ + /* The progressive reader gets passed data and calls application handling + * functions when appropriate. + */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn; /* called after header data fully read */ + png_progressive_row_ptr row_fn; /* called after a row is decoded */ + png_progressive_end_ptr end_fn; /* called after image is complete */ + + /* Progressive read control data */ + png_bytep save_buffer_ptr; /* current location in save_buffer */ + png_bytep save_buffer; /* buffer for previously read data */ + png_bytep current_buffer_ptr; /* current location in current_buffer */ + png_bytep current_buffer; /* buffer for recently used data */ + + size_t save_buffer_size; /* amount of data now in save_buffer */ + size_t save_buffer_max; /* total size of save_buffer */ + size_t buffer_size; /* total amount of available input data */ + size_t current_buffer_size; /* amount of data now in current_buffer */ + + png_uint_32 push_length; /* size of current input chunk */ + png_uint_32 skip_length; /* bytes to skip in input data */ + + int process_mode; /* what push library is currently doing */ + int cur_palette; /* current push library palette index */ +#endif + +#ifdef PNG_WRITE_SUPPORTED + png_rw_ptr write_data_fn;/* write some bytes (must succeed) */ + png_write_status_ptr write_row_fn; /* called after each row is encoded */ +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_flush_ptr output_flush_fn; /* Function for flushing output */ + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +#endif + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_bytep prev_filters; /* filter type(s) of previous row(s) */ + png_uint_16p filter_weights; /* weight(s) for previous line(s) */ + png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ + png_uint_16p filter_costs; /* relative filter calculation cost */ + png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ + png_byte heuristic_method; /* heuristic for row filter selection */ + png_byte num_prev_filters; /* number of weights for previous rows */ +#endif + +#ifdef PNG_WRITE_SUPPORTED + png_byte usr_bit_depth; /* bit depth of users row */ + png_byte usr_channels; /* channels at start of write */ +#endif + +#ifdef PNG_IO_STATE_SUPPORTED + png_uint_32 io_state; /* tells the app read/write progress */ +#endif + + /* ROW BUFFERS + * + * Members that hold pointers to the decompressed image rows. + */ + png_bytep row_buf; /* buffer for the current (unfiltered) row */ +#if (defined PNG_WRITE_FILTER_SUPPORTED) || (defined PNG_READ_SUPPORTED) + png_bytep prev_row; /* buffer to save the previous (unfiltered) row */ +#endif + +#ifdef PNG_READ_SUPPORTED + /* The row_buf and prev_row pointers are misaligned so that the start of the + * row - after the filter byte - is aligned, the 'big_' pointers record the + * original allocated pointer. + */ + png_bytep big_row_buf; + png_bytep big_prev_row; + size_t big_row_buf_size; /* Actual size of both */ +#endif + +#ifdef PNG_WRITE_SUPPORTED + /* This is somewhat excessive, there is no obvious reason on write to + * allocate a buffer for each possible filtered row, only for the one being + * tested and the current best. + * + * TODO: fix this + */ + png_bytep sub_row; /* buffer to save "sub" row when filtering */ + png_bytep up_row; /* buffer to save "up" row when filtering */ + png_bytep avg_row; /* buffer to save "avg" row when filtering */ + png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ +#endif + + /* UNKNOWN CHUNK HANDLING */ + /* TODO: this is excessively complicated, there are multiple ways of doing + * the same thing. It should be cleaned up, possibly by finding out which + * APIs applications really use. + */ +#ifdef PNG_USER_CHUNKS_SUPPORTED + /* General purpose pointer for all user/unknown chunk handling; points to + * application supplied data for use in the read_user_chunk_fn callback + * (currently there is no write side support - the write side must use the + * set_unknown_chunks interface.) + */ + png_voidp user_chunk_ptr; +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + /* This is called back from the unknown chunk handling */ + png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ +#endif #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED /* Temporary storage for unknown chunk that the library doesn't recognize, * used while reading the chunk. @@ -451,33 +370,175 @@ struct png_struct_def png_unknown_chunk unknown_chunk; #endif -/* New member added in libpng-1.2.26 */ - png_size_t old_big_row_buf_size; +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + png_bytep chunk_list; /* List of png_byte[5]; the textual chunk name + * followed by a PNG_HANDLE_* byte */ + int unknown_default; /* As PNG_HANDLE_* */ + unsigned int num_chunk_list; /* Number of entries in the list */ +#endif + + /* USER TRANSFORM SUPPORT */ +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr read_user_transform_fn; /* user read transform */ +#endif +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr write_user_transform_fn; /* user write transform */ +#endif +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + png_voidp user_transform_ptr; /* user supplied data for the above */ + png_byte user_transform_depth; /* bit depth of user transformed pixels */ + png_byte user_transform_channels; /* channels in user transformed pixels */ +#endif + + /* READ TRANSFORM SUPPORT + * + * Quite a lot of things can be done to the original image data on read, and + * most of these are configurable. The data required by the configurable + * read transforms should be stored here. The png_color_16 and png_color_8 + * structures have low alignment requirements and odd sizes, so may cause + * misalignment when present. Member alignment is as follows: + * + * png_color_16 png_uint_16 + * png_color_8 png_byte + */ + /* GAMMA/BACKGROUND/ALPHA-MODE/RGB-TO-GRAY/tRNS/sBIT + * + * These things are all interrelated because they need some or all of the + * gamma tables. Some attempt has been made below to order these members by + * size, so that as little padding as possible is required. + */ +#ifdef PNG_READ_GAMMA_SUPPORTED + png_bytep gamma_table; /* gamma table for 8-bit depth files */ + png_uint_16p gamma_16_table; /* gamma table for 16-bit depth files */ + +#if defined PNG_READ_BACKGROUND_SUPPORTED ||\ + defined PNG_READ_ALPHA_MODE_SUPPORTED ||\ + defined PNG_READ_RGB_TO_GRAY_SUPPORTED + png_bytep gamma_from_1; /* converts from 1.0 to screen */ + png_uint_16p gamma_to_1; /* converts from file to 1.0 */ + png_uint_16p gamma_16_from_1; /* converts from 1.0 to screen */ + png_uint_16p gamma_16_to_1; /* converts from file to 1.0 */ +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ +#endif /* PNG_READ_GAMMA_SUPPORTED */ + +#if defined PNG_READ_tRNS_SUPPORTED || defined PNG_READ_BACKGROUND_SUPPORTED ||\ + defined PNG_READ_EXPAND_SUPPORTED + png_bytep trans_alpha; /* alpha values for paletted files */ +#endif + + /* Integer values */ +#if defined PNG_READ_BACKGROUND_SUPPORTED ||\ + defined PNG_READ_ALPHA_MODE_SUPPORTED + png_fixed_point background_gamma; +#endif +#ifdef PNG_READ_GAMMA_SUPPORTED + png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */ + int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */ +#endif + + /* png_color_16 */ +#if defined PNG_READ_BACKGROUND_SUPPORTED ||\ + defined PNG_READ_ALPHA_MODE_SUPPORTED + png_color_16 background; /* background color in screen gamma space */ + png_color_16 background_1; /* background normalized to gamma 1.0 */ +#endif +#if defined PNG_READ_tRNS_SUPPORTED || defined PNG_READ_BACKGROUND_SUPPORTED ||\ + defined PNG_READ_EXPAND_SUPPORTED + png_color_16 trans_color; /* transparent color for non-paletted files */ +#endif + + /* png_uint_16 */ +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_uint_16 rgb_to_gray_red_coeff; + png_uint_16 rgb_to_gray_green_coeff; + /* The blue coefficient is calculated from the above */ +#endif + + /* png_color_8 */ +#if defined PNG_READ_GAMMA_SUPPORTED || defined PNG_READ_sBIT_SUPPORTED + png_color_8 sig_bit; /* significant bits in each available channel */ +#endif + + /* png_byte */ +#if defined PNG_READ_BACKGROUND_SUPPORTED ||\ + defined PNG_READ_ALPHA_MODE_SUPPORTED + png_byte background_gamma_type; +#endif +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_byte rgb_to_gray_status; +#endif + + /* SHIFT - both READ_SHIFT and WRITE_SHIFT */ +#if defined PNG_READ_SHIFT_SUPPORTED || defined PNG_WRITE_SHIFT_SUPPORTED + png_color_8 shift; /* shift for significant bit tranformation */ +#endif + + /* FILLER SUPPORT (pixel expansion or read, contraction on write) */ +#if defined PNG_READ_FILLER_SUPPORTED || defined PNG_WRITE_FILLER_SUPPORTED + png_uint_16 filler; /* filler bytes for pixel expansion */ +#endif + + /* QUANTIZE (convert to color-mapped) */ +#ifdef PNG_READ_QUANTIZE_SUPPORTED + png_bytep palette_lookup; /* lookup table for quantizing */ + png_bytep quantize_index; /* index translation for palette files */ + png_bytep quantize_sort; /* working sort array */ + png_bytep index_to_palette; /* where the original index currently is in the + * palette + */ + png_bytep palette_to_index; /* which original index points to this palette + * color + */ +#endif + + /* MNG SUPPORT */ +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_uint_32 mng_features_permitted; + png_byte filter_type; +#endif + + /* COMPRESSION AND DECOMPRESSION SUPPORT. + * + * zlib expects a 'zstream' as the fundamental control structure, it allows + * all the parameters to be passed as one pointer. + */ + z_stream zstream; /* decompression structure */ #ifdef PNG_READ_SUPPORTED -/* New member added in libpng-1.2.30 */ - png_bytep read_buffer; /* buffer for reading chunk data */ - png_alloc_size_t read_buffer_size; /* current size of the buffer */ + /* These, and IDAT_read_size below, control how much input and output (at + * most) is available to zlib. + */ + png_uint_32 idat_size; /* current IDAT size for read */ + png_alloc_size_t read_buffer_size; /* current size of the buffer */ #endif + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED + int zlib_text_level; /* holds zlib compression level */ + int zlib_text_method; /* holds zlib compression method */ + int zlib_text_window_bits; /* holds zlib compression window bits */ + int zlib_text_mem_level; /* holds zlib compression memory level */ + int zlib_text_strategy; /* holds zlib compression strategy */ +#endif + +#ifdef PNG_WRITE_SUPPORTED + int zlib_level; /* holds zlib compression level */ + int zlib_method; /* holds zlib compression method */ + int zlib_window_bits; /* holds zlib compression window bits */ + int zlib_mem_level; /* holds zlib compression memory level */ + int zlib_strategy; /* holds zlib compression strategy */ + + int zlib_set_level; /* Actual values set into the zstream on write */ + int zlib_set_method; + int zlib_set_window_bits; + int zlib_set_mem_level; + int zlib_set_strategy; + + png_compression_bufferp zbuffer_list; /* Created on demand during write */ + uInt zbuffer_size; /* size of the actual zlib buffer */ +#endif + #ifdef PNG_SEQUENTIAL_READ_SUPPORTED - uInt IDAT_read_size; /* limit on read buffer size for IDAT */ -#endif - -#ifdef PNG_IO_STATE_SUPPORTED -/* New member added in libpng-1.4.0 */ - png_uint_32 io_state; -#endif - -/* New member added in libpng-1.5.6 */ - png_bytep big_prev_row; - - void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info, - png_bytep row, png_const_bytep prev_row); - -#ifdef PNG_READ_SUPPORTED -#if defined PNG_COLORSPACE_SUPPORTED || defined PNG_GAMMA_SUPPORTED - png_colorspace colorspace; -#endif + uInt IDAT_read_size; /* limit on read buffer size for IDAT */ #endif }; #endif /* PNGSTRUCT_H */ diff --git a/pngtrans.c b/pngtrans.c index 7d7ce90e4..a80c32c12 100644 --- a/pngtrans.c +++ b/pngtrans.c @@ -57,7 +57,9 @@ png_set_packing(png_structrp png_ptr) if (png_ptr->bit_depth < 8) { png_ptr->transformations |= PNG_PACK; - png_ptr->usr_bit_depth = 8; +# ifdef PNG_WRITE_SUPPORTED + png_ptr->usr_bit_depth = 8; +# endif } } #endif diff --git a/pngwio.c b/pngwio.c index 1752e1f30..cb4439f70 100644 --- a/pngwio.c +++ b/pngwio.c @@ -151,6 +151,7 @@ png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr, # endif #endif /* PNG_WRITE_FLUSH_SUPPORTED */ +#ifdef PNG_READ_SUPPORTED /* It is an error to read while writing a png file */ if (png_ptr->read_data_fn != NULL) { @@ -160,5 +161,6 @@ png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr, "Can't set both read_data_fn and write_data_fn in the" " same structure"); } +#endif } #endif /* PNG_WRITE_SUPPORTED */ diff --git a/pngwrite.c b/pngwrite.c index 1ef76d6e7..1578dd3c3 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -216,8 +216,13 @@ png_write_info(png_structrp png_ptr, png_const_inforp info_ptr) if ((png_ptr->transformations & PNG_INVERT_ALPHA) && info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - int j; - for (j = 0; j<(int)info_ptr->num_trans; j++) + int j, jend; + + jend = info_ptr->num_trans; + if (jend > PNG_MAX_PALETTE_LENGTH) + jend = PNG_MAX_PALETTE_LENGTH; + + for (j = 0; jtrans_alpha[j] = (png_byte)(255 - info_ptr->trans_alpha[j]); } @@ -927,42 +932,52 @@ png_set_filter(png_structrp png_ptr, int method, int filters) if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && (method == PNG_INTRAPIXEL_DIFFERENCING)) method = PNG_FILTER_TYPE_BASE; - #endif - if (method == PNG_FILTER_TYPE_BASE) - { - switch (filters & (PNG_ALL_FILTERS | 0x07)) + + /* The only supported method, except for the check above, is + * PNG_FILTER_TYPE_BASE. The code below does not use 'method' other than + * for the check, so just keep going if png_app_error returns. + */ + if (method != PNG_FILTER_TYPE_BASE) + png_app_error(png_ptr, "Unknown custom filter method"); + + /* If filter writing is not supported the 'filters' value must be zero, + * otherwise the value must be a single, valid, filter value or a set of the + * mask values. The defines in png.h are such that the filter masks used in + * this API and internally are 1<<(3+value), value is in the range 0..4, so + * this fits in a byte. + */ +# ifdef PNG_WRITE_FILTER_SUPPORTED + /* Notice that PNG_NO_FILTERS is 0 and passes this test; this is OK + * because filters then gets set to PNG_FILTER_NONE, as is required. + */ + if (filters < PNG_FILTER_VALUE_LAST) + filters = 0x08 << filters; + + else if ((filters & ~PNG_ALL_FILTERS) != 0) { -#ifdef PNG_WRITE_FILTER_SUPPORTED - case 5: - case 6: - case 7: png_app_error(png_ptr, "Unknown row filter for method 0"); - /* FALL THROUGH */ -#endif /* PNG_WRITE_FILTER_SUPPORTED */ - case PNG_FILTER_VALUE_NONE: - png_ptr->do_filter = PNG_FILTER_NONE; break; + png_app_error(png_ptr, "png_set_filter: invalid filters mask/value"); -#ifdef PNG_WRITE_FILTER_SUPPORTED - case PNG_FILTER_VALUE_SUB: - png_ptr->do_filter = PNG_FILTER_SUB; break; + /* For compatibility with the previous behavior assume a mask value was + * passed and ignore the non-mask bits. + */ + filters &= PNG_ALL_FILTERS; - case PNG_FILTER_VALUE_UP: - png_ptr->do_filter = PNG_FILTER_UP; break; - - case PNG_FILTER_VALUE_AVG: - png_ptr->do_filter = PNG_FILTER_AVG; break; - - case PNG_FILTER_VALUE_PAETH: - png_ptr->do_filter = PNG_FILTER_PAETH; break; - - default: - png_ptr->do_filter = (png_byte)filters; break; -#else - default: - png_app_error(png_ptr, "Unknown row filter for method 0"); -#endif /* PNG_WRITE_FILTER_SUPPORTED */ + /* For a possibly foolish consistency (it shouldn't matter) set + * PNG_FILTER_NONE rather than 0. + */ + if (filters == 0) + filters = PNG_FILTER_NONE; } +# else + /* PNG_FILTER_VALUE_NONE and PNG_NO_FILTERS are both 0. */ + if (filters != 0 && filters != PNG_FILTER_NONE) + png_app_error(png_ptr, "png_set_filter: no filters supported"); + filters = PNG_FILTER_NONE; +# endif + +# ifdef PNG_WRITE_FILTER_SUPPORTED /* If we have allocated the row_buf, this means we have already started * with the image and we should have allocated all of the filter buffers * that have been selected. If prev_row isn't already allocated, then @@ -971,75 +986,45 @@ png_set_filter(png_structrp png_ptr, int method, int filters) * wants to start and stop using particular filters during compression, * it should start out with all of the filters, and then add and * remove them after the start of compression. + * + * NOTE: this is a nasty constraint on the code, because it means that the + * prev_row buffer must be maintained even if there are currently no + * 'prev_row' requiring filters active. */ if (png_ptr->row_buf != NULL) { -#ifdef PNG_WRITE_FILTER_SUPPORTED - if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) + /* Repeat the checks in png_write_start_row; 1 pixel high or wide + * images cannot benefit from certain filters. If this isn't done here + * the check below will fire on 1 pixel high images. + */ + if (png_ptr->height == 1) + filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); + + if (png_ptr->width == 1) + filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH); + + if ((filters & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) != 0 + && png_ptr->prev_row == NULL) { - png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, - (png_ptr->rowbytes + 1)); - png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + /* This is the error case, however it is benign - the previous row + * is not available so the filter can't be used. Just warn here. + */ + png_app_warning(png_ptr, + "png_set_filter: UP/AVG/PAETH cannot be added after start"); + filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); } - if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL) - { - if (png_ptr->prev_row == NULL) - { - png_warning(png_ptr, "Can't add Up filter after starting"); - png_ptr->do_filter = (png_byte)(png_ptr->do_filter & - ~PNG_FILTER_UP); - } - - else - { - png_ptr->up_row = (png_bytep)png_malloc(png_ptr, - (png_ptr->rowbytes + 1)); - png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; - } - } - - if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL) - { - if (png_ptr->prev_row == NULL) - { - png_warning(png_ptr, "Can't add Average filter after starting"); - png_ptr->do_filter = (png_byte)(png_ptr->do_filter & - ~PNG_FILTER_AVG); - } - - else - { - png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, - (png_ptr->rowbytes + 1)); - png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; - } - } - - if ((png_ptr->do_filter & PNG_FILTER_PAETH) && - png_ptr->paeth_row == NULL) - { - if (png_ptr->prev_row == NULL) - { - png_warning(png_ptr, "Can't add Paeth filter after starting"); - png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); - } - - else - { - png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, - (png_ptr->rowbytes + 1)); - png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; - } - } - - if (png_ptr->do_filter == PNG_NO_FILTERS) -#endif /* PNG_WRITE_FILTER_SUPPORTED */ - png_ptr->do_filter = PNG_FILTER_NONE; + /* Allocate any required buffers that have not already been allocated. + */ + png_write_alloc_filter_row_buffers(png_ptr, filters); } - } - else - png_error(png_ptr, "Unknown custom filter method"); +# endif /* PNG_WRITE_FILTER_SUPPORTED */ + + /* Finally store the value. + * TODO: this field could probably be removed if neither READ nor + * WRITE_FILTER are supported. + */ + png_ptr->do_filter = (png_byte)filters; /* SAFE: checked above */ } /* This allows us to influence the way in which libpng chooses the "best" diff --git a/pngwutil.c b/pngwutil.c index 46908f797..c7d9e7217 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -1425,10 +1425,14 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, if (color_type == PNG_COLOR_TYPE_PALETTE) { - if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) + if (num_trans <= 0 || num_trans > png_ptr->num_palette) { + /* This is an error which can only be reliably detected late, change to + * a png_app_warning here so that it will fail in debug. It should be + * a png_app_error. + */ png_app_warning(png_ptr, - "Invalid number of transparent colors specified"); + "Invalid number of transparent colors specified"); return; } @@ -1442,7 +1446,7 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, /* One 16 bit value */ if (tran->gray >= (1 << png_ptr->bit_depth)) { - png_app_warning(png_ptr, + png_warning(png_ptr, "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); return; @@ -1474,7 +1478,8 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, else { - png_app_warning(png_ptr, "Can't write tRNS with an alpha channel"); + /* Checked in png_set_tRNS */ + png_warning(png_ptr, "Can't write tRNS with an alpha channel"); } } #endif @@ -1516,6 +1521,7 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) if (buf[0] | buf[2] | buf[4]) #endif { + /* This can no longer happen because it is checked in png_set_tRNS */ png_warning(png_ptr, "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); @@ -1529,6 +1535,7 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) { if (back->gray >= (1 << png_ptr->bit_depth)) { + /* Also checked in png_set_tRNS */ png_warning(png_ptr, "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); @@ -1931,6 +1938,47 @@ png_write_tIME(png_structrp png_ptr, png_const_timep mod_time) } #endif +#ifdef PNG_WRITE_FILTER_SUPPORTED +void /* PRIVATE */ +png_write_alloc_filter_row_buffers(png_structrp png_ptr, int filters) + /* Allocate row buffers for any filters that need them, this is also called + * from png_set_filter if the filters are changed during write to ensure that + * the required buffers exist. png_set_filter ensures that up/avg/paeth are + * only set if png_ptr->prev_row is allocated. + */ +{ + /* The buffer size is determined just by the output row size, not any + * processing requirements. + */ + png_alloc_size_t buf_size = png_ptr->rowbytes + 1; + + if ((filters & PNG_FILTER_SUB) != 0 && png_ptr->sub_row == NULL) + { + png_ptr->sub_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + if ((filters & PNG_FILTER_UP) != 0 && png_ptr->up_row == NULL) + { + png_ptr->up_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + + if ((filters & PNG_FILTER_AVG) != 0 && png_ptr->avg_row == NULL) + { + png_ptr->avg_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + + if ((filters & PNG_FILTER_PAETH) != 0 && png_ptr->paeth_row == NULL) + { + png_ptr->paeth_row = png_voidcast(png_bytep, png_malloc(png_ptr, + buf_size)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } +} +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + /* Initializes the row writing capability of libpng */ void /* PRIVATE */ png_write_start_row(png_structrp png_ptr) @@ -1951,11 +1999,18 @@ png_write_start_row(png_structrp png_ptr) static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; #endif +#ifdef PNG_WRITE_FILTER_SUPPORTED + int filters; +#endif + png_alloc_size_t buf_size; int usr_pixel_depth; png_debug(1, "in png_write_start_row"); + if (png_ptr == NULL) + return; + usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth; buf_size = PNG_ROWBYTES(usr_pixel_depth, png_ptr->width) + 1; @@ -1964,49 +2019,34 @@ png_write_start_row(png_structrp png_ptr) png_ptr->maximum_pixel_depth = (png_byte)usr_pixel_depth; /* Set up row buffer */ - png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, buf_size); + png_ptr->row_buf = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; #ifdef PNG_WRITE_FILTER_SUPPORTED - /* Set up filtering buffer, if using this filter */ - if (png_ptr->do_filter & PNG_FILTER_SUB) - { - png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, png_ptr->rowbytes + 1); + filters = png_ptr->do_filter; - png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; - } + if (png_ptr->height == 1) + filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); - /* We only need to keep the previous row if we are using one of these. */ - if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) - { - /* Set up previous row buffer */ - png_ptr->prev_row = (png_bytep)png_calloc(png_ptr, buf_size); + if (png_ptr->width == 1) + filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH); - if (png_ptr->do_filter & PNG_FILTER_UP) - { - png_ptr->up_row = (png_bytep)png_malloc(png_ptr, - png_ptr->rowbytes + 1); + if (filters == 0) + filters = PNG_FILTER_NONE; - png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; - } + /* We only need to keep the previous row if we are using one of the following + * filters. + */ + if (filters & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) + png_ptr->prev_row = png_voidcast(png_bytep, png_calloc(png_ptr, + buf_size)); - if (png_ptr->do_filter & PNG_FILTER_AVG) - { - png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, - png_ptr->rowbytes + 1); + png_write_alloc_filter_row_buffers(png_ptr, filters); - png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; - } - - if (png_ptr->do_filter & PNG_FILTER_PAETH) - { - png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, - png_ptr->rowbytes + 1); - - png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; - } - } + png_ptr->do_filter = (png_byte)filters; /* in case it was changed above */ +#else + png_ptr->do_filter = PNG_FILTER_NONE; #endif /* PNG_WRITE_FILTER_SUPPORTED */ #ifdef PNG_WRITE_INTERLACING_SUPPORTED