diff --git a/ANNOUNCE b/ANNOUNCE index 2623081be..af80daf11 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -1,5 +1,5 @@ -Libpng 1.7.0beta59 - March 25, 2015 +Libpng 1.7.0beta59 - March 31, 2015 This is not intended to be a public release. It will be replaced within a few weeks by a public version or by another test version. @@ -779,6 +779,17 @@ Version 1.7.0beta58 [March 25, 2015] condition on RELEASE (or not) builds and tidy up the #ifdef handling of functions. +Version 1.7.0beta59 [March 31, 2015] + Transformed rewrite: changed row_info, added checks. This introduces an + internal struct (png_transform_control) to replace row_info and uses + that to implement affirms correctly. The change also adds checks on + the rowbytes calculation and additional checks on most transform + implementations. + Added png_uint_16 range checking, pngvalid tRNS, fixed png_uint_16: + review of previous checks, removal of some where SAFE. pngvalid: add + testing of tRNS for better code coverage pngvalid: correct rgb-to-gray + error calculations. Code coverage is still incomplete: see /*UNTESTED*/ + in pngrtran.c Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/CHANGES b/CHANGES index fb7a0dcdd..7532a8bb7 100644 --- a/CHANGES +++ b/CHANGES @@ -5069,7 +5069,17 @@ Version 1.7.0beta58 [March 25, 2015] condition on RELEASE (or not) builds and tidy up the #ifdef handling of functions. -Version 1.7.0beta59 [March 25, 2015] +Version 1.7.0beta59 [March 31, 2015] + Transformed rewrite: changed row_info, added checks. This introduces an + internal struct (png_transform_control) to replace row_info and uses + that to implement affirms correctly. The change also adds checks on + the rowbytes calculation and additional checks on most transform + implementations. + Added png_uint_16 range checking, pngvalid tRNS, fixed png_uint_16: + review of previous checks, removal of some where SAFE. pngvalid: add + testing of tRNS for better code coverage pngvalid: correct rgb-to-gray + error calculations. Code coverage is still incomplete: see /*UNTESTED*/ + in pngrtran.c Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/autogen.sh b/autogen.sh index 9af34bde2..702a0c110 100755 --- a/autogen.sh +++ b/autogen.sh @@ -73,8 +73,9 @@ done # present bad things are happening. # # The autotools generated files: -libpng_autotools_files="Makefile.in aclocal.m4 config.guess config.h.in\ - config.sub configure depcomp install-sh ltmain.sh missing test-driver" +libpng_autotools_files="Makefile.in aclocal.m4 config.guess config.h.in + config.h.in~ config.sub configure depcomp install-sh ltmain.sh missing\ + test-driver" # # Files generated by versions of configue >2.68 or automake >1.13 (i.e. later # versions than those required by configure.ac): diff --git a/contrib/libtests/pngvalid.c b/contrib/libtests/pngvalid.c index e64415d69..60f23dc16 100644 --- a/contrib/libtests/pngvalid.c +++ b/contrib/libtests/pngvalid.c @@ -258,7 +258,7 @@ make_four_random_bytes(png_uint_32* seed, png_bytep bytes) make_random_bytes(seed, bytes, 4); } -#ifdef PNG_READ_SUPPORTED +#if defined PNG_READ_SUPPORTED || defined PNG_WRITE_tRNS_SUPPORTED static void randomize(void *pv, size_t size) { @@ -267,7 +267,9 @@ randomize(void *pv, size_t size) } #define RANDOMIZE(this) randomize(&(this), sizeof (this)) +#endif /* READ || WRITE_tRNS */ +#ifdef PNG_READ_SUPPORTED static unsigned int random_mod(unsigned int max) { @@ -295,7 +297,8 @@ random_choice(void) /* A numeric ID based on PNG file characteristics. The 'do_interlace' field * simply records whether pngvalid did the interlace itself or whether it * was done by libpng. Width and height must be less than 256. 'palette' is an - * index of the palette to use for formats with a palette (0 otherwise.) + * index of the palette to use for formats with a palette otherwise a boolean + * indicating if a tRNS chunk was generated. */ #define FILEID(col, depth, palette, interlace, width, height, do_interlace) \ ((png_uint_32)((col) + ((depth)<<3) + ((palette)<<8) + ((interlace)<<13) + \ @@ -316,12 +319,16 @@ standard_name(char *buffer, size_t bufsize, size_t pos, png_byte colour_type, png_uint_32 w, png_uint_32 h, int do_interlace) { pos = safecat(buffer, bufsize, pos, colour_types[colour_type]); - if (npalette > 0) + if (colour_type == 3) /* must have a palette */ { pos = safecat(buffer, bufsize, pos, "["); pos = safecatn(buffer, bufsize, pos, npalette); pos = safecat(buffer, bufsize, pos, "]"); } + + else if (npalette != 0) + pos = safecat(buffer, bufsize, pos, "+tRNS"); + pos = safecat(buffer, bufsize, pos, " "); pos = safecatn(buffer, bufsize, pos, bit_depth); pos = safecat(buffer, bufsize, pos, " bit"); @@ -378,25 +385,32 @@ standard_name_from_id(char *buffer, size_t bufsize, size_t pos, png_uint_32 id) static int next_format(png_bytep colour_type, png_bytep bit_depth, - unsigned int* palette_number, int no_low_depth_gray) + unsigned int* palette_number, int low_depth_gray, int tRNS) { if (*bit_depth == 0) { *colour_type = 0; - if (no_low_depth_gray) - *bit_depth = 8; - else + if (low_depth_gray) *bit_depth = 1; + else + *bit_depth = 8; *palette_number = 0; return 1; } - if (*colour_type == 3) + if (*colour_type < 4/*no alpha channel*/) { - /* Add multiple palettes for colour type 3. */ - if (++*palette_number < PALETTE_COUNT(*bit_depth)) + /* Add multiple palettes for colour type 3, one image with tRNS + * and one without for other non-alpha formats: + */ + unsigned int pn = ++*palette_number; + png_byte ct = *colour_type; + + if (((ct == 0/*GRAY*/ || ct/*RGB*/ == 2) && tRNS && pn < 2) || + (ct == 3/*PALETTE*/ && pn < PALETTE_COUNT(*bit_depth))) return 1; + /* No: next bit depth */ *palette_number = 0; } @@ -1959,6 +1973,7 @@ typedef struct png_modifier /* Run tests on reading with a combination of transforms, */ unsigned int test_transform :1; + unsigned int test_tRNS :1; /* Includes tRNS images */ /* When to use the use_input_precision option, this controls the gamma * validation code checks. If set any value that is within the transformed @@ -1990,6 +2005,16 @@ typedef struct png_modifier unsigned int test_gamma_expand16 :1; unsigned int test_exhaustive :1; + /* Whether or not to run the low-bit-depth grayscale tests. This fail on + * gamma images in some cases because of gross inaccuracies in the grayscale + * gamma handling for low bit depth. + */ + unsigned int test_lbg :1; + unsigned int test_lbg_gamma_threshold :1; + unsigned int test_lbg_gamma_transform :1; + unsigned int test_lbg_gamma_sbit :1; + unsigned int test_lbg_gamma_composition :1; + unsigned int log :1; /* Log max error */ /* Buffer information, the buffer size limits the size of the chunks that can @@ -2042,6 +2067,11 @@ modifier_init(png_modifier *pm) pm->test_standard = 0; pm->test_size = 0; pm->test_transform = 0; +# ifdef PNG_WRITE_tRNS_SUPPORTED + pm->test_tRNS = 1; +# else + pm->test_tRNS = 0; +# endif pm->use_input_precision = 0; pm->use_input_precision_sbit = 0; pm->use_input_precision_16to8 = 0; @@ -2054,6 +2084,11 @@ modifier_init(png_modifier *pm) pm->test_gamma_background = 0; pm->test_gamma_alpha_mode = 0; pm->test_gamma_expand16 = 0; + pm->test_lbg = 1; + pm->test_lbg_gamma_threshold = 1; + pm->test_lbg_gamma_transform = 1; + pm->test_lbg_gamma_sbit = 1; + pm->test_lbg_gamma_composition = 1; pm->test_exhaustive = 0; pm->log = 0; @@ -3192,6 +3227,45 @@ init_standard_palette(png_store *ps, png_structp pp, png_infop pi, int npalette, } } +#ifdef PNG_WRITE_tRNS_SUPPORTED +static void +set_random_tRNS(png_structp pp, png_infop pi, PNG_CONST png_byte colour_type, + PNG_CONST int bit_depth) +{ + /* To make this useful the tRNS color needs to match at least one pixel. + * Random values are fine for gray, including the 16-bit case where we know + * that the test image contains all the gray values. For RGB we need more + * method as only 65536 different RGB values are generated. + */ + png_color_16 tRNS; + const png_uint_16 mask = (png_uint_16)((1U << bit_depth)-1); + + RANDOMIZE(tRNS); + + if (colour_type & 2/*RGB*/) + { + if (bit_depth == 8) + { + tRNS.blue = tRNS.red ^ tRNS.green; + tRNS.red &= mask; + tRNS.green &= mask; + tRNS.blue &= mask; + } + + else /* bit_depth == 16 */ + { + tRNS.green = (png_uint_16)(tRNS.red * 257); + tRNS.blue = (png_uint_16)(tRNS.green * 17); + } + } + + else + tRNS.gray &= mask; + + png_set_tRNS(pp, pi, NULL, 0, &tRNS); +} +#endif + /* The number of passes is related to the interlace type. There was no libpng * API to determine this prior to 1.5, so we need an inquiry function: */ @@ -3525,6 +3599,11 @@ make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, if (colour_type == 3) /* palette */ init_standard_palette(ps, pp, pi, 1U << bit_depth, 1/*do tRNS*/); +# ifdef PNG_WRITE_tRNS_SUPPORTED + else if (palette_number) + set_random_tRNS(pp, pi, colour_type, bit_depth); +# endif + png_write_info(pp, pi); if (png_get_rowbytes(pp, pi) != @@ -3598,19 +3677,20 @@ make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, } static void -make_transform_images(png_store *ps) +make_transform_images(png_modifier *pm) { png_byte colour_type = 0; png_byte bit_depth = 0; unsigned int palette_number = 0; /* This is in case of errors. */ - safecat(ps->test, sizeof ps->test, 0, "make standard images"); + safecat(pm->this.test, sizeof pm->this.test, 0, "make standard images"); /* Use next_format to enumerate all the combinations we test, including - * generating multiple low bit depth palette images. + * generating multiple low bit depth palette images. Non-A images (palette + * and direct) are created with and without tRNS chunks. */ - while (next_format(&colour_type, &bit_depth, &palette_number, 0)) + while (next_format(&colour_type, &bit_depth, &palette_number, 1, 1)) { int interlace_type; @@ -3621,7 +3701,7 @@ make_transform_images(png_store *ps) standard_name(name, sizeof name, 0, colour_type, bit_depth, palette_number, interlace_type, 0, 0, 0); - make_transform_image(ps, colour_type, bit_depth, palette_number, + make_transform_image(&pm->this, colour_type, bit_depth, palette_number, interlace_type, name); } } @@ -4287,6 +4367,7 @@ typedef struct standard_display size_t cbRow; /* Bytes in a row of the output image */ int do_interlace; /* Do interlacing internally */ int is_transparent; /* Transparency information was present. */ + int has_tRNS; /* color type GRAY or RGB with a tRNS chunk. */ int speed; /* Doing a speed test */ int use_update_info;/* Call update_info, not start_image */ struct @@ -4619,14 +4700,14 @@ standard_info_part1(standard_display *dp, png_structp pp, png_infop pi) case 0: dp->transparent.red = dp->transparent.green = dp->transparent.blue = trans_color->gray; - dp->is_transparent = 1; + dp->has_tRNS = 1; break; case 2: dp->transparent.red = trans_color->red; dp->transparent.green = trans_color->green; dp->transparent.blue = trans_color->blue; - dp->is_transparent = 1; + dp->has_tRNS = 1; break; case 3: @@ -5518,7 +5599,7 @@ image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display) if (this->colour_type == PNG_COLOR_TYPE_GRAY) { if (this->bit_depth < 8) - this->bit_depth = 8; + this->bit_depth = this->sample_depth = 8; if (this->have_tRNS) { @@ -5553,9 +5634,11 @@ image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display) this->alphaf = 0; else this->alphaf = 1; - - this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA; } + else + this->alphaf = 1; + + this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA; } /* The error in the alpha is zero and the sBIT value comes from the @@ -6373,6 +6456,13 @@ image_transform_png_set_tRNS_to_alpha_set(PNG_CONST image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_tRNS_to_alpha(pp); + + /* If there was a tRNS chunk that would get expanded and add an alpha + * channel is_transparent must be updated: + */ + if (that->this.has_tRNS) + that->this.is_transparent = 1; + this->next->set(this->next, that, pp, pi); } @@ -6431,6 +6521,7 @@ image_transform_png_set_gray_to_rgb_set(PNG_CONST image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_gray_to_rgb(pp); + /* NOTE: this doesn't result in tRNS expansion. */ this->next->set(this->next, that, pp, pi); } @@ -6490,6 +6581,10 @@ image_transform_png_set_expand_set(PNG_CONST image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_expand(pp); + + if (that->this.has_tRNS) + that->this.is_transparent = 1; + this->next->set(this->next, that, pp, pi); } @@ -6540,6 +6635,7 @@ image_transform_png_set_expand_gray_1_2_4_to_8_set( png_infop pi) { png_set_expand_gray_1_2_4_to_8(pp); + /* NOTE: don't expect this to expand tRNS */ this->next->set(this->next, that, pp, pi); } @@ -6571,6 +6667,11 @@ image_transform_png_set_expand_16_set(PNG_CONST image_transform *this, transform_display *that, png_structp pp, png_infop pi) { png_set_expand_16(pp); + + /* NOTE: at present libpng does SET_EXPAND as well, so tRNS is expanded. */ + if (that->this.has_tRNS) + that->this.is_transparent = 1; + this->next->set(this->next, that, pp, pi); } @@ -6941,14 +7042,14 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this, * When DIGITIZE is set because a pre-1.7 version of libpng is being * tested allow a bigger slack. * - * NOTE: this magic number was determined by experiment to be 1.25. - * There's no great merit to the value below, however it only affects - * the limit used for checking for internal calculation errors, not - * the actual limit imposed by pngvalid on the output errors. + * NOTE: this magic number was determined by experiment to be about + * 1.263. There's no great merit to the value below, however it only + * affects the limit used for checking for internal calculation errors, + * not the actual limit imposed by pngvalid on the output errors. */ that->pm->limit += pow( # if DIGITIZE - 1.25 + 1.3 # else 1.0 # endif @@ -7112,7 +7213,8 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, 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 : + const unsigned int gamma_depth = (sample_depth == 16 ? + PNG_MAX_GAMMA_8 : (pm->assume_16_bit_calculations ? PNG_MAX_GAMMA_8 : sample_depth)); int isgray; double r, g, b; @@ -7126,56 +7228,73 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, * will be identical after this operation if there is only one * transform, feel free to delete the png_error checks on this below in * the future (this is just me trying to ensure it works!) + * + * Interval arithmetic is exact, but to implement it it must be + * possible to control the floating point implementation rounding mode. + * This cannot be done in ANSI-C, so instead I reduce the 'lo' values + * by DBL_EPSILON and increase the 'hi' values by the same. */ +# define DD(v,d,r) (digitize(v*(1-DBL_EPSILON), d, r) * (1-DBL_EPSILON)) +# define DU(v,d,r) (digitize(v*(1+DBL_EPSILON), d, r) * (1+DBL_EPSILON)) + r = rlo = rhi = that->redf; rlo -= that->rede; - rlo = digitize(rlo, calc_depth, 1/*round*/); + rlo = DD(rlo, calc_depth, 1/*round*/); rhi += that->rede; - rhi = digitize(rhi, calc_depth, 1/*round*/); + rhi = DU(rhi, calc_depth, 1/*round*/); g = glo = ghi = that->greenf; glo -= that->greene; - glo = digitize(glo, calc_depth, 1/*round*/); + glo = DD(glo, calc_depth, 1/*round*/); ghi += that->greene; - ghi = digitize(ghi, calc_depth, 1/*round*/); + ghi = DU(ghi, calc_depth, 1/*round*/); b = blo = bhi = that->bluef; blo -= that->bluee; - blo = digitize(blo, calc_depth, 1/*round*/); + blo = DD(blo, calc_depth, 1/*round*/); bhi += that->greene; - bhi = digitize(bhi, calc_depth, 1/*round*/); + bhi = DU(bhi, calc_depth, 1/*round*/); isgray = r==g && g==b; if (data.gamma != 1) { PNG_CONST double power = 1/data.gamma; - PNG_CONST double abse = calc_depth == 16 ? .5/65535 : .5/255; + PNG_CONST double abse = .5/(sample_depth == 16 ? 65535 : 255); - /* 'abse' is the absolute error permitted in linear calculations. It - * is used here to capture the error permitted in the handling - * (undoing) of the gamma encoding. Once again digitization occurs - * to handle the upper and lower bounds of the values. This is - * where the real errors are introduced. + /* If a gamma calculation is done it is done using lookup tables of + * precision gamma_depth, so the already digitized value above may + * need to be further digitized here. */ + if (gamma_depth != calc_depth) + { + rlo = DD(rlo, gamma_depth, 0/*truncate*/); + rhi = DU(rhi, gamma_depth, 0/*truncate*/); + glo = DD(glo, gamma_depth, 0/*truncate*/); + ghi = DU(ghi, gamma_depth, 0/*truncate*/); + blo = DD(blo, gamma_depth, 0/*truncate*/); + bhi = DU(bhi, gamma_depth, 0/*truncate*/); + } + + /* 'abse' is the error in the gamma table calculation itself. */ r = pow(r, power); - rlo = digitize(pow(rlo, power)-abse, calc_depth, 1); - rhi = digitize(pow(rhi, power)+abse, calc_depth, 1); + rlo = DD(pow(rlo, power)-abse, calc_depth, 1); + rhi = DU(pow(rhi, power)+abse, calc_depth, 1); g = pow(g, power); - glo = digitize(pow(glo, power)-abse, calc_depth, 1); - ghi = digitize(pow(ghi, power)+abse, calc_depth, 1); + glo = DD(pow(glo, power)-abse, calc_depth, 1); + ghi = DU(pow(ghi, power)+abse, calc_depth, 1); b = pow(b, power); - blo = digitize(pow(blo, power)-abse, calc_depth, 1); - bhi = digitize(pow(bhi, power)+abse, calc_depth, 1); + blo = DD(pow(blo, power)-abse, calc_depth, 1); + bhi = DU(pow(bhi, power)+abse, calc_depth, 1); } /* Now calculate the actual gray values. Although the error in the * coefficients depends on whether they were specified on the command * line (in which case truncation to 15 bits happened) or not (rounding * was used) the maxium error in an individual coefficient is always - * 1/32768, because even in the rounding case the requirement that + * 2/32768, because even in the rounding case the requirement that * coefficients add up to 32768 can cause a larger rounding error. * * The only time when rounding doesn't occur in 1.5.5 and later is when @@ -7186,19 +7305,19 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, { PNG_CONST int do_round = data.gamma != 1 || calc_depth == 16; - PNG_CONST double ce = 1. / 32768; + PNG_CONST double ce = 2. / 32768; - graylo = digitize(rlo * (data.red_coefficient-ce) + + graylo = DD(rlo * (data.red_coefficient-ce) + glo * (data.green_coefficient-ce) + - blo * (data.blue_coefficient-ce), gamma_depth, do_round); - if (graylo <= 0) - graylo = 0; + blo * (data.blue_coefficient-ce), calc_depth, do_round); + if (graylo > gray) /* always accept the right answer */ + graylo = gray; - grayhi = digitize(rhi * (data.red_coefficient+ce) + + grayhi = DU(rhi * (data.red_coefficient+ce) + ghi * (data.green_coefficient+ce) + - bhi * (data.blue_coefficient+ce), gamma_depth, do_round); - if (grayhi >= 1) - grayhi = 1; + bhi * (data.blue_coefficient+ce), calc_depth, do_round); + if (grayhi < gray) + grayhi = gray; } /* And invert the gamma. */ @@ -7206,11 +7325,25 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, { PNG_CONST double power = data.gamma; + /* And this happens yet again, shifting the values once more. */ + if (gamma_depth != sample_depth) + { + rlo = DD(rlo, gamma_depth, 0/*truncate*/); + rhi = DU(rhi, gamma_depth, 0/*truncate*/); + glo = DD(glo, gamma_depth, 0/*truncate*/); + ghi = DU(ghi, gamma_depth, 0/*truncate*/); + blo = DD(blo, gamma_depth, 0/*truncate*/); + bhi = DU(bhi, gamma_depth, 0/*truncate*/); + } + gray = pow(gray, power); - graylo = digitize(pow(graylo, power), sample_depth, 1); - grayhi = digitize(pow(grayhi, power), sample_depth, 1); + graylo = DD(pow(graylo, power), sample_depth, 1); + grayhi = DU(pow(grayhi, power), sample_depth, 1); } +# undef DD +# undef DU + /* Now the error can be calculated. * * If r==g==b because there is no overall gamma correction libpng @@ -7261,16 +7394,28 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, { /* There is no need to do the conversions to and from linear space, * so the calculation should be a lot more accurate. There is a - * built in 1/32768 error in the coefficients because they only have - * 15 bits and are adjusted to make sure they add up to 32768, so - * the result may have an additional error up to 1/32768. (Note - * that adding the 1/32768 here avoids needing to increase the - * global error limits to take this into account.) + * built in error in the coefficients because they only have 15 bits + * and are adjusted to make sure they add up to 32768. This + * involves a integer calculation with truncation of the form: + * + * ((int)(coefficient * 100000) * 32768)/100000 + * + * This is done to the red and green coefficients (the ones + * provided to the API) then blue is calculated from them so the + * result adds up to 32768. In the worst case this can result in + * a -1 error in red and green and a +2 error in blue. Consequently + * the worst case in the calculation below is 2/32768 error. + * + * TODO: consider fixing this in libpng by rounding the calculation + * limiting the error to 1/32768. + * + * Handling this by adding 2/32768 here avoids needing to increase + * the global error limits to take this into account.) */ gray = r * data.red_coefficient + g * data.green_coefficient + b * data.blue_coefficient; err = re * data.red_coefficient + ge * data.green_coefficient + - be * data.blue_coefficient + 1./32768 + gray * 5 * DBL_EPSILON; + be * data.blue_coefficient + 2./32768 + gray * 5 * DBL_EPSILON; } else @@ -7305,7 +7450,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, * previously added input quantization error at this point. */ gray = r * data.red_coefficient + g * data.green_coefficient + - b * data.blue_coefficient - 1./32768 - out_qe; + b * data.blue_coefficient - 2./32768 - out_qe; if (gray <= 0) gray = 0; else @@ -7315,7 +7460,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, } grayhi = rhi * data.red_coefficient + ghi * data.green_coefficient + - bhi * data.blue_coefficient + 1./32768 + out_qe; + bhi * data.blue_coefficient + 2./32768 + out_qe; grayhi *= (1 + 6 * DBL_EPSILON); if (grayhi >= 1) grayhi = 1; @@ -7430,6 +7575,9 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this, else { + if (that->this.has_tRNS) + that->this.is_transparent = 1; + bit_depth = that->this.bit_depth; expand = 1; } @@ -7507,14 +7655,14 @@ image_transform_png_set_background_mod(PNG_CONST image_transform *this, /* Remove the alpha type and set the alpha (not in that order.) */ that->alphaf = 1; that->alphae = 0; - - if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA) - that->colour_type = PNG_COLOR_TYPE_RGB; - else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA) - that->colour_type = PNG_COLOR_TYPE_GRAY; - /* PNG_COLOR_TYPE_PALETTE is not changed */ } + if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA) + that->colour_type = PNG_COLOR_TYPE_RGB; + else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA) + that->colour_type = PNG_COLOR_TYPE_GRAY; + /* PNG_COLOR_TYPE_PALETTE is not changed */ + this->next->mod(this->next, that, pp, display); } @@ -8302,7 +8450,8 @@ perform_transform_test(png_modifier *pm) png_byte bit_depth = 0; unsigned int palette_number = 0; - while (next_format(&colour_type, &bit_depth, &palette_number, 0)) + while (next_format(&colour_type, &bit_depth, &palette_number, pm->test_lbg, + pm->test_tRNS)) { png_uint_32 counter = 0; size_t base_pos; @@ -8605,7 +8754,9 @@ init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp, vi->outlog = outlog(dp->pm, in_depth, out_depth); if ((dp->this.colour_type & PNG_COLOR_MASK_ALPHA) != 0 || - (dp->this.colour_type == 3 && dp->this.is_transparent)) + (dp->this.colour_type == 3 && dp->this.is_transparent) || + ((dp->this.colour_type == 0 || dp->this.colour_type == 2) && + dp->this.has_tRNS)) { vi->do_background = dp->do_background; @@ -8635,7 +8786,7 @@ init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp, vi->background_blue = b; } } - else + else /* Do not expect any background processing */ vi->do_background = 0; if (vi->do_background == 0) @@ -9351,6 +9502,7 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp, png_uint_32 y; PNG_CONST store_palette_entry *in_palette = dp->this.palette; PNG_CONST int in_is_transparent = dp->this.is_transparent; + int process_tRNS; int out_npalette = -1; int out_is_transparent = 0; /* Just refers to the palette case */ store_palette out_palette; @@ -9366,6 +9518,7 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp, processing = (vi.gamma_correction > 0 && !dp->threshold_test) || in_bd != out_bd || in_ct != out_ct || vi.do_background; + process_tRNS = dp->this.has_tRNS && vi.do_background; /* TODO: FIX THIS: MAJOR BUG! If the transformations all happen inside * the palette there is no way of finding out, because libpng fails to @@ -9404,8 +9557,8 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp, /* Handle input alpha - png_set_background will cause the output * alpha to disappear so there is nothing to check. */ - if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 || (in_ct == 3 && - in_is_transparent)) + if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 || + (in_ct == 3 && in_is_transparent)) { PNG_CONST unsigned int input_alpha = in_ct == 3 ? dp->this.palette[in_index].alpha : @@ -9437,6 +9590,35 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp, } } + else if (process_tRNS) + { + /* alpha needs to be set appropriately for this pixel, it is + * currently 1 and needs to be 0 for an input pixel which matches + * the values in tRNS. + */ + switch (in_ct) + { + case 0: /* gray */ + if (sample(std, in_ct, in_bd, x, 0, 0, 0) == + dp->this.transparent.red) + alpha = 0; + break; + + case 2: /* RGB */ + if (sample(std, in_ct, in_bd, x, 0, 0, 0) == + dp->this.transparent.red && + sample(std, in_ct, in_bd, x, 1, 0, 0) == + dp->this.transparent.green && + sample(std, in_ct, in_bd, x, 2, 0, 0) == + dp->this.transparent.blue) + alpha = 0; + break; + + default: + break; + } + } + /* Handle grayscale or RGB components. */ if ((in_ct & PNG_COLOR_MASK_COLOR) == 0) /* grayscale */ (void)gamma_component_validate("gray", &vi, @@ -9546,7 +9728,7 @@ gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn, modification_reset(d.pm->modifications); - /* Get a png_struct for writing the image. */ + /* Get a png_struct for reading the image. */ pp = set_modifier_for_read(d.pm, &pi, d.this.id, name); standard_palette_init(&d.this); @@ -9685,9 +9867,13 @@ perform_gamma_threshold_tests(png_modifier *pm) /* Don't test more than one instance of each palette - it's pointless, in * fact this test is somewhat excessive since libpng doesn't make this * decision based on colour type or bit depth! + * + * CHANGED: now test two palettes and, as a side effect, images with and + * without tRNS. */ - while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/)) - if (palette_number == 0) + while (next_format(&colour_type, &bit_depth, &palette_number, + pm->test_lbg_gamma_threshold, pm->test_tRNS)) + if (palette_number < 2) { double test_gamma = 1.0; while (test_gamma >= .4) @@ -9747,7 +9933,8 @@ static void perform_gamma_transform_tests(png_modifier *pm) png_byte bit_depth = 0; unsigned int palette_number = 0; - while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/)) + while (next_format(&colour_type, &bit_depth, &palette_number, + pm->test_lbg_gamma_transform, pm->test_tRNS)) { unsigned int i, j; @@ -9777,7 +9964,8 @@ static void perform_gamma_sbit_tests(png_modifier *pm) png_byte colour_type = 0, bit_depth = 0; unsigned int npalette = 0; - while (next_format(&colour_type, &bit_depth, &npalette, 1/*gamma*/)) + while (next_format(&colour_type, &bit_depth, &npalette, + pm->test_lbg_gamma_sbit, pm->test_tRNS)) if ((colour_type & PNG_COLOR_MASK_ALPHA) == 0 && ((colour_type == 3 && sbit < 8) || (colour_type != 3 && sbit < bit_depth))) @@ -9968,8 +10156,17 @@ static void gamma_composition_test(png_modifier *pm, } background.index = 193; /* rgb(193,193,193) to detect errors */ + if (!(colour_type & PNG_COLOR_MASK_COLOR)) { + /* Because, currently, png_set_background is always called with + * 'need_expand' false in this case and because the gamma test itself + * doesn't cause an expand to 8-bit for lower bit depths the colour must + * be reduced to the correct range. + */ + if (bit_depth < 8) + background.gray &= (png_uint_16)((1U << bit_depth)-1); + /* Grayscale input, we do not convert to RGB (TBD), so we must set the * background to gray - else libpng seems to fail. */ @@ -10018,9 +10215,18 @@ perform_gamma_composition_tests(png_modifier *pm, int do_background, /* Skip the non-alpha cases - there is no setting of a transparency colour at * present. + * + * TODO: incorrect; the palette case sets tRNS and, now RGB and gray do, + * however the palette case fails miserably so is commented out below. */ - while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/)) - if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0) + while (next_format(&colour_type, &bit_depth, &palette_number, + pm->test_lbg_gamma_composition, pm->test_tRNS)) + if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0 +#if 0 /* TODO: FIXME */ + /*TODO: FIXME: this should work */ + || colour_type == 3 +#endif + || (colour_type != 3 && palette_number != 0)) { unsigned int i, j; @@ -10752,6 +10958,18 @@ int main(int argc, char **argv) pm.ngammas = ARRAY_SIZE(gammas); pm.ngamma_tests = 0; /* default to off */ + /* Low bit depth gray images don't do well in the gamma tests, until + * this is fixed turn them off for some gamma cases: + */ +# ifdef PNG_WRITE_tRNS_SUPPORTED + pm.test_tRNS = 1; +# endif + pm.test_lbg = 0; + pm.test_lbg_gamma_threshold = 1; + pm.test_lbg_gamma_transform = 0/*PNG_LIBPNG_VER >= 10700*/; + pm.test_lbg_gamma_sbit = 1; + pm.test_lbg_gamma_composition = 0; + /* And the test encodings */ pm.encodings = test_encodings; pm.nencodings = ARRAY_SIZE(test_encodings); @@ -10864,7 +11082,7 @@ int main(int argc, char **argv) pm.test_gamma_transform = 1; pm.test_gamma_sbit = 1; pm.test_gamma_scale16 = 1; - pm.test_gamma_background = 1; + pm.test_gamma_background = 1; /* composition */ pm.test_gamma_alpha_mode = 1; } @@ -10913,6 +11131,24 @@ int main(int argc, char **argv) else if (strcmp(*argv, "--noexpand16") == 0) pm.test_gamma_expand16 = 0; + else if (strcmp(*argv, "--low-depth-gray") == 0) + pm.test_lbg = pm.test_lbg_gamma_threshold = + pm.test_lbg_gamma_transform = pm.test_lbg_gamma_sbit = + pm.test_lbg_gamma_composition = 1; + + else if (strcmp(*argv, "--nolow-depth-gray") == 0) + pm.test_lbg = pm.test_lbg_gamma_threshold = + pm.test_lbg_gamma_transform = pm.test_lbg_gamma_sbit = + pm.test_lbg_gamma_composition = 0; + +# ifdef PNG_WRITE_tRNS_SUPPORTED + else if (strcmp(*argv, "--tRNS") == 0) + pm.test_tRNS = 1; +# endif + + else if (strcmp(*argv, "--notRNS") == 0) + pm.test_tRNS = 0; + else if (strcmp(*argv, "--more-gammas") == 0) pm.ngamma_tests = 3U; @@ -11103,7 +11339,7 @@ int main(int argc, char **argv) Try { /* Make useful base images */ - make_transform_images(&pm.this); + make_transform_images(&pm); /* Perform the standard and gamma tests. */ if (pm.test_standard) diff --git a/png.c b/png.c index 3cb119b2b..d362853f0 100644 --- a/png.c +++ b/png.c @@ -689,13 +689,13 @@ png_get_copyright(png_const_structrp png_ptr) #else # ifdef __STDC__ return PNG_STRING_NEWLINE \ - "libpng version 1.7.0beta59 - March 25, 2015" PNG_STRING_NEWLINE \ + "libpng version 1.7.0beta59 - March 31, 2015" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2015 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \ "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ PNG_STRING_NEWLINE; # else - return "libpng version 1.7.0beta59 - March 25, 2015\ + return "libpng version 1.7.0beta59 - March 31, 2015\ Copyright (c) 1998-2015 Glenn Randers-Pehrson\ Copyright (c) 1996-1997 Andreas Dilger\ Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."; diff --git a/pngpriv.h b/pngpriv.h index 97fd50e06..9f87c8c99 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -395,6 +395,15 @@ * * Note that these are not configurable: this is just the affirm code; there's * no reason to allow configuration of these options. + * + * 'debug' is a version of 'affirm' that is completely removed from RELEASE + * builds. This is used when either an unexpected condition is completely + * handled or when it can't be handled even by png_error, for example after a + * memory overwrite. + * + * UNTESTED is used to mark code that has not been tested; it causes an assert + * if the code is executed and (therefore) tested. UNTESTED should not remain + * in release candidate code. */ #define PNG_AFFIRM_TEXT (PNG_RELEASE_BUILD ?\ (defined PNG_ERROR_TEXT_SUPPORTED) :\ @@ -412,12 +421,21 @@ if (!(cond)) png_affirm(pp, PNG_SRC_LINE);\ while (0) # define png_impossiblepp(pp, reason) png_affirm(pp, PNG_SRC_LINE) + +# define debug(cond) do {} while (0) +# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC + /* Make sure there are no 'UNTESTED' macros in released code: */ +# define UNTESTED libpng untested code +# endif #else # define png_affirmpp(pp, cond)\ do\ if (!(cond)) png_affirm(pp, #cond, PNG_SRC_LINE);\ while (0) # define png_impossiblepp(pp, reason) png_affirm(pp, reason, PNG_SRC_LINE) + +# define debug(cond) png_affirmpp(png_ptr, cond) +# define UNTESTED png_affirm(png_ptr, "untested code", PNG_SRC_LINE); #endif #define affirm(cond) png_affirmpp(png_ptr, cond) @@ -909,6 +927,54 @@ PNG_INTERNAL_DATA(const png_byte, png_sRGB_delta, [512]); extern "C" { #endif /* __cplusplus */ +#if defined (PNG_READ_TRANSFORMS_SUPPORTED) ||\ + defined (PNG_WRITE_TRANSFORMS_SUPPORTED) +/* Transform support. Prior to 1.7.0 the internal transform routines (not the + * APIs) took a png_row_infop, like the user transform function, but without + * the png_ptr because it was never used. In 1.7.0 a separate internal + * structure is used in place of this to allow both future development to + * change the structure. + * + * The values in this structure will normally be changed by transformation + * implementations. + */ +typedef struct +{ + png_const_structrp png_ptr; /* png_struct for error handling and some + * transform parameters. + */ + png_uint_32 width; /* width of row */ + unsigned int channels; /* number of channels (1, 2, 3, or 4) */ + unsigned int bit_depth; /* bit depth of row */ + unsigned int flags; /* As below */ +# define PNG_INDEXED 1 /* Indexed/palette PNG */ +# define PNG_RGB_SWAPPED 2 /* as in the PNG_BGR transformation */ +# define PNG_FILLER_IN_ALPHA 4 /* 'alpha' channel is really just a filler */ +# define PNG_ALPHA_SWAPPED 8 /* Alpha is in the first channel */ +# define PNG_ALPHA_INVERTED 16 /* Alpha values inverted */ +# define PNG_INVERTED 32 /* grayscale channel inverted */ +# define PNG_BITS_SHIFTED 64 /* Channels not in range 1..(bit_depth-1) */ +# define PNG_BYTE_SWAPPED 128 /* 'swab', i.e. pairs of bytes swapped */ +# define PNG_PIXEL_SWAPPED 256 /* pixels swapped within bytes */ +# define PNG_BAD_INDEX 512 /* Bad palette image index */ +} png_transform_control, *png_transform_controlp; + +/* Validation: channels and bit_depth can be set to anything required by + * the transform, but the result may not be encodable in PNG. PNG_USURPED + * must be set in this case. This macro detects the detectably unrepresentable + * case channels case. + * + * Channels: must be 1 when PNG_INDEXED is set, must be 1-4 otherwise, so: + * + * (channels-1) <= (((flags & PNG_INDEXED)-1) & 3) + */ +#define PNG_VALID_CHANNELS(ri)\ + (((ri)->channels-1) <= ((((ri)->flags & PNG_INDEXED)-1) & 3)) + +typedef const png_transform_control *png_const_transform_controlp; +typedef const png_row_info *png_const_row_infop; +#endif /* TRANSFORMS */ + /* Internal functions; these are not exported from a DLL however because they * are used within several of the C source files they have to be C extern. * @@ -1364,30 +1430,30 @@ PNG_INTERNAL_FUNCTION(void,png_read_transform_info,(png_structrp png_ptr, /* Shared transform functions, defined in pngtran.c */ #if defined(PNG_WRITE_FILLER_SUPPORTED) || \ defined(PNG_READ_STRIP_ALPHA_SUPPORTED) -PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,(png_row_infop row_info, - png_bytep row, int at_start),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,( + png_transform_controlp row_info, png_bytep row, int at_start),PNG_EMPTY); #endif #ifdef PNG_16BIT_SUPPORTED #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) -PNG_INTERNAL_FUNCTION(void,png_do_swap,(png_row_infop row_info, +PNG_INTERNAL_FUNCTION(void,png_do_swap,(png_transform_controlp row_info, png_bytep row),PNG_EMPTY); #endif #endif #if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ defined(PNG_WRITE_PACKSWAP_SUPPORTED) -PNG_INTERNAL_FUNCTION(void,png_do_packswap,(png_row_infop row_info, +PNG_INTERNAL_FUNCTION(void,png_do_packswap,(png_transform_controlp row_info, png_bytep row),PNG_EMPTY); #endif #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) -PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_row_infop row_info, +PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_transform_controlp row_info, png_bytep row),PNG_EMPTY); #endif #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) -PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info, +PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_transform_controlp row_info, png_bytep row),PNG_EMPTY); #endif @@ -1509,6 +1575,24 @@ PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling, #endif /* READ_UNKNOWN_CHUNKS || HANDLE_AS_UNKNOWN */ /* Handle the transformations for reading and writing */ +#if defined(PNG_READ_TRANSFORMS_SUPPORTED) ||\ + defined(PNG_WRITE_TRANSFORMS_SUPPORTED) +/* Utility functions: */ +PNG_INTERNAL_FUNCTION(void,png_init_transform_control,( + png_const_structrp png_ptr, png_transform_controlp out, + png_const_row_infop in), + PNG_EMPTY); + +/* This function exists to ensure that overflow cannot happen even if there + * are bugs in the transforms or calculation of maximum_pixel_depth. + */ +PNG_INTERNAL_FUNCTION(size_t,png_transform_rowbytes,( + png_const_transform_controlp row_info),PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_end_transform_control,(png_row_infop out, + png_const_transform_controlp in), PNG_EMPTY); +#endif /* TRANSFORMS */ + #ifdef PNG_READ_TRANSFORMS_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_do_read_transformations,(png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY); @@ -1668,7 +1752,7 @@ PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr, #if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \ defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) PNG_INTERNAL_FUNCTION(void,png_do_check_palette_indexes, - (png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY); + (png_structrp png_ptr, png_transform_controlp row_info),PNG_EMPTY); #endif #if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) diff --git a/pngread.c b/pngread.c index 6fa11bbb1..132a50253 100644 --- a/pngread.c +++ b/pngread.c @@ -948,15 +948,12 @@ png_read_destroy(png_structrp png_ptr) } png_ptr->free_me &= ~PNG_FREE_PLTE; -#if defined(PNG_tRNS_SUPPORTED) || \ - defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) if ((png_ptr->free_me & PNG_FREE_TRNS) != 0) { png_free(png_ptr, png_ptr->trans_alpha); png_ptr->trans_alpha = NULL; } png_ptr->free_me &= ~PNG_FREE_TRNS; -#endif inflateEnd(&png_ptr->zstream); diff --git a/pngrtran.c b/pngrtran.c index f61250cd2..af736afa1 100644 --- a/pngrtran.c +++ b/pngrtran.c @@ -1171,7 +1171,7 @@ png_init_background_transformations(png_structrp png_ptr) 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; + const int need_expand = (png_ptr->flags & PNG_FLAG_BACKGROUND_EXPAND) != 0; if (color_type & PNG_COLOR_MASK_PALETTE) required_bit_depth = bit_depth = 8; @@ -1192,7 +1192,7 @@ png_init_background_transformations(png_structrp png_ptr) * does not necessarily match the output the application gets, fix that and * the color type here: */ - if (expand == 0) + if (need_expand == 0) { /* The background bit_depth and color_type need correcting */ if ((transform & PNG_EXPAND) != 0) @@ -1229,6 +1229,61 @@ png_init_background_transformations(png_structrp png_ptr) bit_depth = 16; } + /* Double check the input value: when 'need_expand' is false the app is + * providing a background value for us an it should have 'bit_depth' data in + * it. Unfortunately this may not be the case; we can't check in + * png_set_background because we don't know what transforms the app will end + * up asking for, so we have to check here. Prior to 1.7.0 no check was + * performed and the result could potentially be garbage. + */ + if (bit_depth < 16) /* Else range changes always succeed */ + { + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + /* If the PNG is indexed and the need_expand flag was true the + * background color is a palette index and this index must be in range. + * If, however, need_expand is false the background is an RGB value and + * it must be in the 8 bit range. This duplicates the tests below, + * but this code will probably all disappear in the very near future; + * it is just way to error prone. + */ + if (need_expand) + { + if (png_ptr->background.index >= png_ptr->num_palette || + png_ptr->palette != NULL) + png_app_error(png_ptr, "background has invalid palette index"); + } + + else if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0) + { + if (png_ptr->background.gray > 255) + png_app_error(png_ptr, + "palette background gray value out of range"); + } + + else if (png_ptr->background.red > 255 || + png_ptr->background.green > 255 || + png_ptr->background.blue > 255) + png_app_error(png_ptr, "palette background RGB value out of range"); + } + + else + { + const unsigned int mask = ~((1U << bit_depth) - 1); + + if ((color_type & PNG_COLOR_MASK_COLOR) != 0) + { + if ((png_ptr->background.red & mask) != 0 || + (png_ptr->background.green & mask) != 0 || + (png_ptr->background.blue & mask) != 0) + png_app_error(png_ptr, "background RGB value out of range"); + } + + else if ((png_ptr->background.gray & mask) != 0) + png_app_error(png_ptr, "background gray value out of range"); + } + } + /* 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 @@ -1294,7 +1349,7 @@ png_init_background_transformations(png_structrp png_ptr) * 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) + if (need_expand && (color_type & PNG_COLOR_MASK_PALETTE) != 0) { unsigned int background_index = png_ptr->background.index; @@ -2037,22 +2092,7 @@ defined(PNG_READ_USER_TRANSFORM_SUPPORTED) #endif } -/* This is here because png_row_info doesn't contain a png_ptr, so at present - * the transform routines can't signal an error. Instead we pass '0' as - * as png_ptr to png_check_byte in the non-release cases and do a hard cast - * in release. - * - * TODO: fix this. - */ -#ifdef PNG_RANGE_CHECK_SUPPORTED -# define CB(b) png_check_byte(0, b) -# define CU(u) png_check_u16(0, u) -#else -# define CB(b) ((png_byte)(b)) -# define CU(u) ((png_uint_16)(u)) -#endif - -#ifdef PNG_READ_PACK_SUPPORTED +#if defined (PNG_READ_PACK_SUPPORTED) || defined (PNG_READ_EXPAND_SUPPORTED) /* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, * without changing the actual values. Thus, if you had a row with * a bit depth of 1, you would end up with bytes that only contained @@ -2060,96 +2100,91 @@ defined(PNG_READ_USER_TRANSFORM_SUPPORTED) * png_do_shift() after this. */ static void -png_do_unpack(png_row_infop row_info, png_bytep row) +png_do_unpack(png_transform_controlp row_info, png_bytep row) { png_debug(1, "in png_do_unpack"); +# define png_ptr row_info->png_ptr if (row_info->bit_depth < 8) { - png_uint_32 i; - png_uint_32 row_width=row_info->width; - switch (row_info->bit_depth) { case 1: { - png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); - png_bytep dp = row + (png_size_t)row_width - 1; - png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07); - for (i = 0; i < row_width; i++) + png_const_bytep sp = row + png_transform_rowbytes(row_info) - 1; + /* Because we copy from the last pixel down the shift required + * at the start is 8-pixels_in_last_byte, which is just: + */ + unsigned int shift = 0x7 & -row_info->width; + png_bytep dp; + + row_info->flags |= PNG_BITS_SHIFTED; + row_info->bit_depth = 8; + dp = row + png_transform_rowbytes(row_info); + + while (dp > row) { - *dp = CB((*sp >> shift) & 0x01); - - if (shift == 7) - { - shift = 0; - sp--; - } - - else - shift++; - - dp--; + *--dp = (*sp >> shift) & 0x01; + shift = 0x7 & (shift+1); + if (shift == 0) + --sp; } + + debug(dp == row && shift == 0 && sp == row-1); break; } case 2: { + png_const_bytep sp = row + png_transform_rowbytes(row_info) - 1; + unsigned int shift = 7 & -(row_info->width << 1); + png_bytep dp; - png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); - png_bytep dp = row + (png_size_t)row_width - 1; - png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); - for (i = 0; i < row_width; i++) + row_info->flags |= PNG_BITS_SHIFTED; + row_info->bit_depth = 8; + dp = row + png_transform_rowbytes(row_info); + + while (dp > row) { - *dp = CB((*sp >> shift) & 0x03); - - if (shift == 6) - { - shift = 0; - sp--; - } - - else - shift += 2; - - dp--; + *--dp = (*sp >> shift) & 0x03; + shift = 0x7 & (shift+2); + if (shift == 0) + --sp; } + + debug(dp == row && shift == 0 && sp == row-1); break; } case 4: { - png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); - png_bytep dp = row + (png_size_t)row_width - 1; - png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); - for (i = 0; i < row_width; i++) + png_const_bytep sp = row + png_transform_rowbytes(row_info) - 1; + unsigned int shift = 7 & -(row_info->width << 2); + png_bytep dp; + + row_info->flags |= PNG_BITS_SHIFTED; + row_info->bit_depth = 8; + dp = row + png_transform_rowbytes(row_info); + + while (dp > row) { - *dp = CB((*sp >> shift) & 0x0f); - - if (shift == 4) - { - shift = 0; - sp--; - } - - else - shift = 4; - - dp--; + *--dp = (*sp >> shift) & 0x0f; + shift = 0x7 & (shift+4); + if (shift == 0) + --sp; } + + debug(dp == row && shift == 0 && sp == row-1); break; } default: break; } - row_info->bit_depth = 8; - row_info->pixel_depth = CB(8 * row_info->channels); - row_info->rowbytes = row_width * row_info->channels; } +# undef png_ptr } -#endif +#endif /* READ_PACK || READ_EXPAND */ #ifdef PNG_READ_SHIFT_SUPPORTED /* Reverse the effects of png_do_shift. This routine merely shifts the @@ -2158,41 +2193,37 @@ png_do_unpack(png_row_infop row_info, png_bytep row) * the values back to 0 through 31. */ static void -png_do_unshift(png_row_infop row_info, png_bytep row, - png_const_color_8p sig_bits) +png_do_unshift(png_transform_controlp row_info, png_bytep row) { - int color_type; - png_debug(1, "in png_do_unshift"); /* The palette case has already been handled in the _init routine. */ - color_type = row_info->color_type; - - if (color_type != PNG_COLOR_TYPE_PALETTE) + if (!(row_info->flags & PNG_INDEXED)) { - int shift[4]; - int channels = 0; - int bit_depth = row_info->bit_depth; + png_const_structrp png_ptr = row_info->png_ptr; + unsigned int shift[4]; + unsigned int channels = 0; + unsigned int bit_depth = row_info->bit_depth; - if ((color_type & PNG_COLOR_MASK_COLOR) != 0) + if (row_info->channels > 2) /* at least three channels: color */ { - shift[channels++] = bit_depth - sig_bits->red; - shift[channels++] = bit_depth - sig_bits->green; - shift[channels++] = bit_depth - sig_bits->blue; + shift[channels++] = bit_depth - png_ptr->shift.red; + shift[channels++] = bit_depth - png_ptr->shift.green; + shift[channels++] = bit_depth - png_ptr->shift.blue; } else { - shift[channels++] = bit_depth - sig_bits->gray; + shift[channels++] = bit_depth - png_ptr->shift.gray; } - if ((color_type & PNG_COLOR_MASK_ALPHA) != 0) - { - shift[channels++] = bit_depth - sig_bits->alpha; - } + if (row_info->channels > channels) /* one more channel: alpha */ + shift[channels++] = bit_depth - png_ptr->shift.alpha; + + debug(row_info->channels == channels); { - int c, have_shift; + unsigned int c, have_shift; for (c = have_shift = 0; c < channels; ++c) { @@ -2213,35 +2244,40 @@ png_do_unshift(png_row_infop row_info, png_bytep row, switch (bit_depth) { default: - /* Must be 1bpp gray: should not be here! */ + /* Must be 1bpp gray: should not be here! */ + impossible("unshift bit depth"); /* NOTREACHED */ break; case 2: /* Must be 2bpp gray */ - /* affirm(channels == 1 && shift[0] == 1) */ + debug(channels == 1 && shift[0] == 1); { png_bytep bp = row; - png_bytep bp_end = bp + row_info->rowbytes; + png_bytep bp_end = bp + png_transform_rowbytes(row_info); while (bp < bp_end) - *bp = CB((*bp >> 1) & 0x55), ++bp; + *bp = (*bp >> 1) & 0x55, ++bp; + + row_info->flags |= PNG_BITS_SHIFTED; break; } case 4: /* Must be 4bpp gray */ - /* affirm(channels == 1) */ + debug(channels == 1); { png_bytep bp = row; - png_bytep bp_end = bp + row_info->rowbytes; - int gray_shift = shift[0]; - int mask = 0xf >> gray_shift; + png_bytep bp_end = bp + png_transform_rowbytes(row_info); + unsigned int gray_shift = shift[0]; + unsigned int mask = 0xf >> gray_shift; /* <= 4 bits */ - mask |= mask << 4; + mask |= mask << 4; /* <= 8 bits */ while (bp < bp_end) - *bp = CB((*bp >> gray_shift) & mask), ++bp; + *bp = (png_byte)/*SAFE*/((*bp >> gray_shift) & mask), ++bp; + + row_info->flags |= PNG_BITS_SHIFTED; break; } @@ -2249,39 +2285,42 @@ png_do_unshift(png_row_infop row_info, png_bytep row, /* Single byte components, G, GA, RGB, RGBA */ { png_bytep bp = row; - png_bytep bp_end = bp + row_info->rowbytes; - int channel = 0; + png_bytep bp_end = bp + png_transform_rowbytes(row_info); + unsigned int channel = 0; while (bp < bp_end) { - *bp = CB(*bp >> shift[channel]), ++bp; + *bp = (png_byte)/*SAFE*/(*bp >> shift[channel]), ++bp; if (++channel >= channels) channel = 0; } + + row_info->flags |= PNG_BITS_SHIFTED; break; } -#ifdef PNG_READ_16BIT_SUPPORTED case 16: /* Double byte components, G, GA, RGB, RGBA */ { png_bytep bp = row; - png_bytep bp_end = bp + row_info->rowbytes; - int channel = 0; + png_bytep bp_end = bp + png_transform_rowbytes(row_info); + unsigned int channel = 0; while (bp < bp_end) { - int value = (bp[0] << 8) + bp[1]; + unsigned int value = bp[0]; + value = (value << 8) + bp[1]; /* <= 16 bits */ value >>= shift[channel]; if (++channel >= channels) channel = 0; - *bp++ = CB(value >> 8); + *bp++ = (png_byte)/*SAFE*/(value >> 8); *bp++ = PNG_BYTE(value); } + + row_info->flags |= PNG_BITS_SHIFTED; break; } -#endif } } } @@ -2290,15 +2329,16 @@ png_do_unshift(png_row_infop row_info, png_bytep row, #ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED /* Scale rows of bit depth 16 down to 8 accurately */ static void -png_do_scale_16_to_8(png_row_infop row_info, png_bytep row) +png_do_scale_16_to_8(png_transform_controlp row_info, png_bytep row) { png_debug(1, "in png_do_scale_16_to_8"); +# define png_ptr row_info->png_ptr if (row_info->bit_depth == 16) { - png_bytep sp = row; /* source */ + png_const_bytep sp = row; /* source */ png_bytep dp = row; /* destination */ - png_bytep ep = sp + row_info->rowbytes; /* end+1 */ + png_bytep ep = dp + png_transform_rowbytes(row_info); /* end+1 */ while (sp < ep) { @@ -2333,16 +2373,14 @@ png_do_scale_16_to_8(png_row_infop row_info, png_bytep row) * * (V * 255 + 32895) >> 16 */ - png_int_32 tmp = *sp++; /* must be signed! */ - tmp += (((int)*sp++ - tmp + 128) * 65535) >> 24; - *dp++ = CB(tmp); + tmp += ((*sp++ - tmp + 128) * 65535) >> 24; + *dp++ = png_check_byte(png_ptr, tmp); } row_info->bit_depth = 8; - row_info->pixel_depth = CB(8 * row_info->channels); - row_info->rowbytes = row_info->width * row_info->channels; } +# undef png_ptr } #endif @@ -2351,15 +2389,17 @@ static void /* Simply discard the low byte. This was the default behavior prior * to libpng-1.5.4. */ -png_do_chop(png_row_infop row_info, png_bytep row) +png_do_chop(png_transform_controlp row_info, png_bytep row) { png_debug(1, "in png_do_chop"); +# define png_ptr row_info->png_ptr + if (row_info->bit_depth == 16) { - png_bytep sp = row; /* source */ + png_const_bytep sp = row; /* source */ + png_const_bytep ep = sp + png_transform_rowbytes(row_info); /* end+1 */ png_bytep dp = row; /* destination */ - png_bytep ep = sp + row_info->rowbytes; /* end+1 */ while (sp < ep) { @@ -2368,482 +2408,446 @@ png_do_chop(png_row_infop row_info, png_bytep row) } row_info->bit_depth = 8; - row_info->pixel_depth = CB(8 * row_info->channels); - row_info->rowbytes = row_info->width * row_info->channels; } +# undef png_ptr } #endif #ifdef PNG_READ_SWAP_ALPHA_SUPPORTED static void -png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) +png_do_read_swap_alpha(png_transform_controlp row_info, png_bytep row) { png_debug(1, "in png_do_read_swap_alpha"); +# define png_ptr row_info->png_ptr + debug(!(row_info->flags & PNG_ALPHA_SWAPPED)); + + if (!(row_info->flags & PNG_INDEXED)) { - png_uint_32 row_width = row_info->width; - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + if (row_info->channels == 4) { /* This converts from RGBA to ARGB */ if (row_info->bit_depth == 8) { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_byte save; - png_uint_32 i; + png_bytep dp = row + png_transform_rowbytes(row_info); - for (i = 0; i < row_width; i++) + while (dp >= row+4/*safe*/) { - save = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = save; + png_byte alpha = *--dp; + *dp = dp[-1], --dp; + *dp = dp[-1], --dp; + *dp = dp[-1], --dp; + *dp = alpha; } + + debug(dp == row); + row_info->flags |= PNG_ALPHA_SWAPPED; } -#ifdef PNG_READ_16BIT_SUPPORTED /* This converts from RRGGBBAA to AARRGGBB */ else { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_byte save[2]; - png_uint_32 i; + png_bytep dp = row + png_transform_rowbytes(row_info); - for (i = 0; i < row_width; i++) + while (dp >= row+8/*safe*/) { - save[0] = *(--sp); - save[1] = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = save[0]; - *(--dp) = save[1]; + png_byte alpha_last = *--dp; + png_byte alpha_first = dp[-1]; + + /* dp points to the second alpha byte */ + *dp = dp[-2], --dp; + *dp = dp[-2], --dp; + *dp = dp[-2], --dp; + *dp = dp[-2], --dp; + *dp = dp[-2], --dp; + *dp = dp[-2], --dp; + *dp = alpha_last, --dp; + *dp = alpha_first; } + + debug(dp == row); + row_info->flags |= PNG_ALPHA_SWAPPED; } -#endif } - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + else if (row_info->channels == 2) { /* This converts from GA to AG */ if (row_info->bit_depth == 8) { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_byte save; - png_uint_32 i; + png_bytep dp = row + png_transform_rowbytes(row_info); - for (i = 0; i < row_width; i++) + while (dp >= row+1/*safe*/) { - save = *(--sp); - *(--dp) = *(--sp); - *(--dp) = save; + png_byte alpha = *--dp; + + *dp = dp[-1], --dp; + *dp = alpha; } + + debug(dp == row); + row_info->flags ^= PNG_ALPHA_SWAPPED; } -#ifdef PNG_READ_16BIT_SUPPORTED /* This converts from GGAA to AAGG */ else { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_byte save[2]; - png_uint_32 i; + png_bytep dp = row + png_transform_rowbytes(row_info); - for (i = 0; i < row_width; i++) + while (dp >= row+4/*safe*/) { - save[0] = *(--sp); - save[1] = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = save[0]; - *(--dp) = save[1]; + png_byte alpha_last = *--dp; + png_byte alpha_first = dp[-1]; + + /* dp points to the second alpha byte */ + *dp = dp[-2], --dp; + *dp = dp[-2], --dp; + *dp = alpha_last, --dp; + *dp = alpha_first; } + + debug(dp == row); + row_info->flags ^= PNG_ALPHA_SWAPPED; } -#endif } } +# undef png_ptr } #endif #ifdef PNG_READ_INVERT_ALPHA_SUPPORTED static void -png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) +png_do_read_invert_alpha(png_transform_controlp row_info, png_bytep row) { - png_uint_32 row_width; png_debug(1, "in png_do_read_invert_alpha"); - row_width = row_info->width; - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) +# define png_ptr row_info->png_ptr + debug(!(row_info->flags & PNG_ALPHA_SWAPPED)); + if (row_info->channels == 4) { if (row_info->bit_depth == 8) { - /* This inverts the alpha channel in RGBA */ - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_uint_32 i; + /* This inverts the fourth channel in RGBA */ + png_bytep ep = row + png_transform_rowbytes(row_info); - for (i = 0; i < row_width; i++) - { - *(--dp) = CB(255 - *(--sp)); + for (row += 3; row < ep; row += 4) + *row ^= 0xff; -/* This does nothing: - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - We can replace it with: -*/ - sp-=3; - dp=sp; - } + row_info->flags ^= PNG_ALPHA_INVERTED; } #ifdef PNG_READ_16BIT_SUPPORTED /* This inverts the alpha channel in RRGGBBAA */ else { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_uint_32 i; + /* Need 2 bytes for each pixel, so subtract 1 from ep here: */ + png_bytep ep = row + png_transform_rowbytes(row_info) - 1; - for (i = 0; i < row_width; i++) + for (row += 6; row < ep; row += 8) { - *(--dp) = CB(255 - *(--sp)); - *(--dp) = CB(255 - *(--sp)); - -/* This does nothing: - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - We can replace it with: -*/ - sp-=6; - dp=sp; + row[0] ^= 0xff; + row[1] ^= 0xff; } + + row_info->flags ^= PNG_ALPHA_INVERTED; } #endif } - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + else if (row_info->channels == 2) { if (row_info->bit_depth == 8) { /* This inverts the alpha channel in GA */ - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_uint_32 i; + png_bytep ep = row + png_transform_rowbytes(row_info); - for (i = 0; i < row_width; i++) - { - *(--dp) = CB(255 - *(--sp)); - *(--dp) = *(--sp); - } + for (row += 1; row < ep; row += 2) + *row ^= 0xff; + + row_info->flags ^= PNG_ALPHA_INVERTED; } #ifdef PNG_READ_16BIT_SUPPORTED else { /* This inverts the alpha channel in GGAA */ - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_uint_32 i; + /* Need 2 bytes for each pixel, so subtract 1 from ep here: */ + png_bytep ep = row + png_transform_rowbytes(row_info) - 1; - for (i = 0; i < row_width; i++) + for (row += 2; row < ep; row += 4) { - *(--dp) = CB(255 - *(--sp)); - *(--dp) = CB(255 - *(--sp)); -/* - *(--dp) = *(--sp); - *(--dp) = *(--sp); -*/ - sp-=2; - dp=sp; + row[0] ^= 0xff; + row[1] ^= 0xff; } + + row_info->flags ^= PNG_ALPHA_INVERTED; } #endif } +# undef png_ptr } #endif #ifdef PNG_READ_FILLER_SUPPORTED -/* Add filler channel if we have RGB color */ +/* Add filler channel to 1 and 3 channel non-indexed data */ static void -png_do_read_filler(png_row_infop row_info, png_bytep row, - png_uint_32 filler, png_uint_32 flags) +png_do_read_filler(png_transform_controlp row_info, png_bytep row) { - png_uint_32 i; - png_uint_32 row_width = row_info->width; - -#ifdef PNG_READ_16BIT_SUPPORTED - png_byte hi_filler = CB(filler>>8); -#endif - png_byte lo_filler = CB(filler & 0xff); - png_debug(1, "in png_do_read_filler"); - if ( - row_info->color_type == PNG_COLOR_TYPE_GRAY) + /* TODO: remove these checks, this code will work on any number of + * channels but, at present, png_set_filler relies on this function + * not doing anything in inappropriate cases. + */ + if (!(row_info->flags & PNG_INDEXED) && + (row_info->channels == 1 || row_info->channels == 3) && + (row_info->bit_depth == 8 +#ifdef PNG_READ_16BIT_SUPPORTED + || row_info->bit_depth == 16 +#endif + )) { + png_const_structrp png_ptr = row_info->png_ptr; + png_bytep sp = row + png_transform_rowbytes(row_info); /*input*/ + png_bytep dp; + + ++(row_info->channels); + dp = row + png_transform_rowbytes(row_info); /*output*/ + if (row_info->bit_depth == 8) { - if ((flags & PNG_FLAG_FILLER_AFTER) != 0) + const png_byte fb = PNG_BYTE(png_ptr->filler); + + /* Add a filler before or after the current channels. */ + if ((png_ptr->flags & PNG_FLAG_FILLER_AFTER) != 0) { - /* This changes the data from G to GX */ - png_bytep sp = row + (png_size_t)row_width; - png_bytep dp = sp + (png_size_t)row_width; - for (i = 1; i < row_width; i++) + if (row_info->channels == 2) { - *(--dp) = lo_filler; - *(--dp) = *(--sp); + while (dp >= row+2) + { + *--dp = fb; + *--dp = *--sp; + } + + debug(dp == row && sp == row); + } + + else /* channels == 4 */ + { + while (dp >= row+4) + { + *--dp = fb; + *--dp = *--sp; + *--dp = *--sp; + *--dp = *--sp; + } + + debug(dp == row && sp == row); } - *(--dp) = lo_filler; - row_info->channels = 2; - row_info->pixel_depth = 16; - row_info->rowbytes = row_width * 2; } - else + else /* filler before */ { - /* This changes the data from G to XG */ - png_bytep sp = row + (png_size_t)row_width; - png_bytep dp = sp + (png_size_t)row_width; - for (i = 0; i < row_width; i++) + if (row_info->channels == 2) { - *(--dp) = *(--sp); - *(--dp) = lo_filler; + while (dp >= row+2) + { + *--dp = *--sp; + *--dp = fb; + } + + debug(dp == row && sp == row); + } + + else /* channels == 4 */ + { + while (dp >= row+4) + { + *--dp = *--sp; + *--dp = *--sp; + *--dp = *--sp; + *--dp = fb; + } + + debug(dp == row && sp == row); } - row_info->channels = 2; - row_info->pixel_depth = 16; - row_info->rowbytes = row_width * 2; } } -#ifdef PNG_READ_16BIT_SUPPORTED - else if (row_info->bit_depth == 16) +# ifdef PNG_READ_16BIT_SUPPORTED + else /* bit_depth == 16 */ { - if ((flags & PNG_FLAG_FILLER_AFTER) != 0) + /* Two byte pixels values: */ + const png_byte fb_first = PNG_BYTE(png_ptr->filler >> 8); + const png_byte fb_last = PNG_BYTE(png_ptr->filler); + + /* Add a filler before or after the current channels. */ + if ((png_ptr->flags & PNG_FLAG_FILLER_AFTER) != 0) { - /* This changes the data from GG to GGXX */ - png_bytep sp = row + (png_size_t)row_width * 2; - png_bytep dp = sp + (png_size_t)row_width * 2; - for (i = 1; i < row_width; i++) + if (row_info->channels == 2) { - *(--dp) = lo_filler; - *(--dp) = hi_filler; - *(--dp) = *(--sp); - *(--dp) = *(--sp); + while (dp >= row+4) + { + /* 2 channel bytes, 2 filler bytes */ + *--dp = fb_last; + *--dp = fb_first; + *--dp = *--sp; + *--dp = *--sp; + } + + debug(sp == row && dp == row); + } + + else /* channels == 4 */ + { + while (dp >= row+8) + { + /* 6 channel bytes, 2 filler bytes */ + *--dp = fb_last; + *--dp = fb_first; + dp -= 6, sp -= 6; + memmove(dp, sp, 6); + } + + debug(sp == row && dp == row); } - *(--dp) = lo_filler; - *(--dp) = hi_filler; - row_info->channels = 2; - row_info->pixel_depth = 32; - row_info->rowbytes = row_width * 4; } - else + else /* filler before */ { - /* This changes the data from GG to XXGG */ - png_bytep sp = row + (png_size_t)row_width * 2; - png_bytep dp = sp + (png_size_t)row_width * 2; - for (i = 0; i < row_width; i++) + if (row_info->channels == 2) { - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = lo_filler; - *(--dp) = hi_filler; + while (dp >= row+4) + { + /* 2 channel bytes, 2 filler bytes */ + *--dp = *--sp; + *--dp = *--sp; + *--dp = fb_last; + *--dp = fb_first; + } + + debug(sp == row && dp == row); + } + + else /* channels == 4 */ + { + while (dp >= row+8) + { + /* 6 channel bytes, 2 filler bytes */ + dp -= 6, sp -= 6; + memmove(dp, sp, 6); + *--dp = fb_last; + *--dp = fb_first; + } + + debug(sp == row && dp == row); } - row_info->channels = 2; - row_info->pixel_depth = 32; - row_info->rowbytes = row_width * 4; } + + if (!(png_ptr->transformations & PNG_ADD_ALPHA)) + row_info->flags |= PNG_FILLER_IN_ALPHA; } -#endif - } /* COLOR_TYPE == GRAY */ - else if (row_info->color_type == PNG_COLOR_TYPE_RGB) - { - if (row_info->bit_depth == 8) - { - if ((flags & PNG_FLAG_FILLER_AFTER) != 0) - { - /* This changes the data from RGB to RGBX */ - png_bytep sp = row + (png_size_t)row_width * 3; - png_bytep dp = sp + (png_size_t)row_width; - for (i = 1; i < row_width; i++) - { - *(--dp) = lo_filler; - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - } - *(--dp) = lo_filler; - row_info->channels = 4; - row_info->pixel_depth = 32; - row_info->rowbytes = row_width * 4; - } - - else - { - /* This changes the data from RGB to XRGB */ - png_bytep sp = row + (png_size_t)row_width * 3; - png_bytep dp = sp + (png_size_t)row_width; - for (i = 0; i < row_width; i++) - { - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = lo_filler; - } - row_info->channels = 4; - row_info->pixel_depth = 32; - row_info->rowbytes = row_width * 4; - } - } - -#ifdef PNG_READ_16BIT_SUPPORTED - else if (row_info->bit_depth == 16) - { - if ((flags & PNG_FLAG_FILLER_AFTER) != 0) - { - /* This changes the data from RRGGBB to RRGGBBXX */ - png_bytep sp = row + (png_size_t)row_width * 6; - png_bytep dp = sp + (png_size_t)row_width * 2; - for (i = 1; i < row_width; i++) - { - *(--dp) = lo_filler; - *(--dp) = hi_filler; - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - } - *(--dp) = lo_filler; - *(--dp) = hi_filler; - row_info->channels = 4; - row_info->pixel_depth = 64; - row_info->rowbytes = row_width * 8; - } - - else - { - /* This changes the data from RRGGBB to XXRRGGBB */ - png_bytep sp = row + (png_size_t)row_width * 6; - png_bytep dp = sp + (png_size_t)row_width * 2; - for (i = 0; i < row_width; i++) - { - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = lo_filler; - *(--dp) = hi_filler; - } - - row_info->channels = 4; - row_info->pixel_depth = 64; - row_info->rowbytes = row_width * 8; - } - } -#endif - } /* COLOR_TYPE == RGB */ +# endif + } } #endif #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED /* Expand grayscale files to RGB, with or without alpha */ static void -png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) +png_do_gray_to_rgb(png_transform_controlp row_info, png_bytep row) { - png_uint_32 i; - png_uint_32 row_width = row_info->width; - png_debug(1, "in png_do_gray_to_rgb"); - if (row_info->bit_depth >= 8 && - (row_info->color_type & PNG_COLOR_MASK_COLOR) == 0) +# define png_ptr row_info->png_ptr + + if (!(row_info->flags & PNG_INDEXED) && + (row_info->bit_depth == 8 || row_info->bit_depth == 16) && + (row_info->channels == 1 || row_info->channels == 2)) { - if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + png_bytep sp = row + png_transform_rowbytes(row_info); + png_bytep dp; + + debug(!(row_info->flags & PNG_ALPHA_SWAPPED)); + + row_info->channels += 2; + dp = row + png_transform_rowbytes(row_info); + + if (row_info->channels == 3) { if (row_info->bit_depth == 8) { /* This changes G to RGB */ - png_bytep sp = row + (png_size_t)row_width - 1; - png_bytep dp = sp + (png_size_t)row_width * 2; - for (i = 0; i < row_width; i++) + while (sp > row) { - *(dp--) = *sp; - *(dp--) = *sp; - *(dp--) = *(sp--); + *--dp = *--sp; + *--dp = *sp; + *--dp = *sp; } + + debug(dp == row && sp == row); } else { /* This changes GG to RRGGBB */ - png_bytep sp = row + (png_size_t)row_width * 2 - 1; - png_bytep dp = sp + (png_size_t)row_width * 4; - for (i = 0; i < row_width; i++) + while (sp > row) { - *(dp--) = *sp; - *(dp--) = *(sp - 1); - *(dp--) = *sp; - *(dp--) = *(sp - 1); - *(dp--) = *(sp--); - *(dp--) = *(sp--); + const png_byte hi = *--sp; + const png_byte lo = *--sp; + *--dp = hi; + *--dp = lo; + *--dp = hi; + *--dp = lo; /* it's off to work we go */ + *--dp = hi; + *--dp = lo; } + + debug(dp == row && sp == row); } } - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + else { + debug(row_info->channels == 4); + if (row_info->bit_depth == 8) { /* This changes GA to RGBA */ - png_bytep sp = row + (png_size_t)row_width * 2 - 1; - png_bytep dp = sp + (png_size_t)row_width * 2; - for (i = 0; i < row_width; i++) + while (sp > row) { - *(dp--) = *(sp--); - *(dp--) = *sp; - *(dp--) = *sp; - *(dp--) = *(sp--); + *--dp = *--sp; /* A */ + *--dp = *--sp; /* G -> B */ + *--dp = *sp; /* G -> G */ + *--dp = *sp; /* G -> R */ } + + debug(dp == row && sp == row); } else { /* This changes GGAA to RRGGBBAA */ - png_bytep sp = row + (png_size_t)row_width * 4 - 1; - png_bytep dp = sp + (png_size_t)row_width * 4; - for (i = 0; i < row_width; i++) + while (sp > row) { - *(dp--) = *(sp--); - *(dp--) = *(sp--); - *(dp--) = *sp; - *(dp--) = *(sp - 1); - *(dp--) = *sp; - *(dp--) = *(sp - 1); - *(dp--) = *(sp--); - *(dp--) = *(sp--); + *--dp = *--sp; + *--dp = *--sp; /* A */ + { + const png_byte hi = *--sp; + const png_byte lo = *--sp; + *--dp = hi; + *--dp = lo; + *--dp = hi; + *--dp = lo; + *--dp = hi; + *--dp = lo; + } } + + debug(dp == row && sp == row); } } - row_info->channels = CB(row_info->channels + 2); - row_info->color_type |= PNG_COLOR_MASK_COLOR; - row_info->pixel_depth = CB(row_info->channels * - row_info->bit_depth); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } +# undef png_ptr } #endif @@ -2901,23 +2905,36 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) * to that used above. */ static int -png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) - +png_do_rgb_to_gray(png_transform_controlp row_info, png_bytep row) { int rgb_error = 0; png_debug(1, "in png_do_rgb_to_gray"); - if ((row_info->color_type & PNG_COLOR_MASK_PALETTE) == 0 && - (row_info->color_type & PNG_COLOR_MASK_COLOR) != 0) + if (!(row_info->flags & PNG_INDEXED) && + (row_info->channels == 3 || row_info->channels == 4)) { + png_const_structrp png_ptr = row_info->png_ptr; PNG_CONST png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; PNG_CONST png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; PNG_CONST png_uint_32 bc = 32768 - rc - gc; PNG_CONST png_uint_32 row_width = row_info->width; - PNG_CONST int have_alpha = - (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0; + PNG_CONST int have_alpha = row_info->channels == 4; + png_bytep sp = row; + png_bytep dp = row; + /* NOTE: rc+gc+bc == 32768 and is a (png_uint_32) value, so the worst + * case calculation below (for white) is: + * + * 32768*65535+16384 + * + * Which still fits in 32 (unsigned) bits, and: + * + * (32768*65535+16384) >> 15 + * + * is 65535 (always). Consequently the calculation below is marked + * SAFE. Likewise for a png_byte value the maximum is 255. + */ if (row_info->bit_depth == 8) { /* Notice that gamma to/from 1 are not necessarily inverses (if @@ -2929,25 +2946,24 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) { 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++) { - unsigned int red = *(sp++); - unsigned int green = *(sp++); - unsigned int blue = *(sp++); + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); if (red != green || red != blue) { - red = png_ptr->gamma_to_1[red]; - green = png_ptr->gamma_to_1[green]; - blue = png_ptr->gamma_to_1[blue]; + /* gamma_to_1 is (png_uint_16[]) */ + unsigned int red_1 = png_ptr->gamma_to_1[red]; + unsigned int green_1 = png_ptr->gamma_to_1[green]; + unsigned int blue_1 = png_ptr->gamma_to_1[blue]; rgb_error |= 1; *(dp++) = png_ptr->gamma_from_1[ - (rc*red + gc*green + bc*blue + add)>>shift]; + /*SAFE*/(rc*red_1 + gc*green_1 + bc*blue_1 + add)>>shift]; } else @@ -2958,7 +2974,7 @@ 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++) = png_check_byte(png_ptr, red); + *(dp++) = red; } if (have_alpha != 0) @@ -2968,8 +2984,6 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) else { - png_bytep sp = row; - png_bytep dp = row; png_uint_32 i; for (i = 0; i < row_width; i++) @@ -2981,8 +2995,8 @@ 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; - *(dp++) = png_check_byte(png_ptr, - (rc*red+gc*green+bc*blue+16384)>>15); + *(dp++) = (png_byte)/*SAFE*/ + ((rc*red+gc*green+bc*blue+16384) >> 15); } else @@ -3000,48 +3014,37 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) { 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; for (i = 0; i < row_width; i++) { - png_uint_16 red, green, blue, w; - png_byte hi,lo; + unsigned int red, green, blue; + png_uint_16 w; - hi=*(sp)++; lo=*(sp)++; - red = png_check_u16(png_ptr, (hi << 8) | (lo)); - - hi=*(sp)++; lo=*(sp)++; - green = png_check_u16(png_ptr, (hi << 8) | (lo)); - - hi=*(sp)++; lo=*(sp)++; - blue = png_check_u16(png_ptr, (hi << 8) | (lo)); + red = *sp++ << 8, red |= *sp++; + green = *sp++ << 8, green |= *sp++; + blue = *sp++ << 8, blue |= *sp++; if (red == green && red == blue) { if (png_ptr->gamma_16_table != NULL) - w = png_ptr->gamma_16_table[(red+add) >> shift]; + w = png_ptr->gamma_16_table[/*SAFE*/(red+add) >> shift]; else - w = red; + w = (png_uint_16)/*SAFE*/red; } else { - 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_check_u16(png_ptr, - (rc*red_1 + gc*green_1 + bc*blue_1 + 16384)>>15); - w = png_ptr->gamma_16_from_1[(gray16+add) >> shift]; + red = png_ptr->gamma_16_to_1[/*SAFE*/(red+add) >> shift]; + green = png_ptr->gamma_16_to_1[/*SAFE*/(green+add) >> shift]; + blue = png_ptr->gamma_16_to_1[/*SAFE*/(blue+add) >> shift]; + w = png_ptr->gamma_16_from_1[/*SAFE*/ + (((rc*red + gc*green + bc*blue + 16384)>>15)+add)>>shift]; rgb_error |= 1; } - *(dp++) = png_check_byte(png_ptr, w>>8); + *(dp++) = (png_byte)/*SAFE*/(w>>8); *(dp++) = PNG_BYTE(w); if (have_alpha != 0) @@ -3054,21 +3057,15 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) else { - png_bytep sp = row; - png_bytep dp = row; png_uint_32 i; for (i = 0; i < row_width; i++) { - png_uint_16 red, green, blue, gray16; - png_byte hi,lo; + unsigned int red, green, blue, w; - hi=*(sp)++; lo=*(sp)++; - red = png_check_u16(png_ptr, (hi << 8) | (lo)); - hi=*(sp)++; lo=*(sp)++; - green = png_check_u16(png_ptr, (hi << 8) | (lo)); - hi=*(sp)++; lo=*(sp)++; - blue = png_check_u16(png_ptr, (hi << 8) | (lo)); + red = *sp++ << 8, red |= *sp++; + green = *sp++ << 8, green |= *sp++; + blue = *sp++ << 8, blue |= *sp++; if (red != green || red != blue) rgb_error |= 1; @@ -3077,10 +3074,9 @@ 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_check_u16(png_ptr, - (rc*red+gc*green+bc*blue+16384) >> 15); - *(dp++) = png_check_byte(png_ptr, gray16>>8); - *(dp++) = PNG_BYTE(gray16); + w = (rc*red+gc*green+bc*blue+16384) >> 15; + *(dp++) = (png_byte)/*SAFE*/(w>>8); + *(dp++) = PNG_BYTE(w); if (have_alpha != 0) { @@ -3091,13 +3087,9 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) } } - row_info->channels = png_check_byte(png_ptr, row_info->channels - 2); - row_info->color_type = png_check_byte(png_ptr, row_info->color_type & - ~PNG_COLOR_MASK_COLOR); - row_info->pixel_depth = png_check_byte(png_ptr, row_info->channels * - row_info->bit_depth); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + row_info->channels -= 2; } + return rgb_error; } #endif @@ -3109,8 +3101,9 @@ png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) * at a gamma of 1.0. Paletted files have already been taken care of. */ static void -png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) +png_do_compose(png_transform_controlp row_info, png_bytep row) { + png_const_structrp png_ptr = row_info->png_ptr; png_const_bytep gamma_table = png_ptr->gamma_table; png_const_bytep gamma_from_1 = png_ptr->gamma_from_1; png_const_uint_16p gamma_to_1 = png_ptr->gamma_to_1; @@ -3127,10 +3120,10 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) png_debug(1, "in png_do_compose"); - { - switch (row_info->color_type) + if (!(row_info->flags & PNG_INDEXED)) { + switch (row_info->channels) { - case PNG_COLOR_TYPE_GRAY: + case 1 /*GRAY*/: { switch (row_info->bit_depth) { @@ -3300,7 +3293,8 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) for (i = 0; i < row_width; i++, sp++) { if (*sp == png_ptr->trans_color.gray) - *sp = png_check_byte(png_ptr, png_ptr->background.gray); + *sp = png_check_byte(png_ptr, + png_ptr->background.gray); else *sp = gamma_table[*sp]; @@ -3313,7 +3307,8 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) for (i = 0; i < row_width; i++, sp++) { if (*sp == png_ptr->trans_color.gray) - *sp = png_check_byte(png_ptr, png_ptr->background.gray); + *sp = png_check_byte(png_ptr, + png_ptr->background.gray); } } break; @@ -3333,7 +3328,8 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) if (v == png_ptr->trans_color.gray) { /* Background is already in screen gamma */ - *sp = png_check_byte(png_ptr, png_ptr->background.gray >> 8); + *sp = png_check_byte(png_ptr, + png_ptr->background.gray >> 8); *(sp + 1) = PNG_BYTE(png_ptr->background.gray); } @@ -3357,7 +3353,8 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) if (v == png_ptr->trans_color.gray) { - *sp = png_check_byte(png_ptr, png_ptr->background.gray >> 8); + *sp = png_check_byte(png_ptr, + png_ptr->background.gray >> 8); *(sp + 1) = PNG_BYTE(png_ptr->background.gray); } } @@ -3371,7 +3368,7 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) break; } - case PNG_COLOR_TYPE_RGB: + case 3 /*RGB*/: { if (row_info->bit_depth == 8) { @@ -3385,8 +3382,10 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) *(sp + 2) == png_ptr->trans_color.blue) { *sp = png_check_byte(png_ptr, png_ptr->background.red); - *(sp + 1) = png_check_byte(png_ptr, png_ptr->background.green); - *(sp + 2) = png_check_byte(png_ptr, png_ptr->background.blue); + *(sp + 1) = png_check_byte(png_ptr, + png_ptr->background.green); + *(sp + 2) = png_check_byte(png_ptr, + png_ptr->background.blue); } else @@ -3416,6 +3415,7 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) } } } + else /* if (row_info->bit_depth == 16) */ { if (gamma_16 != NULL) @@ -3451,15 +3451,15 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) else { png_uint_16 v = gamma_16[(r+add) >> shift]; - *sp = png_check_byte(png_ptr, v >> 8); + *sp = (png_byte)/*SAFE*/(v >> 8); *(sp + 1) = PNG_BYTE(v); v = gamma_16[(g+add) >> shift]; - *(sp + 2) = png_check_byte(png_ptr, v >> 8); + *(sp + 2) = (png_byte)/*SAFE*/(v >> 8); *(sp + 3) = PNG_BYTE(v); v = gamma_16[(b+add) >> shift]; - *(sp + 4) = png_check_byte(png_ptr, v >> 8); + *(sp + 4) = (png_byte)/*SAFE*/(v >> 8); *(sp + 5) = PNG_BYTE(v); } } @@ -3470,36 +3470,32 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) sp = row; for (i = 0; i < row_width; i++, sp += 6) { - png_uint_16 r = png_check_u16(png_ptr, - ((*sp) << 8) + *(sp + 1)); - - png_uint_16 g = png_check_u16(png_ptr, - ((*(sp + 2)) << 8) + *(sp + 3)); - - png_uint_16 b = png_check_u16(png_ptr, - ((*(sp + 4)) << 8) + *(sp + 5)); + unsigned int r = ((*sp) << 8) + *(sp + 1); + unsigned int g = ((*(sp + 2)) << 8) + *(sp + 3); + unsigned int b = ((*(sp + 4)) << 8) + *(sp + 5); if (r == png_ptr->trans_color.red && g == png_ptr->trans_color.green && b == png_ptr->trans_color.blue) { - *sp = png_check_byte(png_ptr, - png_ptr->background.red >> 8); + *sp = (png_byte)/*SAFE*/(png_ptr->background.red >> 8); *(sp + 1) = PNG_BYTE(png_ptr->background.red); - *(sp + 2) = png_check_byte(png_ptr, + *(sp + 2) = (png_byte)/*SAFE*/( png_ptr->background.green >> 8); *(sp + 3) = PNG_BYTE(png_ptr->background.green); - *(sp + 4) = png_check_byte(png_ptr, + *(sp + 4) = (png_byte)/*SAFE*/( png_ptr->background.blue >> 8); *(sp + 5) = PNG_BYTE(png_ptr->background.blue); + /*UNTESTED*/ } } + /*UNTESTED*/ } } break; } - case PNG_COLOR_TYPE_GRAY_ALPHA: + case 2 /*GRAY_ALPHA*/: { if (row_info->bit_depth == 8) { @@ -3509,7 +3505,7 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) sp = row; for (i = 0; i < row_width; i++, sp += 2) { - png_uint_16 a = *(sp + 1); + unsigned int a = *(sp + 1); if (a == 0xff) *sp = gamma_table[*sp]; @@ -3568,10 +3564,10 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) if (a == 65535) { - png_uint_16 v; + unsigned int v; v = gamma_16[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = png_check_byte(png_ptr, v >> 8); + *sp = (png_byte)/*SAFE*/(v >> 8); *(sp + 1) = PNG_BYTE(v); } @@ -3632,7 +3628,7 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) break; } - case PNG_COLOR_TYPE_RGB_ALPHA: + case 4 /*RGB_ALPHA*/: { if (row_info->bit_depth == 8) { @@ -3766,7 +3762,8 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) else if (a == 0) { /* Background is already in screen gamma */ - *sp = png_check_byte(png_ptr, png_ptr->background.red >> 8); + *sp = png_check_byte(png_ptr, + png_ptr->background.red >> 8); *(sp + 1) = PNG_BYTE(png_ptr->background.red); *(sp + 2) = png_check_byte(png_ptr, png_ptr->background.green >> 8); @@ -3875,8 +3872,9 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) * build_gamma_table(). */ static void -png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) +png_do_gamma(png_transform_controlp row_info, png_bytep row) { + png_const_structrp png_ptr = row_info->png_ptr; png_const_bytep gamma_table = png_ptr->gamma_table; png_const_uint_16p gamma_16_table = png_ptr->gamma_16_table; int shift = png_ptr->gamma_shift; @@ -3896,9 +3894,9 @@ png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) if (((row_info->bit_depth == 8 && gamma_table != NULL) || (row_info->bit_depth == 16 && gamma_16_table != NULL))) { - switch (row_info->color_type) + if (!(row_info->flags & PNG_INDEXED)) switch (row_info->channels) { - case PNG_COLOR_TYPE_RGB: + case 3 /*RGB*/: { if (row_info->bit_depth == 8) { @@ -3940,7 +3938,7 @@ png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) break; } - case PNG_COLOR_TYPE_RGB_ALPHA: + case 4 /*RGB_ALPHA*/: { if (row_info->bit_depth == 8) { @@ -3986,7 +3984,7 @@ png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) break; } - case PNG_COLOR_TYPE_GRAY_ALPHA: + case 2 /*GRAY_ALPHA*/: { if (row_info->bit_depth == 8) { @@ -4014,7 +4012,7 @@ png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) break; } - case PNG_COLOR_TYPE_GRAY: + case 1 /*GRAY*/: { if (row_info->bit_depth == 8) { @@ -4055,33 +4053,31 @@ png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) * from_1 tables. */ static void -png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr) +png_do_encode_alpha(png_transform_controlp row_info, png_bytep row) { - png_uint_32 row_width = row_info->width; + int step = row_info->channels; png_debug(1, "in png_do_encode_alpha"); - if ((row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0) + if ((step == 2 || step == 4) && !(row_info->flags & PNG_INDEXED)) { + png_const_structrp png_ptr = row_info->png_ptr; PNG_CONST unsigned int shift = png_ptr->gamma_shift; PNG_CONST unsigned int add = (shift > 0 ? 1U<<(shift-1) : 0); + png_uint_32 row_width = row_info->width; if (row_info->bit_depth == 8) { PNG_CONST png_bytep gamma_from_1 = png_ptr->gamma_from_1; - if (gamma_from_1 != NULL) - { - PNG_CONST int step = - (row_info->color_type & PNG_COLOR_MASK_COLOR) != 0 ? 4 : 2; + affirm(gamma_from_1 != NULL); + { /* The alpha channel is the last component: */ row += step - 1; for (; row_width > 0; --row_width, row += step) *row = gamma_from_1[(257U**row+add)>>shift]; - - return; } } @@ -4089,10 +4085,10 @@ png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { PNG_CONST png_uint_16p gamma_16_from_1 = png_ptr->gamma_16_from_1; - if (gamma_16_from_1 != NULL) + affirm(gamma_16_from_1 != NULL); + { - PNG_CONST int step = - (row_info->color_type & PNG_COLOR_MASK_COLOR) != 0 ? 8 : 4; + step *= 2; /* The alpha channel is the last component: */ row += step - 2; @@ -4105,16 +4101,9 @@ png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr) *row = png_check_byte(png_ptr, v >> 8); *(row + 1) = PNG_BYTE(v); } - - return; } } } - - /* Only get to here if called with a weird row_info; no harm has been done, - * so just issue a warning. - */ - png_warning(png_ptr, "png_do_encode_alpha: unexpected call"); } #endif @@ -4123,380 +4112,355 @@ png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr) * upon whether you supply trans and num_trans. */ static void -png_do_expand_palette(png_row_infop row_info, png_bytep row, - png_const_colorp palette, png_const_bytep trans_alpha, int num_trans) +png_do_expand_palette(png_transform_controlp row_info, png_bytep row) { - int shift, value; - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width=row_info->width; - png_debug(1, "in png_do_expand_palette"); - if (row_info->color_type == PNG_COLOR_TYPE_PALETTE) + if ((row_info->flags & PNG_INDEXED) && row_info->channels == 1) { + png_const_structrp png_ptr = row_info->png_ptr; + + /* Unpack packed pixels into 1-per-byte: */ if (row_info->bit_depth < 8) { - switch (row_info->bit_depth) - { - case 1: - { - sp = row + (png_size_t)((row_width - 1) >> 3); - dp = row + (png_size_t)row_width - 1; - shift = 7 - (int)((row_width + 7) & 0x07); - for (i = 0; i < row_width; i++) - { - if ((*sp >> shift) & 0x01) - *dp = 1; - - else - *dp = 0; - - if (shift == 7) - { - shift = 0; - sp--; - } - - else - shift++; - - dp--; - } - break; - } - - case 2: - { - sp = row + (png_size_t)((row_width - 1) >> 2); - dp = row + (png_size_t)row_width - 1; - shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); - for (i = 0; i < row_width; i++) - { - value = (*sp >> shift) & 0x03; - *dp = CB(value); - if (shift == 6) - { - shift = 0; - sp--; - } - - else - shift += 2; - - dp--; - } - break; - } - - case 4: - { - sp = row + (png_size_t)((row_width - 1) >> 1); - dp = row + (png_size_t)row_width - 1; - shift = (int)((row_width & 0x01) << 2); - for (i = 0; i < row_width; i++) - { - value = (*sp >> shift) & 0x0f; - *dp = CB(value); - if (shift == 4) - { - shift = 0; - sp--; - } - - else - shift += 4; - - dp--; - } - break; - } - - default: - break; - } - row_info->bit_depth = 8; - row_info->pixel_depth = 8; - row_info->rowbytes = row_width; + png_do_unpack(row_info, row); + debug(row_info->flags & PNG_BITS_SHIFTED); + row_info->flags &= ~PNG_BITS_SHIFTED; } - if (row_info->bit_depth == 8) - { + affirm(row_info->bit_depth == 8); + + { /* 8-bit per index, unpack to RGB or RGBA */ + png_const_colorp palette = png_ptr->palette; + const int num_palette = png_ptr->num_palette; + int num_trans = png_ptr->num_trans; + int bad_index = 0; + png_const_bytep sp = row + png_transform_rowbytes(row_info); + png_bytep dp; + + if (num_trans > num_palette) + num_trans = num_palette; /* 11.3.2.1: tRNS no longer than palette */ + + if (num_trans > 0) /* Unpack to RGBA */ { - if (num_trans > 0) + png_const_bytep trans_alpha = png_ptr->trans_alpha; + + row_info->channels = 4; + dp = row + png_transform_rowbytes(row_info); + + while (dp >= row+4) { - sp = row + (png_size_t)row_width - 1; - dp = row + (png_size_t)(row_width << 2) - 1; + const int index = *--sp; - for (i = 0; i < row_width; i++) + if (index < num_trans) + *--dp = trans_alpha[index]; + + else + *--dp = 0xff; + + if (index < num_palette) { - if ((int)(*sp) >= num_trans) - *dp-- = 0xff; - - else - *dp-- = trans_alpha[*sp]; - - *dp-- = palette[*sp].blue; - *dp-- = palette[*sp].green; - *dp-- = palette[*sp].red; - sp--; - } - row_info->bit_depth = 8; - row_info->pixel_depth = 32; - row_info->rowbytes = row_width * 4; - row_info->color_type = 6; - row_info->channels = 4; - } - - else - { - sp = row + (png_size_t)row_width - 1; - dp = row + (png_size_t)(row_width * 3) - 1; - - for (i = 0; i < row_width; i++) - { - *dp-- = palette[*sp].blue; - *dp-- = palette[*sp].green; - *dp-- = palette[*sp].red; - sp--; + *--dp = palette[index].blue; + *--dp = palette[index].green; + *--dp = palette[index].red; } - row_info->bit_depth = 8; - row_info->pixel_depth = 24; - row_info->rowbytes = row_width * 3; - row_info->color_type = 2; - row_info->channels = 3; + else + { + bad_index = index; + *--dp = 0; /* default to black */ + *--dp = 0; + *--dp = 0; + } } + + debug(dp == row && sp == row); } + + else /* Unpack to RGB */ + { + row_info->channels = 3; + dp = row + png_transform_rowbytes(row_info); + + while (dp >= row+3) + { + const int index = *--sp; + + if (index < num_palette) + { + *--dp = palette[index].blue; + *--dp = palette[index].green; + *--dp = palette[index].red; + } + + else + { + bad_index = index; + *--dp = 0; /* default to black */ + *--dp = 0; + *--dp = 0; + } + } + + debug(sp == row && sp == row); + } + + /* At this point we have squirted new RGB or RGBA values into + * the row, this zaps all the error flags *and* PNG_INDEXED, + * if a bad index we detected we record that (it's not a good idea + * to output a warning on every row!) + */ + if (bad_index) + row_info->flags = PNG_BAD_INDEX; + else + row_info->flags = 0; } } } +/* Like do_unpack except that the packed data is expanded to the full 8-bit + * range; scaled up. This is not a good thing to do on an indexed image; + * the indices will be invalid. + */ +static void +png_do_expand_channels(png_transform_controlp row_info, png_bytep row) +{ + png_debug(1, "in png_do_expand_channels"); + +# define png_ptr row_info->png_ptr + debug(!(row_info->flags & PNG_BITS_SHIFTED)); + + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + png_const_bytep sp = row + png_transform_rowbytes(row_info)-1; + unsigned int shift = 0x7 & -row_info->width; + png_bytep dp; + + row_info->bit_depth = 8; + dp = row + png_transform_rowbytes(row_info); + + while (dp > row) + { + *--dp = (png_byte)/*SAFE*/(((*sp >> shift) & 0x01) * 255); + shift = 0x7 & (shift+1); + if (shift == 0) + --sp; + } + + debug(dp == row && shift == 0 && sp == row-1); + break; + } + + case 2: + { + png_const_bytep sp = row + png_transform_rowbytes(row_info)-1; + unsigned int shift = 7 & -(row_info->width << 1); + png_bytep dp; + + row_info->bit_depth = 8; + dp = row + png_transform_rowbytes(row_info); + + while (dp > row) + { + *--dp = (png_byte)/*SAFE*/(((*sp >> shift) & 0x03) * 85); + shift = 0x7 & (shift+2); + if (shift == 0) + --sp; + } + + debug(dp == row && shift == 0 && sp == row-1); + break; + } + + case 4: + { + png_const_bytep sp = row + png_transform_rowbytes(row_info)-1; + unsigned int shift = 7 & -(row_info->width << 2); + png_bytep dp; + + row_info->bit_depth = 8; + dp = row + png_transform_rowbytes(row_info); + + while (dp > row) + { + *--dp = (png_byte)/*SAFE*/(((*sp >> shift) & 0x0f) * 17); + shift = 0x7 & (shift+4); + if (shift == 0) + --sp; + } + + debug(dp == row && shift == 0 && sp == row-1); + break; + } + + default: + break; + } + } +# undef png_ptr +} + /* If the bit depth < 8, it is expanded to 8. Also, if the already * expanded transparency value is supplied, an alpha channel is built. */ static void -png_do_expand(png_row_infop row_info, png_bytep row, - png_const_color_16p trans_color) +png_do_expand(png_transform_controlp row_info, png_bytep row) { - int shift, value; - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width=row_info->width; + png_const_structrp png_ptr = row_info->png_ptr; png_debug(1, "in png_do_expand"); + if (row_info->channels == 1 && !(row_info->flags & PNG_INDEXED)) { - if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + /* Grayscale (1 channel), tRNS expansion requires that the data + * be expanded to 8-bit pixels and the tRNS 'gray' value is expanded + * to match. ISO PNG 11.3.2.1 suggests that only the low order bits + * are considered when the bit depth is less than 16. + */ + if (png_ptr->num_trans != 0 && + (png_ptr->transformations & PNG_EXPAND_tRNS) != 0) { - unsigned int gray = trans_color != NULL ? trans_color->gray : 0; + unsigned int gray = png_ptr->trans_color.gray; - if (row_info->bit_depth < 8) + switch (row_info->bit_depth) { - switch (row_info->bit_depth) - { - case 1: + case 1: + gray &= 0x1; + gray |= gray << 1; + /*FALL THROUGH*/ + case 2: + gray &= 0x3; + gray |= gray << 2; + /*FALL THROUGH*/ + case 4: + gray &= 0xf; + gray |= gray << 4; + png_do_expand_channels(row_info, row); + affirm(row_info->bit_depth == 8); + /*FALL THROUGH*/ + case 8: + gray &= 0xff; { - gray = (gray & 0x01) * 0xff; - sp = row + (png_size_t)((row_width - 1) >> 3); - dp = row + (png_size_t)row_width - 1; - shift = 7 - (int)((row_width + 7) & 0x07); - for (i = 0; i < row_width; i++) + png_const_bytep sp = row + png_transform_rowbytes(row_info); + png_bytep dp; + + row_info->channels = 2; + dp = row + png_transform_rowbytes(row_info); + + while (dp >= row+2) { - if ((*sp >> shift) & 0x01) - *dp = 0xff; + const png_byte g = *--sp; - else - *dp = 0; + *--dp = (g == gray) ? 0 : 0xff; + *--dp = g; + } + + debug(dp == row && sp == row); + } + break; + + case 16: + { + png_const_bytep sp = row + png_transform_rowbytes(row_info); + png_bytep dp; + + row_info->channels = 2; + dp = row + png_transform_rowbytes(row_info); + + while (dp >= row+4) + { + dp -= 4; + sp -= 2; - if (shift == 7) { - shift = 0; - sp--; + const unsigned int g = (sp[0] << 8) | sp[1]; + + dp[2] = dp[3] = (g == gray) ? 0 : 0xff; + dp[1] = PNG_BYTE(g); + dp[0] = PNG_BYTE(g >> 8); } - - else - shift++; - - dp--; - } - break; - } - - case 2: - { - gray = (gray & 0x03) * 0x55; - sp = row + (png_size_t)((row_width - 1) >> 2); - dp = row + (png_size_t)row_width - 1; - shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); - for (i = 0; i < row_width; i++) - { - value = (*sp >> shift) & 0x03; - *dp = CB(value | (value << 2) | (value << 4) | - (value << 6)); - if (shift == 6) - { - shift = 0; - sp--; - } - - else - shift += 2; - - dp--; - } - break; - } - - case 4: - { - gray = (gray & 0x0f) * 0x11; - sp = row + (png_size_t)((row_width - 1) >> 1); - dp = row + (png_size_t)row_width - 1; - shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); - for (i = 0; i < row_width; i++) - { - value = (*sp >> shift) & 0x0f; - *dp = CB(value | (value << 4)); - if (shift == 4) - { - shift = 0; - sp--; - } - - else - shift = 4; - - dp--; - } - break; - } - - default: - break; - } - - row_info->bit_depth = 8; - row_info->pixel_depth = 8; - row_info->rowbytes = row_width; - } - - if (trans_color != NULL) - { - if (row_info->bit_depth == 8) - { - gray = gray & 0xff; - sp = row + (png_size_t)row_width - 1; - dp = row + (png_size_t)(row_width << 1) - 1; - - for (i = 0; i < row_width; i++) - { - if (*sp == gray) - *dp-- = 0; - - else - *dp-- = 0xff; - - *dp-- = *sp--; - } - } - - else if (row_info->bit_depth == 16) - { - unsigned int gray_high = (gray >> 8) & 0xff; - unsigned int gray_low = gray & 0xff; - sp = row + row_info->rowbytes - 1; - dp = row + (row_info->rowbytes << 1) - 1; - for (i = 0; i < row_width; i++) - { - if (*(sp - 1) == gray_high && *(sp) == gray_low) - { - *dp-- = 0; - *dp-- = 0; } - else - { - *dp-- = 0xff; - *dp-- = 0xff; - } - - *dp-- = *sp--; - *dp-- = *sp--; + debug(dp == row && sp == row); } - } + break; - row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; - row_info->channels = 2; - row_info->pixel_depth = CB(row_info->bit_depth << 1); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, - row_width); + default: + impossible("bit depth invalid"); } } - else if (row_info->color_type == PNG_COLOR_TYPE_RGB && - trans_color != NULL) + + else if (row_info->bit_depth < 8) /* but no tRNS */ + png_do_expand_channels(row_info, row); + } + + else if (row_info->channels == 3 && + png_ptr->num_trans != 0 && + (png_ptr->transformations & PNG_EXPAND_tRNS) != 0) + { + if (row_info->bit_depth == 8) { - if (row_info->bit_depth == 8) - { - png_byte red = CB(trans_color->red); - png_byte green = CB(trans_color->green); - png_byte blue = CB(trans_color->blue); - sp = row + (png_size_t)row_info->rowbytes - 1; - dp = row + (png_size_t)(row_width << 2) - 1; - for (i = 0; i < row_width; i++) - { - if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) - *dp-- = 0; + png_const_bytep sp = row + png_transform_rowbytes(row_info); + png_bytep dp; + png_uint_32 trans = + ((((png_ptr->trans_color.blue & 0xff) << 8) | + (png_ptr->trans_color.green & 0xff) ) << 8) | + (png_ptr->trans_color.red & 0xff); - else - *dp-- = 0xff; - - *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; - } - } - else if (row_info->bit_depth == 16) - { - png_byte red_high = CB(trans_color->red >> 8); - png_byte green_high = CB(trans_color->green >> 8); - png_byte blue_high = CB(trans_color->blue >> 8); - png_byte red_low = PNG_BYTE(trans_color->red); - png_byte green_low = PNG_BYTE(trans_color->green); - png_byte blue_low = PNG_BYTE(trans_color->blue); - sp = row + row_info->rowbytes - 1; - dp = row + (png_size_t)(row_width << 3) - 1; - for (i = 0; i < row_width; i++) - { - if (*(sp - 5) == red_high && - *(sp - 4) == red_low && - *(sp - 3) == green_high && - *(sp - 2) == green_low && - *(sp - 1) == blue_high && - *(sp ) == blue_low) - { - *dp-- = 0; - *dp-- = 0; - } - - else - { - *dp-- = 0xff; - *dp-- = 0xff; - } - - *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; - } - } - row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; row_info->channels = 4; - row_info->pixel_depth = CB(row_info->bit_depth << 2); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + dp = row + png_transform_rowbytes(row_info); + + while (dp >= row+4) + { + png_uint_32 pixel = *--sp; /* B */ + pixel = (pixel << 8) | *--sp; /* G */ + pixel = (pixel << 8) | *--sp; /* R */ + + *--dp = (pixel == trans) ? 0 : 0xff; + *--dp = PNG_BYTE(pixel >> 16); /* B */ + *--dp = PNG_BYTE(pixel >> 8); /* G */ + *--dp = PNG_BYTE(pixel ); /* R */ + } + + debug(dp == row && sp == row); + } + + else if (row_info->bit_depth == 16) + { + /* The full 6 bytes of the input RRGGBB need to be compared against + * the transparent color value. Allow the compiler to choose how to + * do this by using the standard library routines. + */ + png_const_bytep sp = row + png_transform_rowbytes(row_info); + png_bytep dp; + png_byte trans[6]; + + trans[0] = PNG_BYTE(png_ptr->trans_color.red >> 8); + trans[1] = PNG_BYTE(png_ptr->trans_color.red); + trans[2] = PNG_BYTE(png_ptr->trans_color.green >> 8); + trans[3] = PNG_BYTE(png_ptr->trans_color.green); + trans[4] = PNG_BYTE(png_ptr->trans_color.blue >> 8); + trans[5] = PNG_BYTE(png_ptr->trans_color.blue); + + row_info->channels = 4; + dp = row + png_transform_rowbytes(row_info); + + while (dp >= row+8) + { + png_byte alpha; + dp -= 8; + sp -= 6; + + alpha = memcmp(trans, sp, 6) ? 0xff : 0; + memmove(dp, sp, 6); + dp[7] = dp[6] = alpha; + } + + debug(dp == row && sp == row); } } } @@ -4507,12 +4471,14 @@ png_do_expand(png_row_infop row_info, png_bytep row, * whole row to 16 bits. Has no effect otherwise. */ static void -png_do_expand_16(png_row_infop row_info, png_bytep row) +png_do_expand_16(png_transform_controlp row_info, png_bytep row) { - if (row_info->bit_depth == 8 && - row_info->color_type != PNG_COLOR_TYPE_PALETTE) + png_debug(1, "in png_do_expand16"); + +# define png_ptr row_info->png_ptr + if (row_info->bit_depth == 8 && !(row_info->flags & PNG_INDEXED)) { - /* The row have a sequence of bytes containing [0..255] and we need + /* The rows have a sequence of bytes containing [0..255] and we need * to turn it into another row containing [0..65535], to do this we * calculate: * @@ -4521,22 +4487,24 @@ png_do_expand_16(png_row_infop row_info, png_bytep row) * Which happens to be exactly input * 257 and this can be achieved * simply by byte replication in place (copying backwards). */ - png_byte *sp = row + row_info->rowbytes; /* source, last byte + 1 */ - png_byte *dp = sp + row_info->rowbytes; /* destination, end + 1 */ + png_const_bytep sp = row + png_transform_rowbytes(row_info); + png_bytep dp; + + row_info->bit_depth = 16; + dp = row + png_transform_rowbytes(row_info); + while (dp > sp) dp[-2] = dp[-1] = *--sp, dp -= 2; - row_info->rowbytes *= 2; - row_info->bit_depth = 16; - row_info->pixel_depth = CB(row_info->channels * 16); + debug(dp == row && sp == row); } +# undef png_ptr } #endif #ifdef PNG_READ_QUANTIZE_SUPPORTED static void -png_do_quantize(png_row_infop row_info, png_bytep row, - png_const_bytep palette_lookup, png_const_bytep quantize_lookup) +png_do_quantize(png_transform_controlp row_info, png_bytep row) { png_bytep sp, dp; png_uint_32 i; @@ -4546,7 +4514,9 @@ png_do_quantize(png_row_infop row_info, png_bytep row, if (row_info->bit_depth == 8) { - if (row_info->color_type == PNG_COLOR_TYPE_RGB && palette_lookup) + png_const_bytep palette_lookup = row_info->png_ptr->palette_lookup; + + if (row_info->channels == 3 && palette_lookup) { int r, g, b, p; sp = row; @@ -4576,14 +4546,11 @@ png_do_quantize(png_row_infop row_info, png_bytep row, *dp++ = palette_lookup[p]; } - row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->flags |= PNG_INDEXED; row_info->channels = 1; - row_info->pixel_depth = row_info->bit_depth; - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && - palette_lookup != NULL) + else if (row_info->channels == 4 && palette_lookup != NULL) { int r, g, b, p; sp = row; @@ -4607,15 +4574,15 @@ png_do_quantize(png_row_infop row_info, png_bytep row, *dp++ = palette_lookup[p]; } - row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->flags |= PNG_INDEXED; row_info->channels = 1; - row_info->pixel_depth = row_info->bit_depth; - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } - else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && - quantize_lookup) + else if (row_info->channels == 1 && (row_info->flags & PNG_INDEXED) && + row_info->png_ptr->quantize_index != NULL) { + png_const_bytep quantize_lookup = row_info->png_ptr->quantize_index; + sp = row; for (i = 0; i < row_width; i++, sp++) @@ -4632,18 +4599,13 @@ png_do_quantize(png_row_infop row_info, png_bytep row, * decide how it fits in with the other transformations here. */ void /* PRIVATE */ -png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) +png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info_in) { + png_transform_control display; + png_debug(1, "in png_do_read_transformations"); - if (png_ptr->row_buf == NULL) - { - /* Prior to 1.5.4 this output row/pass where the NULL pointer is, but this - * error is incredibly rare and incredibly easy to debug without this - * information. - */ - png_error(png_ptr, "NULL row buffer"); - } + affirm(png_ptr->row_buf != NULL); /* The following is debugging; prior to 1.5.4 the code was never compiled in; * in 1.5.4 PNG_FLAG_DETECT_UNINITIALIZED was added and the macro @@ -4658,47 +4620,37 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) * png_read_update_info() after setting transforms that expand pixels. * This check added to libpng-1.2.19 (but not enabled until 1.5.4). */ - png_error(png_ptr, "Uninitialized row"); + png_error(png_ptr, "missing png_read_start_image or update_info"); } + /* Ok, it looks genuine, set up the control structure from the supplied + * row_info. + */ + png_init_transform_control(png_ptr, &display, row_info_in); + #ifdef PNG_READ_EXPAND_SUPPORTED if ((png_ptr->transformations & PNG_EXPAND) != 0) { - if (row_info->color_type == PNG_COLOR_TYPE_PALETTE) - { - png_do_expand_palette(row_info, png_ptr->row_buf + 1, - png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans); - } + if (display.flags & PNG_INDEXED) + png_do_expand_palette(&display, png_ptr->row_buf + 1); else - { - if (png_ptr->num_trans != 0 && - (png_ptr->transformations & PNG_EXPAND_tRNS) != 0) - png_do_expand(row_info, png_ptr->row_buf + 1, - &(png_ptr->trans_color)); - - else - png_do_expand(row_info, png_ptr->row_buf + 1, - NULL); - } + png_do_expand(&display, png_ptr->row_buf + 1); } #endif #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0 && (png_ptr->transformations & PNG_COMPOSE) == 0 && - (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA || - row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) - png_do_strip_channel(row_info, png_ptr->row_buf + 1, + (display.channels == 4 || display.channels == 2)) + png_do_strip_channel(&display, png_ptr->row_buf + 1, 0 /* at_start == false, because SWAP_ALPHA happens later */); #endif #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0) { - int rgb_error = - png_do_rgb_to_gray(png_ptr, row_info, - png_ptr->row_buf + 1); + int rgb_error = png_do_rgb_to_gray(&display, png_ptr->row_buf + 1); if (rgb_error != 0) { @@ -4751,13 +4703,13 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) */ if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0 && (png_ptr->flags & PNG_FLAG_BACKGROUND_IS_GRAY) == 0) - png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1); + png_do_gray_to_rgb(&display, png_ptr->row_buf + 1); #endif #if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ defined(PNG_READ_ALPHA_MODE_SUPPORTED) if ((png_ptr->transformations & PNG_COMPOSE) != 0) - png_do_compose(row_info, png_ptr->row_buf + 1, png_ptr); + png_do_compose(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_GAMMA_SUPPORTED @@ -4770,36 +4722,40 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) defined(PNG_READ_ALPHA_MODE_SUPPORTED) /* Because PNG_COMPOSE does the gamma transform if there is something to * do (if there is an alpha channel or transparency.) + * WARNING: prior to 1.7.0 this was checking png_ptr->color_type, which + * probably means that the gamma would get dropped if the alpha + * channel was stripped yet PNG_COMPOSE was also set. */ !((png_ptr->transformations & PNG_COMPOSE) != 0 && - ((png_ptr->num_trans != 0) || - (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)) && + (png_ptr->num_trans != 0 || + display.channels == 2 || display.channels == 4)) && #endif /* Because png_init_read_transformations transforms the palette, unless - * RGB_TO_GRAY will do the transform. + * RGB_TO_GRAY will do the transform. Note that this does need to check + * the original color type because the expand_palette call preceeds this + * check. */ (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) - png_do_gamma(row_info, png_ptr->row_buf + 1, png_ptr); + png_do_gamma(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0 && (png_ptr->transformations & PNG_COMPOSE) != 0 && - (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA || - row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) - png_do_strip_channel(row_info, png_ptr->row_buf + 1, + (display.channels == 2 || display.channels == 4)) + png_do_strip_channel(&display, png_ptr->row_buf + 1, 0 /* at_start == false, because SWAP_ALPHA happens later */); #endif #ifdef PNG_READ_ALPHA_MODE_SUPPORTED if ((png_ptr->transformations & PNG_ENCODE_ALPHA) != 0 && - (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0) - png_do_encode_alpha(row_info, png_ptr->row_buf + 1, png_ptr); + (display.channels == 2 || display.channels == 4)) + png_do_encode_alpha(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED if ((png_ptr->transformations & PNG_SCALE_16_TO_8) != 0) - png_do_scale_16_to_8(row_info, png_ptr->row_buf + 1); + png_do_scale_16_to_8(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED @@ -4808,18 +4764,12 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) * calling the API or in a TRANSFORM flag) this is what happens. */ if ((png_ptr->transformations & PNG_16_TO_8) != 0) - png_do_chop(row_info, png_ptr->row_buf + 1); + png_do_chop(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_QUANTIZE_SUPPORTED if ((png_ptr->transformations & PNG_QUANTIZE) != 0) - { - png_do_quantize(row_info, png_ptr->row_buf + 1, - png_ptr->palette_lookup, png_ptr->quantize_index); - - if (row_info->rowbytes == 0) - png_error(png_ptr, "png_do_quantize returned rowbytes=0"); - } + png_do_quantize(&display, png_ptr->row_buf + 1); #endif /* READ_QUANTIZE */ #ifdef PNG_READ_EXPAND_16_SUPPORTED @@ -4829,79 +4779,81 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) * better accuracy results faster!) */ if ((png_ptr->transformations & PNG_EXPAND_16) != 0) - png_do_expand_16(row_info, png_ptr->row_buf + 1); + png_do_expand_16(&display, png_ptr->row_buf + 1); #endif #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) != 0 && (png_ptr->flags & PNG_FLAG_BACKGROUND_IS_GRAY) != 0) - png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1); + png_do_gray_to_rgb(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_INVERT_SUPPORTED if ((png_ptr->transformations & PNG_INVERT_MONO) != 0) - png_do_invert(row_info, png_ptr->row_buf + 1); + png_do_invert(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_INVERT_ALPHA_SUPPORTED if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0) - png_do_read_invert_alpha(row_info, png_ptr->row_buf + 1); + png_do_read_invert_alpha(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_SHIFT_SUPPORTED if ((png_ptr->transformations & PNG_SHIFT) != 0) - png_do_unshift(row_info, png_ptr->row_buf + 1, - &(png_ptr->shift)); + png_do_unshift(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_PACK_SUPPORTED if ((png_ptr->transformations & PNG_PACK) != 0) - png_do_unpack(row_info, png_ptr->row_buf + 1); + png_do_unpack(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED /* Added at libpng-1.5.10 */ - if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && - png_ptr->num_palette_max >= 0) - png_do_check_palette_indexes(png_ptr, row_info); + if ((display.flags & PNG_INDEXED) != 0 && png_ptr->num_palette_max >= 0) + png_do_check_palette_indexes(png_ptr, &display); #endif #ifdef PNG_READ_BGR_SUPPORTED if ((png_ptr->transformations & PNG_BGR) != 0) - png_do_bgr(row_info, png_ptr->row_buf + 1); + png_do_bgr(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_PACKSWAP_SUPPORTED if ((png_ptr->transformations & PNG_PACKSWAP) != 0) - png_do_packswap(row_info, png_ptr->row_buf + 1); + png_do_packswap(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_FILLER_SUPPORTED if ((png_ptr->transformations & PNG_FILLER) != 0) - png_do_read_filler(row_info, png_ptr->row_buf + 1, - (png_uint_32)png_ptr->filler, png_ptr->flags); + png_do_read_filler(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_SWAP_ALPHA_SUPPORTED if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0) - png_do_read_swap_alpha(row_info, png_ptr->row_buf + 1); + png_do_read_swap_alpha(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_READ_16BIT_SUPPORTED #ifdef PNG_READ_SWAP_SUPPORTED if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0) - png_do_swap(row_info, png_ptr->row_buf + 1); + png_do_swap(&display, png_ptr->row_buf + 1); #endif #endif + /* The user transform expects a png_row_info, and it would be inconvenient + * to change this. + */ + png_end_transform_control(row_info_in, &display); + #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0) { if (png_ptr->read_user_transform_fn != NULL) (*(png_ptr->read_user_transform_fn)) /* User read transform function */ (png_ptr, /* png_ptr */ - row_info, /* row_info: */ + row_info_in, /* row_info: */ /* png_uint_32 width; width of row */ /* png_size_t rowbytes; number of bytes in row */ /* png_byte color_type; color type of pixels */ @@ -4909,17 +4861,19 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) /* png_byte channels; number of channels (1-4) */ /* png_byte pixel_depth; bits per pixel (depth*channels) */ png_ptr->row_buf + 1); /* start of pixel data for row */ + #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED if (png_ptr->user_transform_depth != 0) - row_info->bit_depth = png_ptr->user_transform_depth; + row_info_in->bit_depth = png_ptr->user_transform_depth; if (png_ptr->user_transform_channels != 0) - row_info->channels = png_ptr->user_transform_channels; + row_info_in->channels = png_ptr->user_transform_channels; #endif - row_info->pixel_depth = png_check_byte(png_ptr, row_info->bit_depth * - row_info->channels); + row_info_in->pixel_depth = png_check_byte(png_ptr, + row_info_in->bit_depth * row_info_in->channels); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_info->width); + row_info_in->rowbytes = + PNG_ROWBYTES(row_info_in->pixel_depth, row_info_in->width); } #endif } diff --git a/pngrutil.c b/pngrutil.c index 9884bab1f..0bc5ea838 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -4179,7 +4179,7 @@ png_read_start_row(png_structrp png_ptr) static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; #endif - int max_pixel_depth; + unsigned int max_pixel_depth; png_size_t row_bytes; png_debug(1, "in png_read_start_row"); @@ -4344,7 +4344,7 @@ png_read_start_row(png_structrp png_ptr) defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) if (png_ptr->transformations & PNG_USER_TRANSFORM) { - int user_pixel_depth = png_ptr->user_transform_depth * + unsigned int user_pixel_depth = png_ptr->user_transform_depth * png_ptr->user_transform_channels; if (user_pixel_depth > max_pixel_depth) @@ -4355,7 +4355,7 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) /* This value is stored in png_struct and double checked in the row read * code. */ - png_ptr->maximum_pixel_depth = png_check_byte(png_ptr, max_pixel_depth); + png_ptr->maximum_pixel_depth = max_pixel_depth; png_ptr->transformed_pixel_depth = 0; /* calculated on demand */ /* Align the width on the next larger 8 pixels. Mainly used diff --git a/pngstruct.h b/pngstruct.h index 2b28f04b1..52b4869ae 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -165,11 +165,16 @@ struct png_struct_def * and faster. */ png_colorp palette; /* palette from the input file */ + png_bytep trans_alpha; /* alpha values for paletted files */ + 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 */ + /* TODO: usr_width is used in write, iwidth is used in read, the two fields + * could be made one. + */ 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 */ @@ -181,7 +186,7 @@ struct png_struct_def png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */ png_uint_32 free_me; /* items libpng is responsible for freeing */ - int maximum_pixel_depth; /* pixel depth used for the row buffers */ + unsigned 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 */ #endif @@ -421,12 +426,6 @@ struct png_struct_def #endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ #endif /* READ_GAMMA */ -#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) diff --git a/pngtrans.c b/pngtrans.c index f9ead7c40..5997bef8d 100644 --- a/pngtrans.c +++ b/pngtrans.c @@ -259,80 +259,89 @@ png_set_invert_mono(png_structrp png_ptr) /* Invert monochrome grayscale data */ void /* PRIVATE */ -png_do_invert(png_row_infop row_info, png_bytep row) +png_do_invert(png_transform_controlp row_info, png_bytep row) { png_debug(1, "in png_do_invert"); +# define png_ptr row_info->png_ptr + /* This test removed from libpng version 1.0.13 and 1.2.0: * if (row_info->bit_depth == 1 && */ - if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + if (row_info->channels == 1) { - png_bytep rp = row; - png_size_t i; - png_size_t istop = row_info->rowbytes; + if (!(row_info->flags & PNG_INDEXED)) /* GRAY */ + { + png_bytep rp = row + png_transform_rowbytes(row_info); - for (i = 0; i < istop; i++) - *rp++ ^= 0xff; + /* Don't care about the bit depth: */ + while (rp > row) + *--rp ^= 0xff; + + row_info->flags |= PNG_INVERTED; + } } - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && - row_info->bit_depth == 8) + else if (row_info->channels == 2) /* GRAY ALPHA */ { - png_bytep rp = row; - png_size_t i; - png_size_t istop = row_info->rowbytes; + if (row_info->bit_depth == 8) + { + png_bytep rp; - for (i = 0; i < istop; i += 2) - *rp ^= 0xff, rp += 2; + row_info->flags |= PNG_INVERTED; + rp = row + png_transform_rowbytes(row_info); + + /* Go backwards, so rp[-1] is alpha and rp[-2] is gray: */ + while (rp >= row+2) + rp -= 2, *rp ^= 0xff; + } + +# ifdef PNG_16BIT_SUPPORTED + else if (row_info->bit_depth == 16) + { + png_bytep rp; + + row_info->flags |= PNG_INVERTED; + rp = row + png_transform_rowbytes(row_info); + + /* The same, but now we have GGAA: */ + while (rp >= row+4) + rp -= 3, *rp ^= 0xff, *--rp ^= 0xff; + } +# endif } - -#ifdef PNG_16BIT_SUPPORTED - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && - row_info->bit_depth == 16) - { - png_bytep rp = row; - png_size_t i; - png_size_t istop = row_info->rowbytes; - - for (i = 0; i < istop; i += 4) - *rp++ ^= 0xff, *rp++ ^= 0xff, rp += 2; - } -#endif +# undef png_ptr } -#endif +#endif /* READ_INVERT || WRITE_INVERT */ #ifdef PNG_16BIT_SUPPORTED #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) /* Swaps byte order on 16 bit depth images */ void /* PRIVATE */ -png_do_swap(png_row_infop row_info, png_bytep row) +png_do_swap(png_transform_controlp row_info, png_bytep row) { png_debug(1, "in png_do_swap"); +# define png_ptr row_info->png_ptr + if (row_info->bit_depth == 16) { - png_bytep rp = row; - png_uint_32 i; - png_uint_32 istop= row_info->width * row_info->channels; + png_bytep rp; - for (i = 0; i < istop; i++, rp += 2) + row_info->flags |= PNG_BYTE_SWAPPED; + rp = row + png_transform_rowbytes(row_info); + + while (rp >= row+2) { -#ifdef PNG_BUILTIN_BSWAP16_SUPPORTED - /* Feature added to libpng-1.6.11 for testing purposes, not - * enabled by default. - */ - *(png_uint_16*)rp = __builtin_bswap16(*(png_uint_16*)rp); -#else - png_byte t = *rp; - *rp = *(rp + 1); - *(rp + 1) = t; -#endif + png_byte save = *--rp; + *rp = rp[-1], --rp; + *rp = save; } } +# undef png_ptr } -#endif -#endif +#endif /* READ_SWAP || WRITE_SWAP */ +#endif /* 16_BIT */ #if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) static PNG_CONST png_byte onebppswaptable[256] = { @@ -442,16 +451,16 @@ static PNG_CONST png_byte fourbppswaptable[256] = { /* Swaps pixel packing order within bytes */ void /* PRIVATE */ -png_do_packswap(png_row_infop row_info, png_bytep row) +png_do_packswap(png_transform_controlp row_info, png_bytep row) { png_debug(1, "in png_do_packswap"); +# define png_ptr row_info->png_ptr + if (row_info->bit_depth < 8) { - png_bytep rp; - png_const_bytep end, table; - - end = row + row_info->rowbytes; + png_bytep ep; + png_const_bytep table; if (row_info->bit_depth == 1) table = onebppswaptable; @@ -465,11 +474,15 @@ png_do_packswap(png_row_infop row_info, png_bytep row) else return; - for (rp = row; rp < end; rp++) - *rp = table[*rp]; + row_info->flags |= PNG_PIXEL_SWAPPED; + ep = row + png_transform_rowbytes(row_info); + + while (row < ep) + *row = table[*row], ++row; } +# undef png_ptr } -#endif /* PACKSWAP || WRITE_PACKSWAP */ +#endif /* READ_PACKSWAP || WRITE_PACKSWAP */ #if defined(PNG_WRITE_FILLER_SUPPORTED) || \ defined(PNG_READ_STRIP_ALPHA_SUPPORTED) @@ -482,11 +495,14 @@ png_do_packswap(png_row_infop row_info, png_bytep row) * end (not in the middle) of each pixel. */ void /* PRIVATE */ -png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start) +png_do_strip_channel(png_transform_controlp row_info, png_bytep row, + int at_start) { - png_bytep sp = row; /* source pointer */ - png_bytep dp = row; /* destination pointer */ - png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */ + png_const_bytep sp = row; /* source pointer */ + png_bytep dp = row; /* destination pointer */ + png_const_bytep ep = row + png_transform_rowbytes(row_info); /* beyond end */ + +# define png_ptr row_info->png_ptr /* At the start sp will point to the first byte to copy and dp to where * it is copied to. ep always points just beyond the end of the row, so @@ -509,8 +525,6 @@ png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start) /* For a 1 pixel wide image there is nothing to do */ while (sp < ep) *dp++ = *sp, sp += 2; - - row_info->pixel_depth = 8; } else if (row_info->bit_depth == 16) @@ -522,18 +536,13 @@ png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start) while (sp < ep) *dp++ = *sp++, *dp++ = *sp, sp += 3; - - row_info->pixel_depth = 16; } else return; /* bad bit depth */ row_info->channels = 1; - - /* Finally fix the color type if it records an alpha channel */ - if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - row_info->color_type = PNG_COLOR_TYPE_GRAY; + debug(dp == row + png_transform_rowbytes(row_info)); } /* RGBA, RGBX, XRGB cases */ @@ -549,8 +558,6 @@ png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start) /* Note that the loop adds 3 to dp and 4 to sp each time. */ while (sp < ep) *dp++ = *sp++, *dp++ = *sp++, *dp++ = *sp, sp += 2; - - row_info->pixel_depth = 24; } else if (row_info->bit_depth == 16) @@ -567,104 +574,80 @@ png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start) *dp++ = *sp++, *dp++ = *sp++; *dp++ = *sp++, *dp++ = *sp, sp += 3; } - - row_info->pixel_depth = 48; } else return; /* bad bit depth */ row_info->channels = 3; - - /* Finally fix the color type if it records an alpha channel */ - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - row_info->color_type = PNG_COLOR_TYPE_RGB; + debug(dp == row + png_transform_rowbytes(row_info)); } else return; /* The filler channel has gone already */ - - /* Fix the rowbytes value. */ - row_info->rowbytes = dp-row; +# undef png_ptr } -#endif +#endif /* WRITE_FILLER || READ_STRIP_ALPHA */ #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) /* Swaps red and blue bytes within a pixel */ void /* PRIVATE */ -png_do_bgr(png_row_infop row_info, png_bytep row) +png_do_bgr(png_transform_controlp row_info, png_bytep row) { + unsigned int channels; + png_debug(1, "in png_do_bgr"); - if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0) +# define png_ptr row_info->png_ptr + + channels = row_info->channels; + + if (channels == 3 || channels == 4) { - png_uint_32 row_width = row_info->width; + png_const_bytep ep = row + png_transform_rowbytes(row_info); + if (row_info->bit_depth == 8) { - if (row_info->color_type == PNG_COLOR_TYPE_RGB) - { - png_bytep rp; - png_uint_32 i; + ep -= channels; /* Last pixel */ + row_info->flags ^= PNG_RGB_SWAPPED; - for (i = 0, rp = row; i < row_width; i++, rp += 3) - { - png_byte save = *rp; - *rp = *(rp + 2); - *(rp + 2) = save; - } + while (row <= ep) + { + png_byte save = row[0]; + row[0] = row[2]; + row[2] = save; + row += channels; } - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - { - png_bytep rp; - png_uint_32 i; - - for (i = 0, rp = row; i < row_width; i++, rp += 4) - { - png_byte save = *rp; - *rp = *(rp + 2); - *(rp + 2) = save; - } - } + debug(row == ep+channels); } -#ifdef PNG_16BIT_SUPPORTED - else if (row_info->bit_depth == 16) - { - if (row_info->color_type == PNG_COLOR_TYPE_RGB) +# ifdef PNG_16BIT_SUPPORTED + else if (row_info->bit_depth == 16) { - png_bytep rp; - png_uint_32 i; + channels *= 2; /* now in bytes */ - for (i = 0, rp = row; i < row_width; i++, rp += 6) + ep -= channels; /* Last pixel */ + row_info->flags |= PNG_RGB_SWAPPED; + + while (row <= ep) { - png_byte save = *rp; - *rp = *(rp + 4); - *(rp + 4) = save; - save = *(rp + 1); - *(rp + 1) = *(rp + 5); - *(rp + 5) = save; - } - } + png_byte save = row[0]; + row[0] = row[4]; + row[4] = save; - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - { - png_bytep rp; - png_uint_32 i; + save = row[1]; + row[1] = row[5]; + row[5] = save; - for (i = 0, rp = row; i < row_width; i++, rp += 8) - { - png_byte save = *rp; - *rp = *(rp + 4); - *(rp + 4) = save; - save = *(rp + 1); - *(rp + 1) = *(rp + 5); - *(rp + 5) = save; + row += channels; } + + debug(row == ep+channels); } - } -#endif +# endif } +# undef png_ptr } #endif /* READ_BGR || WRITE_BGR */ @@ -672,19 +655,24 @@ png_do_bgr(png_row_infop row_info, png_bytep row) defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) /* Added at libpng-1.5.10 */ void /* PRIVATE */ -png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info) +png_do_check_palette_indexes(png_structrp png_ptr, + png_transform_controlp row_info) { if (png_ptr->num_palette < (1 << row_info->bit_depth) && png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */ { - /* Calculations moved outside switch in an attempt to stop different - * compiler warnings. 'padding' is in *bits* within the last byte, it is - * an 'int' because pixel_depth becomes an 'int' in the expression below, - * and this calculation is used because it avoids warnings that other - * forms produced on either GCC or MSVC. + /* Padding is the unused bits in the last byte: 8 - bits-in-last-byte, + * which reduces to 7 & (-total_bits), so we don't care about overflow + * in the unsigned calculation here: + */ + unsigned int padding = + 7 & -(row_info->bit_depth * row_info->channels * row_info->width); + png_bytep rp = png_ptr->row_buf + png_transform_rowbytes(row_info); + + /* Note that png_ptr->row_buf starts with a filter byte, so rp is + * currently pointing to the last byte in the row, not just after + * it. */ - int padding = (-row_info->pixel_depth * row_info->width) & 7; - png_bytep rp = png_ptr->row_buf + row_info->rowbytes; switch (row_info->bit_depth) { @@ -771,6 +759,136 @@ png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info) } #endif /* CHECK_FOR_INVALID_INDEX */ +#if defined(PNG_READ_TRANSFORMS_SUPPORTED) ||\ + defined(PNG_WRITE_TRANSFORMS_SUPPORTED) +/* Utility functions: */ +void +png_init_transform_control(png_const_structrp png_ptr, + png_transform_controlp out, png_const_row_infop row_info) +{ + out->png_ptr = png_ptr; + + /* At the start expect row_info to be consistent with png_ptr: */ + if (png_ptr->mode & PNG_IS_READ_STRUCT) + { + debug(png_ptr->iwidth == row_info->width); + debug(png_ptr->color_type == row_info->color_type); + debug(png_ptr->bit_depth == row_info->bit_depth); + } + + out->width = row_info->width; + out->flags = 0; + out->bit_depth = row_info->bit_depth; + + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_GRAY: + out->channels = 1; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + out->channels = 2; + break; + + case PNG_COLOR_TYPE_PALETTE: + affirm(!(png_ptr->mode & PNG_IS_READ_STRUCT) || + png_ptr->palette != NULL); + out->flags |= PNG_INDEXED; + out->channels = 1; + break; + + case PNG_COLOR_TYPE_RGB: + out->channels = 3; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + out->channels = 4; + break; + + default: + impossible("invalid PNG color type"); + } +} + +size_t +png_transform_rowbytes(png_const_transform_controlp row_info) +{ +# define png_ptr row_info->png_ptr + /* For this not to overflow the pixel depth calculation must not overflow + * and the pixel depth must be less than maximum_pixel_depth. + */ + /* The release code uses PNG_ROWBYTES, so make sure that it will not + * overflow. To test this it is necessary to generate some very wide + * images and ensure that the code errors out before getting here. + */ + unsigned int channels = row_info->channels; + unsigned int bit_depth = row_info->bit_depth; + unsigned int pixel_bits = channels * bit_depth; + size_t width = row_info->width; + + affirm(bit_depth < 256 && channels < 256 && + pixel_bits <= png_ptr->maximum_pixel_depth); + + return PNG_ROWBYTES(pixel_bits, width); +# undef png_ptr +} + +static unsigned int +transform_color_type(png_const_transform_controlp row_info) +{ + const unsigned int ch = row_info->channels - 1; + const unsigned int indexed = row_info->flags & PNG_INDEXED; + + /* That is 0, 1, 2, 3 for G/PALETTE, GA, RGB, RGBA. Check the + * numbers: + */ +# if PNG_INDEXED != PNG_COLOR_MASK_PALETTE ||\ + PNG_FILLER_IN_ALPHA != PNG_COLOR_MASK_ALPHA ||\ + PNG_COLOR_MASK_PALETTE != 1 ||\ + PNG_COLOR_MASK_COLOR != 2 ||\ + PNG_COLOR_MASK_ALPHA != 4 +# error Unexpected PNG color type defines +# endif + + /* The following preserves all the bits in row_info->channels except the + * top bit and generates a correct PNG color type for the defined values + * and an incorrect one for all undefined cases. + * + * Note that when PNG_FILLER_IN_ALPHA is set in the flags + */ + return indexed /*PALETTE*/ | + ((ch & 2) ^ (indexed << 1) /*COLOR*/) | + (((ch & 1) << 2/*ALPHA*/) & ~row_info->flags) | + ((ch & ~3) << 1 /*INVALID*/); +} + +void +png_end_transform_control(png_row_infop out, png_const_transform_controlp in) +{ +# define png_ptr in->png_ptr /* for affirm/impossible */ + out->width = in->width; + out->rowbytes = png_transform_rowbytes(in); + + out->color_type = png_check_byte(png_ptr, transform_color_type(in)); + out->bit_depth = png_check_byte(png_ptr, in->bit_depth); + out->channels = png_check_byte(png_ptr, in->channels); + out->pixel_depth = png_check_byte(png_ptr, in->channels * in->bit_depth); + +# ifdef PNG_WARNINGS_SUPPORTED + if ((in->flags & PNG_BAD_INDEX) != 0) + png_warning(png_ptr, "palette image had bad index"); +# endif + + /* At the end expect row_info to be consistent with png_ptr: */ + if (!(png_ptr->mode & PNG_IS_READ_STRUCT)) + { + debug(png_ptr->color_type == out->color_type); + debug(png_ptr->bit_depth == out->bit_depth); + } +# undef png_ptr +} +#endif /* READ_TRANSFORMS | WRITE_TRANSFORMS */ + #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED diff --git a/pngwrite.c b/pngwrite.c index 7110b5596..78053adae 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -882,7 +882,12 @@ png_write_row(png_structrp png_ptr, png_const_bytep row) /* Check for out-of-range palette index */ if (row_info.color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_palette_max >= 0) - png_do_check_palette_indexes(png_ptr, &row_info); + { + png_transform_control display; + + png_init_transform_control(png_ptr, &display, &row_info); + png_do_check_palette_indexes(png_ptr, &display); + } #endif /* Find a filter if necessary, filter the row and write it out. */ diff --git a/pngwtran.c b/pngwtran.c index d8a11f2a4..dc4c99f07 100644 --- a/pngwtran.c +++ b/pngwtran.c @@ -17,160 +17,111 @@ #ifdef PNG_WRITE_SUPPORTED #ifdef PNG_WRITE_TRANSFORMS_SUPPORTED -/* This is here because png_row_info doesn't contain a png_ptr, so at present - * the transform routines can't signal an error. Instead we pass '0' as - * as png_ptr to png_check_byte in the non-release cases and do a hard cast - * in release. - * - * TODO: fix this. - */ -#ifdef PNG_RANGE_CHECK_SUPPORTED -# define CB(b) png_check_byte(0, b) -# define CU(u) png_check_u16(0, u) -#else -# define CB(b) ((png_byte)(b)) -# define CU(u) ((png_uint_16)(u)) -#endif - #ifdef PNG_WRITE_PACK_SUPPORTED -/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The +/* Pack pixels into bytes. Get the true bit depth from png_ptr. The * row_info bit depth should be 8 (one pixel per byte). The channels * should be 1 (this only happens on grayscale and paletted images). */ static void -png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +png_do_pack(png_transform_controlp row_info, png_bytep row) { png_debug(1, "in png_do_pack"); - if (row_info->bit_depth == 8 && - row_info->channels == 1) +# define png_ptr row_info->png_ptr + + /* The comment suggests the following must be true. + * TODO: test this. + */ + affirm(row_info->bit_depth == 8 && row_info->channels == 1); + { - switch ((int)bit_depth) + switch (png_ptr->bit_depth) { case 1: { - png_bytep sp, dp; - int mask, v; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + png_const_bytep ep = row + png_transform_rowbytes(row_info); + png_bytep dp = row; + unsigned int mask = 0x80, v = 0; - sp = row; - dp = row; - mask = 0x80; - v = 0; - - for (i = 0; i < row_width; i++) + while (row < ep) { - if (*sp != 0) + if (*row++ != 0) v |= mask; - sp++; + mask >>= 1; - if (mask > 1) - mask >>= 1; - - else + if (mask == 0) { mask = 0x80; - *dp = CB(v); - dp++; + *dp++ = (png_byte)/*SAFE*/v; v = 0; } } if (mask != 0x80) - *dp = CB(v); + *dp++ = (png_byte)/*SAFE*/v; + row_info->bit_depth = 1; break; } case 2: { - png_bytep sp, dp; - int shift, v; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + png_const_bytep ep = row + png_transform_rowbytes(row_info); + png_bytep dp = row; + unsigned int shift = 8, v = 0; - sp = row; - dp = row; - shift = 6; - v = 0; - - for (i = 0; i < row_width; i++) + while (row < ep) { - png_byte value; - - value = PNG_BYTE(*sp & 0x03); - v |= (value << shift); + shift -= 2; + v |= (*row++ & 0x3) << shift; if (shift == 0) { - shift = 6; - *dp = CB(v); - dp++; + shift = 8; + *dp++ = png_check_byte(png_ptr, v); v = 0; } - - else - shift -= 2; - - sp++; } - if (shift != 6) - *dp = CB(v); + if (shift != 8) + *dp++ = png_check_byte(png_ptr, v); + row_info->bit_depth = 2; break; } case 4: { - png_bytep sp, dp; - int shift, v; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + png_const_bytep ep = row + png_transform_rowbytes(row_info); + png_bytep dp = row; + unsigned int shift = 8, v = 0; - sp = row; - dp = row; - shift = 4; - v = 0; - - for (i = 0; i < row_width; i++) + while (row < ep) { - png_byte value; - - value = PNG_BYTE(*sp & 0x0f); - v |= (value << shift); + shift -= 4; + v |= ((*row++ & 0xf) << shift); if (shift == 0) { - shift = 4; - *dp = CB(v); - dp++; + shift = 8; + *dp++ = png_check_byte(png_ptr, v); v = 0; } - - else - shift -= 4; - - sp++; } - if (shift != 4) - *dp = CB(v); + if (shift != 8) + *dp++ = png_check_byte(png_ptr, v); + row_info->bit_depth = 4; break; } default: break; } - - row_info->bit_depth = CB(bit_depth); - row_info->pixel_depth = CB(bit_depth * row_info->channels); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, - row_info->width); } +# undef png_ptr } #endif @@ -181,19 +132,26 @@ png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) * bit depth 4, but the pixels only had values from 0 to 7, you * would pass 3 as bit_depth, and this routine would translate the * data to 0 to 15. + * + * NOTE: this is horrible complexity for no value. Once people suggested they + * were selling 16-bit displays with 5:6:5 bits spread R:G:B but so far as I + * could determine these displays produced intermediate grey (uncolored) colors, + * which is impossible with a true 5:6:5, so most likely 5:6:5 was marketing. */ static void -png_do_shift(png_row_infop row_info, png_bytep row, - png_const_color_8p bit_depth) +png_do_shift(png_transform_controlp row_info, png_bytep row) { png_debug(1, "in png_do_shift"); - if (row_info->color_type != PNG_COLOR_TYPE_PALETTE) +# define png_ptr row_info->png_ptr + + if (!(row_info->flags & PNG_INDEXED) && (row_info->channels-1) <= 3) { + png_const_color_8p bit_depth = &png_ptr->shift; int shift_start[4], shift_dec[4]; int channels = 0; - if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0) + if (row_info->channels == 3 || row_info->channels == 4) { shift_start[channels] = row_info->bit_depth - bit_depth->red; shift_dec[channels] = bit_depth->red; @@ -208,27 +166,29 @@ png_do_shift(png_row_infop row_info, png_bytep row, channels++; } - else + else /* 1 or 2 channels */ { shift_start[channels] = row_info->bit_depth - bit_depth->gray; shift_dec[channels] = bit_depth->gray; channels++; } - if ((row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0) + if (row_info->channels == 2 || row_info->channels == 4) { shift_start[channels] = row_info->bit_depth - bit_depth->alpha; shift_dec[channels] = bit_depth->alpha; channels++; } - /* With low row depths, could only be grayscale, so one channel */ + /* With low res depths, could only be grayscale, so one channel */ if (row_info->bit_depth < 8) { png_bytep bp = row; png_size_t i; unsigned int mask; - png_size_t row_bytes = row_info->rowbytes; + size_t row_bytes = png_transform_rowbytes(row_info); + + affirm(row_info->channels == 1); if (bit_depth->gray == 1 && row_info->bit_depth == 2) mask = 0x55; @@ -256,7 +216,7 @@ png_do_shift(png_row_infop row_info, png_bytep row, out |= (v >> (-j)) & mask; } - *bp = CB(out); + *bp = png_check_byte(png_ptr, out); } } @@ -285,7 +245,7 @@ png_do_shift(png_row_infop row_info, png_bytep row, out |= v >> (-j); } - *bp = CB(out); + *bp = png_check_byte(png_ptr, out); } } @@ -312,200 +272,174 @@ png_do_shift(png_row_infop row_info, png_bytep row, else value |= v >> (-j); } - *bp++ = CB(value >> 8); + *bp++ = png_check_byte(png_ptr, value >> 8); *bp++ = PNG_BYTE(value); } } } + +# undef png_ptr } #endif #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED static void -png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) +png_do_write_swap_alpha(png_transform_controlp row_info, png_bytep row) { png_debug(1, "in png_do_write_swap_alpha"); +# define png_ptr row_info->png_ptr { - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + if (row_info->channels == 4) { if (row_info->bit_depth == 8) { - /* This converts from ARGB to RGBA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + png_const_bytep ep = row + png_transform_rowbytes(row_info) - 4; - for (i = 0, sp = dp = row; i < row_width; i++) + /* This converts from ARGB to RGBA */ + while (row <= ep) { - png_byte save = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = save; + png_byte save = row[0]; + row[0] = row[1]; + row[1] = row[2]; + row[2] = row[3]; + row[3] = save; + row += 4; } + + debug(row == ep+4); } #ifdef PNG_WRITE_16BIT_SUPPORTED - else + else if (row_info->bit_depth == 16) { /* This converts from AARRGGBB to RRGGBBAA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + png_const_bytep ep = row + png_transform_rowbytes(row_info) - 8; - for (i = 0, sp = dp = row; i < row_width; i++) + while (row <= ep) { - png_byte save[2]; - save[0] = *(sp++); - save[1] = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = save[0]; - *(dp++) = save[1]; + png_byte s0 = row[0]; + png_byte s1 = row[1]; + memmove(row, row+2, 6); + row[6] = s0; + row[7] = s1; + row += 8; } + + debug(row == ep+8); } #endif /* WRITE_16BIT */ } - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + else if (row_info->channels == 2) { if (row_info->bit_depth == 8) { /* This converts from AG to GA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + png_const_bytep ep = row + png_transform_rowbytes(row_info) - 2; - for (i = 0, sp = dp = row; i < row_width; i++) + /* This converts from ARGB to RGBA */ + while (row <= ep) { - png_byte save = *(sp++); - *(dp++) = *(sp++); - *(dp++) = save; + png_byte save = *row; + *row = row[1], ++row; + *row++ = save; } + + debug(row == ep+2); } #ifdef PNG_WRITE_16BIT_SUPPORTED else { /* This converts from AAGG to GGAA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + png_const_bytep ep = row + png_transform_rowbytes(row_info) - 4; - for (i = 0, sp = dp = row; i < row_width; i++) + while (row <= ep) { - png_byte save[2]; - save[0] = *(sp++); - save[1] = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = save[0]; - *(dp++) = save[1]; + png_byte save = row[0]; + row[0] = row[2]; + row[2] = save; + + save = row[1]; + row[1] = row[3]; + row[3] = save; + + row += 4; } + + debug(row == ep+4); } #endif /* WRITE_16BIT */ } } + +# undef png_ptr } #endif #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED static void -png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) +png_do_write_invert_alpha(png_transform_controlp row_info, png_bytep row) { png_debug(1, "in png_do_write_invert_alpha"); +# define png_ptr row_info->png_ptr { - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + if (row_info->channels == 4) { if (row_info->bit_depth == 8) { /* This inverts the alpha channel in RGBA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + png_const_bytep ep = row + png_transform_rowbytes(row_info) - 1; - for (i = 0, sp = dp = row; i < row_width; i++) - { - /* Does nothing - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - */ - sp+=3; dp = sp; - *(dp++) = CB(255 - *(sp++)); - } + row += 3; /* alpha channel */ + while (row <= ep) + *row ^= 0xff, row += 4; } #ifdef PNG_WRITE_16BIT_SUPPORTED - else + else if (row_info->bit_depth == 16) { /* This inverts the alpha channel in RRGGBBAA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + png_const_bytep ep = row + png_transform_rowbytes(row_info) - 2; - for (i = 0, sp = dp = row; i < row_width; i++) - { - /* Does nothing - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - */ - sp+=6; dp = sp; - *(dp++) = CB(255 - *(sp++)); - *(dp++) = CB(255 - *(sp++)); - } + row += 6; + + while (row <= ep) + row[0] ^= 0xff, row[1] ^= 0xff, row += 8; } #endif /* WRITE_16BIT */ } - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + else if (row_info->channels == 2) { if (row_info->bit_depth == 8) { /* This inverts the alpha channel in GA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + png_const_bytep ep = row + png_transform_rowbytes(row_info) - 1; - for (i = 0, sp = dp = row; i < row_width; i++) - { - *(dp++) = *(sp++); - *(dp++) = CB(255 - *(sp++)); - } + ++row; + + while (row <= ep) + *row ^= 0xff, row += 2; } #ifdef PNG_WRITE_16BIT_SUPPORTED else { /* This inverts the alpha channel in GGAA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + png_const_bytep ep = row + png_transform_rowbytes(row_info) - 2; - for (i = 0, sp = dp = row; i < row_width; i++) - { - /* Does nothing - *(dp++) = *(sp++); - *(dp++) = *(sp++); - */ - sp+=2; dp = sp; - *(dp++) = CB(255 - *(sp++)); - *(dp++) = CB(255 - *(sp++)); - } + row += 2; + + while (row <= ep) + row[0] ^= 0xff, row[1] ^= 0xff, row += 4; } #endif /* WRITE_16BIT */ } } +# undef png_ptr } #endif @@ -513,8 +447,10 @@ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) * transformations is significant. */ void /* PRIVATE */ -png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info) +png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info_in) { + png_transform_control display; + png_debug(1, "in png_do_write_transformations"); if (png_ptr == NULL) @@ -526,7 +462,7 @@ png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info) (*(png_ptr->write_user_transform_fn)) /* User write transform function */ (png_ptr, /* png_ptr */ - row_info, /* row_info: */ + row_info_in, /* row_info: */ /* png_uint_32 width; width of row */ /* png_size_t rowbytes; number of bytes in row */ /* png_byte color_type; color type of pixels */ @@ -536,55 +472,65 @@ png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info) png_ptr->row_buf + 1); /* start of pixel data for row */ #endif + png_init_transform_control(png_ptr, &display, row_info_in); + #ifdef PNG_WRITE_FILLER_SUPPORTED if ((png_ptr->transformations & PNG_FILLER) != 0) - png_do_strip_channel(row_info, png_ptr->row_buf + 1, + png_do_strip_channel(&display, png_ptr->row_buf + 1, !(png_ptr->flags & PNG_FLAG_FILLER_AFTER)); #endif #ifdef PNG_WRITE_PACKSWAP_SUPPORTED if ((png_ptr->transformations & PNG_PACKSWAP) != 0) - png_do_packswap(row_info, png_ptr->row_buf + 1); + png_do_packswap(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_WRITE_PACK_SUPPORTED if ((png_ptr->transformations & PNG_PACK) != 0) - png_do_pack(row_info, png_ptr->row_buf + 1, - (png_uint_32)png_ptr->bit_depth); + png_do_pack(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_WRITE_SWAP_SUPPORTED # ifdef PNG_16BIT_SUPPORTED if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0) - png_do_swap(row_info, png_ptr->row_buf + 1); + png_do_swap(&display, png_ptr->row_buf + 1); # endif #endif #ifdef PNG_WRITE_SHIFT_SUPPORTED if ((png_ptr->transformations & PNG_SHIFT) != 0) - png_do_shift(row_info, png_ptr->row_buf + 1, - &(png_ptr->shift)); + png_do_shift(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0) - png_do_write_swap_alpha(row_info, png_ptr->row_buf + 1); + png_do_write_swap_alpha(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0) - png_do_write_invert_alpha(row_info, png_ptr->row_buf + 1); + png_do_write_invert_alpha(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_WRITE_BGR_SUPPORTED if ((png_ptr->transformations & PNG_BGR) != 0) - png_do_bgr(row_info, png_ptr->row_buf + 1); + png_do_bgr(&display, png_ptr->row_buf + 1); #endif #ifdef PNG_WRITE_INVERT_SUPPORTED if ((png_ptr->transformations & PNG_INVERT_MONO) != 0) - png_do_invert(row_info, png_ptr->row_buf + 1); + png_do_invert(&display, png_ptr->row_buf + 1); #endif + + /* Clear the flags; they are irrelevant because the write code is + * reversing transformations to get PNG data but the shared transformation + * code assumes input PNG data. Only PNG_INDEXED is required. + */ + if ((display.flags & PNG_BAD_INDEX) != 0) + png_error(png_ptr, "palette data has out of range index"); + + display.flags &= PNG_INDEXED; + png_end_transform_control(row_info_in, &display); } #endif /* WRITE_TRANSFORMS */ #endif /* WRITE */ diff --git a/pngwutil.c b/pngwutil.c index 05aa4e05f..4167d4876 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -2006,7 +2006,7 @@ png_write_start_row(png_structrp png_ptr) #endif png_alloc_size_t buf_size; - int usr_pixel_depth; + unsigned int usr_pixel_depth; png_debug(1, "in png_write_start_row"); @@ -2018,7 +2018,7 @@ png_write_start_row(png_structrp png_ptr) /* 1.5.6: added to allow checking in the row write code. */ png_ptr->transformed_pixel_depth = png_ptr->pixel_depth; - png_ptr->maximum_pixel_depth = png_check_byte(png_ptr, usr_pixel_depth); + png_ptr->maximum_pixel_depth = usr_pixel_depth; /* Set up row buffer */ png_ptr->row_buf = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size));