diff --git a/example.c b/example.c index f333e0542..75cc174c9 100644 --- a/example.c +++ b/example.c @@ -524,7 +524,7 @@ void read_png(FILE *fp, int sig_read) /* File is already open */ /* Add filler (or alpha) byte (before/after each RGB triplet) */ png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); -#ifdef PNG_READ_INTERLACING_SUPPORTED +#ifdef PNG_READ_DEINTERLACE_SUPPORTED /* Turn on interlace handling. REQUIRED if you are not using * png_read_image(). To see how to handle interlacing passes, * see the png_read_row() method below: @@ -532,7 +532,7 @@ void read_png(FILE *fp, int sig_read) /* File is already open */ number_passes = png_set_interlace_handling(png_ptr); #else number_passes = 1; -#endif /* READ_INTERLACING */ +#endif /* READ_DEINTERLACE */ /* Optional call to gamma correct and add the background to the palette @@ -715,7 +715,7 @@ row_callback(png_structp png_ptr, png_bytep new_row, */ png_bytep old_row = ((png_bytep *)our_data)[row_num]; -#ifdef PNG_READ_INTERLACING_SUPPORTED +#ifdef PNG_READ_DEINTERLACE_SUPPORTED /* If both rows are allocated then copy the new row * data to the corresponding row data. */ @@ -744,7 +744,7 @@ row_callback(png_structp png_ptr, png_bytep new_row, * to pass the current row as new_row, and the function will combine * the old row and the new row. */ -#endif /* READ_INTERLACING */ +#endif /* READ_DEINTERLACE */ } end_callback(png_structp png_ptr, png_infop info) diff --git a/png.c b/png.c index f2f1456a0..5bf584539 100644 --- a/png.c +++ b/png.c @@ -424,7 +424,7 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, /* Free any tRNS entry */ if (((mask & PNG_FREE_TRNS) & info_ptr->free_me) != 0) { - info_ptr->valid &= ~PNG_INFO_tRNS; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS); png_free(png_ptr, info_ptr->trans_alpha); info_ptr->trans_alpha = NULL; info_ptr->num_trans = 0; @@ -439,7 +439,7 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, png_free(png_ptr, info_ptr->scal_s_height); info_ptr->scal_s_width = NULL; info_ptr->scal_s_height = NULL; - info_ptr->valid &= ~PNG_INFO_sCAL; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_sCAL); } #endif @@ -462,7 +462,7 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, png_free(png_ptr, info_ptr->pcal_params); info_ptr->pcal_params = NULL; } - info_ptr->valid &= ~PNG_INFO_pCAL; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_pCAL); } #endif @@ -474,7 +474,7 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, png_free(png_ptr, info_ptr->iccp_profile); info_ptr->iccp_name = NULL; info_ptr->iccp_profile = NULL; - info_ptr->valid &= ~PNG_INFO_iCCP; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_iCCP); } #endif @@ -504,7 +504,7 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, png_free(png_ptr, info_ptr->splt_palettes); info_ptr->splt_palettes = NULL; info_ptr->splt_palettes_num = 0; - info_ptr->valid &= ~PNG_INFO_sPLT; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_sPLT); } } #endif @@ -539,7 +539,7 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, { png_free(png_ptr, info_ptr->hist); info_ptr->hist = NULL; - info_ptr->valid &= ~PNG_INFO_hIST; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_hIST); } #endif @@ -548,7 +548,7 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, { png_free(png_ptr, info_ptr->palette); info_ptr->palette = NULL; - info_ptr->valid &= ~PNG_INFO_PLTE; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_PLTE); info_ptr->num_palette = 0; } @@ -565,12 +565,12 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, png_free(png_ptr, info_ptr->row_pointers); info_ptr->row_pointers = NULL; } - info_ptr->valid &= ~PNG_INFO_IDAT; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_IDAT); } #endif if (num != -1) - mask &= ~PNG_FREE_MUL; + mask &= PNG_BIC_MASK(PNG_FREE_MUL); info_ptr->free_me &= ~mask; } @@ -605,26 +605,31 @@ png_init_io(png_structrp png_ptr, png_FILE_p fp) if (png_ptr == NULL) return; - png_ptr->io_ptr = (png_voidp)fp; -} -# endif + if (png_ptr->rw_data_fn == NULL) + { +# ifdef PNG_READ_SUPPORTED + if (png_ptr->read_struct) + png_set_read_fn(png_ptr, fp, png_default_read_data); +# ifdef PNG_WRITE_SUPPORTED + else +# endif /* WRITE */ +# endif /* READ */ +# ifdef PNG_WRITE_SUPPORTED + if (!png_ptr->read_struct) + png_set_write_fn(png_ptr, fp, png_default_write_data, +# ifdef PNG_WRITE_FLUSH_SUPPORTED + png_default_flush +# else + NULL +# endif + ); +# endif /* WRITE */ + } -# ifdef PNG_SAVE_INT_32_SUPPORTED -/* The png_save_int_32 function assumes integers are stored in two's - * complement format. If this isn't the case, then this routine needs to - * be modified to write data in two's complement format. Note that, - * the following works correctly even if png_int_32 has more than 32 bits - * (compare the more complex code required on read for sign extension.) - */ -void PNGAPI -png_save_int_32(png_bytep buf, png_int_32 i) -{ - buf[0] = (png_byte)((i >> 24) & 0xff); - buf[1] = (png_byte)((i >> 16) & 0xff); - buf[2] = (png_byte)((i >> 8) & 0xff); - buf[3] = (png_byte)(i & 0xff); + else + png_ptr->io_ptr = fp; } -# endif +# endif /* STDIO */ # ifdef PNG_TIME_RFC1123_SUPPORTED /* Convert the supplied time into an RFC 1123 string suitable for use in @@ -828,18 +833,6 @@ png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name) */ return PNG_HANDLE_CHUNK_AS_DEFAULT; } - -#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\ - defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED) -int /* PRIVATE */ -png_chunk_unknown_handling(png_const_structrp png_ptr, png_uint_32 chunk_name) -{ - png_byte chunk_string[5]; - - PNG_CSTRING_FROM_CHUNK(chunk_string, chunk_name); - return png_handle_as_unknown(png_ptr, chunk_string); -} -#endif /* READ_UNKNOWN_CHUNKS || HANDLE_AS_UNKNOWN */ #endif /* SET_UNKNOWN_CHUNKS */ /* This function was added to libpng-1.0.7 */ @@ -938,13 +931,19 @@ png_colorspace_check_gamma(png_const_structrp png_ptr, * 0: the new gamma value is the libpng estimate for an ICC profile * 1: the new gamma value comes from a gAMA chunk * 2: the new gamma value comes from an sRGB chunk + * + * API CHANGE: libpng 1.7.0: prior to 1.7 the check below used the build-time + * constant PNG_GAMMA_THRESHOLD_FIXED and the results would therefore depend + * on a parameter that was intended for tuning the READ_GAMMA support. In + * 1.7 a fixed value of +/-1% is used instead; this reflects the fact that + * gamma values are rarely quoted to more than 2 decimal digits of precision. */ { png_fixed_point gtest; if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && (png_muldiv(>est, colorspace->gamma, PNG_FP_1, gAMA) == 0 || - png_gamma_significant(gtest) != 0)) + gtest < PNG_FP_1 - 1000 || gtest > PNG_FP_1 + 1000)) { /* Either this is an sRGB image, in which case the calculated gamma * approximation should match, or this is an image with a profile and the @@ -986,16 +985,28 @@ png_colorspace_set_gamma(png_const_structrp png_ptr, * In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk * handling code, which only required the value to be >0. */ - png_const_charp errmsg; +# define ERRMSG (defined PNG_TRANSFORM_MECH_SUPPORTED) &&\ + (defined PNG_ERROR_TEXT_SUPPORTED) +# if ERRMSG + png_const_charp errmsg; +# endif if (gAMA < 16 || gAMA > 625000000) - errmsg = "gamma value out of range"; + { +# if ERRMSG + errmsg = "gamma value out of range"; +# endif + } # ifdef PNG_READ_gAMA_SUPPORTED /* Allow the application to set the gamma value more than once */ - else if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + else if (png_ptr->read_struct && (colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0) + { +# if ERRMSG errmsg = "duplicate"; +# endif + } # endif /* Do nothing if the colorspace is already invalid */ @@ -1022,6 +1033,7 @@ png_colorspace_set_gamma(png_const_structrp png_ptr, } /* Error exit - errmsg has been set. */ +# undef ERRMSG colorspace->flags |= PNG_COLORSPACE_INVALID; png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR); } @@ -1032,7 +1044,7 @@ png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr) if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) { /* Everything is invalid */ - info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB| + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB| PNG_INFO_iCCP); # ifdef PNG_COLORSPACE_SUPPORTED @@ -1054,20 +1066,20 @@ png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr) info_ptr->valid |= PNG_INFO_sRGB; else - info_ptr->valid &= ~PNG_INFO_sRGB; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_sRGB); if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) info_ptr->valid |= PNG_INFO_cHRM; else - info_ptr->valid &= ~PNG_INFO_cHRM; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_cHRM); # endif if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0) info_ptr->valid |= PNG_INFO_gAMA; else - info_ptr->valid &= ~PNG_INFO_gAMA; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_gAMA); } } @@ -1085,6 +1097,25 @@ png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr) #endif #ifdef PNG_COLORSPACE_SUPPORTED +/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */ +static png_fixed_point +png_reciprocal(png_fixed_point a) +{ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = floor(1E10/a+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; +#else + png_fixed_point res; + + if (png_muldiv(&res, PNG_FP_1, PNG_FP_1, a) != 0) + return res; +#endif + + return 0; /* error/overflow */ +} + /* Added at libpng-1.5.5 to support read and write of true CIEXYZ values for * cHRM, as opposed to using chromaticities. These internal APIs return * non-zero on a parameter error. The X, Y and Z values are required to be @@ -1847,7 +1878,7 @@ png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, int /* PRIVATE */ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, png_const_charp name, png_uint_32 profile_length, - png_const_bytep profile/* first 132 bytes only */, int color_type) + png_const_bytep profile/* first 132 bytes only */, int is_color) { png_uint_32 temp; @@ -1934,13 +1965,13 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, switch (temp) { case 0x52474220: /* 'RGB ' */ - if ((color_type & PNG_COLOR_MASK_COLOR) == 0) + if (!is_color) return png_icc_profile_error(png_ptr, colorspace, name, temp, "RGB color space not permitted on grayscale PNG"); break; case 0x47524159: /* 'GRAY' */ - if ((color_type & PNG_COLOR_MASK_COLOR) != 0) + if (is_color) return png_icc_profile_error(png_ptr, colorspace, name, temp, "Gray color space not permitted on RGB PNG"); break; @@ -2270,14 +2301,14 @@ png_icc_set_sRGB(png_const_structrp png_ptr, int /* PRIVATE */ png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace, png_const_charp name, png_uint_32 profile_length, png_const_bytep profile, - int color_type) + int is_color) { if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) return 0; if (png_icc_check_length(png_ptr, colorspace, name, profile_length) != 0 && png_icc_check_header(png_ptr, colorspace, name, profile_length, profile, - color_type) != 0 && + is_color) != 0 && png_icc_check_tag_table(png_ptr, colorspace, name, profile_length, profile) != 0) { @@ -2292,84 +2323,40 @@ png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace, return 0; } #endif /* iCCP */ - -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -void /* PRIVATE */ -png_colorspace_set_rgb_coefficients(png_structrp png_ptr) -{ - /* Set the rgb_to_gray coefficients from the colorspace. */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_RGB_TO_GRAY_SET) == 0 && - (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) - { - /* png_set_background has not been called, get the coefficients from the Y - * values of the colorspace colorants. - */ - png_fixed_point r = png_ptr->colorspace.end_points_XYZ.red_Y; - png_fixed_point g = png_ptr->colorspace.end_points_XYZ.green_Y; - png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blue_Y; - png_fixed_point total = r+g+b; - - if (total > 0 && - r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 && - g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 && - b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 && - r+g+b <= 32769) - { - /* We allow 0 coefficients here. r+g+b may be 32769 if two or - * all of the coefficients were rounded up. Handle this by - * reducing the *largest* coefficient by 1; this matches the - * approach used for the default coefficients in pngrtran.c - */ - int add = 0; - - if (r+g+b > 32768) - add = -1; - else if (r+g+b < 32768) - add = 1; - - if (add != 0) - { - if (g >= r && g >= b) - g += add; - else if (r >= g && r >= b) - r += add; - else - b += add; - } - - /* Check for an internal error. */ - if (r+g+b != 32768) - impossible("error handling cHRM coefficients"); - - else - { - png_ptr->rgb_to_gray_red_coeff = png_check_u16(png_ptr, r); - png_ptr->rgb_to_gray_green_coeff = png_check_u16(png_ptr, g); - } - } - - /* This is a png_error at present even though it could be ignored - - * it should never happen, but it is important that if it does, the - * bug is fixed. - */ - else - impossible("error handling cHRM->XYZ"); - } -} -#endif /* READ_RGB_TO_GRAY */ - #endif /* COLORSPACE */ -#ifdef __GNUC__ -/* This exists solely to work round a warning from GNU C. */ -static int /* PRIVATE */ -png_gt(size_t a, size_t b) +png_alloc_size_t /* PRIVATE */ +png_calc_rowbytes(png_const_structrp png_ptr, unsigned int pixel_depth, + png_uint_32 row_width) { - return a > b; + png_alloc_size_t rowbytes = row_width; + + /* Carefully calculate the row buffer size. */ + if (pixel_depth > 8) + { + if ((pixel_depth & 7) != 0) + png_error(png_ptr, "unsupported pixel byte size"); + + pixel_depth >>= 3; /* Now in bytes */ + + if (rowbytes > PNG_SIZE_MAX/pixel_depth) + png_error(png_ptr, "image row exceeds system limits"); + + rowbytes *= pixel_depth; + } + + else /* Less than 1 byte per pixel */ switch (pixel_depth) + { + case 1: rowbytes += 7; rowbytes >>= 3; break; + case 2: rowbytes += 3; rowbytes >>= 2; break; + case 4: rowbytes += 1; rowbytes >>= 1; break; + case 8: break; + default: + png_error(png_ptr, "unsupported pixel bit size"); + } + + return rowbytes; } -#else -# define png_gt(a,b) ((a) > (b)) -#endif void /* PRIVATE */ png_check_IHDR(png_const_structrp png_ptr, @@ -2392,28 +2379,6 @@ png_check_IHDR(png_const_structrp png_ptr, error = 1; } - if (png_gt(((width + 7) & (~7)), - ((PNG_SIZE_MAX - - 48 /* big_row_buf hack */ - - 1) /* filter byte */ - / 8) /* 8-byte RGBA pixels */ - - 1)) /* extra max_pixel_depth pad */ - { - /* The size of the row must be within the limits of this architecture. - * Because the read code can perform arbitrary transformations the - * maximum size is checked here. Because the code in png_read_start_row - * adds extra space "for safety's sake" in several places a conservative - * limit is used here. - * - * NOTE: it would be far better to check the size that is actually used, - * but the effect in the real world is minor and the changes are more - * extensive, therefore much more dangerous and much more difficult to - * write in a way that avoids compiler warnings. - */ - png_warning(png_ptr, "Image width is too large for this architecture"); - error = 1; - } - #ifdef PNG_SET_USER_LIMITS_SUPPORTED if (width > png_ptr->user_width_max) #else @@ -2502,9 +2467,9 @@ png_check_IHDR(png_const_structrp png_ptr, (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && (color_type == PNG_COLOR_TYPE_RGB || - color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) { - png_warning(png_ptr, "Unknown filter method in IHDR"); + png_warning(png_ptr, "Invalid filter method in IHDR"); error = 1; } @@ -2515,16 +2480,23 @@ png_check_IHDR(png_const_structrp png_ptr, } } -#else +#else /* !MNG_FEATURES */ if (filter_type != PNG_FILTER_TYPE_BASE) { png_warning(png_ptr, "Unknown filter method in IHDR"); error = 1; } -#endif +#endif /* !MNG_FEATURES */ if (error == 1) png_error(png_ptr, "Invalid IHDR data"); + + /* Finally, if the IHDR data is correct, check it against the system + * limits (NOTE: this need not be done; the IDAT handling code repeats the + * check in both read and write.) + */ + (void)png_calc_rowbytes(png_ptr, + PNG_COLOR_TYPE_CHANNELS(color_type) * bit_depth, width); } #if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) @@ -3260,37 +3232,6 @@ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times, } #endif /* GAMMA || INCH_CONVERSIONS */ -#ifdef PNG_GAMMA_SUPPORTED /* more fixed point functions for gamma */ -/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */ -png_fixed_point -png_reciprocal(png_fixed_point a) -{ -#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED - double r = floor(1E10/a+.5); - - if (r <= 2147483647. && r >= -2147483648.) - return (png_fixed_point)r; -#else - png_fixed_point res; - - if (png_muldiv(&res, 100000, 100000, a) != 0) - return res; -#endif - - return 0; /* error/overflow */ -} - -/* This is the shared test on whether a gamma value is 'significant' - whether - * it is worth doing gamma correction. - */ -int /* PRIVATE */ -png_gamma_significant(png_fixed_point gamma_val) -{ - return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED || - gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED; -} -#endif - /* HARDWARE OPTION SUPPORT */ #ifdef PNG_SET_OPTION_SUPPORTED int PNGAPI @@ -3299,9 +3240,9 @@ png_set_option(png_structrp png_ptr, int option, int onoff) if (png_ptr != NULL && option >= 0 && option < PNG_OPTION_NEXT && (option & 1) == 0) { - int mask = 3 << option; - int setting = (2 + (onoff != 0)) << option; - int current = png_ptr->options; + unsigned int mask = 3U << option; + unsigned int setting = (2U + (onoff != 0)) << option; + unsigned int current = png_ptr->options; png_ptr->options = png_check_byte(png_ptr, (current & ~mask) | setting); @@ -3310,7 +3251,48 @@ png_set_option(png_structrp png_ptr, int option, int onoff) return PNG_OPTION_INVALID; } -#endif +#endif /* SET_OPTION */ + +/* SOFTWARE SETTING SUPPORT */ +#ifdef PNG_SETTING_SUPPORTED +png_int_32 PNGAPI +png_setting(png_structrp png_ptr, int setting, png_int_32 value) +{ + switch (setting) + { +# ifdef PNG_READ_GAMMA_SUPPORTED + case PNG_GAMMA_MINIMUM: + if (value < 0 || value > 0xFFFF) + value = PNG_GAMMA_THRESHOLD_FIXED; + { + png_int_32 old = png_ptr->gamma_threshold; + png_ptr->gamma_threshold = PNG_UINT_16(value); + return old; + } + break; + +#if 0 /*NYI*/ + case PNG_GAMMA_ACCURACY: + if (value < 0 || value > 1600) + value = PNG_DEFAULT_GAMMA_ACCURACY; + { + png_int_32 old = png_ptr->gamma_accuracy; + png_ptr->gamma_accuracy = value; + return old; + } + break; +#endif /*NYI*/ +# endif /* READ_GAMMA */ + + default: + break; + } + + PNG_UNUSED(png_ptr) + PNG_UNUSED(value) + return PNG_UNSUPPORTED_SETTING; +} +#endif /* SETTING */ /* sRGB support */ #if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ @@ -3501,13 +3483,13 @@ png_image_free_function(png_voidp argument) # ifdef PNG_STDIO_SUPPORTED if (cp->owned_file != 0) { - FILE *fp = png_voidcast(FILE*, cp->png_ptr->io_ptr); + FILE *fp = png_voidcast(FILE*, png_get_io_ptr(cp->png_ptr)); cp->owned_file = 0; /* Ignore errors here. */ if (fp != NULL) { - cp->png_ptr->io_ptr = NULL; + png_init_io(cp->png_ptr, NULL); (void)fclose(fp); } } @@ -3570,5 +3552,34 @@ png_image_error(png_imagep image, png_const_charp error_message) return 0; } +#ifdef PNG_STDIO_SUPPORTED +typedef struct +{ + png_structrp png_ptr; + png_FILE_p fp; +} +png_image_init_io_struct; + +static int +image_init_io(png_voidp display) +{ + png_image_init_io_struct *p = + png_voidcast(png_image_init_io_struct*, display); + + png_init_io(p->png_ptr, p->fp); + return 1; +} + +int /* PRIVATE */ +png_image_init_io(png_imagep image, png_FILE_p fp) +{ + png_image_init_io_struct s; + + s.png_ptr = image->opaque->png_ptr; + s.fp = fp; + + return png_safe_execute(image, image_init_io, &s); +} +#endif /* STDIO */ #endif /* SIMPLIFIED READ/WRITE */ #endif /* READ || WRITE */ diff --git a/png.h b/png.h index 91121bf5c..bfbde0ae4 100644 --- a/png.h +++ b/png.h @@ -568,9 +568,15 @@ * following macro tests for a negative number and generates the machine format * directly by portable arithmetic operations. The cost is that the argument * 'b1' is evaluated twice. + * + * NOTE: the 0x7fffffffU BIC is there to ensure that potential overflow in the + * cast does not occur. This fixes the case where 1's complement machines could + * be forced into an overflow by an invalid value in the stream and, therefore, + * potentially raise an arithmetic exception; the invalid value is converted to + * 0 and any resultant problems will be caught later in the libpng checking. */ #define PNG_S32(b1, b2, b3, b4) ((b1) & 0x80\ - ? -(png_int_32)((PNG_U32(b1, b2, b3, b4) ^ 0xffffffff) + 1)\ + ? -(png_int_32)(((PNG_U32(b1, b2, b3, b4)^0xffffffffU)+1U)&0x7fffffffU)\ : (png_int_32)PNG_U32(b1, b2, b3, b4)) /* Constants for known chunk types. @@ -892,9 +898,11 @@ typedef png_time * * png_timepp; */ typedef struct png_unknown_chunk_t { - png_byte name[5]; /* Textual chunk name with '\0' terminator */ - png_byte *data; /* Data, should not be modified on read! */ - size_t size; + png_byte *data; /* Data, should not be modified on read! */ + png_uint_32 size; /* Size of data, must not exceed 0x7fffffff. + * API CHANGE 1.7.0: changed from 'size_t' + */ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ /* On write 'location' must be set using the flag values listed below. * Notice that on read it is set by libpng however the values stored have @@ -902,7 +910,7 @@ typedef struct png_unknown_chunk_t * bitmask. On write set only one bit - setting multiple bits may cause the * chunk to be written in multiple places. */ - png_byte location; /* mode of operation at read time */ + png_byte location; /* mode of operation at read time */ } png_unknown_chunk; @@ -912,9 +920,9 @@ typedef png_unknown_chunk * * png_unknown_chunkpp; #endif /* Flag values for the unknown chunk location byte. */ -#define PNG_HAVE_IHDR 0x01 -#define PNG_HAVE_PLTE 0x02 -#define PNG_AFTER_IDAT 0x08 +#define PNG_HAVE_IHDR 0x01U +#define PNG_HAVE_PLTE 0x02U +#define PNG_AFTER_IDAT 0x08U /* Maximum positive integer used in PNG is (2^31)-1 */ #define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) @@ -931,12 +939,12 @@ typedef png_unknown_chunk * * png_unknown_chunkpp; /* These describe the color_type field in png_info. */ /* color type masks */ -#define PNG_COLOR_MASK_PALETTE 1 -#define PNG_COLOR_MASK_COLOR 2 -#define PNG_COLOR_MASK_ALPHA 4 +#define PNG_COLOR_MASK_PALETTE 1U +#define PNG_COLOR_MASK_COLOR 2U +#define PNG_COLOR_MASK_ALPHA 4U /* color types. Note that not all combinations are legal */ -#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_GRAY 0U #define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) #define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) #define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) @@ -1054,22 +1062,53 @@ typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); -/* The following callback receives png_uint_32 row_number, int pass for the - * png_bytep data of the row. When transforming an interlaced image the - * row number is the row number within the sub-image of the interlace pass, so - * the value will increase to the height of the sub-image (not the full image) - * then reset to 0 for the next pass. +/* WARNING: the API for this callback is poorly documented and produces + * unexpected results when dealing with interlaced images. For non-interlaced + * images the parameters are straightforward: * - * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to - * find the output pixel (x,y) given an interlaced sub-image pixel - * (row,col,pass). (See below for these macros.) + * next_row: a pointer to the transformed row read from the PNG input + * stream, it has png_get_image_width() pixels. + * row_y: the y ordinate of the image; 0..png_get_image_height()-1 + * pass: 0 + * + * For interlaced images if png_set_interlace_handling has been called (libpng + * does *not* call this itself) the parameters are the same except that the + * pass will be the pass in the range 0..6 (NOTE: one less than the PNG spec) + * and 'next_row' will be NULL if (and only if) the row does not contribute + * to the output in 'blocky' display mode. + * + * pass: 0..6 + * + * If 'next_row' is not NULL it is necessary for the application to combine the + * pixels with the output. This can most easily be done by calling + * png_progressive_combine_row(). Note that the 'next_row' data cannot be + * changed; even though the value is passed to png_progressive_combine_row the + * pointer is not used, it is just a flag , if it is NULL nothing will happen. + * + * If png_set_interlace_handling has not been called the callback only gets + * called for original PNG interlaced row: + * + * row_y: the y ordinate in the pass; 0..PNG_PASS_ROWS()-1 + * + * What is more if PNG_PASS_COLS() is 0 the entire pass will be skipped. The + * row data is not full width and there is no guarantee that the buffer passed + * in 'next_row' is able to accomodate the full width of output pixels, however + * 'next_row' will never be NULL. + * + * Use PNG_ROW_FROM_PASS_ROW(row_y, pass) and PNG_COL_FROM_PASS_COL(col, pass) + * to find the output pixel (x,y) given an interlaced sub-image pixel + * (row_y,col,pass). (See below for these macros.) + * + * Note that in this latter case if you want to do the 'blocky' display update + * method you have to work out all the details yourself with regard to which + * pixels to set for each row and whether to replicate it to the following + * rows of the image. */ -typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, - png_uint_32, int)); +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, + png_bytep next_row, png_uint_32 row_y, int pass)); #endif -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, png_bytep)); #endif @@ -1335,15 +1374,45 @@ PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED /* Reduce RGB to grayscale. */ -#define PNG_ERROR_ACTION_NONE 1 -#define PNG_ERROR_ACTION_WARN 2 -#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_ERROR_ACTION_NO_CHECK 0 +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 #define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, int error_action, double red, double green)) PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, int error_action, png_fixed_point red, png_fixed_point green)) + /* Convert RGB pixels to gray (CIE Y) values, the red and green value must be + * less than or equal to 1, if either is negative a set of defaults + * corresponding to the sRGB standard are used. + * + * The error action specifies whether to check for r==g==b in each pixel, if + * it is 0 (PNG_ERROR_ACTION_NO_CHECK; added in libpng 1.7.0) no check will + * be performed, otherwise a check is performed and the result can be + * retrieved using png_get_rgb_to_gray_status (which just returns a + * true if a non-gray pixel was encountered). + * + * Pass PNG_ERROR_ACTION_ERROR if you are confident that the image only + * contains gray pixels (you have already checked); the check is still + * performed but a very optimized code path is used for RGB to gray + * convertion. + * + * If you want to extract a single channel pass PNG_FP_1 for the coefficient + * for that channel and 0 for the rest (0 for both red and green to extract + * blue). + * + * NOTE: the default coefficients used if negative values are passed for red + * or green are based on the cHRM chunk if available, otherwise sRGB. The + * calculation returns the Y (luminance value) corresponding to the white + * point of the PNG. UNLESS THE WHITE POINT IS D50 THIS IS NOT A CIEXYZ Y + * VALUE. It is the luminance of the pixel perceived by a viewer completely + * adapted to the white point of the PNG, this may not be what you want + * because to interpret it you have to also record the white point of the + * PNG. To obtain CIEXYZ Y values read the cHRM chunk XYZ values and + * chromatically adapt them to D50 + */ PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp png_ptr)); @@ -1536,7 +1605,7 @@ PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p true_bits)); #endif -#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ +#if defined(PNG_READ_DEINTERLACE_SUPPORTED) || \ defined(PNG_WRITE_INTERLACING_SUPPORTED) /* Have the code handle the interlacing. Returns the number of passes. * MUST be called before png_read_update_info or png_start_read_image, @@ -1641,7 +1710,7 @@ PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, png_bytep display_row)); #endif -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_READ_IMAGE_SUPPORTED /* Read the whole image into memory at once. */ PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); #endif @@ -1710,13 +1779,28 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, * header file (zlib.h) for an explination of the compression functions. */ -#ifdef PNG_WRITE_SUPPORTED -/* Set the filtering method(s) used by libpng. Currently, the only valid - * value for "method" is 0. +#ifdef PNG_WRITE_FILTER_SUPPORTED +/* Control the filtering method(s) used by libpng for the write of subsequent + * rows of the image. The argument is either a single filter value (one of the + * PNG_FILTER_VALUE_ defines below), in which case that filter will be used on + * following rows, or a mask of filter values (logical or of the PNG_FILTER_ + * bit masks and follow PNG_FILTER_VALUE_*). + * + * The set of filters may be changed at any time, the new values will affect the + * next row written. + * + * Prior to 1.7.0 it was only possible to add the filters that use the previous + * row if at least one of them was selected at the start of the write. + * + * In 1.7.0 if a filter is added which causes the previous row to be required + * (anywhere in the interlace pass after row 0) the use of the filter will be + * delayed until the row after the next one. + * + * The 'method' must match that passed to png_set_IHDR; it cannot be changed. */ PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, int filters)); -#endif /* WRITE */ +#endif /* WRITE_FILTER */ /* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. * These defines match the values in the PNG specification. @@ -1750,13 +1834,13 @@ PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, #ifdef PNG_WRITE_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ -PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, - int heuristic_method, int num_weights, png_const_doublep filter_weights, - png_const_doublep filter_costs) PNG_DEPRECATED) -PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, +PNG_FP_EXPORT(68, PNG_DEPRECATED void, png_set_filter_heuristics, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_doublep filter_weights, png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, PNG_DEPRECATED void, png_set_filter_heuristics_fixed, (png_structrp png_ptr, int heuristic_method, int num_weights, png_const_fixed_point_p filter_weights, - png_const_fixed_point_p filter_costs) PNG_DEPRECATED) + png_const_fixed_point_p filter_costs)) #endif /* WRITE_WEIGHTED_FILTER */ /* Set the library compression level. Currently, valid values range from @@ -1909,8 +1993,8 @@ PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); /* This callback is called only for *unknown* chunks. If * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known * chunks to be treated as unknown, however in this case the callback must do - * any processing required by the chunk (e.g. by calling the appropriate - * png_set_ APIs.) + * any processing required by the chunk and this is not possible for any chunk + * that affects the image reading (e.g. PLTE, tRNS). * * There is no write support - on write, by default, all the chunks in the * 'unknown' list are written in the specified position. @@ -1926,6 +2010,11 @@ PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); * happens) so it was not possible to discard unknown chunk data if a * user callback was installed. * positive: The chunk was handled, libpng will ignore/discard it. + * + * WARNING: if this callback is set every chunk in the stream is temporarily + * read into a memory buffer. This has potential performance implications, + * particularly for small PNG images with large amounts of ancilliary + * information. */ PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); @@ -1968,7 +2057,7 @@ PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); */ PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); -#ifdef PNG_READ_INTERLACING_SUPPORTED +#ifdef PNG_READ_DEINTERLACE_SUPPORTED /* Function that combines rows. 'new_row' is a flag that should come from * the callback and be non-NULL if anything needs to be done; the library * stores its own version of the new data internally and ignores the passed @@ -1976,7 +2065,7 @@ PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); */ PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, png_bytep old_row, png_const_bytep new_row)); -#endif /* READ_INTERLACING */ +#endif /* READ_DEINTERLACE */ #endif /* PROGRESSIVE_READ */ PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, @@ -2039,16 +2128,18 @@ PNG_REMOVED(101, void, png_free_default, (png_const_structrp png_ptr, /* Fatal error in PNG image of libpng - can't continue */ PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, png_const_charp error_message), PNG_NORETURN); - -/* The same, but the chunk name is prepended to the error string. */ -PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, - png_const_charp error_message), PNG_NORETURN); - #else /* Fatal error in PNG image of libpng - can't continue */ PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); # define png_error(s1,s2) png_err(s1) -# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); +#else +# define png_chunk_error(s1,s2) png_error(s1,s2) #endif #ifdef PNG_WARNINGS_SUPPORTED @@ -2080,11 +2171,11 @@ PNG_EXPORT(109, void, png_set_benign_errors, (png_structrp png_ptr, int allowed)); #else # ifdef PNG_ALLOW_BENIGN_ERRORS -# define png_benign_error png_warning -# define png_chunk_benign_error png_chunk_warning +# define png_benign_error(pp,e) png_warning(pp,e) +# define png_chunk_benign_error(pp,e) png_chunk_warning(pp,e) # else -# define png_benign_error png_error -# define png_chunk_benign_error png_chunk_error +# define png_benign_error(pp,e) png_error(pp,e) +# define png_chunk_benign_error(pp,e) png_chunk_error(pp,e) # endif #endif @@ -2105,16 +2196,18 @@ PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 flag)); /* Returns number of bytes needed to hold a transformed row. */ -PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, +PNG_EXPORT(111, png_alloc_size_t, png_get_rowbytes, (png_const_structrp png_ptr, png_const_inforp info_ptr)); -#ifdef PNG_INFO_IMAGE_SUPPORTED +#ifdef PNG_READ_PNG_SUPPORTED /* Returns row_pointers, which is an array of pointers to scanlines that was * returned from png_read_png(). */ PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, png_const_inforp info_ptr)); +#endif +#ifdef PNG_WRITE_PNG_SUPPORTED /* Set row_pointers, which is an array of pointers to scanlines for use * by png_write_png(). */ @@ -2541,7 +2634,7 @@ PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, */ PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, png_const_bytep chunk_name)); -#endif +#endif /* SET_UNKNOWN_CHUNKS */ #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED PNG_EXPORT(174, void, png_set_unknown_chunks, (png_structrp png_ptr, @@ -2554,6 +2647,12 @@ PNG_EXPORT(174, void, png_set_unknown_chunks, (png_structrp png_ptr, * code won't be compiled on earlier versions you can rely on * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing * the correct thing. + * + * API CHANGE: in 1.7.0 this API will not work on read unless + * PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED is set; it may be necessary to change + * code to check the latter SUPPORTED flag. png_set_keep_unknown_chunks + * will issue a warning if it is asked to save a chunk and there is no read + * support. */ PNG_EXPORT(175, void, png_set_unknown_chunk_location, @@ -2570,17 +2669,15 @@ PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, png_inforp info_ptr, int mask)); -#ifdef PNG_INFO_IMAGE_SUPPORTED /* The "params" pointer is currently not used and is for future expansion. */ -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_READ_PNG_SUPPORTED PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, int transforms, png_voidp params)); #endif -#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_PNG_SUPPORTED PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, int transforms, png_voidp params)); #endif -#endif PNG_EXPORT(180, png_const_charp, png_get_copyright, (png_const_structrp png_ptr)); @@ -2707,7 +2804,7 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, #define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) /* Two macros to help evaluate the number of rows or columns in each - * pass. This is expressed as a shift - effectively log2 of the number or + * pass. This is expressed as a shift - effectively 3-log2 of the number or * rows or columns in each 8x8 tile of the original image. */ #define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) @@ -2747,6 +2844,25 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, #define PNG_COL_IN_INTERLACE_PASS(x, pass) \ ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) +/* Whether the pass is in the image at all, taking into account the full image + * width and height, evaluates 'pass' lots of times, but width and height at + * most once each. + */ +#define PNG_PASS_IN_IMAGE(width, height, pass)\ + ((height) > PNG_PASS_START_ROW(pass) && (width) > PNG_PASS_START_COL(pass)) + +/* A macro to find the last pass (in the range 0 to 6) given an image width and + * height. Then two macros two find whether a given image row or column which + * is prsent in the pass is the last row or column in the pass. Note that these + * macros return 'true' for earlier rows or columns of the image that are *not* + * in the pass. + */ +#define PNG_LAST_PASS(width, height) ((height) > 1 ? 6 : ((width) > 1 ? 5 : 0)) +#define PNG_LAST_PASS_ROW(y, pass, height)\ + ((y) + PNG_PASS_ROW_OFFSET(pass) >= (height)) +#define PNG_LAST_PASS_COL(x, pass, width)\ + ((x) + PNG_PASS_COL_OFFSET(pass) >= (width)) + #ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED /* With these routines we avoid an integer divide, which will be slower on * most machines. However, it does take more operations than the corresponding @@ -2802,10 +2918,17 @@ PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, /* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ #ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); -#endif -#ifdef PNG_SAVE_INT_32_SUPPORTED -PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); -#endif + +/* This becomes a macro in 1.7 because the old implementation was wrong; it + * failed to do the cast. ANSI C requires the cast to convert a negative number + * to the 2's complement form, so this just works: + */ +#define png_save_int_32(b, i) png_save_uint_32(b, i); +#endif /* WRITE_INT_FUNCTIONS */ + +/* Apps that used this will use the macro in 1.7. */ +PNG_REMOVED(206, void, png_save_int_32, (png_bytep buf, png_int_32 i), + PNG_DEPRECATED) /* Place a 16-bit number into a buffer in PNG byte order. * The parameter is declared unsigned int, not png_uint_16, @@ -2850,12 +2973,79 @@ PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); #ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED PNG_EXPORT(242, void, png_set_check_for_invalid_index, - (png_structrp png_ptr, int allowed)); -# ifdef PNG_GET_PALETTE_MAX_SUPPORTED + (png_structrp png_ptr, int enabled_if_greater_than_0)); + /* By default the check is enabled on both read and write, passing a value + * which is 0 or negative disables the check. Disabling the check also + * prevents the following API from working. + */ +#endif /* CHECK_FOR_INVALID_INDEX */ +#ifdef PNG_GET_PALETTE_MAX_SUPPORTED PNG_EXPORT(243, int, png_get_palette_max, (png_const_structrp png_ptr, png_const_inforp info_ptr)); -# endif -#endif /* CHECK_FOR_INVALID_INDEX */ + /* The info_ptr is not used, it may be NULL in 1.7.0 (not in earlier + * versions). If the information is not available because + * png_set_check_for_invalid_index disabled the check this API returns -1. + * Valid results can only be obtained after the complete image has been read, + * though it may be called at any time to get the result so far. + */ +#endif /* GET_PALETTE_MAX */ + +/* Memory format options; these return information about the layout of the + * transformed row using the Simplified API PNG_FORMAT_ values (see below for + * the #defines). + * + * These are only relevant if read or write transforms are supported; these + * may cause the memory format of pixel data to differ from that used in the + * PNG file itself. Nevertheless the APIs are supported regardless of whether + * transforms are applied; use these to consistently and safely determine the + * layout of the image in memory. + * + * Some of the same information can be obtained from png_info, however this + * does not record whether the byte or bit formats have been changed. + */ +PNG_EXPORT(245, unsigned int, png_memory_format, (png_structrp png_ptr)); + /* The in-memory format as a bitmask of PNG_FORMAT_FLAG_ values. All the + * flags listed below are used. If PNG_FORMAT_FLAG_INVALID is set the + * following caveats apply to the interpretation of PNG_FORMAT_FLAG_LINEAR: + * + * The gamma may differ from the sRGB (!LINEAR) or 1.0 (LINEAR). Call + * png_memory_gamma to find the correct value. + * + * The channel depth may differ from 8 (!LINEAR) or 16 (LINEAR). Call + * png_memory_channel_depth to find the correct value. + * + * It is only valid to call these APIS *after* either png_read_update_info + * or png_start_read_image on read or after the first row of an image has + * been written on write. + * + * To find the number of channels in each pixel from the returned value, + * 'fmt' use: + * + * PNG_FORMAT_CHANNELS(fmt) + */ + +PNG_EXPORT(246, unsigned int, png_memory_channel_depth, (png_structrp png_ptr)); + /* The actual depth of each channel in the image, to determine the full pixel + * depth (in bits) use: + * + * png_memory_channel_depth(pp) * PNG_FORMAT_CHANNELS(fmt) + */ + +#ifdef PNG_GAMMA_SUPPORTED +PNG_EXPORT(247, png_fixed_point, png_memory_gamma, (png_structrp png_ptr)); + /* The actual gamma of the image data, scaled by 100,000. This is the + * encoding gamma, e.g. 1/2.2 for sRGB. If the gamma is unknown this will + * return 0. + * + * On write this invariably returns 0; libpng does not change the gamma of + * the data on write. + * + * Note that this is not always the exact inverse of the 'screen gamma' + * passed to png_set_gamma; internal optimizations remove attempts to make + * small changes to the gamma value. This function returns the actual + * output value. + */ +#endif /* GAMMA */ /******************************************************************************* * Section 5: SIMPLIFIED API @@ -3015,20 +3205,79 @@ typedef struct #define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ #define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2 byte channels else 1 byte */ #define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ +#define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ + /* other bits RESERVED */ -#ifdef PNG_FORMAT_BGR_SUPPORTED -# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ -#endif +/* The PNG color type value can be derived from a format which repesents a valid + * PNG format using the following macro. Note that if any of the non-PNG + * format elements are use, such as BGR or AFIRST, the color type value that + * results does represent the number of channels in the format but may not + * represent their order or encoding. + * + * NOTE: the format can encode illegal PNG formats, such as a colormap with + * alpha or without color; these are legal simplified API formats which produce + * data that cannot be represented as PNG regardless of channel order or + * encoding. + * + * The macro below is the bit shift version, a multiplicative version which only + * evaluates 'f' once is: + * + * ((((((((f) * 0x111) & 0x128) * 0x3) & 0x130) * 0x5) >> 6) & 0x7) + */ +#define PNG_COLOR_TYPE_FROM_FORMAT(f)\ + ((((f) & (!((f) & PNG_FORMAT_FLAG_AFILLER))) << 2) |\ + (((f) & PNG_FORMAT_FLAG_COLOR) ) |\ + (((f) & PNG_FORMAT_FLAG_COLORMAP) >> 3)) -#ifdef PNG_FORMAT_AFIRST_SUPPORTED -# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ -#endif +/* The inverse: note that PNG_FORMAT_FLAG_LINEAR is not set by this macro and + * that there is no handling for a 'filler' channel, consequently the macro must + * only be used on genuine PNG color types, not the result of png_get_color_type + * after transforms have been applied to the original PNG data. + */ +#define PNG_FORMAT_FROM_COLOR_TYPE(c)\ + ((((c) & PNG_COLOR_MASK_ALPHA) >> 2) |\ + (((c) & PNG_COLOR_MASK_COLOR) ) |\ + (((c) & PNG_COLOR_MASK_PALETTE) << 3)) + +/* The following flags are not used by the simplified API but may be returned + * by png_memory_format. Presence of any of these flags means that the values + * in the image (in memory) cannot be handled 'normally'. + */ +#define PNG_FORMAT_FLAG_AFILLER 0x40U /* The 'alpha' channel is a filler: + * PNG_FORMAT_FLAG_ALPHA is set however the value in the alpha channel + * is not an alpha value and (therefore) cannot be used for alpha + * computations, it is just a filler value. PNG_COLOR_TYPE_FROM_FORMAT + * will return a color type *without* PNG_COLOR_MASK_ALPHA, however + * PNG_FORMAT_CHANNELS will return the correct number, including the + * filler channel. + */ +#define PNG_FORMAT_FLAG_SWAPPED 0x80U /* bytes or bits swapped: + * When the bit depth is 16 this means that the bytes within the + * components have been swapped, when the bit depth is less than 8 + * it means the pixels within the bytes have been swapped. It should + * not be set for 8-bit compononents (it is meaningless). */ +#define PNG_FORMAT_FLAG_RANGE 0x100U /* component range not 0..bit-depth: + * Low-bit-depth grayscale components have been unpacked into bytes + * without scaling, or RGB[A] pixels have been shifted back to the + * significant-bit range from the sBIT chunk or channels (currently + * alpha or gray) have been inverted. */ +#define PNG_FORMAT_FLAG_INVALID 0x8000U /* Invalid simplified API channel depth: + * For single channel grayscale and palette indexed image data PNG + * supports bit depths of 1, 2 or 4 bits per pixel (and per channel) + * packed into bytes. The simplified API macros will not work with + * these formats (the simplified API always uses 8 or 16-bit channels). + * In the simplified API 'linear' images always have 16-bit channels + * and non-linear images are always sRGB encoded. If the INVALID flag + * is set then this may not be true; it is necessary to check the + * memory format bit-depth and gamma separately. + */ /* Commonly used formats have predefined macros. * * First the single byte (sRGB) formats: */ -#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GRAY 0U #define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA #define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) #define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR @@ -3119,6 +3368,12 @@ typedef struct * color-mapped image. */ +#define PNG_FORMAT_CHANNELS(fmt) PNG_IMAGE_PIXEL_CHANNELS(fmt) + /* Synonym for the above for use with the result of png_get_memory_format. + * This exists to avoid confusion with the PNG_IMAGE_ macros which do not + * work on all possible results of png_get_memory_format. + */ + #define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) /* The size, in bytes, of each component in a pixel; 1 for a color-mapped @@ -3324,7 +3579,14 @@ PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, * to do this in user mode, in which case it is necessary to discover * the capabilities in an OS specific way. Such capabilities are * listed here when libpng has support for them and must be turned - * ON by the application if present. + * ON by the application if present. Check pnglibconf.h for options + * appropriate to your hardware. + * + * In general 'PNG_EXTENSIONS' controls hardware optimizations; these + * are not supported parts of libpng and, if there are problems with + * them, bugs should be ported to the implementers. Depending on the + * configuration it may not be possible to disable extensions at run + * time. * * SOFTWARE: sometimes software optimizations actually result in performance * decrease on some architectures or systems, or with some sets of @@ -3332,9 +3594,7 @@ PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, * selected at run time. */ #ifdef PNG_SET_OPTION_SUPPORTED -#ifdef PNG_ARM_NEON_API_SUPPORTED -# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ -#endif +#define PNG_EXTENSIONS 0 /* BOTH: enable or disable extensions */ #define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ #define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ #define PNG_OPTION_NEXT 6 /* Next option - numbers must be even */ @@ -3347,23 +3607,102 @@ PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, int onoff)); -#endif +#endif /* SET_OPTION */ + +/* Support for software run-time settings. + * + * These settings allow tuning of the parameters used internally by libpng to + * achieve either greater performance, lower memory utilization or greater + * accuracy in image processing operations. + * + * The parameter is a single png_int_32, the result is the previous setting or + * 0x8000000 if the setting is not supported.. + */ +#ifdef PNG_SETTING_SUPPORTED +PNG_EXPORT(248, png_int_32, png_setting, (png_structrp png_ptr, int setting, + png_int_32 value)); + +#define PNG_UNSUPPORTED_SETTING 0x80000000 + +/* Available settings: */ +#define PNG_GAMMA_MINIMUM 1 + /* SETTING: threshold below which gamma correction is not done, the default + * (set when the library is built) is PNG_GAMMA_THRESHOLD_FIXED, the + * parameter is a png_fixed_point number, the difference from PNG_FP_1 above + * which gamma correction will be performed. + * + * The value '153' is sufficient to maintain 1% accuracy in 16-bit linear + * calculations over a 655:1 range; over the maximum range possible with the + * 16-bit linear representation. Reasonable values are: + * + * 0: always do gamma correction, even if the gamma values are + * identical. The only point to this is to avoid a bug in the + * optimized (no gamma correction) code path, or for testing. + * 2: always do gamma correction if there is any significant + * difference. Notice that '1' will result in gamma correction in + * many cases when the screen gamma is the inverse of the encoding + * gamma because of inaccuracies in the representation of gamma. + * 153: do gamma correction if it is needed to maintain the accuracy of + * 16-bit linear calculations at 1% or below. + * 216: maintain 1% accuracy over a 100:1 dynamic range in 16-bit linear + * calculations. This matches the widely accepted numbers for human + * perception of differences within an image, however that doesn't + * mean that such high accuracy is required to avoid artefacts; such + * accuracy (such a low number) is only required if versions of the + * same image with and without gamma correction are to be compared + * visually. + * 5000: this is the default from libpng 1.6 and earlier. Using this + * produces changes in image contrast that are visible when suitable + * images are compared side-by-side however they are not obvious and + * it is inconceivable that a user would notice the change unless + * the user was very familiar with the image and the viewing + * environment. + * + * Values between 216 and 5000 produce varying very small changes in image + * contrast. Values above 10,000 (10%) produce noticeable increase or + * decrease in contrast which will probably change how the image is + * perceived. + */ +#if 0 /*NYI*/ +#define PNG_GAMMA_ACCURACY 2 + /* SETTING: controls the accuracy of the gamma calculations when the results + * are cached. The default is PNG_DEFAULT_GAMMA_ACCURACY. The number is 100 + * times the number of bits, 'b', used in the internal tables when the input + * is linear, permitted values are 0..1600 however '0' causes the caching to + * be skipped entirely (so gives maximum accuracy with no caching!) + * + * The accuracy in the linear domain for a value 'a' is: + * + * +/-(.5/2^a) + * + * so for the default-default of 665 this means the accuracy is +/-0.5% and + * this ensures that almost-equal input values do not differ by more than 1% + * in the output, meeting the accepted requirement for human vision. + * + * The default value has no effect on input narrower than 16 bits. For n-bit + * input the total table size is ((n-v)+1)*(2^v), where 'v' is a/gamma and + * 'gamma' is the gamma encoding of the input: + * + * n a gamma 'v' table size + * 8 6.65 .45455 14.6 256 + * 16 6.65 .45455 14.6 65536 + * 16 6.65 1.0 6.65 1280 + * 16 6 1.0 6 704 + * 16 5 1.0 5 384 + */ +#endif /*NYI*/ +#endif /* SETTING */ /******************************************************************************* * END OF HARDWARE OPTIONS ******************************************************************************/ -/* Maintainer: Put new public prototypes here ^, in libpng.3, and project - * defs - */ - - /* The last ordinal number (this is the *last* one already used; the next * one to use is one more than this.) Maintainer, remember to add an entry to * scripts/symbols.def as well. */ #ifdef PNG_EXPORT_LAST_ORDINAL - PNG_EXPORT_LAST_ORDINAL(244); + PNG_EXPORT_LAST_ORDINAL(248); #endif #ifdef __cplusplus diff --git a/pngdebug.h b/pngdebug.h index 6a01b106e..344ded2d8 100644 --- a/pngdebug.h +++ b/pngdebug.h @@ -36,13 +36,13 @@ /* These settings control the formatting of messages in png.c and pngerror.c */ /* Moved to pngdebug.h at 1.5.0 */ # ifndef PNG_LITERAL_SHARP -# define PNG_LITERAL_SHARP 0x23 +# define PNG_LITERAL_SHARP 0x23U # endif # ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET -# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b +# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5bU # endif # ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET -# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d +# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5dU # endif # ifndef PNG_STRING_NEWLINE # define PNG_STRING_NEWLINE "\n" diff --git a/pngerror.c b/pngerror.c index afd5110a1..e849d647c 100644 --- a/pngerror.c +++ b/pngerror.c @@ -366,8 +366,7 @@ png_benign_error(png_const_structrp png_ptr, png_const_charp error_message) if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0) { # ifdef PNG_READ_SUPPORTED - if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && - png_ptr->chunk_name != 0) + if (png_ptr->read_struct && png_ptr->chunk_name != 0) png_chunk_warning(png_ptr, error_message); else # endif @@ -377,8 +376,7 @@ png_benign_error(png_const_structrp png_ptr, png_const_charp error_message) else { # ifdef PNG_READ_SUPPORTED - if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && - png_ptr->chunk_name != 0) + if (png_ptr->read_struct && png_ptr->chunk_name != 0) png_chunk_error(png_ptr, error_message); else # endif @@ -421,10 +419,14 @@ png_app_error(png_const_structrp png_ptr, png_const_charp error_message) #if defined(PNG_WARNINGS_SUPPORTED) || \ (defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)) /* These utilities are used internally to build an error message that relates - * to the current chunk. The chunk name comes from png_ptr->chunk_name, - * which is used to prefix the message. The message is limited in length - * to 63 bytes. The name characters are output as hex digits wrapped in [] - * if the character is invalid. + * to the current chunk. The chunk name comes from png_ptr->chunk_name unless + * png_ptr->zowner is set in which case that is used in preference. This is + * used to prefix the message. The message is limited in length to 63 bytes. + * The name characters are output as hex digits wrapped in [] if the character + * is invalid. + * + * Using 'zowner' means that IDAT errors at the end of the IDAT stream are still + * reported as from the IDAT chunks. */ #define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) static PNG_CONST char png_digit[16] = { @@ -436,9 +438,12 @@ static void /* PRIVATE */ png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp error_message) { - png_uint_32 chunk_name = png_ptr->chunk_name; + png_uint_32 chunk_name = png_ptr->zowner; int iout = 0, ishift = 24; + if (chunk_name == 0) + chunk_name = png_ptr->chunk_name; + while (ishift >= 0) { int c = (int)(chunk_name >> ishift) & 0xff; @@ -530,17 +535,14 @@ png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp #endif /* READ */ void /* PRIVATE */ -png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error) +(png_chunk_report)(png_const_structrp png_ptr, png_const_charp message, + int error) { -# ifndef PNG_WARNINGS_SUPPORTED - PNG_UNUSED(message) -# endif - /* This is always supported, but for just read or just write it * unconditionally does the right thing. */ # if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED) - if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) + if (png_ptr->read_struct) # endif # ifdef PNG_READ_SUPPORTED @@ -548,13 +550,16 @@ png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error) if (error < PNG_CHUNK_ERROR) png_chunk_warning(png_ptr, message); - else + else if (error < PNG_CHUNK_FATAL) png_chunk_benign_error(png_ptr, message); + + else + png_chunk_error(png_ptr, message); } # endif # if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED) - else if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0) + else if (!png_ptr->read_struct) # endif # ifdef PNG_WRITE_SUPPORTED @@ -562,10 +567,17 @@ png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error) if (error < PNG_CHUNK_WRITE_ERROR) png_app_warning(png_ptr, message); - else + else if (error < PNG_CHUNK_FATAL) png_app_error(png_ptr, message); + + else + png_error(png_ptr, message); } # endif + +# ifndef PNG_ERROR_TEXT_SUPPORTED + PNG_UNUSED(message) +# endif } #ifdef PNG_ERROR_TEXT_SUPPORTED @@ -881,7 +893,7 @@ png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode) if (png_ptr != NULL) { png_ptr->flags &= - ((~(PNG_FLAG_STRIP_ERROR_NUMBERS | + ((PNG_BIC_MASK(PNG_FLAG_STRIP_ERROR_NUMBERS | PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); } } @@ -1106,8 +1118,8 @@ affirm_text(png_charp buffer, size_t bufsize, #endif /* AFFIRM_TEXT */ -PNG_FUNCTION(void, png_affirm,(png_const_structrp png_ptr, - param_deb(png_const_charp condition) unsigned int position), PNG_NORETURN) +PNG_FUNCTION(void,png_affirm,(png_const_structrp png_ptr, + param_deb(png_const_charp condition) unsigned int position),PNG_NORETURN) { # if PNG_AFFIRM_TEXT char buffer[512]; @@ -1154,6 +1166,17 @@ PNG_FUNCTION(void, png_affirm,(png_const_structrp png_ptr, /* The character/byte checking APIs. These do their own calls to png_affirm * because the caller provides the position. */ +unsigned int /* PRIVATE */ +png_bit_affirm(png_const_structrp png_ptr, unsigned int position, + unsigned int u, unsigned int bits) +{ + /* The following avoids overflow errors even if 'bits' is 16 or 32: */ + if (u <= (1U << bits)-1U) + return u; + + png_affirm(png_ptr, param_deb("(bit field) range") position); +} + char /* PRIVATE */ png_char_affirm(png_const_structrp png_ptr, unsigned int position, int c) { diff --git a/pngget.c b/pngget.c index d3bebd9a3..2737d3886 100644 --- a/pngget.c +++ b/pngget.c @@ -27,16 +27,18 @@ png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr, return(0); } -png_size_t PNGAPI +png_alloc_size_t PNGAPI png_get_rowbytes(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - return(info_ptr->rowbytes); + return png_calc_rowbytes(png_ptr, + PNG_FORMAT_CHANNELS(info_ptr->format) * info_ptr->bit_depth, + info_ptr->width); - return(0); + return 0; } -#ifdef PNG_INFO_IMAGE_SUPPORTED +#ifdef PNG_READ_PNG_SUPPORTED png_bytepp PNGAPI png_get_rows(png_const_structrp png_ptr, png_const_inforp info_ptr) { @@ -80,7 +82,8 @@ png_byte PNGAPI png_get_color_type(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - return info_ptr->color_type; + return png_check_byte(png_ptr, + PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format)); return (0); } @@ -317,7 +320,7 @@ png_get_y_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr) static png_uint_32 ppi_from_ppm(png_uint_32 ppm) { -#if 0 +#if 0 /*NOT USED*/ /* The conversion is *(2.54/100), in binary (32 digits): * .00000110100000001001110101001001 */ @@ -477,7 +480,7 @@ png_byte PNGAPI png_get_channels(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - return(info_ptr->channels); + return png_check_byte(png_ptr, PNG_FORMAT_CHANNELS(info_ptr->format)); return (0); } @@ -822,7 +825,7 @@ png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr, *bit_depth = info_ptr->bit_depth; if (color_type != NULL) - *color_type = info_ptr->color_type; + *color_type = PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format); if (compression_type != NULL) *compression_type = info_ptr->compression_type; @@ -833,16 +836,7 @@ png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr, if (interlace_type != NULL) *interlace_type = info_ptr->interlace_type; - /* This is redundant if we can be sure that the info_ptr values were all - * assigned in png_set_IHDR(). We do the check anyhow in case an - * application has ignored our advice not to mess with the members - * of info_ptr directly. - */ - png_check_IHDR(png_ptr, info_ptr->width, info_ptr->height, - info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, - info_ptr->compression_type, info_ptr->filter_type); - - return (1); + return 1; } #ifdef PNG_oFFs_SUPPORTED @@ -1080,7 +1074,7 @@ png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "tRNS"); - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + if ((info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) != 0) { if (trans_alpha != NULL) { @@ -1092,7 +1086,7 @@ png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr, *trans_color = &(info_ptr->trans_color); } - else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ + else /* if (info_ptr->format not colormapped */ { if (trans_color != NULL) { @@ -1132,7 +1126,7 @@ png_get_unknown_chunks(png_const_structrp png_ptr, png_inforp info_ptr, #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED png_byte PNGAPI -png_get_rgb_to_gray_status (png_const_structrp png_ptr) +png_get_rgb_to_gray_status(png_const_structrp png_ptr) { if (png_ptr) return png_ptr->rgb_to_gray_status; @@ -1157,7 +1151,7 @@ png_get_compression_buffer_size(png_const_structrp png_ptr) return 0; #ifdef PNG_WRITE_SUPPORTED - if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) + if (png_ptr->read_struct) #endif { #ifdef PNG_SEQUENTIAL_READ_SUPPORTED @@ -1217,18 +1211,4 @@ png_get_io_chunk_type (png_const_structrp png_ptr) return png_ptr->chunk_name; } #endif /* IO_STATE */ - -#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED -# ifdef PNG_GET_PALETTE_MAX_SUPPORTED -int PNGAPI -png_get_palette_max(png_const_structrp png_ptr, png_const_inforp info_ptr) -{ - if (png_ptr != NULL && info_ptr != NULL) - return png_ptr->num_palette_max; - - return (-1); -} -# endif -#endif - #endif /* READ || WRITE */ diff --git a/pnginfo.h b/pnginfo.h index c8c874dd1..04a2d9afd 100644 --- a/pnginfo.h +++ b/pnginfo.h @@ -11,7 +11,7 @@ * and license in png.h */ - /* png_info is a structure that holds the information in a PNG file so +/* png_info is a structure that holds the information in a PNG file so * that the application can find out the characteristics of the image. * If you are reading the file, this structure will tell you what is * in the PNG file. If you are writing the file, fill in the information @@ -57,25 +57,17 @@ struct png_info_def /* The following are necessary for every PNG file */ png_uint_32 width; /* width of image in pixels (from IHDR) */ png_uint_32 height; /* height of image in pixels (from IHDR) */ - png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */ - png_size_t rowbytes; /* bytes needed to hold an untransformed row */ + unsigned int valid; /* valid chunk data (see PNG_INFO_ in png.h) */ png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */ - png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */ - png_uint_16 num_trans; /* number of transparent palette color (tRNS) */ - png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */ - png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */ + unsigned int num_palette:9; /* number of color entries in "palette" (PLTE) */ + unsigned int num_trans:9; /* number of transparent palette color (tRNS) */ + unsigned int bit_depth:6; /* 1, 2, 4, 8, 16 or 32 bits/channel */ + unsigned int format:PNG_RF_BITS; /* row format; see png_struct.h */ /* The following three should have been named *_method not *_type */ png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */ png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */ png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ - /* The following are set by png_set_IHDR, called from the application on - * write, but the are never actually used by the write code. - */ - png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */ - png_byte pixel_depth; /* number of bits per pixel */ - png_byte spare_byte; /* to align the data, and for future use */ - #ifdef PNG_READ_SUPPORTED /* This is never set during write */ png_byte signature[8]; /* magic bytes read by libpng from start of file */ diff --git a/pngpread.c b/pngpread.c index 503f8e7f2..abf5d1d01 100644 --- a/pngpread.c +++ b/pngpread.c @@ -16,405 +16,8 @@ #ifdef PNG_PROGRESSIVE_READ_SUPPORTED -/* Push model modes */ -#define PNG_READ_SIG_MODE 0 -#define PNG_READ_CHUNK_MODE 1 -#define PNG_READ_IDAT_MODE 2 -#define PNG_READ_tEXt_MODE 4 -#define PNG_READ_zTXt_MODE 5 -#define PNG_READ_DONE_MODE 6 -#define PNG_READ_iTXt_MODE 7 -#define PNG_ERROR_MODE 8 - -#define PNG_PUSH_SAVE_BUFFER_IF_FULL \ -if (png_ptr->push_length + 4 > png_ptr->buffer_size) \ - { png_push_save_buffer(png_ptr); return; } -#define PNG_PUSH_SAVE_BUFFER_IF_LT(N) \ -if (png_ptr->buffer_size < N) \ - { png_push_save_buffer(png_ptr); return; } - -void PNGAPI -png_process_data(png_structrp png_ptr, png_inforp info_ptr, - png_bytep buffer, png_size_t buffer_size) -{ - if (png_ptr == NULL || info_ptr == NULL) - return; - - png_push_restore_buffer(png_ptr, buffer, buffer_size); - - while (png_ptr->buffer_size) - { - png_process_some_data(png_ptr, info_ptr); - } -} - -png_size_t PNGAPI -png_process_data_pause(png_structrp png_ptr, int save) -{ - if (png_ptr != NULL) - { - /* It's easiest for the caller if we do the save; then the caller doesn't - * have to supply the same data again: - */ - if (save != 0) - png_push_save_buffer(png_ptr); - else - { - /* This includes any pending saved bytes: */ - png_size_t remaining = png_ptr->buffer_size; - png_ptr->buffer_size = 0; - - /* So subtract the saved buffer size, unless all the data - * is actually 'saved', in which case we just return 0 - */ - if (png_ptr->save_buffer_size < remaining) - return remaining - png_ptr->save_buffer_size; - } - } - - return 0; -} - -png_uint_32 PNGAPI -png_process_data_skip(png_structrp png_ptr) -{ - /* TODO: Deprecate and remove this API. - * Somewhere the implementation of this seems to have been lost, - * or abandoned. It was only to support some internal back-door access - * to png_struct) in libpng-1.4.x. - */ - png_app_warning(png_ptr, -"png_process_data_skip is not implemented in any current version of libpng"); - return 0; -} - -/* What we do with the incoming data depends on what we were previously - * doing before we ran out of data... - */ -void /* PRIVATE */ -png_process_some_data(png_structrp png_ptr, png_inforp info_ptr) -{ - if (png_ptr == NULL) - return; - - switch (png_ptr->process_mode) - { - case PNG_READ_SIG_MODE: - { - png_push_read_sig(png_ptr, info_ptr); - break; - } - - case PNG_READ_CHUNK_MODE: - { - png_push_read_chunk(png_ptr, info_ptr); - break; - } - - case PNG_READ_IDAT_MODE: - { - png_push_read_IDAT(png_ptr); - break; - } - - default: - { - png_ptr->buffer_size = 0; - break; - } - } -} - -/* Read any remaining signature bytes from the stream and compare them with - * the correct PNG signature. It is possible that this routine is called - * with bytes already read from the signature, either because they have been - * checked by the calling application, or because of multiple calls to this - * routine. - */ -void /* PRIVATE */ -png_push_read_sig(png_structrp png_ptr, png_inforp info_ptr) -{ - unsigned int num_checked = png_ptr->sig_bytes; - unsigned int num_to_check = 8 - num_checked; - - if (png_ptr->buffer_size < num_to_check) - num_to_check = (int)/*SAFE*/png_ptr->buffer_size; - - png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), - num_to_check); - png_ptr->sig_bytes = png_check_byte(png_ptr, - png_ptr->sig_bytes + num_to_check); - - if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) - { - if (num_checked < 4 && - png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) - png_error(png_ptr, "Not a PNG file"); - - else - png_error(png_ptr, "PNG file corrupted by ASCII conversion"); - } - else - { - if (png_ptr->sig_bytes >= 8) - { - png_ptr->process_mode = PNG_READ_CHUNK_MODE; - } - } -} - -void /* PRIVATE */ -png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) -{ - png_uint_32 chunk_name; -#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED - int keep; /* unknown handling method */ -#endif - - /* First we make sure we have enough data for the 4-byte chunk name - * and the 4-byte chunk length before proceeding with decoding the - * chunk data. To fully decode each of these chunks, we also make - * sure we have enough data in the buffer for the 4-byte CRC at the - * end of every chunk (except IDAT, which is handled separately). - */ - if ((png_ptr->mode & PNG_HAVE_CHUNK_HEADER) == 0) - { - png_byte chunk_length[4]; - png_byte chunk_tag[4]; - - PNG_PUSH_SAVE_BUFFER_IF_LT(8) - png_push_fill_buffer(png_ptr, chunk_length, 4); - png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); - png_reset_crc(png_ptr); - png_crc_read(png_ptr, chunk_tag, 4); - png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); - png_check_chunk_name(png_ptr, png_ptr->chunk_name); - png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; - } - - chunk_name = png_ptr->chunk_name; - - if (chunk_name == png_IDAT) - { - if ((png_ptr->mode & PNG_AFTER_IDAT) != 0) - png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; - - /* If we reach an IDAT chunk, this means we have read all of the - * header chunks, and we can start reading the image (or if this - * is called after the image has been read - we have an error). - */ - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_error(png_ptr, "Missing IHDR before IDAT"); - - else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - (png_ptr->mode & PNG_HAVE_PLTE) == 0) - png_error(png_ptr, "Missing PLTE before IDAT"); - - png_ptr->mode |= PNG_HAVE_IDAT; - png_ptr->process_mode = PNG_READ_IDAT_MODE; - - if ((png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT) == 0) - if (png_ptr->push_length == 0) - return; - - if ((png_ptr->mode & PNG_AFTER_IDAT) != 0) - png_benign_error(png_ptr, "Too many IDATs found[p]"); - } - - if (chunk_name == png_IHDR) - { - if (png_ptr->push_length != 13) - png_error(png_ptr, "Invalid IHDR length"); - - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); - } - - else if (chunk_name == png_IEND) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); - - png_ptr->process_mode = PNG_READ_DONE_MODE; - png_push_have_end(png_ptr, info_ptr); - } - -#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED - else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, keep); - - if (chunk_name == png_PLTE) - png_ptr->mode |= PNG_HAVE_PLTE; - } -#endif - - else if (chunk_name == png_PLTE) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); - } - - else if (chunk_name == png_IDAT) - { - png_ptr->idat_size = png_ptr->push_length; - png_ptr->process_mode = PNG_READ_IDAT_MODE; - png_push_have_info(png_ptr, info_ptr); - png_ptr->zstream.avail_out = - (uInt) PNG_ROWBYTES(png_ptr->pixel_depth, - png_ptr->iwidth) + 1; - png_ptr->zstream.next_out = png_ptr->row_buf; - return; - } - -#ifdef PNG_READ_gAMA_SUPPORTED - else if (png_ptr->chunk_name == png_gAMA) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sBIT_SUPPORTED - else if (png_ptr->chunk_name == png_sBIT) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_cHRM_SUPPORTED - else if (png_ptr->chunk_name == png_cHRM) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sRGB_SUPPORTED - else if (chunk_name == png_sRGB) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_iCCP_SUPPORTED - else if (png_ptr->chunk_name == png_iCCP) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sPLT_SUPPORTED - else if (chunk_name == png_sPLT) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_tRNS_SUPPORTED - else if (chunk_name == png_tRNS) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_bKGD_SUPPORTED - else if (chunk_name == png_bKGD) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_hIST_SUPPORTED - else if (chunk_name == png_hIST) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_pHYs_SUPPORTED - else if (chunk_name == png_pHYs) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_oFFs_SUPPORTED - else if (chunk_name == png_oFFs) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); - } -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED - else if (chunk_name == png_pCAL) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sCAL_SUPPORTED - else if (chunk_name == png_sCAL) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_tIME_SUPPORTED - else if (chunk_name == png_tIME) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_tEXt_SUPPORTED - else if (chunk_name == png_tEXt) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_zTXt_SUPPORTED - else if (chunk_name == png_zTXt) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_iTXt_SUPPORTED - else if (chunk_name == png_iTXt) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); - } -#endif - - else - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, - PNG_HANDLE_CHUNK_AS_DEFAULT); - } - - png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; -} - -void PNGCBAPI +/* Standard callbacks */ +static void PNGCBAPI png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) { png_bytep ptr; @@ -440,6 +43,7 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) png_ptr->save_buffer_size -= save_size; png_ptr->save_buffer_ptr += save_size; } + if (length != 0 && png_ptr->current_buffer_size != 0) { png_size_t save_size; @@ -457,590 +61,775 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) } } -void /* PRIVATE */ +/* Push model modes: png_chunk_op plus extras */ +typedef enum +{ + /* all the png_chunk_op codes, plus: */ + png_read_signature = 0, /* starting value */ + png_read_chunk_header, + png_read_end_IDAT, + png_read_done, + png_read_chunk, /* Not a state, use these derived from png_chunk_op: */ + png_read_chunk_skip = png_read_chunk+png_chunk_skip, + png_read_chunk_unknown = png_read_chunk+png_chunk_unknown, + png_read_chunk_process_all = png_read_chunk+png_chunk_process_all, + png_read_chunk_process_part = png_read_chunk+png_chunk_process_part +} png_read_mode; + +static void +png_push_save_buffer_partial(png_structrp png_ptr, size_t amount) +{ + /* Copy 'amount' bytes to the end of the save buffer from the current + * buffer. + */ + png_bytep buffer; + size_t save_size = png_ptr->save_buffer_size; + + if (save_size > PNG_SIZE_MAX - amount) + png_error(png_ptr, "save buffer overflow"); + + if (png_ptr->save_buffer_max < save_size + amount) + { + /* Reallocate the save buffer. */ + buffer = png_voidcast(png_bytep, png_malloc(png_ptr, save_size + amount)); + memcpy(buffer, png_ptr->save_buffer_ptr, save_size); + png_free(png_ptr, png_ptr->save_buffer); + png_ptr->save_buffer_ptr = png_ptr->save_buffer = buffer; + } + + else if (png_ptr->save_buffer_max - + (png_ptr->save_buffer_ptr - png_ptr->save_buffer) < save_size + amount) + { + /* Move the existing saved data */ + buffer = png_ptr->save_buffer; + memmove(buffer, png_ptr->save_buffer_ptr, save_size); + png_ptr->save_buffer_ptr = buffer; + } + + else /* Just copy the data */ + buffer = png_ptr->save_buffer_ptr; + + memcpy(buffer+save_size, png_ptr->current_buffer_ptr, amount); + png_ptr->save_buffer_size = save_size + amount; + png_ptr->current_buffer_ptr += amount; + png_ptr->current_buffer_size -= amount; + png_ptr->buffer_size -= amount; +} + +static void png_push_save_buffer(png_structrp png_ptr) { - if (png_ptr->save_buffer_size != 0) - { - if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) - { - png_size_t i, istop; - png_bytep sp; - png_bytep dp; - - istop = png_ptr->save_buffer_size; - for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; - i < istop; i++, sp++, dp++) - { - *dp = *sp; - } - } - } - if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > - png_ptr->save_buffer_max) - { - png_size_t new_max; - png_bytep old_buffer; - - if (png_ptr->save_buffer_size > PNG_SIZE_MAX - - (png_ptr->current_buffer_size + 256)) - { - png_error(png_ptr, "Potential overflow of save_buffer"); - } - - new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; - old_buffer = png_ptr->save_buffer; - png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr, - (png_size_t)new_max); - - if (png_ptr->save_buffer == NULL) - { - png_free(png_ptr, old_buffer); - png_error(png_ptr, "Insufficient memory for save_buffer"); - } - - memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); - png_free(png_ptr, old_buffer); - png_ptr->save_buffer_max = new_max; - } - if (png_ptr->current_buffer_size) - { - memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, - png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); - png_ptr->save_buffer_size += png_ptr->current_buffer_size; - png_ptr->current_buffer_size = 0; - } - png_ptr->save_buffer_ptr = png_ptr->save_buffer; + png_push_save_buffer_partial(png_ptr, png_ptr->current_buffer_size); + /* This terminates the process loop. */ png_ptr->buffer_size = 0; } -void /* PRIVATE */ +#define PNG_PUSH_SAVE_BUFFER_IF_FULL \ +if (png_ptr->chunk_length + 4 > png_ptr->buffer_size) \ + { png_push_save_buffer(png_ptr); return; } +#define PNG_PUSH_SAVE_BUFFER_IF_LT(N) \ +if (png_ptr->buffer_size < N) \ + { png_push_save_buffer(png_ptr); return; } + +png_size_t PNGAPI +png_process_data_pause(png_structrp png_ptr, int save) +{ + if (png_ptr != NULL) + { + /* It's easiest for the caller if we do the save; then the caller doesn't + * have to supply the same data again: + */ + if (save != 0) + png_push_save_buffer(png_ptr); + else + { + /* This includes any pending saved bytes: */ + png_size_t remaining = png_ptr->buffer_size; + png_ptr->buffer_size = 0; /* Terminate the process loop */ + + /* So subtract the saved buffer size, unless all the data + * is actually 'saved', in which case we just return 0 + */ + if (png_ptr->save_buffer_size < remaining) + return remaining - png_ptr->save_buffer_size; + } + } + + return 0; +} + +png_uint_32 PNGAPI +png_process_data_skip(png_structrp png_ptr) +{ + if (png_ptr != NULL && png_ptr->process_mode == png_read_chunk_skip) + { + /* At the end of png_process_data the buffer size must be 0 (see the loop + * above) so we can detect a broken call here: + */ + if (png_ptr->buffer_size != 0) + png_app_error(png_ptr, + "png_process_data_skip called inside png_process_data"); + + /* If is impossible for there to be a saved buffer at this point - + * otherwise we could not be in SKIP mode. This will also happen if + * png_process_skip is called inside png_process_data (but only very + * rarely.) + */ + else if (png_ptr->save_buffer_size != 0) + png_app_error(png_ptr, "png_process_data_skip called with saved data"); + + else + { + /* Skipping png_ptr->chunk_length of data then checking the CRC, after + * that a new chunk header will be read. + */ + png_ptr->process_mode = png_read_chunk_header; + return png_ptr->chunk_length + 4; + } + } + + return 0; +} + +static void png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer, png_size_t buffer_length) { - png_ptr->current_buffer = buffer; png_ptr->current_buffer_size = buffer_length; png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; - png_ptr->current_buffer_ptr = png_ptr->current_buffer; + png_ptr->current_buffer_ptr = buffer; } -void /* PRIVATE */ -png_push_read_IDAT(png_structrp png_ptr) +/* Read any remaining signature bytes from the stream and compare them with + * the correct PNG signature. It is possible that this routine is called + * with bytes already read from the signature, either because they have been + * checked by the calling application, or because of multiple calls to this + * routine. + */ +static void +png_push_read_signature(png_structrp png_ptr, png_inforp info_ptr) { - if ((png_ptr->mode & PNG_HAVE_CHUNK_HEADER) == 0) + unsigned int num_checked = png_ptr->sig_bytes; + unsigned int num_to_check = 8 - num_checked; + + if (png_ptr->buffer_size < num_to_check) + num_to_check = (int)/*SAFE*/png_ptr->buffer_size; + + png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), + num_to_check); + png_ptr->sig_bytes = png_check_byte(png_ptr, + png_ptr->sig_bytes + num_to_check); + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) { - png_byte chunk_length[4]; - png_byte chunk_tag[4]; - - /* TODO: this code can be commoned up with the same code in push_read */ - PNG_PUSH_SAVE_BUFFER_IF_LT(8) - png_push_fill_buffer(png_ptr, chunk_length, 4); - png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); - png_reset_crc(png_ptr); - png_crc_read(png_ptr, chunk_tag, 4); - png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); - png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; - - if (png_ptr->chunk_name != png_IDAT) - { - png_ptr->process_mode = PNG_READ_CHUNK_MODE; - - if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0) - png_error(png_ptr, "Not enough compressed data"); - - return; - } - - png_ptr->idat_size = png_ptr->push_length; - } - - if (png_ptr->idat_size != 0 && png_ptr->save_buffer_size != 0) - { - png_size_t save_size = png_ptr->save_buffer_size; - png_uint_32 idat_size = png_ptr->idat_size; - - /* We want the smaller of 'idat_size' and 'current_buffer_size', but they - * are of different types and we don't know which variable has the fewest - * bits. Carefully select the smaller and cast it to the type of the - * larger - this cannot overflow. Do not cast in the following test - it - * will break on either 16 or 64 bit platforms. - */ - if (idat_size < save_size) - save_size = (png_size_t)idat_size; + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); else - idat_size = (png_uint_32)save_size; + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + + else if (png_ptr->sig_bytes >= 8) + png_ptr->process_mode = png_read_chunk_header; +} + +static void +png_push_crc_finish(png_structrp png_ptr) + /* CRC the remainder of the chunk data; png_struct::chunk_length must be the + * amount of data left to read excluding the CRC. + */ +{ + if (png_ptr->chunk_length != 0 && png_ptr->save_buffer_size != 0) + { + png_size_t save_size = png_ptr->save_buffer_size; + png_uint_32 skip_length = png_ptr->chunk_length; + + /* We want the smaller of 'skip_length' and 'save_buffer_size', but + * they are of different types and we don't know which variable has the + * fewest bits. Carefully select the smaller and cast it to the type of + * the larger - this cannot overflow. Do not cast in the following test + * - it will break on either 16 or 64 bit platforms. + */ + if (skip_length < save_size) + save_size = (png_size_t)/*SAFE*/skip_length; + + else + skip_length = (png_uint_32)/*SAFE*/save_size; png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); - png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); - - png_ptr->idat_size -= idat_size; + png_ptr->chunk_length -= skip_length; png_ptr->buffer_size -= save_size; png_ptr->save_buffer_size -= save_size; png_ptr->save_buffer_ptr += save_size; } - if (png_ptr->idat_size != 0 && png_ptr->current_buffer_size != 0) + if (png_ptr->chunk_length != 0 && png_ptr->current_buffer_size != 0) { png_size_t save_size = png_ptr->current_buffer_size; - png_uint_32 idat_size = png_ptr->idat_size; + png_uint_32 skip_length = png_ptr->chunk_length; - /* We want the smaller of 'idat_size' and 'current_buffer_size', but they - * are of different types and we don't know which variable has the fewest - * bits. Carefully select the smaller and cast it to the type of the - * larger - this cannot overflow. + /* We want the smaller of 'skip_length' and 'current_buffer_size', here, + * the same problem exists as above and the same solution. */ - if (idat_size < save_size) - save_size = (png_size_t)idat_size; + if (skip_length < save_size) + save_size = (png_size_t)/*SAFE*/skip_length; else - idat_size = (png_uint_32)save_size; + skip_length = (png_uint_32)/*SAFE*/save_size; png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); - png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); - - png_ptr->idat_size -= idat_size; + png_ptr->chunk_length -= skip_length; png_ptr->buffer_size -= save_size; png_ptr->current_buffer_size -= save_size; png_ptr->current_buffer_ptr += save_size; } - if (png_ptr->idat_size == 0) + + if (png_ptr->chunk_length == 0) { PNG_PUSH_SAVE_BUFFER_IF_LT(4) png_crc_finish(png_ptr, 0); - png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; - png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->zowner = 0; + png_ptr->process_mode = png_read_chunk_header; } } -void /* PRIVATE */ -png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, - png_size_t buffer_length) +static void +png_push_read_unknown(png_structrp png_ptr, png_inforp info_ptr) { - /* The caller checks for a non-zero buffer length. */ - affirm(buffer_length > 0 && buffer != NULL); - - /* This routine must process all the data it has been given - * before returning, calling the row callback as required to - * handle the uncompressed results. + /* Handle an unknown chunk. All the data is available but it may not + * all be in the same buffer. png_handle_unknown needs the chunk data in + * just one buffer. */ - png_ptr->zstream.next_in = buffer; - /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */ - png_ptr->zstream.avail_in = (uInt)buffer_length; + png_bytep buffer; + png_uint_32 chunk_length = png_ptr->chunk_length; - /* Keep going until the decompressed data is all processed - * or the stream marked as finished. - */ - while (png_ptr->zstream.avail_in > 0 && - (png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0) + if (png_ptr->save_buffer_size > 0) { - int ret; + png_size_t save_size = png_ptr->save_buffer_size; - /* We have data for zlib, but we must check that zlib - * has someplace to put the results. It doesn't matter - * if we don't expect any results -- it may be the input - * data is just the LZ end code. - */ - if (!(png_ptr->zstream.avail_out > 0)) + if (save_size < chunk_length) { - /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */ - png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth, - png_ptr->iwidth) + 1); - - png_ptr->zstream.next_out = png_ptr->row_buf; + /* Copy the current_buffer_ptr data into the save buffer. */ + png_push_save_buffer_partial(png_ptr, chunk_length - save_size); + save_size = chunk_length; } - /* Using Z_SYNC_FLUSH here means that an unterminated - * LZ stream (a stream with a missing end code) can still - * be handled, otherwise (Z_NO_FLUSH) a future zlib - * implementation might defer output and therefore - * change the current behavior (see comments in inflate.c - * for why this doesn't happen at present with zlib 1.2.5). - */ - ret = inflate(&png_ptr->zstream, Z_SYNC_FLUSH); + buffer = png_ptr->save_buffer_ptr; + png_ptr->save_buffer_ptr = buffer+chunk_length; + png_ptr->save_buffer_size = save_size-chunk_length; + png_ptr->buffer_size -= chunk_length; + affirm(png_ptr->buffer_size >= 4); + } - /* Check for any failure before proceeding. */ - if (ret != Z_OK && ret != Z_STREAM_END) + else + { + affirm(png_ptr->current_buffer_size >= chunk_length+4); + buffer = png_ptr->current_buffer_ptr; + png_ptr->current_buffer_ptr = buffer+chunk_length; + png_ptr->current_buffer_size -= chunk_length; + png_ptr->buffer_size -= chunk_length; + } + + /* Now check the CRC, before attempting the unknown handling. */ + png_calculate_crc(png_ptr, buffer, chunk_length); + png_crc_finish(png_ptr, 0); +# ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + png_handle_unknown(png_ptr, info_ptr, buffer); +# else /* !READ_UNKNOWN_CHUNKS */ + PNG_UNUSED(info_ptr) +# endif /* !READ_UNKNOWN_CHUNKS */ + png_ptr->process_mode = png_read_chunk_header; +} + +static void +png_push_have_row(png_structrp png_ptr, png_bytep row) +{ + if (png_ptr->row_fn != NULL) + { + png_uint_32 row_number = png_ptr->row_number; + png_byte pass = png_ptr->pass; + + if (png_ptr->interlaced) { - /* Terminate the decompression. */ - png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; - png_ptr->zowner = 0; - - /* This may be a truncated stream (missing or - * damaged end code). Treat that as a warning. + /* If the row de-interlace is not being done by PNG this wacky API + * delivers the row number in the pass to the caller. We know that + * if we get here the row exists, so the number is just one less than + * the height of an interlaced image with just the rows up to this + * one: */ - if (png_ptr->row_number >= png_ptr->num_rows || - png_ptr->pass > 6) - png_warning(png_ptr, "Truncated compressed data in IDAT"); - - else - png_error(png_ptr, "Decompression error in IDAT"); - - /* Skip the check on unprocessed input */ - return; - } - - /* Did inflate output any data? */ - if (png_ptr->zstream.next_out != png_ptr->row_buf) - { - /* Is this unexpected data after the last row? - * If it is, artificially terminate the LZ output - * here. - */ - if (png_ptr->row_number >= png_ptr->num_rows || - png_ptr->pass > 6) +# ifdef PNG_READ_DEINTERLACE_SUPPORTED + if (!png_ptr->do_interlace) +# endif { - /* Extra data. */ - png_warning(png_ptr, "Extra compressed data in IDAT"); - png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; - png_ptr->zowner = 0; + affirm(PNG_ROW_IN_INTERLACE_PASS(row_number, pass) && row != NULL); + row_number = PNG_PASS_ROWS(row_number+1, pass); + affirm(row_number > 0); + --row_number; + } + } - /* Do no more processing; skip the unprocessed - * input check below. + (*(png_ptr->row_fn))(png_ptr, row, row_number, pass); + } +} + +static void +png_push_read_sync_zstream(png_structp png_ptr, png_bytep *bufferp, + size_t *buffer_lengthp) + /* Synchronize the png_struct progressive read buffer + * {*bufferp,*buffer_lengthp} with png_struct::zstream.next_in, on the + * assumption that the zstream had previously been set up with *bufferp. + */ +{ + png_bytep original_start = *bufferp; + png_alloc_size_t bytes_consumed = png_ptr->zstream.next_in - original_start; + + affirm(buffer_lengthp != NULL); + + /* Calculate the CRC for the consumed data: */ + png_calculate_crc(png_ptr, original_start, bytes_consumed); + + /* Update the buffer pointers and the various lengths: */ + *bufferp = original_start + bytes_consumed; /* == png_ptr->zstream.next_in */ + + affirm(bytes_consumed <= *buffer_lengthp); + *buffer_lengthp -= (size_t)/*SAFE*/bytes_consumed; + + affirm(bytes_consumed <= png_ptr->chunk_length); + png_ptr->chunk_length -= (png_uint_32)/*SAFE*/bytes_consumed; + + affirm(bytes_consumed <= png_ptr->buffer_size); + png_ptr->buffer_size -= (size_t)/*SAFE*/bytes_consumed; +} + +static void +png_push_read_process_IDAT(png_structp png_ptr, png_bytep *bufferp, + size_t *buffer_lengthp) + /* If the the *buffer_lengthp parameter is NULL there is no more input, + * png_struct::mode & PNG_AFTER_IDAT must be set at this point. + */ +{ + png_alloc_size_t buffer_length; + + if (buffer_lengthp != NULL) + buffer_length = *buffer_lengthp; + + else /* end of IDAT */ + { + /* SECURITY: if this affirm fails the code would go into an infinite loop; + * see the handling of avail_in == 0 in png_inflate_IDAT. + */ + affirm(png_ptr->mode & PNG_AFTER_IDAT); + buffer_length = 0; + } + + /* This routine attempts to process all the data it has been given before + * returning, calling the row callback as required to handle the + * uncompressed results. + * + * If a pause happens during processing (png_ptr->buffer_size is set to 0) + * or the end of the chunk is encountered the routine may return without + * handling all the input data. + */ + if (buffer_length > png_ptr->chunk_length) + { + buffer_length = png_ptr->chunk_length; + + /* This works because the last part of a 'skip' is to read and check the + * CRC, then the process mode is set to png_read_chunk_header. + */ + if (buffer_length == 0) + png_ptr->process_mode = png_read_chunk_skip; + } + + /* It is possble for buffer_length to be zero at this point if the stream + * caontains a zero length IDAT. This is handled below. + */ + png_ptr->zstream.next_in = *bufferp; + + while (buffer_length > 0 || buffer_lengthp == NULL) + { + if (buffer_length >= ZLIB_IO_MAX) + { + png_ptr->zstream.avail_in = ZLIB_IO_MAX; + buffer_length -= ZLIB_IO_MAX; + } + + else + { + png_ptr->zstream.avail_in = (uInt)/*SAFE*/buffer_length; + buffer_length = 0; + } + + /* The last row may already have been processed. + * + * row_number is the *current* row number in the range 0..height-1. It is + * updated only by the call to png_read_process_IDAT that follows the call + * which returns something other than png_row_incomplete. + * + * At the end of the image that call must *NOT* be made; png_process_IDAT + * must not be called after the last row. png_struct::zstream_eod is set + * below to allow this condition to be detected. + * + * Note that png_read_process_IDAT handles errors in the LZ compressed + * data (i.e. the cases where png_struct::zstream_error is set) by filling + * the rows in with 0, which is a safe value, so keep calling it until we + * reach the end of the image. + */ + if (!png_ptr->zstream_eod) + { + png_bytep row_buffer = NULL; + png_row_op row_op = png_read_process_IDAT(png_ptr); + + if (row_op != png_row_incomplete) + { + /* Have a complete row, so check for end-of-image; do this here + * because interlaced images can end on earlier rows or passes but + * we keep calling png_read_process_IDAT until it updates row_number + * to the last row of the actual image: */ + if (png_ptr->row_number+1 >= png_ptr->height && + (!png_ptr->interlaced || png_ptr->pass == 6)) + png_ptr->zstream_eod = 1; /* end of image */ + } + + switch (row_op) + { + case png_row_incomplete: + /* more IDAT data needed for row */ + debug(png_ptr->zstream.avail_in == 0); + + /* png_inflate_IDAT is supposed to handle this and recognize a + * call with 0 avail_in as end of stream: + */ + affirm(buffer_lengthp != NULL); + continue; + + case png_row_process: + /* If a row was obtained after the end of the IDAT this was done + * by fabricating data, ensure this is reported, else there is a + * security issue; normally libpng does a png_error in this + * case, even if the error is ignored png_struct::zstream_error + * should be set so somehow the error wasn't noticed! + */ + affirm(buffer_lengthp != NULL || png_ptr->zstream_error); + + /* png_struct::row_buffer contains a complete, transformed, row; + * this is processed in both 'sparkle' and 'block' mode. + */ + row_buffer = png_ptr->row_buffer; + break; + + case png_row_repeat: + /* row not in this pass, but the existing row in + * png_struct::row_buffer may be used, this is only required if + * the 'block' or 'rectangle' mode of display is done and libpng + * is handling the de-interlace; when the app does it it only + * see the real rows. + */ +# ifdef PNG_READ_DEINTERLACE_SUPPORTED + if (png_ptr->do_interlace) + { + row_buffer = png_ptr->row_buffer; + break; + } +# endif + continue; + + case png_row_skip: + /* row not in pass and no appropriate data; skip this row, + * nothing more need be done, except the read_row_fn. The use + * of 'NULL' to mean this row doesn't contribute to the output + * is historical and not documented; + */ +# ifdef PNG_READ_DEINTERLACE_SUPPORTED + if (png_ptr->do_interlace) + break; +# endif + continue; + + default: + impossible("not reached"); + } + + /* Here if there is a row to process. */ + + /* Now adjust the buffer pointers before calling png_push_have_row + * because the callback might call png_process_data_pause and that + * calls png_push_save_row. (Yes, this is insane; it was forced on + * libpng by writers of an external app that ignored the instructions + * not to fiddle with the insides of png_struct in version 1.4. It + * will probably be fixed here before 1.7.0 is released by removing + * the need for the save buffer entirely.) + */ + if (buffer_lengthp != NULL) + png_push_read_sync_zstream(png_ptr, bufferp, buffer_lengthp); + + /* Process one row: */ + png_push_have_row(png_ptr, row_buffer); + + /* The buffer pointer and size may have changed at this point, + * so everything needs to be reloaded if we can continue reading. + */ + if (buffer_lengthp != NULL) /* not at end of IDATs */ + { + if (png_ptr->chunk_length == 0) + png_ptr->process_mode = png_read_chunk_skip; + + /* If the buffer_size has been set to zero more input is required, + * this may be a 'pause', and if the specific input buffer being + * processed has been exhaused then more input is also required. + * Otherwise we can keep going, however the input buffer may have + * been changed by the app callback, so do a complete reload: + */ + else if (png_ptr->buffer_size > 0 && *buffer_lengthp > 0) + png_push_read_process_IDAT(png_ptr, bufferp, buffer_lengthp); + return; } - /* Do we have a complete row? */ - if (png_ptr->zstream.avail_out == 0) - png_push_process_row(png_ptr); + /* If we can't continue reading because there is no more IDAT data this + * may still be a pause. + */ + if (png_ptr->buffer_size == 0) + return; + + /* Else continue, with zero data: */ + continue; } - /* And check for the end of the stream. */ - if (ret == Z_STREAM_END) - png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; - } + affirm(png_ptr->zstream_eod); - /* All the data should have been processed, if anything - * is left at this point we have bytes of IDAT data - * after the zlib end code. - */ - if (png_ptr->zstream.avail_in > 0) - png_warning(png_ptr, "Extra compression data in IDAT"); -} - -void /* PRIVATE */ -png_push_process_row(png_structrp png_ptr) -{ - /* 1.5.6: row_info moved out of png_struct to a local here. */ - png_row_info row_info; - - row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */ - row_info.color_type = png_ptr->color_type; - row_info.bit_depth = png_ptr->bit_depth; - row_info.channels = png_ptr->channels; - row_info.pixel_depth = png_ptr->pixel_depth; - row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); - - if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE) - { - if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST) - png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1, - png_ptr->prev_row + 1, png_ptr->row_buf[0]); - else - png_error(png_ptr, "bad adaptive filter value"); - } - - /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before - * 1.5.6, while the buffer really is this big in current versions of libpng - * it may not be in the future, so this was changed just to copy the - * interlaced row count: - */ - memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1); - -#ifdef PNG_READ_TRANSFORMS_SUPPORTED - if (png_ptr->transformations != 0) - png_do_read_transformations(png_ptr, &row_info); -#endif - - /* The transformed pixel depth should match the depth now in row_info. */ - if (png_ptr->transformed_pixel_depth == 0) - { - png_ptr->transformed_pixel_depth = row_info.pixel_depth; - if (row_info.pixel_depth > png_ptr->maximum_pixel_depth) - png_error(png_ptr, "progressive row overflow"); - } - - else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth) - png_error(png_ptr, "internal progressive row size calculation error"); - - -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* Expand interlaced rows to full size */ - if (png_ptr->interlaced != 0 && - (png_ptr->transformations & PNG_INTERLACE) != 0) - { - if (png_ptr->pass < 6) - png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass, - png_ptr->transformations); - - switch (png_ptr->pass) + if (png_ptr->zowner == 0 || png_read_finish_IDAT(png_ptr)) { - case 0: - { - int i; - for (i = 0; i < 8 && png_ptr->pass == 0; i++) - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */ - } + /* The zlib stream has ended, there may still be input data in + * png_ptr->zstream.next_in, restore this. + */ + debug(png_ptr->zowner == 0 && png_ptr->zstream_ended); - if (png_ptr->pass == 2) /* Pass 1 might be empty */ + if (buffer_lengthp != NULL) + { + png_push_read_sync_zstream(png_ptr, bufferp, buffer_lengthp); + + /* If the chunk_length is greater than 0 then there is extra data, + * report this once. Notice that for IDAT after the end of the + * stream we keep coming to this point and doing the skip. + */ + if (png_ptr->chunk_length > 0) { - for (i = 0; i < 4 && png_ptr->pass == 2; i++) + if (!png_ptr->zstream_error) { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); + png_chunk_benign_error(png_ptr, + "too much IDAT data (progressive read)"); + png_ptr->zstream_error = 1; } } - if (png_ptr->pass == 4 && png_ptr->height <= 4) - { - for (i = 0; i < 2 && png_ptr->pass == 4; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - } - - if (png_ptr->pass == 6 && png_ptr->height <= 4) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - - break; - } - - case 1: - { - int i; - for (i = 0; i < 8 && png_ptr->pass == 1; i++) - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - } - - if (png_ptr->pass == 2) /* Skip top 4 generated rows */ - { - for (i = 0; i < 4 && png_ptr->pass == 2; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - } - - break; - } - - case 2: - { - int i; - - for (i = 0; i < 4 && png_ptr->pass == 2; i++) - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - } - - for (i = 0; i < 4 && png_ptr->pass == 2; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - - if (png_ptr->pass == 4) /* Pass 3 might be empty */ - { - for (i = 0; i < 2 && png_ptr->pass == 4; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - } - - break; - } - - case 3: - { - int i; - - for (i = 0; i < 4 && png_ptr->pass == 3; i++) - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - } - - if (png_ptr->pass == 4) /* Skip top two generated rows */ - { - for (i = 0; i < 2 && png_ptr->pass == 4; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - } - - break; - } - - case 4: - { - int i; - - for (i = 0; i < 2 && png_ptr->pass == 4; i++) - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - } - - for (i = 0; i < 2 && png_ptr->pass == 4; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - - if (png_ptr->pass == 6) /* Pass 5 might be empty */ - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - - break; - } - - case 5: - { - int i; - - for (i = 0; i < 2 && png_ptr->pass == 5; i++) - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - } - - if (png_ptr->pass == 6) /* Skip top generated row */ - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - - break; - } - - default: - case 6: - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - - if (png_ptr->pass != 6) - break; - - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); + /* In any case CRC the chunk, skipping any unneeded data: */ + png_ptr->process_mode = png_read_chunk_skip; } + return; } + + /* else more input is required */ + /* NOTE: this test only fires on a small (less than 5 byte) IDAT chunk + * which just contains the LZ EOF and the Adler32 CRC. + */ + affirm(png_ptr->zowner == png_IDAT && !png_ptr->zstream_ended); } - else -#endif - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - } + + /* At this point all the input has been consumed, however the CRC has not + * been done and the three length fields in png_struct, *buffer_lengthp, + * buffer_size and chunk_length, all need updating. + */ + png_push_read_sync_zstream(png_ptr, bufferp, buffer_lengthp); } -void /* PRIVATE */ -png_read_push_finish_row(png_structrp png_ptr) +static void +png_push_read_IDAT(png_structrp png_ptr) { -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - - /* Start of interlace block */ - static PNG_CONST png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static PNG_CONST png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; - - /* Start of interlace block in the y direction */ - static PNG_CONST png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; - - /* Offset to next interlace block in the y direction */ - static PNG_CONST png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; - - /* Height of interlace block. This is not currently used - if you need - * it, uncomment it here and in png.h - static PNG_CONST png_byte png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; - */ -#endif - - png_ptr->row_number++; - if (png_ptr->row_number < png_ptr->num_rows) - return; - -#ifdef PNG_READ_INTERLACING_SUPPORTED - if (png_ptr->interlaced != 0) + if (png_ptr->save_buffer_size > 0) { - png_ptr->row_number = 0; - memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + png_push_read_process_IDAT(png_ptr, &png_ptr->save_buffer_ptr, + &png_ptr->save_buffer_size); - do - { - png_ptr->pass++; - if ((png_ptr->pass == 1 && png_ptr->width < 5) || - (png_ptr->pass == 3 && png_ptr->width < 3) || - (png_ptr->pass == 5 && png_ptr->width < 2)) - png_ptr->pass++; + /* This is a slight optimization; normally when the process mode changes + * there will still be something in the buffer: + */ + if (png_ptr->save_buffer_size > 0) + return; - if (png_ptr->pass > 7) - png_ptr->pass--; - - if (png_ptr->pass >= 7) - break; - - png_ptr->iwidth = (png_ptr->width + - png_pass_inc[png_ptr->pass] - 1 - - png_pass_start[png_ptr->pass]) / - png_pass_inc[png_ptr->pass]; - - if ((png_ptr->transformations & PNG_INTERLACE) != 0) - break; - - png_ptr->num_rows = (png_ptr->height + - png_pass_yinc[png_ptr->pass] - 1 - - png_pass_ystart[png_ptr->pass]) / - png_pass_yinc[png_ptr->pass]; - - } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); + /* Check for a change in process mode or an application pause before + * checking the current input buffer. This is only rarely reached. + */ + if (png_ptr->process_mode != png_read_chunk_process_part || + png_ptr->buffer_size == 0) + return; } -#endif /* READ_INTERLACING */ + + if (png_ptr->current_buffer_size > 0) + png_push_read_process_IDAT(png_ptr, &png_ptr->current_buffer_ptr, + &png_ptr->current_buffer_size); } -void /* PRIVATE */ +static void +png_push_finish_IDAT(png_structrp png_ptr) + /* Called once when the first chunk after IDAT is seen. */ +{ + /* All of the IDAT data has been processed, however the stream may have + * been truncated and the image rows may not all have been processed. + * Clean up here (this doesn't read anything.) + * + * 1.7.0: this attempts some measure of compatibility with the sequential + * API, if the IDAT is truncated and the resultant error reporting doesn't + * abort the read the image is filled in using zeros of pixel data. This + * actually happens inside png_inflate_IDAT (pngrutil.c) when called with + * z_stream::avail_in == 0. + */ + while (png_ptr->zowner == png_IDAT) + { + png_byte b = 0, *pb = &b; + + png_push_read_process_IDAT(png_ptr, &pb, NULL/*end of IDAT*/); + + if (png_ptr->zowner == 0) + break; + + if (png_ptr->buffer_size == 0) /* pause */ + return; + } + + png_ptr->process_mode = png_check_bits(png_ptr, png_ptr->process_mode >> 4, + 4); +} + +static void png_push_have_info(png_structrp png_ptr, png_inforp info_ptr) { if (png_ptr->info_fn != NULL) (*(png_ptr->info_fn))(png_ptr, info_ptr); } -void /* PRIVATE */ +static void png_push_have_end(png_structrp png_ptr, png_inforp info_ptr) { if (png_ptr->end_fn != NULL) (*(png_ptr->end_fn))(png_ptr, info_ptr); } -void /* PRIVATE */ -png_push_have_row(png_structrp png_ptr, png_bytep row) +static void +png_push_read_chunk_header(png_structrp png_ptr, png_infop info_ptr) { - if (png_ptr->row_fn != NULL) - (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, - (int)png_ptr->pass); + /* Called to read a new chunk header and work out how to handle the remainder + * of the data. + */ + unsigned int mode; /* mode prior to the header */ + png_byte chunk_length[4]; + png_byte chunk_tag[4]; + + PNG_PUSH_SAVE_BUFFER_IF_LT(8) + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->chunk_length = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, chunk_tag, 4); + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); + mode = png_ptr->mode; + png_ptr->process_mode = png_check_bits(png_ptr, + png_read_chunk+png_find_chunk_op(png_ptr), 4); + + /* Is this the first IDAT chunk? */ + if ((mode ^ png_ptr->mode) & PNG_HAVE_IDAT) + png_push_have_info(png_ptr, info_ptr); + + /* Is it the chunk after the last IDAT chunk? */ + else if (((mode ^ png_ptr->mode) & PNG_AFTER_IDAT) != 0) + png_ptr->process_mode = png_check_bits(png_ptr, + (png_ptr->process_mode << 4) + png_read_end_IDAT, 8); } -#ifdef PNG_READ_INTERLACING_SUPPORTED +/* What we do with the incoming data depends on what we were previously + * doing before we ran out of data... + */ +static void +png_process_some_data(png_structrp png_ptr, png_inforp info_ptr) +{ + if (png_ptr == NULL) + return; + + switch (png_ptr->process_mode & 0xf) + { + case png_read_signature: + png_push_read_signature(png_ptr, info_ptr); + return; + + case png_read_chunk_header: + png_push_read_chunk_header(png_ptr, info_ptr); + return; + + case png_read_chunk_skip: + png_push_crc_finish(png_ptr); + return; + + case png_read_chunk_unknown: + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_push_read_unknown(png_ptr, info_ptr); + return; + + case png_read_chunk_process_all: + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_chunk(png_ptr, info_ptr); + + if (png_ptr->mode & PNG_HAVE_IEND) + { + png_ptr->process_mode = png_read_done; + png_push_have_end(png_ptr, info_ptr); + png_ptr->buffer_size = 0; + } + + else + png_ptr->process_mode = png_read_chunk_header; + return; + + case png_read_chunk_process_part: + debug(png_ptr->chunk_name == png_IDAT && + (png_ptr->mode & PNG_HAVE_IDAT) && + !(png_ptr->mode & PNG_AFTER_IDAT)); + + if (png_ptr->zowner == 0 && !png_ptr->zstream_ended) /* first time */ + png_read_start_IDAT(png_ptr); + + png_push_read_IDAT(png_ptr); + return; + + case png_read_end_IDAT: + png_push_finish_IDAT(png_ptr); + return; + + case png_read_done: + png_app_error(png_ptr, "read beyond end of stream"); + png_ptr->buffer_size = 0; + return; + + default: + impossible("invalid process mode"); + } +} + +void PNGAPI +png_process_data(png_structrp png_ptr, png_inforp info_ptr, + png_bytep buffer, png_size_t buffer_size) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_push_restore_buffer(png_ptr, buffer, buffer_size); + + while (png_ptr->buffer_size) + png_process_some_data(png_ptr, info_ptr); +} + +#ifdef PNG_READ_DEINTERLACE_SUPPORTED void PNGAPI png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row, png_const_bytep new_row) @@ -1049,13 +838,13 @@ png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row, return; /* new_row is a flag here - if it is NULL then the app callback was called - * from an empty row (see the calls to png_struct::row_fn below), otherwise - * it must be png_ptr->row_buf+1 + * from an empty row (see the calls to png_struct::row_fn above), otherwise + * it must be png_ptr->row_buffer */ if (new_row != NULL) png_combine_row(png_ptr, old_row, 1/*blocky display*/); } -#endif /* READ_INTERLACING */ +#endif /* READ_DEINTERLACE */ void PNGAPI png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr, diff --git a/pngpriv.h b/pngpriv.h index 55f15ff5b..b991b44f7 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -123,104 +123,6 @@ #endif /* VERSION_INFO_ONLY */ -/* Compile time options. - * ===================== - * In a multi-arch build the compiler may compile the code several times for the - * same object module, producing different binaries for different architectures. - * When this happens configure-time setting of the target host options cannot be - * done and this interferes with the handling of the ARM NEON optimizations, and - * possibly other similar optimizations. Put additional tests here; in general - * this is needed when the same option can be changed at both compile time and - * run time depending on the target OS (i.e. iOS vs Android.) - * - * NOTE: symbol prefixing does not pass $(CFLAGS) to the preprocessor, because - * this is not possible with certain compilers (Oracle SUN OS CC), as a result - * it is necessary to ensure that all extern functions that *might* be used - * regardless of $(CFLAGS) get declared in this file. The test on __ARM_NEON - * below is one example of this behavior because it is controlled by the - * presence or not of -mfpu=neon on the GCC command line, it is possible to do - * this in $(CC), e.g. "CC=gcc -mfpu=neon", but people who build libpng rarely - * do this. - */ -#ifndef PNG_ARM_NEON_OPT - /* ARM NEON optimizations are being controlled by the compiler settings, - * typically the target FPU. If the FPU supports NEON instructions then the - * compiler will define __ARM_NEON and we can rely unconditionally on NEON - * instructions not crashing, otherwise we must disable use of NEON - * instructions. - * - * NOTE: at present these optimizations depend on 'ALIGNED_MEMORY', so they - * can only be turned on automatically if that is supported too. If - * PNG_ARM_NEON_OPT is set in CPPFLAGS (to >0) then arm/arm_init.c will fail - * to compile with an appropriate #error if ALIGNED_MEMORY has been turned - * off. - * - * Note that older versions of GCC defined __ARM_NEON__; this is no longer - * supported. Also 32-bit ARM versions of GCC required the NEON FPU mode to - * be turned on explicitly on the command line. If this is not done (on - * 32-bit ARM) NEON code will not be included. - * - * To disable ARM_NEON optimizations entirely, and skip compiling the - * associated assembler code, pass --enable-arm-neon=no to configure - * or put -DPNG_ARM_NEON_OPT=0 in CPPFLAGS. - */ -# if defined(__ARM_NEON) && defined(PNG_ALIGNED_MEMORY_SUPPORTED) -# define PNG_ARM_NEON_OPT 2 -# else -# define PNG_ARM_NEON_OPT 0 -# endif -#endif - -#if PNG_ARM_NEON_OPT > 0 - /* NEON optimizations are to be at least considered by libpng, so enable the - * callbacks to do this. - */ -# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_neon - - /* By default the 'intrinsics' code in arm/filter_neon_intrinsics.c is used - * if possible - if __ARM_NEON is set and the compiler version is not known - * to be broken. This is controlled by PNG_ARM_NEON_IMPLEMENTATION which can - * be: - * - * 1 The intrinsics code (the default with __ARM_NEON) - * 2 The hand coded assembler (the default without __ARM_NEON) - * - * It is possible to set PNG_ARM_NEON_IMPLEMENTATION in CPPFLAGS, however - * this is *NOT* supported and may cease to work even after a minor revision - * to libpng. It *is* valid to do this for testing purposes, e.g. speed - * testing or a new compiler, but the results should be communicated to the - * libpng implementation list for incorporation in the next minor release. - */ -# ifndef PNG_ARM_NEON_IMPLEMENTATION -# ifdef __ARM_NEON -# if defined(__clang__) - /* At present it is unknown by the libpng developers which versions - * of clang support the intrinsics, however some or perhaps all - * versions do not work with the assembler so this may be - * irrelevant, so just use the default (do nothing here.) - */ -# elif defined(__GNUC__) - /* GCC 4.5.4 NEON support is known to be broken. 4.6.3 is known to - * work, so if this *is* GCC, or G++, look for a version >4.5 - */ -# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6) -# define PNG_ARM_NEON_IMPLEMENTATION 2 -# endif /* no GNUC support */ -# endif /* __GNUC__ */ -# else /* !defined __ARM_NEON__ */ - /* The 'intrinsics' code simply won't compile without compiler support - * and that support switches on __ARM_NEON, so use the assembler: - */ -# define PNG_ARM_NEON_IMPLEMENTATION 2 -# endif /* __ARM_NEON__ */ -# endif /* !defined PNG_ARM_NEON_IMPLEMENTATION */ - -# ifndef PNG_ARM_NEON_IMPLEMENTATION - /* Use the intrinsics code by default. */ -# define PNG_ARM_NEON_IMPLEMENTATION 1 -# endif -#endif /* PNG_ARM_NEON_OPT > 0 */ - /* Is this a build of a DLL where compilation of the object modules requires * different preprocessor settings to those required for a simple library? If * so PNG_BUILD_DLL must be set. @@ -430,6 +332,8 @@ do\ if (!(cond)) png_affirm(pp, PNG_SRC_LINE);\ while (0) +# define png_affirmexp(pp, cond)\ + ((cond) ? (void)0 : png_affirm(pp, PNG_SRC_LINE)) # define png_impossiblepp(pp, reason) png_affirm(pp, PNG_SRC_LINE) # define debug(cond) do {} while (0) @@ -437,19 +341,25 @@ /* Make sure there are no 'UNTESTED' macros in released code: */ # define UNTESTED libpng untested code # endif +# define NOT_REACHED do {} while (0) #else # define png_affirmpp(pp, cond)\ do\ if (!(cond)) png_affirm(pp, #cond, PNG_SRC_LINE);\ while (0) +# define png_affirmexp(pp, cond)\ + ((cond) ? (void)0 : png_affirm(pp, #cond, PNG_SRC_LINE)) # 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); +# define NOT_REACHED png_affirm(png_ptr, "NOT REACHED", PNG_SRC_LINE) #endif #define affirm(cond) png_affirmpp(png_ptr, cond) +#define affirmexp(cond) png_affirmexp(png_ptr, cond) #define impossible(cond) png_impossiblepp(png_ptr, cond) +#define implies(a, b) debug(!(a) || (b)) /* The defines for PNG_SRC_FILE: */ #define PNG_SRC_FILE_(f,lines) PNG_SRC_FILE_ ## f + lines @@ -465,7 +375,7 @@ #define PNG_SRC_FILE_pngrutil (PNG_SRC_FILE_pngrtran +8192) #define PNG_SRC_FILE_pngset (PNG_SRC_FILE_pngrutil +8192) #define PNG_SRC_FILE_pngtrans (PNG_SRC_FILE_pngset +2048) -#define PNG_SRC_FILE_pngwio (PNG_SRC_FILE_pngtrans +2048) +#define PNG_SRC_FILE_pngwio (PNG_SRC_FILE_pngtrans +4096) #define PNG_SRC_FILE_pngwrite (PNG_SRC_FILE_pngwio +1024) #define PNG_SRC_FILE_pngwtran (PNG_SRC_FILE_pngwrite +4096) #define PNG_SRC_FILE_pngwutil (PNG_SRC_FILE_pngwtran +1024) @@ -500,56 +410,6 @@ PNG_apply(arm_filter_neon_intrinsics)\ PNG_end -/* GCC complains about assignments of an (int) expression to a (char) even when - * it can readily determine that the value is in range. This makes arithmetic - * on (char) or (png_byte) values tedious. The warning is not issued by - * default, but libpng coding rules require no warnings leading to excessive, - * ridiculous and dangerous expressions of the form: - * - * = (char)(expression & 0xff) - * - * They are dangerous because they hide the warning, which might actually be - * valid, and therefore merely enable introduction of undetected overflows when - * code is modified. - * - * The following macros exist to reliably detect any overflow in non-release - * builds. The theory here is that we really want to know about overflows, not - * merely hide a basically flawed compiler warning by throwing unnecessary casts - * into the code. The warnings disappear in RC builds so that the released - * (STABLE) version just assigns the value (with, possibly, a warning if someone - * turns on the -Wconversion GCC warning.) - * - * Doing it this way ensures that the code meets two very important aims: - * - * 1) Overflows are detected in pre-release tests; previously versions of libpng - * have been released that really did have overflows in the RGB calculations. - * 2) In release builds GCC specific operations, which may reduce the ability - * of other compilers and even GCC to optimize the code, are avoided. - * - * There is one important extra consequence for pre-release code; it is - * performing a lot of checks in pixel arithmetic that the release code won't - * perform. As a consequence a build time option, RANGE_CHECK, is provided - * to allow the checks to be turned off in pre-release when building for - * performance testing. This is a standard "_SUPPORTED" option except that it - * cannot be set in the system configuration (pnglibconf.h, pnglibconf.dfa). - * - * A separate macro PNG_BYTE() is provided to safely convert an unsigned value - * to the PNG byte range 0..255. This handles the fact that, technically, - * an ANSI-C (unsigned char), hence a (png_byte), may be able to store values - * outside this range. Note that if you are building on a system where this is - * true libpng is almost certainly going to produce errors; it has never been - * tested on such a system. For the moment pngconf.h ensures that this will - * not happen. - * - * PNG_UINT_16 does the same thing for a 16-bit value passed in an (int) or - * (png_uint_32) (where checking is not expected.) - */ -#if !PNG_RELEASE_BUILD -# ifndef PNG_NO_RANGE_CHECK /* Turn off even in pre-release */ -# define PNG_RANGE_CHECK_SUPPORTED -# endif -#endif - /* SECURITY and SAFETY: * * libpng is built with support for internal limits on image dimensions and @@ -700,96 +560,28 @@ * are defined in png.h because they need to be visible to applications * that call png_set_unknown_chunk(). */ -/* #define PNG_HAVE_IHDR 0x01 (defined in png.h) */ -/* #define PNG_HAVE_PLTE 0x02 (defined in png.h) */ -#define PNG_HAVE_IDAT 0x04 -/* #define PNG_AFTER_IDAT 0x08 (defined in png.h) */ -#define PNG_HAVE_IEND 0x10 - /* 0x20 (unused) */ - /* 0x40 (unused) */ - /* 0x80 (unused) */ -#define PNG_HAVE_CHUNK_HEADER 0x100 -#define PNG_WROTE_tIME 0x200 -#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 - /* 0x800 (unused) */ -#define PNG_HAVE_PNG_SIGNATURE 0x1000 -#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ - /* 0x4000 (unused) */ -#define PNG_IS_READ_STRUCT 0x8000 /* Else is a write struct */ +/* #define PNG_HAVE_IHDR 0x01 (defined as (int) in png.h) */ +/* #define PNG_HAVE_PLTE 0x02 (defined as (int) in png.h) */ +#define PNG_HAVE_IDAT 0x04U +/* #define PNG_AFTER_IDAT 0x08 (defined as (int) in png.h) */ +#define PNG_HAVE_IEND 0x10U +#define PNG_HAVE_PNG_SIGNATURE 0x20U -/* Flags for the transformations the PNG library does on the image data */ -#define PNG_BGR 0x0001 -#define PNG_INTERLACE 0x0002 -#define PNG_PACK 0x0004 -#define PNG_SHIFT 0x0008 -#define PNG_SWAP_BYTES 0x0010 -#define PNG_INVERT_MONO 0x0020 -#define PNG_QUANTIZE 0x0040 -#define PNG_COMPOSE 0x0080 /* Was PNG_BACKGROUND */ - /* 0x0100 unused */ -#define PNG_EXPAND_16 0x0200 /* Added to libpng 1.5.2 */ -#define PNG_16_TO_8 0x0400 /* Becomes 'chop' in 1.5.4 */ -#define PNG_RGBA 0x0800 -#define PNG_EXPAND 0x1000 -#define PNG_GAMMA 0x2000 -#define PNG_GRAY_TO_RGB 0x4000 -#define PNG_FILLER 0x8000 -#define PNG_PACKSWAP 0x10000 -#define PNG_SWAP_ALPHA 0x20000 -#define PNG_STRIP_ALPHA 0x40000 -#define PNG_INVERT_ALPHA 0x80000 -#define PNG_USER_TRANSFORM 0x100000 -#define PNG_RGB_TO_GRAY_ERR 0x200000 -#define PNG_RGB_TO_GRAY_WARN 0x400000 -#define PNG_RGB_TO_GRAY 0x600000 /* two bits, RGB_TO_GRAY_ERR|WARN */ -#define PNG_ENCODE_ALPHA 0x800000 /* Added to libpng-1.5.4 */ -#define PNG_ADD_ALPHA 0x1000000 /* Added to libpng-1.2.7 */ -#define PNG_EXPAND_tRNS 0x2000000 /* Added to libpng-1.2.9 */ -#define PNG_SCALE_16_TO_8 0x4000000 /* Added to libpng-1.5.4 */ - /* 0x8000000 unused */ - /* 0x10000000 unused */ - /* 0x20000000 unused */ - /* 0x40000000 unused */ -/* Flags for png_create_struct */ -#define PNG_STRUCT_PNG 0x0001 -#define PNG_STRUCT_INFO 0x0002 - -/* Scaling factor for filter heuristic weighting calculations */ -#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) -#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) - -/* Flags for the png_ptr->flags rather than declaring a byte for each one */ -#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 -#define PNG_FLAG_ZSTREAM_INITIALIZED 0x0002 /* Added to libpng-1.6.0 */ - /* 0x0004 unused */ -#define PNG_FLAG_ZSTREAM_ENDED 0x0008 /* Added to libpng-1.6.0 */ - /* 0x0010 unused */ - /* 0x0020 unused */ -#define PNG_FLAG_ROW_INIT 0x0040 -#define PNG_FLAG_FILLER_AFTER 0x0080 -#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 -#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 -#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 -#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 -#define PNG_FLAG_ASSUME_sRGB 0x1000 /* Added to libpng-1.5.4 */ -#define PNG_FLAG_OPTIMIZE_ALPHA 0x2000 /* Added to libpng-1.5.4 */ -#define PNG_FLAG_DETECT_UNINITIALIZED 0x4000 /* Added to libpng-1.5.4 */ -/* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000 */ -/* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000 */ -#define PNG_FLAG_LIBRARY_MISMATCH 0x20000 -#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000 -#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000 -#define PNG_FLAG_BENIGN_ERRORS_WARN 0x100000 /* Added to libpng-1.4.0 */ -#define PNG_FLAG_APP_WARNINGS_WARN 0x200000 /* Added to libpng-1.6.0 */ -#define PNG_FLAG_APP_ERRORS_WARN 0x400000 /* Added to libpng-1.6.0 */ -#define PNG_FLAG_BACKGROUND_IS_GRAY 0x800000 -#define PNG_FLAG_BACKGROUND_EXPAND 0x1000000 - /* 0x2000000 unused */ - /* 0x4000000 unused */ - /* 0x8000000 unused */ - /* 0x10000000 unused */ - /* 0x20000000 unused */ - /* 0x40000000 unused */ +/* Flags for the png_ptr->flags. + * TODO: change to bit fields. + */ +#define PNG_FLAG_LIBRARY_MISMATCH 0x001U +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x002U +#define PNG_FLAG_CRC_ANCILLARY_USE 0x004U +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x008U +#define PNG_FLAG_CRC_CRITICAL_USE 0x010U +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x020U +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x040U +#define PNG_FLAG_STRIP_ERROR_TEXT 0x080U +#define PNG_FLAG_IDAT_ERRORS_WARN 0x100U +#define PNG_FLAG_BENIGN_ERRORS_WARN 0x200U +#define PNG_FLAG_APP_WARNINGS_WARN 0x400U +#define PNG_FLAG_APP_ERRORS_WARN 0x800U #define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ PNG_FLAG_CRC_ANCILLARY_NOWARN) @@ -797,21 +589,12 @@ #define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ PNG_FLAG_CRC_CRITICAL_IGNORE) -#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ - PNG_FLAG_CRC_CRITICAL_MASK) - -/* Save typing and make code easier to understand */ - -#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ - abs((int)((c1).green) - (int)((c2).green)) + \ - abs((int)((c1).blue) - (int)((c2).blue))) - #if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) /* See below for the definitions of the tables used in these macros */ #define PNG_sRGB_FROM_LINEAR(pp, linear) png_check_byte(pp,\ (png_sRGB_base[(linear)>>15] +\ - ((((linear)&0x7fff)*png_sRGB_delta[(linear)>>15])>>12)) >> 8) + ((((linear)&0x7fffU)*png_sRGB_delta[(linear)>>15])>>12)) >> 8) /* Given a value 'linear' in the range 0..255*65535 calculate the 8-bit sRGB * encoded value with maximum error 0.646365. Note that the input is not a * 16-bit value; it has been multiplied by 255! */ @@ -825,14 +608,43 @@ * 65535 - this has a maximum error of .502. Over the range 0..65535*65535 it * only gives off-by-one errors and only for 0.5% (1 in 200) of the values. */ -#define PNG_DIV65535(v24) (((v24) + 32895) >> 16) -#define PNG_DIV257(v16) PNG_DIV65535((png_uint_32)(v16) * 255) +#define PNG_DIV65535(v24) (((v24) + 32895U) >> 16) +#define PNG_DIV257(v16) PNG_DIV65535((png_uint_32)(v16) * 255U) -/* Added to libpng-1.2.6 JB */ +/* Added to libpng-1.2.6 JB + * Modified in libpng-1.7.0 to avoid the intermediate calculation overflow + * when: + * + * pixel_bits == 4: any width over 0x3FFFFFFEU overflows + * pixel_bits == 2: any width over 0x7FFFFFFCU overflows + * + * In both these cases any width results in a rowbytes that fits in 32 bits. + * The problem arose in previous versions because the calculation used was + * simply ((width x pixel-bit-depth)+7)/8. At the cost of more calculations + * on pixel_depth this avoids the problem. + */ +#define PNG_SHIFTOF(pixel_bits/*<8*/) \ + ( (pixel_bits) == 1 ? 3 : \ + ( (pixel_bits) == 2 ? 2 : \ + ( (pixel_bits) == 4 ? 1 : \ + 0/*force bytes*/ ) ) ) +#define PNG_ADDOF(pixel_bits/*<8*/) ((1U<= 8 ? \ - ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \ - (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) ) + ((pixel_bits) >= 8 ? \ + ((png_alloc_size_t)(width) * ((pixel_bits) >> 3)) : \ + (((png_alloc_size_t)(width) + PNG_ADDOF(pixel_bits)) >> \ + PNG_SHIFTOF(pixel_bits)) ) + +/* This macros, added in 1.7.0, makes it easy to deduce the number of channels + * and therefore the pixel depth from the color type. The PNG specification + * numbers are used in preference to the png.h constants to make it more clear + * why the macro works. + */ +#define PNG_COLOR_TYPE_CHANNELS(ct)\ + (((ct) & PNG_COLOR_MASK_PALETTE) ?\ + 1U : 1U+((ct) & 2U/*COLOR*/)+(((ct)>>2)&1U/*ALPHA*/)) +#define PNG_CHANNELS(ps) PNG_COLOR_TYPE_CHANNELS((ps).color_type) +#define PNG_PIXEL_DEPTH(ps) (PNG_CHANNELS(ps) * (ps).bit_depth) /* PNG_OUT_OF_RANGE returns true if value is outside the range * ideal-delta..ideal+delta. Each argument is evaluated twice. @@ -842,6 +654,18 @@ #define PNG_OUT_OF_RANGE(value, ideal, delta) \ ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) +/* Handling of bit-field masks. Because the expression: + * + * bit_field & ~mask + * + * has implementation defined behavior in ANSI C-90 for many (int) values of + * 'mask' and because some of these are defined in png.h and passed in (int) + * parameters use of '~' has been expunged in libpng 1.7 and replaced by this + * macro, which is well defined in ANSI C-90 (there is a similar, 16-bit, + * version in pngstruct.h for the colorspace flags.) + */ +#define PNG_BIC_MASK(flags) (0xFFFFFFFFU - (flags)) + /* Conversions between fixed and floating point, only defined if * required (to make sure the code doesn't accidentally use float * when it is supposedly disabled.) @@ -947,7 +771,60 @@ PNG_INTERNAL_FUNCTION(void, png_affirm,(png_const_structrp png_ptr, param_deb(png_const_charp condition) unsigned int position), PNG_NORETURN); /* Character/byte range checking. */ +/* GCC complains about assignments of an (int) expression to a (char) even when + * it can readily determine that the value is in range. This makes arithmetic + * on (char) or (png_byte) values tedious. The warning is not issued by + * default, but libpng coding rules require no warnings leading to excessive, + * ridiculous and dangerous expressions of the form: + * + * = (char)(expression & 0xff) + * + * They are dangerous because they hide the warning, which might actually be + * valid, and therefore merely enable introduction of undetected overflows when + * code is modified. + * + * The following macros exist to reliably detect any overflow in non-release + * builds. The theory here is that we really want to know about overflows, not + * merely hide a basically flawed compiler warning by throwing unnecessary casts + * into the code. The warnings disappear in RC builds so that the released + * (STABLE) version just assigns the value (with, possibly, a warning if someone + * turns on the -Wconversion GCC warning.) + * + * Doing it this way ensures that the code meets two very important aims: + * + * 1) Overflows are detected in pre-release tests; previously versions of libpng + * have been released that really did have overflows in the RGB calculations. + * 2) In release builds GCC specific operations, which may reduce the ability + * of other compilers and even GCC to optimize the code, are avoided. + * + * There is one important extra consequence for pre-release code; it is + * performing a lot of checks in pixel arithmetic that the release code won't + * perform. As a consequence a build time option, RANGE_CHECK, is provided + * to allow the checks to be turned off in pre-release when building for + * performance testing. This is a standard "_SUPPORTED" option except that it + * cannot be set in the system configuration (pnglibconf.h, pnglibconf.dfa). + * + * A separate macro PNG_BYTE() is provided to safely convert an unsigned value + * to the PNG byte range 0..255. This handles the fact that, technically, + * an ANSI-C (unsigned char), hence a (png_byte), may be able to store values + * outside this range. Note that if you are building on a system where this is + * true libpng is almost certainly going to produce errors; it has never been + * tested on such a system. For the moment pngconf.h ensures that this will + * not happen. + * + * PNG_UINT_16 does the same thing for a 16-bit value passed in an (int) or + * (png_uint_32) (where checking is not expected.) + */ +#if !PNG_RELEASE_BUILD +# ifndef PNG_NO_RANGE_CHECK /* Turn off even in pre-release */ +# define PNG_RANGE_CHECK_SUPPORTED +# endif +#endif + #ifdef PNG_RANGE_CHECK_SUPPORTED +PNG_INTERNAL_FUNCTION(unsigned int, png_bit_affirm,(png_const_structrp png_ptr, + unsigned int position, unsigned int u, unsigned int bits), PNG_EMPTY); + PNG_INTERNAL_FUNCTION(char, png_char_affirm,(png_const_structrp png_ptr, unsigned int position, int c), PNG_EMPTY); @@ -971,18 +848,21 @@ PNG_INTERNAL_FUNCTION(png_uint_16, png_u16_affirm,(png_const_structrp png_ptr, # define png_check_u16(pp, u) (u) #endif +# define png_check_bits(pp, u, bits)\ + (((1U<<(bits))-1) & png_bit_affirm((pp), PNG_SRC_LINE, (u), (bits))) # define png_check_char(pp, c) (png_char_affirm((pp), PNG_SRC_LINE, (c))) # define png_check_byte(pp, b) (png_byte_affirm((pp), PNG_SRC_LINE, (b))) -# define PNG_BYTE(b) ((png_byte)((b) & 0xff)) -# define PNG_UINT_16(u) ((png_uint_16)((u) & 0xffff)) +# define PNG_BYTE(b) ((png_byte)((b) & 0xFFU)) +# define PNG_UINT_16(u) ((png_uint_16)((u) & 0xFFFFU)) # define png_handled(pp, m) (png_handled_affirm((pp), (m), PNG_SRC_LINE)) #elif !(defined PNG_REMOVE_CASTS) +# define png_check_bits(pp, u, bits) (((1U<<(bits))-1U) & (u)) # define png_check_char(pp, c) ((char)(c)) # define png_check_byte(pp, b) ((png_byte)(b)) # define png_check_u16(pp, u) ((png_uint_16)(u)) # define png_handled(pp, m) ((void)0) -# define PNG_BYTE(b) ((png_byte)((b) & 0xff)) -# define PNG_UINT_16(u) ((png_uint_16)((u) & 0xffff)) +# define PNG_BYTE(b) ((png_byte)((b) & 0xFFU)) +# define PNG_UINT_16(u) ((png_uint_16)((u) & 0xFFFFU)) #else /* This is somewhat trust-me-it-works: if PNG_REMOVE_CASTS is defined then * the casts, which might otherwise change the values, are completely @@ -993,12 +873,13 @@ PNG_INTERNAL_FUNCTION(png_uint_16, png_u16_affirm,(png_const_structrp png_ptr, * NOTE: this seems safe at present but might lead to unexpected results * if someone writes code to depend on the truncation. */ +# define png_check_bits(pp, u, bits) (u) # define png_check_char(pp, c) (c) # define png_check_byte(pp, b) (b) # define png_check_u16(pp, u) (u) # define png_handled(pp, m) ((void)0) # define PNG_BYTE(b) (b) -# define PNG_UINT_16(b) (u) +# define PNG_UINT_16(u) (u) #endif /* RANGE_CHECK */ /* Utility macro to mark a handled error condition ; when control reaches this @@ -1008,6 +889,13 @@ PNG_INTERNAL_FUNCTION(png_uint_16, png_u16_affirm,(png_const_structrp png_ptr, */ #define handled(m) png_handled(png_ptr, (m)) +/* Safe calculation of a rowbytes value; does a png_error if the system limits + * are exceeded. + */ +png_alloc_size_t /* PRIVATE */ +png_calc_rowbytes(png_const_structrp png_ptr, unsigned int pixel_depth, + png_uint_32 row_width); + /* Zlib support */ #define PNG_UNEXPECTED_ZLIB_RETURN (-7) PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret), @@ -1022,16 +910,6 @@ PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr, /* Free the buffer list used by the compressed write code. */ #endif -#ifdef PNG_WRITE_FILTER_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_write_alloc_filter_row_buffers, - (png_structrp png_ptr, int filters),PNG_EMPTY); - /* Allocate pixel row buffers to cache filtered rows while testing candidate - * filters. - * TODO: avoid this, only one spare row buffer (at most) is required, this - * wastes a lot of memory for large images. - */ -#endif - #if defined(PNG_FLOATING_POINT_SUPPORTED) && \ !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \ @@ -1098,28 +976,26 @@ PNG_INTERNAL_FUNCTION(voidpf,png_zalloc,(voidpf png_ptr, uInt items, uInt size), /* Function to free memory for zlib. PNGAPI is disallowed. */ PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY); -/* Next four functions are used internally as callbacks. PNGCBAPI is required - * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3, changed to - * PNGCBAPI at 1.5.0 +/* The next three functions are used by png_init_io to set the default + * implementations for reading or writing to a stdio (png_FILE_p) stream. + * They can't be static because in 1.7 png_init_io needs to reference them. */ - +#ifdef PNG_STDIO_SUPPORTED +# ifdef PNG_READ_SUPPORTED PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr, png_bytep data, png_size_t length),PNG_EMPTY); +# endif /* READ */ -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr, - png_bytep buffer, png_size_t length),PNG_EMPTY); -#endif - +# ifdef PNG_WRITE_SUPPORTED PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr, png_bytep data, png_size_t length),PNG_EMPTY); -#ifdef PNG_WRITE_FLUSH_SUPPORTED -# ifdef PNG_STDIO_SUPPORTED +# ifdef PNG_WRITE_FLUSH_SUPPORTED PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_flush,(png_structp png_ptr), PNG_EMPTY); -# endif -#endif +# endif /* WRITE_FLUSH */ +# endif /* WRITE */ +#endif /* STDIO */ /* Reset the CRC variable */ PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY); @@ -1132,10 +1008,6 @@ PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr, PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr, png_inforp info_ptr),PNG_EMPTY); -/* Read the chunk header (length + type name) */ -PNG_INTERNAL_FUNCTION(png_uint_32,png_read_chunk_header,(png_structrp png_ptr), - PNG_EMPTY); - /* Read data from whatever input you are using into the "data" buffer */ PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data, png_size_t length),PNG_EMPTY); @@ -1148,9 +1020,6 @@ PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf, PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr, png_uint_32 skip),PNG_EMPTY); -/* Read the CRC from the file and compare it to the libpng calculated CRC */ -PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY); - /* Calculate the CRC over a section of data. Note that we are only * passing a maximum of 64K on systems that have this as a memory limit, * since this is the maximum buffer size we can specify. @@ -1172,7 +1041,7 @@ PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr, int compression_method, int filter_method, int interlace_method),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr, - png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY); + png_const_colorp palette, unsigned int num_pal),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr, png_const_bytep row_data, png_alloc_size_t row_data_length, int flush), @@ -1280,403 +1149,533 @@ PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr, int unit, png_const_charp width, png_const_charp height),PNG_EMPTY); #endif -/* Called when finished processing a row of data */ -PNG_INTERNAL_FUNCTION(void,png_write_finish_row,(png_structrp png_ptr), - PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_copy_row,(png_const_structrp png_ptr, + png_bytep dp),PNG_EMPTY); + /* Copy the row in row_buffer; this is the 'simple' case of png_combine_row + * where no adjustment to the pixel spacing is required. + */ -/* Internal use only. Called before first row of data */ -PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr), - PNG_EMPTY); - -/* Combine a row of data, dealing with alpha, etc. if requested. 'row' is an - * array of png_ptr->width pixels. If the image is not interlaced or this - * is the final pass this just does a memcpy, otherwise the "display" flag - * is used to determine whether to copy pixels that are not in the current pass. - * - * Because 'png_do_read_interlace' (below) replicates pixels this allows this - * function to achieve the documented 'blocky' appearance during interlaced read - * if display is 1 and the 'sparkle' appearance, where existing pixels in 'row' - * are not changed if they are not in the current pass, when display is 0. - * - * 'display' must be 0 or 1, otherwise the memcpy will be done regardless. - * - * The API always reads from the png_struct row buffer and always assumes that - * it is full width (png_do_read_interlace has already been called.) - * - * This function is only ever used to write to row buffers provided by the - * caller of the relevant libpng API and the row must have already been - * transformed by the read transformations. - * - * The PNG_USE_COMPILE_TIME_MASKS option causes generation of pre-computed - * bitmasks for use within the code, otherwise runtime generated masks are used. - * The default is compile time masks. - */ -#ifndef PNG_USE_COMPILE_TIME_MASKS -# define PNG_USE_COMPILE_TIME_MASKS 1 -#endif +#ifdef PNG_READ_DEINTERLACE_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr, png_bytep row, int display),PNG_EMPTY); - -#ifdef PNG_READ_INTERLACING_SUPPORTED -/* Expand an interlaced row: the 'row_info' describes the pass data that has - * been read in and must correspond to the pixels in 'row', the pixels are - * expanded (moved apart) in 'row' to match the final layout, when doing this - * the pixels are *replicated* to the intervening space. This is essential for - * the correct operation of png_combine_row, above. - */ -PNG_INTERNAL_FUNCTION(void,png_do_read_interlace,(png_row_infop row_info, - png_bytep row, int pass, png_uint_32 transformations),PNG_EMPTY); -#endif +#endif /* READ_DEINTERLACE */ /* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ #ifdef PNG_WRITE_INTERLACING_SUPPORTED -/* Grab pixels out of a row for an interlaced pass */ -PNG_INTERNAL_FUNCTION(void,png_do_write_interlace,(png_row_infop row_info, - png_bytep row, int pass),PNG_EMPTY); +/* Turn on write interlacing */ +PNG_INTERNAL_FUNCTION(void,png_set_write_interlace,(png_structrp),PNG_EMPTY); #endif -/* Unfilter a row: check the filter value before calling this, there is no point - * calling it for PNG_FILTER_VALUE_NONE. +#ifdef PNG_WRITE_FILTER_SUPPORTED +/* Choose the best filter to use and filter the row data returning a buffer to + * the result and filling in 'filter_byte' appropriately. */ -PNG_INTERNAL_FUNCTION(void,png_read_filter_row,(png_structrp pp, png_row_infop - row_info, png_bytep row, png_const_bytep prev_row, int filter),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(png_const_bytep,png_write_filter_row, + (png_structrp png_ptr, png_const_bytep unfiltered_row, int first_pass_row, + png_const_bytep previous_row, png_alloc_size_t rowbytes, unsigned int bpp, + png_bytep filter_byte),PNG_EMPTY); +#endif -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,(png_row_infop row_info, - png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,(png_row_infop - row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon,(png_row_infop - row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon,(png_row_infop - row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,(png_row_infop - row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,(png_row_infop - row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop - row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); - -/* Choose the best filter to use and filter the row data */ -PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr, - png_row_infop row_info),PNG_EMPTY); - -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr, - png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY); - /* Read 'avail_out' bytes of data from the IDAT stream. If the output buffer - * is NULL the function checks, instead, for the end of the stream. In this - * case a benign error will be issued if the stream end is not found or if - * extra data has to be consumed. - */ -PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr), - PNG_EMPTY); - /* This cleans up when the IDAT LZ stream does not end when the last image - * byte is read; there is still some pending input. +#ifdef PNG_TRANSFORM_MECH_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_transform_free,(png_const_structrp png_ptr, + png_transformp *list),PNG_EMPTY); + /* Free the entire transform list, from the given point on. the argument is + * set to NULL. */ -PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr), - PNG_EMPTY); - /* Finish a row while reading, dealing with interlacing passes, etc. */ -#endif /* SEQUENTIAL_READ */ - -/* Initialize the row buffers, etc. */ -PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_init_transform_control,( + png_transform_controlp out, png_structp png_ptr),PNG_EMPTY); + /* Initialize a transform control for running the transform list forward (the + * read case, and write initialization, but the write case is called within + * pngtrans.c by the above function.) + */ #ifdef PNG_READ_TRANSFORMS_SUPPORTED +PNG_INTERNAL_FUNCTION(unsigned int,png_run_this_transform_list_forwards, + (png_transform_controlp tc, png_transformp *start, png_transformp *end), + PNG_EMPTY); + /* Used by the transform cache code to run a sub-list, from *start to the + * transform containing *end. + */ +#endif /* READ_TRANSFORMS */ + +#ifdef PNG_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(unsigned int,png_run_transform_list_forwards, + (png_structp png_ptr, png_transform_controlp tc),PNG_EMPTY); + /* Run the transform list in the forwards direction (from PNG format to + * memory format). The transform control must be initialized completely by + * the caller. This function takes account of transforms which delete + * themselves during the run; it must be used. + */ +#endif /* READ */ + +#ifdef PNG_WRITE_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_run_transform_list_backwards, + (png_structp png_ptr, png_transform_controlp tc),PNG_EMPTY); + /* Run the transform list in the backwards direction (from memory format to + * PNG format). The transform control must be initialized completely by + * the caller. This function takes account of transforms which delete + * themselves during the run; it must be used. + */ +#endif /* WRITE */ + +PNG_INTERNAL_FUNCTION(png_transformp,png_add_transform,(png_structrp png_ptr, + size_t size, png_transform_fn fn, unsigned int order),PNG_EMPTY); + /* Add a transform, using the information in 'order' to control the position + * of the transform in the list, returning a pointer to the transform. The + * top 8 bits of 'order' control the position in the list. If a transform + * does not already exist in the list with the given value a new transform + * will be created and 'fn' and 'order' set. If there is a transform with + * that value 'fn' must match and 'order' will be updated by combining the + * new value in with a bitwise or (|). It is up to the function (fn) or the + * caller of png_add_transform to determine whether the combination is valid. + * + * 'size' is used when creating a new transform, it may be larger than + * (sizeof png_transform) if required to accomodate extra data. + * + * Prior to 1.7.0 transforms were executed in an order hard-wired into the + * code that executed the transform functions. This was summarized in the + * read case by the following comment from pngrtran.c + * (png_init_read_transformations), note that this has been marked up to + * indicate which PNG formats the transforms in the list apply to: + * + * *: applies to most formats + * A: only formats with alpha + * L: only low-bit-depth (less than 8 bits per component/pixel) + * H: only high-bit-depth (16-bits per component) + * + > From the code of png_do_read_transformations the order is: + * + * GGRR For example column: . no action + * AGG r acts on read + * BB w acts on write + * A B acts on both read and write + > + > r.r. 1) PNG_EXPAND (including PNG_EXPAND_tRNS) + > .r.r 2) PNG_STRIP_ALPHA (if no compose) + > ..rr 3) PNG_RGB_TO_GRAY + > rr.. 4) PNG_GRAY_TO_RGB iff !PNG_FLAG_BACKGROUND_IS_GRAY + > rrrr 5) PNG_COMPOSE + > rrrr 6) PNG_GAMMA + > .r.r 7) PNG_STRIP_ALPHA (if compose) + > .r.r 8) PNG_ENCODE_ALPHA + > rrrr 9) PNG_SCALE_16_TO_8 + > rrrr 10) PNG_16_TO_8 + > ..rr 11) PNG_QUANTIZE (converts to palette) + > rrrr 12) PNG_EXPAND_16 + > rr.. 13) PNG_GRAY_TO_RGB iff PNG_FLAG_BACKGROUND_IS_GRAY + > BB.. 14) PNG_INVERT_MONO + > .B.B 15) PNG_INVERT_ALPHA + > BBBB 16) PNG_SHIFT + * + * Note that transforms from this point on are used in 1.7.0 on palette + * indices as well; a png_set_pack request (for example) packs the palette + * index values if the output will be palettized and the grayscale values + * if it will not be (if the output is low-bit-grayscale, not palette.) + * + > B... 17) PNG_PACK + > ..BB 18) PNG_BGR + > B... 19) PNG_PACKSWAP + > rwrw 20) PNG_FILLER (includes PNG_ADD_ALPHA) + > .B.B 21) PNG_SWAP_ALPHA + > BBBB 22) PNG_SWAP_BYTES + > BBBB 23) PNG_USER_TRANSFORM [must be last] + * + * Finally, outside the set of transforms prior to 1.7.0, the libpng + * interlace handling required the pixels to be replicated to match the pixel + * spacing in the image row; the first part the pre-1.7.0 interlace support, + * this is still the case when reading, but for writing the interlace is now + * a transform: + * + > BBBB 24) png_do_{read,write}_interlace (interlaced images only). + * + * First transforms are grouped according to basic function using the top 3 + * bits of the order code: + */ +# define PNG_TR_START 0x0000U /* initial ops on the PNG data */ +# define PNG_TR_ARITHMETIC 0x2000U /* arithmetic linear operations */ +# define PNG_TR_CHANNEL 0x4000U /* PNG conformant format changes */ +# define PNG_TR_QUANTIZE 0x6000U /* quantize and following operations */ +# define PNG_TR_ENCODING 0x8000U /* Row encoding transforms */ +# define PNG_TR_INTERLACE 0xA000U /* write interlace transform */ + /* + * In libpng 1.7.0 the check on palette index values is moved to the start + * (of read, end of write, which is where it was before) immediately after + * the MNG filter handling + */ +# define PNG_TR_MNG_INTRAPIXEL (PNG_TR_START + 0x0100U) + /* Perform intra-pixel differencing (write) or un-differencing on read. */ +# define PNG_TR_CHECK_PALETTE (PNG_TR_START + 0x0200U) + /* Done before at the start on read, at the end on write to give a + * consistent postion: + * + * PNG_RWTR_CHECK_PALETTE PI W11: happens in pngwrite.c last + */ +# define PNG_TR_INIT_ALPHA (PNG_TR_START + 0x0300U) + /* This just handles alpha/tRNS initialization issues to resolve the + * inter-dependencies with tRNS expansion and background composition; it + * doesn't do anything itself, just sets flags and pushes transforms. + */ + /* + * Prior to 1.7 the arithmetic operations interleaved with the RGB-to-gray + * and alpha strip byte level ops. This was done to reduce the amount of + * data processed, i.e. it was an optimization not a requirement. These + * operations were preceded by the 'expand' operations, which is the + * opposite; it was done to simplify the code and actually slows things down + * in the low bit depth gray case. The full list of operations after expand, + * in the 1.6 order, is: + * + * PNG_TR_STRIP_ALPHA png_do_strip_channel (sometimes) + * PNG_TR_RGB_TO_GRAY png_do_rgb_to_gray + * PNG_TR_GRAY_TO_RGB png_do_gray_to_rgb (sometimes) + * PNG_TR_COMPOSE png_do_compose + * PNG_TR_GAMMA png_do_gamma (if no RGB_TO_GRAY) + * PNG_TR_STRIP_ALPHA png_do_strip_channel (other times) + * PNG_TR_ENCODE_ALPHA png_do_encode_alpha + * + * In 1.7 the operations are moved round somewhat, including moving alpha and + * 16-to-8 bit reduction later. This leaves the following operations: + * + * PNG_TR_RGB_TO_GRAY png_do_rgb_to_gray + * PNG_TR_COMPOSE png_do_compose + * PNG_TR_GAMMA png_do_gamma (if no RGB_TO_GRAY) + * PNG_TR_ENCODE_ALPHA png_do_encode_alpha + * + * Prior to 1.7 some combinations of transforms would do gamma correction + * twice, the actual implementation in 1.7 is to use the following order and + * rely on the cache code to optimize gray 1,2,4,8 and (of course) palette. + */ +# define PNG_TR_COMPOSE (PNG_TR_ARITHMETIC + 0x0100U) + /* Handle background composition. This may need to push a gray-to-rgb + * transform if the background is RGB for gray input. This precedes RGB + * to gray convertion so that it can handle tRNS appropriately when the + * background is in the PNG encoding however, typically, the processing + * happens at PNG_TR_COMPOSE_ALPHA below. + * + * NOTE: this must be the first arithmetic transform because the code in + * png_init_background relies on png_transform_control::gamma being the + * original PNG gamma. + */ +# define PNG_TR_RGB_TO_GRAY (PNG_TR_ARITHMETIC + 0x0200U) /* to gray */ + /* Convert any RGB input (8/16 bit depth, RGB, RGBA) to linear gray + * 16-bit. This happens first because it cannot be cached; the input data + * has 24 or 48 bits of uncorrelated data so the transform has to happen + * pixel-by-pixel. Internally the transform may maintain an 8 or 16-bit + * gamma correction table (to 16-bit linear) to speed things up. + * + * NOTE: this transform must follow PNG_TR_COMPOSE with no intervening + * transforms; see the code in png_init_background (pngrtran.c) which + * relies on this during PNG_TC_INIT_FORMAT. + */ +# define PNG_TR_COMPOSE_ALPHA (PNG_TR_ARITHMETIC + 0x0300U) + /* Compose alpha composition and tRNS handling when the background is a + * screen color. Pushed by PNG_TR_COMPOSE as required. + */ +# define PNG_TR_GAMMA_ENCODE (PNG_TR_ARITHMETIC + 0x1F00U) /* last */ + /* Gamma encode the input. This encodes the gray or RGB channels to the + * required bit depth and either scales the alpha channel or encodes it as + * well, depending on the requested alpha encoding. + */ + /* + * The 'expand' operations come after the arithmetic ones in libpng 1.7, this + * forces the arithmetic stuff to do the expand, but since arithmetic is (in + * 1.7) normally done in 16-bit linear this avoids spurious expands. + */ +# define PNG_TR_EXPAND (PNG_TR_CHANNEL + 0x0100U) + /* Includes: + * + * PNG_TR_EXPAND_PALETTE palette images only, includes tRNS + * PNG_TR_EXPAND_LBP_GRAY grayscale low-bit depth only + * PNG_TR_EXPAND_tRNS non-palette images only + */ +# define PNG_TR_SCALE_16_TO_8 (PNG_TR_CHANNEL + 0x0200U) + /* Comes after the expand and before the chop version; note that it works on + * the pixel values directly, so it is a linear transform on a non-linear + * value. + */ + /* + * To handle transforms that affect the palette entries, not the palette + * indices in the row data, libpng 1.7 reorders some of the post-quantize + * transformations to put all the "PC" transforms ahead of all the "PI" + * transforms. The "PC" transforms that came after png_do_quantize in libpng + * 1.6 cannot be ordered to be before so they are included in the + * PNG_TR_QUANTIZE section. The PI transforms are all in PNG_TR_ENCODING, + * PNG_GRAY_TO_RGB is moved before PNG_TR_QUANTIZE to avoid the unpredictable + * behavior of png_set_quantize that otherwise arises. + * + * The transforms in the PNG_TR_QUANTIZE section are: + * + * PNG_TR_EXPAND_16 !P !W + * PNG_RWTR_INVERT_MONO !P W10: invert the gray channel + * PNG_RWTR_INVERT_ALPHA PC W8: invert the alpha channel + * PNG_RWTR_SHIFT PC W6: read: down, write: scale up + * PNG_RWTR_BGR !P W9 + * PNG_RWTR_FILLER !P W2: add on R, remove on W + * PNG_RWTR_SWAP_ALPHA !P W7 + * PNG_RWTR_SWAP_16 !P W5 + * + * The ones in PNG_TR_ENCODING are: + * + * PNG_RWTR_PACK PI W4: R: unpack bytes, W: pack + * PNG_RWTR_PIXEL_SWAP PI W3: Swap pixels in a byte + * PNG_RWTR_USER PI W1 + */ + +# define PNG_TR_CHANNEL_PREQ (PNG_TR_CHANNEL + 0x1F00U) + /* The channel swap transforms that must happen before PNG_TR_QUANTIZE: + * + * PNG_TR_STRIP_ALPHA + * PNG_TR_CHOP_16_TO_8 + * PNG_TR_GRAY_TO_RGB + */ +# define PNG_TR_CHANNEL_POSTQ (PNG_TR_QUANTIZE + 0x0100U) + /* The post-quantize channel swap transforms: + * + * PNG_TR_EXPAND_16 !P !W + * PNG_RWTR_BGR !P W9 + * PNG_RWTR_FILLER !P W2: (filler) add on R, remove on W + * PNG_RWTR_SWAP_ALPHA !P W7 + * PNG_RWTR_SWAP_16 !P W5 + * + * The 'CHANNEL' operation sets the transform_control channel_add flag for + * use below. + */ +# define PNG_TR_INVERT (PNG_TR_QUANTIZE + 0x0200U) + /* Invert MONO and ALPHA. If the channel_add flag is set in the transform + * control INVERT_ALPHA will not be done; the png_add_alpha/filler APIs + * happened after png_set_invert_alpha in earlier versions so the filler + * value had to include the invert. + * + * PNG_RWTR_INVERT_MONO !P W10: invert the gray channel + * PNG_RWTR_INVERT_ALPHA PC W8: invert the alpha channel + */ +# define PNG_TR_SHIFT (PNG_TR_QUANTIZE + 0x0300U) + /* The channel shift, except that if the channel_add flag has been set the + * alpha channel is not shifted. + * + * PNG_RWTR_SHIFT PC W6: read: down, write: scale up + */ +# define PNG_TR_PACK (PNG_TR_ENCODING + 0x0200U) + /* PNG_RWTR_PACK PI W4: R: unpack bytes, W: pack */ +# define PNG_TR_PIXEL_SWAP (PNG_TR_ENCODING + 0x0300U) + /* PNG_RWTR_PIXEL_SWAP PI W3: Swap pixels in a byte */ +# define PNG_TR_USER (PNG_TR_ENCODING + 0x1F00U) + /* The user transform; must be last before the interlace handling because it + * does unpredictable things to the format. + * + * PNG_RWTR_USER PI W1 + */ + +PNG_INTERNAL_FUNCTION(png_transformp,png_push_transform,(png_structrp png_ptr, + size_t size, png_transform_fn fn, png_transformp *transform, + png_transform_controlp tc),PNG_EMPTY); + /* As png_add_transform except that the new transform is inserted ahead of + * the given transform (*transform). The new transform is returned, but it + * will also invariably be in *transform. If 'tc' is not NULL the transform + * callback will also be called; it needs to be called if this function is + * called while transforms are being run. + * + * 'fn' must not be NULL. + * + * The transform is inserted with the same 'order' as the passed in + * *transform, that transform and following transforms are moved up ('order' + * is incremented) as required to make space. Consequently, unlike with + * png_add_transform, the transform will always be new. To detect loops + * (*transform)->fn must not be the same as the passed in 'fn'. + */ + +PNG_INTERNAL_FUNCTION(png_voidp,png_transform_cast_check, + (png_const_structp png_ptr, unsigned int src_line, png_transformp tr, + size_t size),PNG_EMPTY); + /* Given a pointer to a transform, 'tr' validate that the underlying derived + * class has size 'size' using the tr->size field and return the same + * pointer. If there is a size mismatch the function does an affirm using + * the given line number. + */ +#define png_transform_cast(type, pointer) png_voidcast(type*,\ + png_transform_cast_check(png_ptr, PNG_SRC_LINE, (pointer), sizeof (type))) + /* This takes a pointer to a transform and safely returns a pointer to a + * derived transform class (type); type must not have the pointer. It + * validates the 'size' field. Derived classes start with a png_transform + * as the first member called 'tr'. + */ +#endif /* TRANSFORM_MECH_SUPPORTED */ + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +/* Remove a transform from a list, moving the next transform down into + * *transform. + */ +PNG_INTERNAL_FUNCTION(void,png_remove_transform,(png_const_structp png_ptr, + png_transformp *transform),PNG_EMPTY); + +/* Initializer for read transforms that handles caching, palette update and + * palette expansion. + */ +PNG_INTERNAL_FUNCTION(unsigned int,png_read_init_transform_mech, + (png_structp png_ptr, png_transform_control *tc),PNG_EMPTY); + /* Optional call to update the users info structure */ PNG_INTERNAL_FUNCTION(void,png_read_transform_info,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); + png_inforp info_ptr),PNG_EMPTY); #endif -#if defined(PNG_READ_TRANSFORMS_SUPPORTED) ||\ - defined(PNG_WRITE_TRANSFORMS_SUPPORTED) -/***************************** READ and WRITE TRANSFORMS *********************** - * These structures are used in pngrtran.c, pngwtran.c and pngtrans.c to hold - * information about transforms in progress. This mechanism was introduced in - * libpng 1.7.0 to ensure reliable transform code and to fix multiple bugs in - * the pre-1.7 transform handling. - * - * Prior to 1.7.0 the internal transform routines 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. - ***************************** READ and WRITE TRANSFORMS **********************/ -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 priority; /* priority of the previous transform (see the - * explanation below for png_transform). */ -# ifdef PNG_READ_GAMMA_SUPPORTED - png_fixed_point gamma; /* Actual gamma of the row data */ - png_fixed_point gamma_out; /* Expected final gamma after gamma encoding */ -# endif - 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) +/* APIs which do a tranform on both read and write but where the implementation + * is separate for each; the read and write init functions are in pngrtran.c or + * pngwtran.c, the API is in pngtrans.c */ -#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; +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_init_read_pack,(png_transformp *transform, + png_transform_controlp tc),PNG_EMPTY); +#endif /* READ_PACK || READ_EXPAND */ +#ifdef PNG_WRITE_PACK_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_init_write_pack,(png_transformp *transform, + png_transform_controlp tc),PNG_EMPTY); +#endif /* WRITE_PACK */ /* 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_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_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_transform_controlp row_info, - png_bytep row),PNG_EMPTY); -#endif +#endif /* FILLER */ #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_transform_controlp row_info, png_bytep row),PNG_EMPTY); -#endif +#endif /* INVERT */ -#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) -PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_transform_controlp row_info, +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_invert_alpha,(png_transform_controlp row_info, png_bytep row),PNG_EMPTY); -#endif -#endif /* READ_TRANSFORMS || WRITE_TRANSFORMS */ +#endif /* INVERT_ALPHA */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_add_rgb_to_gray_byte_ops,(png_structrp png_ptr, + png_transform_controlp tc, unsigned int index, unsigned int order), + PNG_EMPTY); + /* This is an init-time utility to add appropriate byte ops to select a given + * channel from R/G/B. + */ +#endif /* READ_RGB_TO_GRAY */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) &&\ + defined(PNG_READ_BACKGROUND_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_push_gray_to_rgb_byte_ops,(png_transformp *tr, + png_transform_controlp tc), PNG_EMPTY); + /* This is an init-time utility to push appropriate byte ops to expand a + * grayscale PNG data set to RGB. It calls the function callback so 'tc' + * must be non-NULL. + */ +#endif /* GRAY_TO_RGB && READ_BACKGROUND */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_add_strip_alpha_byte_ops,(png_structrp png_ptr), + PNG_EMPTY); + /* Called from pngrtran.c to add the relevant byte op. */ +#endif /* READ_STRIP_ALPHA */ /* The following decodes the appropriate chunks, and does error correction, * then calls the appropriate callback for the chunk if it is valid. */ -/* Decode the IHDR chunk */ -PNG_INTERNAL_FUNCTION(void,png_handle_IHDR,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_handle_PLTE,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_handle_IEND,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#ifdef PNG_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(png_bytep,png_read_buffer,(png_structrp png_ptr, + png_alloc_size_t new_size, int warn),PNG_EMPTY); + /* Manage the dynamically allocated read buffer */ -#ifdef PNG_READ_bKGD_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_bKGD,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +/* Shared READ IDAT handling: */ +PNG_INTERNAL_FUNCTION(void,png_read_start_IDAT,(png_structrp png_ptr), + PNG_EMPTY); + /* Initialize the row buffers, etc. */ + +typedef enum +{ + png_row_incomplete, + /* more IDAT data needed for row */ + png_row_process, + /* png_struct::row_buffer contains a complete, transformed, row */ + png_row_repeat, + /* row not in this pass, but the existing row may be used */ + png_row_skip + /* row not in pass and no appropriate data; skip this row */ +} png_row_op; +PNG_INTERNAL_FUNCTION(png_row_op,png_read_process_IDAT,(png_structrp png_ptr), + PNG_EMPTY); + /* Process a block of IDAT data; the routine returns early if it has + * obtained a row. It is valid to call this routine with no input data; + * it will return PNG_ROW_INCOMPLETE if it needs input. + */ + +PNG_INTERNAL_FUNCTION(int,png_read_finish_IDAT,(png_structrp png_ptr), + PNG_EMPTY); + /* Complete reading of the IDAT chunks. This returns 0 if more data is to + * be read, 1 if the zlib stream has terminated. Call this routine with + * zstream.avail_in greater than zero unless there is no more input data. + * When zstream_avail_in is 0 on entry and the stream does not terminate + * an "IDAT truncated" error will be output. + * + * ENTRY: png_ptr->zstream.{next,avail}_in points to more IDAT data, if + * available, otherwise avail_in should be 0. + * RET 0: the LZ stream is still active, more IDAT date is required, if + * available, the routine *must* be called again. + * RET 1: the LZ stream has been closed and an error may have been output; + * png_ptr->zstream_error says whether it has. If not and there + * is more IDAT data available the caller should output an + * appropriate (too much IDAT) error message. + */ +#endif /* READ */ + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_cache_known_unknown,(png_structrp png_ptr, + png_const_bytep add, int keep),PNG_EMPTY); + /* Update the png_struct::known_unknown bit cache which stores whether each + * known chunk should be treated as unknown. + */ #endif -#ifdef PNG_READ_cHRM_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif +typedef enum +{ + png_chunk_skip = 0, /* Skip this chunk */ + png_chunk_unknown, /* Pass the chunk to png_handle_unknown */ + png_chunk_process_all, /* Process the chunk all at once */ + png_chunk_process_part /* Process the chunk in parts (for IDAT) */ +} png_chunk_op; -#ifdef PNG_READ_gAMA_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_hIST_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_hIST,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_iCCP_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_iCCP,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif /* READ_iCCP */ - -#ifdef PNG_READ_iTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_iTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_oFFs_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_oFFs,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_pCAL,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_pHYs_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_pHYs,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_sBIT_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sBIT,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_sCAL_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sCAL,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_sPLT_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sPLT,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif /* READ_sPLT */ - -#ifdef PNG_READ_sRGB_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sRGB,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_tEXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_tEXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_tIME_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_tIME,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_tRNS_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_tRNS,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_zTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_zTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_structrp png_ptr, - png_uint_32 chunk_name),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(png_chunk_op,png_find_chunk_op,(png_structrp png_ptr), + PNG_EMPTY); + /* Given a chunk in png_struct::{chunk_name,chunk_length} validate the name + * and work out how it should be handled. This function checks the chunk + * location using png_struct::mode and will set the mode appropriately for + * the known critical chunks but otherwise makes no changes to the stream + * read state. + */ +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY); - /* This is the function that gets called for unknown chunks. The 'keep' - * argument is either non-zero for a known chunk that has been set to be - * handled as unknown or zero for an unknown chunk. By default the function - * just skips the chunk or errors out if it is critical. + png_inforp info_ptr, png_bytep chunk_data),PNG_EMPTY); + /* Handle an unknown chunk that needs to be processed. It is only valid + * to call this after png_find_chunk_op returns png_chunk_unknown. The + * data argument points to the png_struct::chunk_length bytes of the chunk + * data. */ +#endif /* READ_UNKNOWN_CHUNKS */ -#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\ - defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED) -PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling, - (png_const_structrp png_ptr, png_uint_32 chunk_name),PNG_EMPTY); - /* Exactly as the API png_handle_as_unknown() except that the argument is a - * 32-bit chunk name, not a string. +PNG_INTERNAL_FUNCTION(void,png_handle_chunk,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); + /* The chunk to handle is in png_struct::chunk_name,chunk_length. + * + * NOTE: at present it is only valid to call this after png_find_chunk_op + * has returned png_chunk_process_all and all the data is available for + * png_handle_chunk (via the libpng read callback.) */ -#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); -#endif -#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_do_write_transformations,(png_structrp png_ptr, - png_row_infop row_info),PNG_EMPTY); -#endif - -#ifdef PNG_READ_TRANSFORMS_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_init_read_transformations,(png_structrp png_ptr), - PNG_EMPTY); -#endif - -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_read_chunk,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_sig,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_check_crc,(png_structrp png_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr), - PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr, - png_bytep buffer, png_size_t buffer_length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr, - png_bytep buffer, png_size_t buffer_length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr), - PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_have_row,(png_structrp png_ptr, - png_bytep row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_end,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr), - PNG_EMPTY); -# ifdef PNG_READ_tEXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_handle_tEXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_tEXt,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -# endif -# ifdef PNG_READ_zTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_handle_zTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_zTXt,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -# endif -# ifdef PNG_READ_iTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_handle_iTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_iTXt,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -# endif - -#endif /* PROGRESSIVE_READ */ +PNG_INTERNAL_FUNCTION(void,png_init_row_info,(png_structrp png_ptr),PNG_EMPTY); + /* Set the png_struct::row_ members from the PNG file information, running + * transforms if required. + */ /* Added at libpng version 1.6.0 */ #ifdef PNG_GAMMA_SUPPORTED @@ -1724,7 +1723,7 @@ PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr, #ifdef PNG_iCCP_SUPPORTED PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr, png_colorspacerp colorspace, png_const_charp name, - png_uint_32 profile_length, png_const_bytep profile, int color_type), + png_uint_32 profile_length, png_const_bytep profile, int is_color), PNG_EMPTY); /* The 'name' is used for information only */ @@ -1735,7 +1734,7 @@ PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr, PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr, png_colorspacerp colorspace, png_const_charp name, png_uint_32 profile_length, - png_const_bytep profile /* first 132 bytes only */, int color_type), + png_const_bytep profile /* first 132 bytes only */, int is_color), PNG_EMPTY); PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr, png_colorspacerp colorspace, png_const_charp name, @@ -1749,14 +1748,8 @@ PNG_INTERNAL_FUNCTION(void,png_icc_set_sRGB,( * be zero to indicate that it is not available. It is used, if provided, * as a fast check on the profile when checking to see if it is sRGB. */ -#endif +#endif /* sRGB */ #endif /* iCCP */ - -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients, - (png_structrp png_ptr), PNG_EMPTY); - /* Set the rgb_to_gray coefficients from the colorspace Y values */ -#endif /* READ_RGB_TO_GRAY */ #endif /* COLORSPACE */ /* Added at libpng version 1.4.0 */ @@ -1765,13 +1758,6 @@ PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr, int color_type, int interlace_type, int compression_type, int filter_type),PNG_EMPTY); -/* Added at libpng version 1.5.10 */ -#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_transform_controlp row_info),PNG_EMPTY); -#endif - #if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) PNG_INTERNAL_FUNCTION(void,png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN); @@ -1889,11 +1875,19 @@ PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr, * data set via an application call to a png_set_ API and png_app_error or * png_app_warning is used as appropriate. * + * With PNG_CHUNK_FATAL an error can be marked as unrecoverable, and the + * function will not return. + * * The 'error' parameter must have one of the following values: */ #define PNG_CHUNK_WARNING 0 /* never an error */ #define PNG_CHUNK_WRITE_ERROR 1 /* an error only on write */ #define PNG_CHUNK_ERROR 2 /* always an error */ +#define PNG_CHUNK_FATAL 3 /* an unrecoverable error */ + +#ifndef PNG_ERROR_TEXT_SUPPORTED +# define png_chunk_report(pp,e,v) png_chunk_report(pp,NULL,v) +#endif /* ASCII to FP interfaces, currently only implemented if sCAL * support is required. @@ -2029,22 +2023,9 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string, */ PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a, png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY); -#endif +#endif /* GAMMA || INCH_CONVERSIONS || READ_pHYs */ -#ifdef PNG_GAMMA_SUPPORTED -/* Calculate a reciprocal - used for gamma values. This returns - * 0 if the argument is 0 in order to maintain an undefined value; - * there are no warnings. - */ -PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a), - PNG_EMPTY); - -/* Return true if the gamma value is significantly different from 1.0 */ -PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value), - PNG_EMPTY); -#endif - -#ifdef PNG_READ_GAMMA_SUPPORTED +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED /* Internal fixed point gamma correction. These APIs are called as * required to convert single values - they don't need to be fast, * they are not used when processing image pixel values. @@ -2052,12 +2033,31 @@ PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value), * While the input is an 'unsigned' value it must actually be the * correct bit value - 0..255 or 0..65535 as required. */ -PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_16bit_correct,( - png_const_structrp png_ptr, png_uint_32 value, png_fixed_point gamma_value), +PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_16bit_correct, + (png_const_structrp png_ptr, png_uint_32 value, png_fixed_point gamma_value), PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr), - PNG_EMPTY); -#endif + +/* Internal check function to saw if the gamma of the PNG data is far enough + * from the given screen gamma to require gamma correction (only needed for a + * bug work-round in the simplified API). + * TODO: it should be possible to remove the bug work-round in 1.7 + */ +PNG_INTERNAL_FUNCTION(int,png_need_gamma_correction,(png_const_structrp png_ptr, + png_fixed_point gamma, int sRGB_output),PNG_EMPTY); +#endif /* SIMPLIFIED_READ && */ + +/* This is a utility macro to say whether a gamma value is close enough to sRGB. + * The test is now hardwired: + * + * API CHANGE: prior to 1.7 this would depend on the build-time + * PNG_GAMMA_THRESHOLD_FIXED setting, which would cause inconsistent results + * when the setting was changed. Since this setting can now be changed at + * run-time it seems more sensible to have a single fixed definition of 'sRGB'. + * + * The test is approximately +/- 1%, it allows any decimal value from 0.45 (the + * two digit rounded version of 1/2.2) to just under 0.46). + */ +#define PNG_GAMMA_IS_sRGB(g) ((g) >= 45000 && (g) < 46000) /* SIMPLIFIED READ/WRITE SUPPORT */ #if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ @@ -2108,6 +2108,14 @@ PNG_INTERNAL_FUNCTION(int,png_safe_execute,(png_imagep image, PNG_INTERNAL_FUNCTION(int,png_image_error,(png_imagep image, png_const_charp error_message),PNG_EMPTY); +/* Safely initialize a stdio pointer - used by both the read and the write + * code. + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_image_init_io,(png_imagep image, png_FILE_p file), + PNG_EMPTY); +#endif /* STDIO */ + #ifndef PNG_SIMPLIFIED_READ_SUPPORTED /* png_image_free is used by the write code but not exported */ PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY); @@ -2137,6 +2145,11 @@ PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_neon, #include "pngdebug.h" +/* EXTENSION SPECIFIC FUNCTIONS */ +#ifdef PNG_EXTENSION_HEADER +# include PNG_EXTENSION_HEADER +#endif /* EXTENSION_HEADER */ + #ifdef __cplusplus } #endif diff --git a/pngread.c b/pngread.c index 576b1f033..3d7b1247f 100644 --- a/pngread.c +++ b/pngread.c @@ -38,12 +38,12 @@ png_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action) break; case PNG_CRC_WARN_USE: /* Warn/use data */ - png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags &= PNG_BIC_MASK(PNG_FLAG_CRC_CRITICAL_MASK); png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE; break; case PNG_CRC_QUIET_USE: /* Quiet/use data */ - png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags &= PNG_BIC_MASK(PNG_FLAG_CRC_CRITICAL_MASK); png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE | PNG_FLAG_CRC_CRITICAL_IGNORE; break; @@ -55,7 +55,7 @@ png_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action) case PNG_CRC_DEFAULT: default: - png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags &= PNG_BIC_MASK(PNG_FLAG_CRC_CRITICAL_MASK); break; } @@ -66,18 +66,18 @@ png_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action) break; case PNG_CRC_WARN_USE: /* Warn/use data */ - png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags &= PNG_BIC_MASK(PNG_FLAG_CRC_ANCILLARY_MASK); png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE; break; case PNG_CRC_QUIET_USE: /* Quiet/use data */ - png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags &= PNG_BIC_MASK(PNG_FLAG_CRC_ANCILLARY_MASK); png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN; break; case PNG_CRC_ERROR_QUIT: /* Error/quit */ - png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags &= PNG_BIC_MASK(PNG_FLAG_CRC_ANCILLARY_MASK); png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN; break; @@ -85,7 +85,7 @@ png_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action) case PNG_CRC_DEFAULT: default: - png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags &= PNG_BIC_MASK(PNG_FLAG_CRC_ANCILLARY_MASK); break; } } @@ -117,31 +117,34 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, if (png_ptr != NULL) { - png_ptr->mode = PNG_IS_READ_STRUCT; + png_ptr->read_struct = 1; /* Added in libpng-1.6.0; this can be used to detect a read structure if * required (it will be zero in a write structure.) */ # ifdef PNG_SEQUENTIAL_READ_SUPPORTED png_ptr->IDAT_read_size = PNG_IDAT_READ_SIZE; -# endif +# endif /* SEQUENTIAL_READ */ # ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; + png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; /* In stable builds only warn if an application error can be completely * handled. */ # if PNG_RELEASE_BUILD - png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; + png_ptr->flags |= PNG_FLAG_APP_ERRORS_WARN; # endif -# endif +# endif /* BENIGN_READ_ERRORS */ - /* TODO: delay this, it can be done in png_init_io (if the app doesn't - * do it itself) avoiding setting the default function if it is not - * required. - */ - png_set_read_fn(png_ptr, NULL, NULL); +# ifdef PNG_READ_GAMMA_SUPPORTED + /* Default gamma correction values: */ +#if 0 /*NYI*/ + png_ptr->gamma_accuracy = PNG_DEFAULT_GAMMA_ACCURACY; +#endif /*NYI*/ + png_ptr->gamma_threshold = PNG_GAMMA_THRESHOLD_FIXED; +# endif /* READ_GAMMA */ } return png_ptr; @@ -149,6 +152,62 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, #ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the chunk header (length + type name). + * Put the type name into png_ptr->chunk_name, and return the length. + */ +static void +png_read_chunk_header(png_structrp png_ptr) +{ + png_byte buf[8]; + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR; +#endif + + /* Read the length and the chunk name. + * This must be performed in a single I/O call. + */ + png_read_data(png_ptr, buf, 8); + + /* Put the chunk name into png_ptr->chunk_name. */ + png_ptr->chunk_length = png_get_uint_31(png_ptr, buf); + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4); + + png_debug2(0, "Reading %lx chunk, length = %lu", + (unsigned long)png_ptr->chunk_name, (unsigned long)length); + + /* Reset the crc and run it over the chunk name. */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, buf + 4, 4); + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA; +#endif +} + +static void +png_read_sequential_unknown(png_structrp png_ptr, png_inforp info_ptr) +{ +# ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + /* Read the data for an unknown chunk. The read buffer is used: */ + png_bytep buffer = png_read_buffer(png_ptr, png_ptr->chunk_length, + PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)); /* error if critical */ + + if (buffer != NULL) + { + png_crc_read(png_ptr, buffer, png_ptr->chunk_length); + png_crc_finish(png_ptr, 0); + png_handle_unknown(png_ptr, info_ptr, buffer); + } + + else /* out of memory on an ancillary chunk; skip the chunk */ +# else /* !READ_UNKNOWN_CHUNKS */ + /* or, no support for reading unknown chunks, so just skip it. */ + PNG_UNUSED(info_ptr) +# endif /* !READ_UNKNOWN_CHUNKS */ + png_crc_finish(png_ptr, png_ptr->chunk_length); +} + /* Read the information before the actual image data. This has been * changed in v0.90 to allow reading a file that already has the magic * bytes read from the stream. You can tell libpng how many bytes have @@ -160,196 +219,65 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, void PNGAPI png_read_info(png_structrp png_ptr, png_inforp info_ptr) { -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - int keep; -#endif - png_debug(1, "in png_read_info"); if (png_ptr == NULL || info_ptr == NULL) return; - /* Read and check the PNG file signature. */ + /* Read and check the PNG file signature (this may do nothing if it has + * already been read.) + */ png_read_sig(png_ptr, info_ptr); - for (;;) + /* Loop reading chunks until an IDAT is encountered or we reach the end of + * the stream (IEND). + * + * Prior to 1.7.0 this function behaved very weirdly if called after the + * IDATs had been read; it would keep on reading chunks util it found + * another IDAT. This could cause it to read beyond IEND, damaging the + * state in the host stream. This is now caught by the check below. + */ + while ((png_ptr->mode & (PNG_HAVE_IEND|PNG_HAVE_IDAT)) == 0) { - png_uint_32 length = png_read_chunk_header(png_ptr); - png_uint_32 chunk_name = png_ptr->chunk_name; - - /* IDAT logic needs to happen here to simplify getting the two flags - * right. - */ - if (chunk_name == png_IDAT) + png_read_chunk_header(png_ptr); + switch (png_find_chunk_op(png_ptr)) { - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "Missing IHDR before IDAT"); - - else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - (png_ptr->mode & PNG_HAVE_PLTE) == 0) - png_chunk_error(png_ptr, "Missing PLTE before IDAT"); - - else if ((png_ptr->mode & PNG_AFTER_IDAT) != 0) - png_chunk_benign_error(png_ptr, "Too many IDATs found[s]"); - - png_ptr->mode |= PNG_HAVE_IDAT; - } - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - png_ptr->mode |= PNG_AFTER_IDAT; - - /* This should be a binary subdivision search or a hash for - * matching the chunk name rather than a linear search. - */ - if (chunk_name == png_IHDR) - png_handle_IHDR(png_ptr, info_ptr, length); - - else if (chunk_name == png_IEND) - png_handle_IEND(png_ptr, info_ptr, length); - -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) - { - png_handle_unknown(png_ptr, info_ptr, length, keep); - - if (chunk_name == png_PLTE) - png_ptr->mode |= PNG_HAVE_PLTE; - - else if (chunk_name == png_IDAT) - { - png_ptr->idat_size = 0; /* It has been consumed */ + default: + impossible("invalid chunk op"); + /* FALL THROUGH */ + case png_chunk_skip: + png_crc_finish(png_ptr, png_ptr->chunk_length); break; - } + + case png_chunk_unknown: + png_read_sequential_unknown(png_ptr, info_ptr); + break; + + case png_chunk_process_all: + png_handle_chunk(png_ptr, info_ptr); + break; + + case png_chunk_process_part: + debug(png_ptr->mode & PNG_HAVE_IDAT); + return; } -#endif - else if (chunk_name == png_PLTE) - png_handle_PLTE(png_ptr, info_ptr, length); - - else if (chunk_name == png_IDAT) - { - png_ptr->idat_size = length; - break; - } - -#ifdef PNG_READ_bKGD_SUPPORTED - else if (chunk_name == png_bKGD) - png_handle_bKGD(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_cHRM_SUPPORTED - else if (chunk_name == png_cHRM) - png_handle_cHRM(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_gAMA_SUPPORTED - else if (chunk_name == png_gAMA) - png_handle_gAMA(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_hIST_SUPPORTED - else if (chunk_name == png_hIST) - png_handle_hIST(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_oFFs_SUPPORTED - else if (chunk_name == png_oFFs) - png_handle_oFFs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED - else if (chunk_name == png_pCAL) - png_handle_pCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sCAL_SUPPORTED - else if (chunk_name == png_sCAL) - png_handle_sCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pHYs_SUPPORTED - else if (chunk_name == png_pHYs) - png_handle_pHYs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sBIT_SUPPORTED - else if (chunk_name == png_sBIT) - png_handle_sBIT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sRGB_SUPPORTED - else if (chunk_name == png_sRGB) - png_handle_sRGB(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iCCP_SUPPORTED - else if (chunk_name == png_iCCP) - png_handle_iCCP(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sPLT_SUPPORTED - else if (chunk_name == png_sPLT) - png_handle_sPLT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tEXt_SUPPORTED - else if (chunk_name == png_tEXt) - png_handle_tEXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tIME_SUPPORTED - else if (chunk_name == png_tIME) - png_handle_tIME(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tRNS_SUPPORTED - else if (chunk_name == png_tRNS) - png_handle_tRNS(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_zTXt_SUPPORTED - else if (chunk_name == png_zTXt) - png_handle_zTXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iTXt_SUPPORTED - else if (chunk_name == png_iTXt) - png_handle_iTXt(png_ptr, info_ptr, length); -#endif - - else - png_handle_unknown(png_ptr, info_ptr, length, - PNG_HANDLE_CHUNK_AS_DEFAULT); } -} -#endif /* SEQUENTIAL_READ */ -/* Optional call to update the users info_ptr structure */ -void PNGAPI -png_read_update_info(png_structrp png_ptr, png_inforp info_ptr) -{ - png_debug(1, "in png_read_update_info"); + /* The loop was ended by IDAT or IEND, but if an IEND was seen the read code + * (png_handle_position in pngrutil.c) should have errored out, therefore: + */ +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + affirm(png_ptr->chunk_name == png_IDAT && ((png_ptr->known_unknown)&1U)); +# else + debug(png_ptr->chunk_name == png_IDAT); + impossible("unknown IDAT"); +# endif - if (png_ptr != NULL) - { - if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) - { - png_read_start_row(png_ptr); - -# ifdef PNG_READ_TRANSFORMS_SUPPORTED - png_read_transform_info(png_ptr, info_ptr); -# else - PNG_UNUSED(info_ptr) -# endif - } - - /* New in 1.6.0 this avoids the bug of doing the initializations twice */ - else - png_app_error(png_ptr, - "png_read_update_info/png_start_read_image: duplicate call"); - } + /* And the code cannot have left it unread; it must have called one of the + * handlers, so we are skipping IDAT. + */ } -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Initialize palette, background, etc, after transformations * are set, but before any reading takes place. This allows * the user to obtain a gamma-corrected palette, for example. @@ -362,323 +290,179 @@ png_start_read_image(png_structrp png_ptr) if (png_ptr != NULL) { - if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) - png_read_start_row(png_ptr); + if (png_ptr->zowner != png_IDAT) + png_read_start_IDAT(png_ptr); - /* New in 1.6.0 this avoids the bug of doing the initializations twice */ + /* New in 1.6.0 this avoids the bug of doing the initializations twice, + * it could be a warning but in practice it indicates that the app may + * have made png_get_ calls on png_ptr assuming that it hadn't been + * 'started'. + */ else png_app_error(png_ptr, "png_start_read_image/png_read_update_info: duplicate call"); } } -#endif /* SEQUENTIAL_READ */ -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED -#ifdef PNG_MNG_FEATURES_SUPPORTED -/* Undoes intrapixel differencing, - * NOTE: this is apparently only supported in the 'sequential' reader. - */ static void -png_do_read_intrapixel(png_row_infop row_info, png_bytep row) +png_read_IDAT(png_structrp png_ptr) { - png_debug(1, "in png_do_read_intrapixel"); + /* Read more input data, up to png_struct::IDAT_read_size, stop at the + * end of the IDAT stream: + */ + uInt IDAT_size = 0; + png_bytep buffer = + png_read_buffer(png_ptr, png_ptr->IDAT_read_size, 0/*error*/); - if ( - (row_info->color_type & PNG_COLOR_MASK_COLOR) != 0) + png_ptr->zstream.next_in = buffer; + + while (png_ptr->chunk_name == png_IDAT && + IDAT_size < png_ptr->IDAT_read_size) { - int bytes_per_pixel; - png_uint_32 row_width = row_info->width; + png_uint_32 l = png_ptr->chunk_length; - if (row_info->bit_depth == 8) + if (l == 0) /* end of this IDAT */ { - png_bytep rp; - png_uint_32 i; + png_crc_finish(png_ptr, 0); + png_read_chunk_header(png_ptr); - if (row_info->color_type == PNG_COLOR_TYPE_RGB) - bytes_per_pixel = 3; - - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - bytes_per_pixel = 4; - - else - return; - - for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + if (png_ptr->chunk_name != png_IDAT) /* end of all IDAT */ { - /* TODO: explain the +256 */ - *(rp) = (png_byte)((256 + *rp + *(rp + 1)) & 0xff); - *(rp+2) = (png_byte)((256 + *(rp + 2) + *(rp + 1)) & 0xff); + png_ptr->mode |= PNG_AFTER_IDAT; + break; } + + l = png_ptr->chunk_length; } - else if (row_info->bit_depth == 16) - { - png_bytep rp; - png_uint_32 i; - if (row_info->color_type == PNG_COLOR_TYPE_RGB) - bytes_per_pixel = 6; + /* Read from the IDAT chunk into the buffer, up to IDAT_read_size: + */ + if (l > png_ptr->IDAT_read_size - IDAT_size) /* SAFE: while check */ + l = png_ptr->IDAT_read_size - IDAT_size; - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - bytes_per_pixel = 8; - - else - return; - - for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) - { - png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); - png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); - png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); - /* TODO: explain the +65536 */ - png_uint_32 red = (s0 + s1 + 65536) & 0xffff; - png_uint_32 blue = (s2 + s1 + 65536) & 0xffff; - *(rp ) = (png_byte)((red >> 8) & 0xff); - *(rp + 1) = (png_byte)(red & 0xff); - *(rp + 4) = (png_byte)((blue >> 8) & 0xff); - *(rp + 5) = (png_byte)(blue & 0xff); - } - } + png_crc_read(png_ptr, buffer+IDAT_size, l); + IDAT_size += /*SAFE*/l; + png_ptr->chunk_length -= l; } + + /* IDAT_size may be zero if the compressed image stream is truncated; + * this is likely given a broken PNG. + */ + png_ptr->zstream.next_in = buffer; + png_ptr->zstream.avail_in = IDAT_size; } -#endif /* MNG_FEATURES */ + +#ifndef PNG_READ_DEINTERLACE_SUPPORTED + /* No png_combine_row; just copy the row bytes: */ +# define png_combine_row(pp, pb, display) png_copy_row((pp), (pb)) +#endif void PNGAPI png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) + /* It is valid to call this API with both 'row' and 'dsp_row' NULL, all + * the processing gets done. This is only useful for, either, performance + * testing (but it skips png_combine_row) or if there is a user transform + * or user row callback which actually uses the row data. + */ { - png_row_info row_info; - if (png_ptr == NULL) return; png_debug2(1, "in png_read_row (row %lu, pass %d)", (unsigned long)png_ptr->row_number, png_ptr->pass); - /* png_read_start_row sets the information (in particular iwidth) for this - * interlace pass. + /* Check the row number; if png_read_process_IDAT is called too many times + * if issues an affirm, but, while this is appropriate for the progressive + * reader, it is an app error if it happens here. */ - if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) - png_read_start_row(png_ptr); - - /* 1.5.6: row_info moved out of png_struct to a local here. */ - row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */ - row_info.color_type = png_ptr->color_type; - row_info.bit_depth = png_ptr->bit_depth; - row_info.channels = png_ptr->channels; - row_info.pixel_depth = png_ptr->pixel_depth; - row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); - -#ifdef PNG_WARNINGS_SUPPORTED - if (png_ptr->row_number == 0 && png_ptr->pass == 0) + if (png_ptr->read_started && png_ptr->row_number == png_ptr->height-1 && + png_ptr->pass == (png_ptr->interlaced == PNG_INTERLACE_NONE ? 0 : 6)) { - /* Check for transforms that have been set but were defined out */ -#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED) - if ((png_ptr->transformations & PNG_INVERT_MONO) != 0) - png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined"); -#endif - -#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) - if ((png_ptr->transformations & PNG_FILLER) != 0) - png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined"); -#endif - -#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ - !defined(PNG_READ_PACKSWAP_SUPPORTED) - if ((png_ptr->transformations & PNG_PACKSWAP) != 0) - png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined"); -#endif - -#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) - if ((png_ptr->transformations & PNG_PACK) != 0) - png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined"); -#endif - -#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) - if ((png_ptr->transformations & PNG_SHIFT) != 0) - png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined"); -#endif - -#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) - if ((png_ptr->transformations & PNG_BGR) != 0) - png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined"); -#endif - -#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) - if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0) - png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined"); -#endif + png_app_error(png_ptr, "Too many calls to png_read_row"); + return; } -#endif /* WARNINGS */ -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* If interlaced and we do not need a new row, combine row and return. - * Notice that the pixels we have from previous rows have been transformed - * already; we can only combine like with like (transformed or - * untransformed) and, because of the libpng API for interlaced images, this - * means we must transform before de-interlacing. + /* Check this right at the start; functions like png_read_process_IDAT + * regard this condition as an internal error: */ - if (png_ptr->interlaced != 0 && - (png_ptr->transformations & PNG_INTERLACE) != 0) + if (png_ptr->zowner != png_IDAT) + png_read_start_IDAT(png_ptr); + + /* So reading has started: */ + png_ptr->read_started = 1; + + for (;;) { - switch (png_ptr->pass) + if (png_ptr->zstream.avail_in == 0) + png_read_IDAT(png_ptr); + + /* So... zstream.next_in may still be 0, but this may be enough for the + * next row if zlib is storing enough output state (it only need be enough + * for one byte, because png_read_process_IDAT keeps the next filter byte, + * so on the last row of the image only one byte might be required.) + * + * png_read_process_IDAT handles the case where the input has ended; mode + * has PNG_AFTER_IDAT set, by either doing png_error or using 0 bytes for + * the data (after issuing a warning.) + */ + switch (png_read_process_IDAT(png_ptr)) { - case 0: - if (png_ptr->row_number & 0x07) - { - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 1/*display*/); - png_read_finish_row(png_ptr); - return; - } - break; + case png_row_incomplete: + /* more IDAT data needed for row */ + debug(png_ptr->zstream.avail_in == 0); + continue; - case 1: - if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) - { - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 1/*display*/); + case png_row_process: + /* png_struct::row_buffer contains a complete, transformed, row; + * this is processed in both 'sparkle' and 'block' mode. The final + * parameter to png_combine_row is false, meaning only overwrite + * the pixels corresponding to this pass: + */ + if (row != NULL) + png_combine_row(png_ptr, row, 0/*'sparkle'*/); - png_read_finish_row(png_ptr); - return; - } - break; + goto display_row; /* Skip the 'DEINT' check */ - case 2: - if ((png_ptr->row_number & 0x07) != 4) - { - if (dsp_row != NULL && (png_ptr->row_number & 4)) - png_combine_row(png_ptr, dsp_row, 1/*display*/); + case png_row_repeat: + /* row not in this pass, but the existing row in + * png_struct::row_buffer may be used, this is only done if the + * 'block' or 'rectangle' mode of display is required. The final + * parameter to png_combine_row is true, meaning overwrite all the + * pixels belong to this and *later* passes. + */ +# ifdef PNG_READ_DEINTERLACE_SUPPORTED + if (!png_ptr->do_interlace) +# endif + continue; - png_read_finish_row(png_ptr); - return; - } - break; + display_row: + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 1/*'rectangle/block'*/); - case 3: - if ((png_ptr->row_number & 3) || png_ptr->width < 3) - { - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 1/*display*/); + goto row_fn; /* Skip the 'DEINT' check */ - png_read_finish_row(png_ptr); - return; - } - break; + case png_row_skip: + /* row not in pass and no appropriate data; skip this row, nothing + * more need be done, except the read_row_fn: + */ +# ifdef PNG_READ_DEINTERLACE_SUPPORTED + if (!png_ptr->do_interlace) +# endif + continue; - case 4: - if ((png_ptr->row_number & 3) != 2) - { - if (dsp_row != NULL && (png_ptr->row_number & 2)) - png_combine_row(png_ptr, dsp_row, 1/*display*/); + row_fn: + if (png_ptr->read_row_fn != NULL) + png_ptr->read_row_fn(png_ptr, png_ptr->row_number, + png_ptr->pass); - png_read_finish_row(png_ptr); - return; - } - break; - - case 5: - if ((png_ptr->row_number & 1) || png_ptr->width < 2) - { - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 1/*display*/); - - png_read_finish_row(png_ptr); - return; - } - break; + return; default: - case 6: - if ((png_ptr->row_number & 1) == 0) - { - png_read_finish_row(png_ptr); - return; - } - break; + impossible("not reached"); } } -#endif - - if ((png_ptr->mode & PNG_HAVE_IDAT) == 0) - png_error(png_ptr, "Invalid attempt to read row data"); - - /* Fill the row with IDAT data: */ - png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1); - - if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE) - { - if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST) - png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1, - png_ptr->prev_row + 1, png_ptr->row_buf[0]); - else - png_error(png_ptr, "bad adaptive filter value"); - } - - /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before - * 1.5.6, while the buffer really is this big in current versions of libpng - * it may not be in the future, so this was changed just to copy the - * interlaced count: - */ - memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1); - -#ifdef PNG_MNG_FEATURES_SUPPORTED - if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && - (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) - { - /* Intrapixel differencing */ - png_do_read_intrapixel(&row_info, png_ptr->row_buf + 1); - } -#endif - -#ifdef PNG_READ_TRANSFORMS_SUPPORTED - if (png_ptr->transformations) - png_do_read_transformations(png_ptr, &row_info); -#endif - - /* The transformed pixel depth should match the depth now in row_info. */ - if (png_ptr->transformed_pixel_depth == 0) - { - png_ptr->transformed_pixel_depth = row_info.pixel_depth; - if (row_info.pixel_depth > png_ptr->maximum_pixel_depth) - png_error(png_ptr, "sequential row overflow"); - } - - else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth) - png_error(png_ptr, "internal sequential row size calculation error"); - -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* Expand interlaced rows to full size */ - if (png_ptr->interlaced != 0 && - (png_ptr->transformations & PNG_INTERLACE) != 0) - { - if (png_ptr->pass < 6) - png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass, - png_ptr->transformations); - - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 1/*display*/); - - if (row != NULL) - png_combine_row(png_ptr, row, 0/*row*/); - } - - else -#endif - { - if (row != NULL) - png_combine_row(png_ptr, row, -1/*ignored*/); - - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, -1/*ignored*/); - } - png_read_finish_row(png_ptr); - - if (png_ptr->read_row_fn != NULL) - (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); - } -#endif /* SEQUENTIAL_READ */ -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read one or more rows of image data. If the image is interlaced, * and png_set_interlace_handling() has been called, the rows need to * contain the contents of the rows from the previous pass. If the @@ -745,12 +529,12 @@ png_read_rows(png_structrp png_ptr, png_bytepp row, } #endif /* SEQUENTIAL_READ */ -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_READ_IMAGE_SUPPORTED /* Read the entire image. If the image has an alpha channel or a tRNS * chunk, and you have called png_handle_alpha()[*], you will need to * initialize the image to the current image that PNG will be overlaying. * We set the num_rows again here, in case it was incorrectly set in - * png_read_start_row() by a call to png_read_update_info() or + * png_read_start_IDAT() by a call to png_read_update_info() or * png_start_read_image() if png_set_interlace_handling() wasn't called * prior to either of these functions like it should have been. You can * only call this function once. If you desire to have an image for @@ -761,55 +545,31 @@ png_read_rows(png_structrp png_ptr, png_bytepp row, void PNGAPI png_read_image(png_structrp png_ptr, png_bytepp image) { - png_uint_32 i, image_height; + png_uint_32 image_height; int pass, j; - png_bytepp rp; png_debug(1, "in png_read_image"); if (png_ptr == NULL) return; -#ifdef PNG_READ_INTERLACING_SUPPORTED - if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) - { + if (png_ptr->zowner != png_IDAT) pass = png_set_interlace_handling(png_ptr); - /* And make sure transforms are initialized. */ - png_start_read_image(png_ptr); - } + else { - if (png_ptr->interlaced != 0 && - (png_ptr->transformations & PNG_INTERLACE) == 0) - { - /* Caller called png_start_read_image or png_read_update_info without - * first turning on the PNG_INTERLACE transform. We can fix this here, - * but the caller should do it! - */ - png_warning(png_ptr, "Interlace handling should be turned on when " - "using png_read_image"); - /* Make sure this is set correctly */ - png_ptr->num_rows = png_ptr->height; - } + if (png_ptr->interlaced == 0) + pass = 1; - /* Obtain the pass number, which also turns on the PNG_INTERLACE flag in - * the above error case. - */ - pass = png_set_interlace_handling(png_ptr); + else + pass = PNG_INTERLACE_ADAM7_PASSES; } -#else - if (png_ptr->interlaced) - png_error(png_ptr, - "Cannot read interlaced image -- interlace handler disabled"); - pass = 1; -#endif - - image_height=png_ptr->height; - - for (j = 0; j < pass; j++) + for (j = 0, image_height = png_ptr->height; j < pass; ++j) { - rp = image; + png_bytepp rp = image; + png_uint_32 i; + for (i = 0; i < image_height; i++) { png_read_row(png_ptr, *rp, NULL); @@ -817,7 +577,7 @@ png_read_image(png_structrp png_ptr, png_bytepp image) } } } -#endif /* SEQUENTIAL_READ */ +#endif /* READ_IMAGE */ #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the end of the PNG file. Will not read past the end of the @@ -827,161 +587,137 @@ png_read_image(png_structrp png_ptr, png_bytepp image) void PNGAPI png_read_end(png_structrp png_ptr, png_inforp info_ptr) { -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - int keep; -#endif - png_debug(1, "in png_read_end"); if (png_ptr == NULL) return; - /* If png_read_end is called in the middle of reading the rows there may - * still be pending IDAT data and an owned zstream. Deal with this here. + /* When this routine is entered it is possible that an IDAT chunk still + * remains to be read. There are three conditions: + * + * 1) The app decided to handle IDAT as unknown, libpng will have consumed + * the first IDAT in png_read_info, the rest will be consumed as normal + * chunks by calls to png_handle_chunk below. + * + * 2) The app did not start to read an image, so png_read_start_IDAT was + * not called and png_struct::zowner is not png_IDAT. The first IDAT + * must still be skipped then the code below will skip the remainder. + * + * 3) The app did start to read the image. png_struct::zowner is png_IDAT + * and we need to close down the IDAT reading code. There may also be + * pending IDAT chunks, these are passed to png_read_finish_IDAT here so + * that error detection happens. If the app didn't read all the rows + * libpng will issue an 'extra compressed data' error, we could supress + * that by warning that not all the rows have been read and setting + * png_struct::zstream_error if necessary. */ -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - if (png_chunk_unknown_handling(png_ptr, png_IDAT) == 0) -#endif - png_read_finish_IDAT(png_ptr); - -#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED - /* Report invalid palette index; added at libng-1.5.10 */ - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - png_ptr->num_palette_max > png_ptr->num_palette) - png_benign_error(png_ptr, "Read palette index exceeding num_palette"); -#endif - - do +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (!(png_ptr->known_unknown & 1U)) +# endif { - png_uint_32 length = png_read_chunk_header(png_ptr); - png_uint_32 chunk_name = png_ptr->chunk_name; - - if (chunk_name == png_IEND) - png_handle_IEND(png_ptr, info_ptr, length); - - else if (chunk_name == png_IHDR) - png_handle_IHDR(png_ptr, info_ptr, length); - - else if (info_ptr == NULL) - png_crc_finish(png_ptr, length); - -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) + if (png_ptr->zowner == png_IDAT) { - if (chunk_name == png_IDAT) - { - if ((length > 0) || - (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT) != 0) - png_benign_error(png_ptr, "Too many IDATs found(a1)"); + /* Normal case: read to the end of the IDAT chunks. */ + while (!png_read_finish_IDAT(png_ptr)) { + /* This will adjust zstream.next/avail_in appropriately and if + * necessary read the next chunk. After this avail_in may still + * be zero, but if it is then PNG_AFTER_IDAT should be set. + */ + debug(png_ptr->zstream.avail_in == 0); + png_read_IDAT(png_ptr); + debug(png_ptr->zstream.avail_in > 0 || + (png_ptr->mode & PNG_AFTER_IDAT) != 0); } - png_handle_unknown(png_ptr, info_ptr, length, keep); - if (chunk_name == png_PLTE) - png_ptr->mode |= PNG_HAVE_PLTE; - } -#endif - else if (chunk_name == png_IDAT) - { - /* Zero length IDATs are legal after the last IDAT has been - * read, but not after other chunks have been read. + debug(png_ptr->zstream.avail_in == 0 && png_ptr->zowner == 0); + + /* If this is still an IDAT then it hasn't been finished; at least + * the CRC has not been read. If there is data left in it then + * an error may need to be output. Note that the code below handles + * any additional chunks. */ - if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT) != 0) - png_benign_error(png_ptr, "Too many IDATs found(a2)"); + if (png_ptr->chunk_name == png_IDAT) + { + if (png_ptr->chunk_length > 0 && !png_ptr->zstream_error) + { + png_chunk_benign_error(png_ptr, "too much IDAT data (read)"); + png_ptr->zstream_error = 1; + } - png_crc_finish(png_ptr, length); + png_crc_finish(png_ptr, png_ptr->chunk_length); + png_read_chunk_header(png_ptr); + } } - else if (chunk_name == png_PLTE) - png_handle_PLTE(png_ptr, info_ptr, length); -#ifdef PNG_READ_bKGD_SUPPORTED - else if (chunk_name == png_bKGD) - png_handle_bKGD(png_ptr, info_ptr, length); -#endif + else if (png_ptr->chunk_name == png_IDAT) + { + /* This IDAT has not been processed, the remainder will be finished + * in the loop. This is the case where IDAT is being skipped because + * the rows weren't read, this is OK, but warn anyway. + */ + png_crc_finish(png_ptr, png_ptr->chunk_length); + png_app_warning(png_ptr, "image reading skipped"); + png_ptr->zstream_error = 1; /* Prevent 'too much IDAT' errors */ + png_read_chunk_header(png_ptr); + } -#ifdef PNG_READ_cHRM_SUPPORTED - else if (chunk_name == png_cHRM) - png_handle_cHRM(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_gAMA_SUPPORTED - else if (chunk_name == png_gAMA) - png_handle_gAMA(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_hIST_SUPPORTED - else if (chunk_name == png_hIST) - png_handle_hIST(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_oFFs_SUPPORTED - else if (chunk_name == png_oFFs) - png_handle_oFFs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED - else if (chunk_name == png_pCAL) - png_handle_pCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sCAL_SUPPORTED - else if (chunk_name == png_sCAL) - png_handle_sCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pHYs_SUPPORTED - else if (chunk_name == png_pHYs) - png_handle_pHYs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sBIT_SUPPORTED - else if (chunk_name == png_sBIT) - png_handle_sBIT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sRGB_SUPPORTED - else if (chunk_name == png_sRGB) - png_handle_sRGB(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iCCP_SUPPORTED - else if (chunk_name == png_iCCP) - png_handle_iCCP(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sPLT_SUPPORTED - else if (chunk_name == png_sPLT) - png_handle_sPLT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tEXt_SUPPORTED - else if (chunk_name == png_tEXt) - png_handle_tEXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tIME_SUPPORTED - else if (chunk_name == png_tIME) - png_handle_tIME(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tRNS_SUPPORTED - else if (chunk_name == png_tRNS) - png_handle_tRNS(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_zTXt_SUPPORTED - else if (chunk_name == png_zTXt) - png_handle_zTXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iTXt_SUPPORTED - else if (chunk_name == png_iTXt) - png_handle_iTXt(png_ptr, info_ptr, length); -#endif + else /* This might work, if the signature was read, but just in case: */ + png_app_error(png_ptr, "Missing call to png_read_info"); + } +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED else - png_handle_unknown(png_ptr, info_ptr, length, - PNG_HANDLE_CHUNK_AS_DEFAULT); - } while ((png_ptr->mode & PNG_HAVE_IEND) == 0); + { + /* IDAT is unknown, the chunk that terminated the loop must be an IDAT + * and it has been processed. Get a new chunk header. + */ + if (png_ptr->chunk_name == png_IDAT) + png_read_chunk_header(png_ptr); + + else + png_app_error(png_ptr, + "Missing call to png_read_info with unknown IDAT"); + } +# endif + + if ((png_ptr->mode & PNG_HAVE_IEND) == 0) for (;;) + { + switch (png_find_chunk_op(png_ptr)) + { + default: + impossible("invalid chunk op"); + /* FALL THROUGH */ + case png_chunk_skip: + png_crc_finish(png_ptr, png_ptr->chunk_length); + break; + + case png_chunk_unknown: + png_read_sequential_unknown(png_ptr, info_ptr); + break; + + case png_chunk_process_all: + png_handle_chunk(png_ptr, info_ptr); + break; + + case png_chunk_process_part: + debug(png_ptr->chunk_name == png_IDAT); + debug(!(png_ptr->mode & PNG_AFTER_IDAT)); + if (png_ptr->chunk_length > 0 && !png_ptr->zstream_error) + { + png_chunk_benign_error(png_ptr, "too many IDAT chunks"); + png_ptr->zstream_error = 1; + } + + /* Skip it: */ + png_crc_finish(png_ptr, png_ptr->chunk_length); + return; + } + + if ((png_ptr->mode & PNG_HAVE_IEND) != 0) + break; + + png_read_chunk_header(png_ptr); + } } #endif /* SEQUENTIAL_READ */ @@ -991,51 +727,51 @@ png_read_destroy(png_structrp png_ptr) { png_debug(1, "in png_read_destroy"); -#ifdef PNG_READ_GAMMA_SUPPORTED - png_destroy_gamma_table(png_ptr); -#endif - - png_free(png_ptr, png_ptr->big_row_buf); - png_ptr->big_row_buf = NULL; - png_free(png_ptr, png_ptr->big_prev_row); - png_ptr->big_prev_row = NULL; + png_free(png_ptr, png_ptr->row_buffer); + png_ptr->row_buffer = NULL; + png_free(png_ptr, png_ptr->alt_buffer); + png_ptr->alt_buffer = NULL; png_free(png_ptr, png_ptr->read_buffer); png_ptr->read_buffer = NULL; -#ifdef PNG_READ_QUANTIZE_SUPPORTED - png_free(png_ptr, png_ptr->palette_lookup); - png_ptr->palette_lookup = NULL; - png_free(png_ptr, png_ptr->quantize_index); - png_ptr->quantize_index = NULL; -#endif - if ((png_ptr->free_me & PNG_FREE_PLTE) != 0) { - png_zfree(png_ptr, png_ptr->palette); + png_free(png_ptr, png_ptr->palette); + png_ptr->num_palette = 0; png_ptr->palette = NULL; } - png_ptr->free_me &= ~PNG_FREE_PLTE; + png_ptr->free_me &= PNG_BIC_MASK(PNG_FREE_PLTE); +#ifdef PNG_READ_tRNS_SUPPORTED if ((png_ptr->free_me & PNG_FREE_TRNS) != 0) { png_free(png_ptr, png_ptr->trans_alpha); + png_ptr->num_trans = 0; png_ptr->trans_alpha = NULL; } - png_ptr->free_me &= ~PNG_FREE_TRNS; + png_ptr->free_me &= PNG_BIC_MASK(PNG_FREE_TRNS); +#endif - inflateEnd(&png_ptr->zstream); + if (png_ptr->zstream.state != NULL) + { + int ret = inflateEnd(&png_ptr->zstream); + + if (ret != Z_OK) + { + png_zstream_error(png_ptr, ret); + png_warning(png_ptr, png_ptr->zstream.msg); + } + } + +#ifdef PNG_TRANSFORM_MECH_SUPPORTED + png_transform_free(png_ptr, &png_ptr->transform_list); +#endif #ifdef PNG_PROGRESSIVE_READ_SUPPORTED png_free(png_ptr, png_ptr->save_buffer); png_ptr->save_buffer = NULL; #endif -#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) && \ - defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) - png_free(png_ptr, png_ptr->unknown_chunk.data); - png_ptr->unknown_chunk.data = NULL; -#endif - #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED png_free(png_ptr, png_ptr->chunk_list); png_ptr->chunk_list = NULL; @@ -1084,12 +820,21 @@ png_set_read_status_fn(png_structrp png_ptr, png_read_status_ptr read_row_fn) } -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED -#ifdef PNG_INFO_IMAGE_SUPPORTED +#ifdef PNG_READ_PNG_SUPPORTED +#ifdef __GNUC__ +/* This exists solely to work round a warning from GNU C. */ +static int /* PRIVATE */ +png_gt(size_t a, size_t b) +{ + return a > b; +} +#else +# define png_gt(a,b) ((a) > (b)) +#endif + void PNGAPI -png_read_png(png_structrp png_ptr, png_inforp info_ptr, - int transforms, - voidp params) +png_read_png(png_structrp png_ptr, png_inforp info_ptr, int transforms, + voidp params) { if (png_ptr == NULL || info_ptr == NULL) return; @@ -1098,7 +843,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, * PNG file before the first IDAT (image data chunk). */ png_read_info(png_ptr, info_ptr); - if (info_ptr->height > PNG_UINT_32_MAX/(sizeof (png_bytep))) + if (png_gt(info_ptr->height, PNG_SIZE_MAX/(sizeof (png_bytep)))) png_error(png_ptr, "Image is too high to process with png_read_png()"); /* -------------- image transformations start here ------------------- */ @@ -1266,6 +1011,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, if (info_ptr->row_pointers == NULL) { png_uint_32 iptr; + png_alloc_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); info_ptr->row_pointers = png_voidcast(png_bytepp, png_malloc(png_ptr, info_ptr->height * (sizeof (png_bytep)))); @@ -1277,7 +1023,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, for (iptr = 0; iptr < info_ptr->height; iptr++) info_ptr->row_pointers[iptr] = png_voidcast(png_bytep, - png_malloc(png_ptr, info_ptr->rowbytes)); + png_malloc(png_ptr, rowbytes)); } png_read_image(png_ptr, info_ptr->row_pointers); @@ -1288,8 +1034,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, PNG_UNUSED(params) } -#endif /* INFO_IMAGE */ -#endif /* SEQUENTIAL_READ */ +#endif /* READ_PNG */ #ifdef PNG_SIMPLIFIED_READ_SUPPORTED /* SIMPLIFIED READ @@ -1421,24 +1166,13 @@ png_image_format(png_structrp png_ptr) return format; } -/* Is the given gamma significantly different from sRGB? The test is the same - * one used in pngrtran.c when deciding whether to do gamma correction. The - * arithmetic optimizes the division by using the fact that the inverse of the - * file sRGB gamma is 2.2 +/* Is the given gamma significantly different from sRGB? */ static int png_gamma_not_sRGB(png_fixed_point g) { - if (g < PNG_FP_1) - { - /* An uninitialized gamma is assumed to be sRGB for the simplified API. */ - if (g == 0) - return 0; - - return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */); - } - - return 1; + /* An uninitialized gamma is assumed to be sRGB for the simplified API. */ + return g != 0 && !PNG_GAMMA_IS_sRGB(g); } /* Do the main body of a 'png_image_begin_read' function; read the PNG file @@ -1515,15 +1249,9 @@ png_image_begin_read_from_stdio(png_imagep image, FILE* file) { if (file != NULL) { - if (png_image_read_init(image) != 0) - { - /* This is slightly evil, but png_init_io doesn't do anything other - * than this and we haven't changed the standard IO functions so - * this saves a 'safe' function. - */ - image->opaque->png_ptr->io_ptr = file; + if (png_image_read_init(image) != 0 && + png_image_init_io(image, file) != 0) return png_safe_execute(image, png_image_read_header, image); - } } else @@ -1549,9 +1277,9 @@ png_image_begin_read_from_file(png_imagep image, const char *file_name) if (fp != NULL) { - if (png_image_read_init(image) != 0) + if (png_image_read_init(image) != 0 && + png_image_init_io(image, fp) != 0) { - image->opaque->png_ptr->io_ptr = fp; image->opaque->owned_file = 1; return png_safe_execute(image, png_image_read_header, image); } @@ -1607,8 +1335,20 @@ png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need) } } -int PNGAPI png_image_begin_read_from_memory(png_imagep image, - png_const_voidp memory, png_size_t size) +static int +image_init_memory_io(png_voidp param) + /* Set the read function and pointer for a memory read, the io pointer is + * just the imagep so it is passed in directly. + */ +{ + png_imagep image = png_voidcast(png_imagep, param); + png_set_read_fn(image->opaque->png_ptr, image, png_image_memory_read); + return 1; +} + +int PNGAPI +png_image_begin_read_from_memory(png_imagep image, png_const_voidp memory, + png_size_t size) { if (image != NULL && image->version == PNG_IMAGE_VERSION) { @@ -1622,10 +1362,9 @@ int PNGAPI png_image_begin_read_from_memory(png_imagep image, */ image->opaque->memory = png_voidcast(png_const_bytep, memory); image->opaque->size = size; - image->opaque->png_ptr->io_ptr = image; - image->opaque->png_ptr->read_data_fn = png_image_memory_read; - return png_safe_execute(image, png_image_read_header, image); + return png_safe_execute(image, image_init_memory_io, image) && + png_safe_execute(image, png_image_read_header, image); } } @@ -1705,13 +1444,19 @@ png_image_skip_unused_chunks(png_structrp png_ptr) static void set_file_encoding(png_image_read_control *display) { - png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma; - if (png_gamma_significant(g) != 0) + if (png_need_gamma_correction(display->image->opaque->png_ptr, + 0/*PNG gamma*/, 0/*not sRGB*/)) { - if (png_gamma_not_sRGB(g) != 0) + png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma; + + if (png_gamma_not_sRGB(g)) { display->file_encoding = P_FILE; - display->gamma_to_linear = png_reciprocal(g); + /* Record the reciprocal of 'g', the colorspace gamma. If this + * overflows just store FP_1. + */ + if (!png_muldiv(&display->gamma_to_linear, PNG_FP_1, PNG_FP_1, g)) + display->gamma_to_linear = PNG_FP_1; } else @@ -3185,7 +2930,7 @@ png_image_read_colormapped(png_voidp argument) png_controlp control = image->opaque; png_structrp png_ptr = control->png_ptr; png_inforp info_ptr = control->info_ptr; - + int color_type, bit_depth; int passes = 0; /* As a flag */ PNG_SKIP_CHUNKS(png_ptr); @@ -3199,6 +2944,12 @@ png_image_read_colormapped(png_voidp argument) png_read_update_info(png_ptr, info_ptr); + /* Avoid the 'easy access' functions below because this allows them to be + * disabled; there are not useful with the simplified API. + */ + color_type = PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format); + bit_depth = info_ptr->bit_depth; + /* The expected output can be deduced from the colormap_processing option. */ switch (display->colormap_processing) { @@ -3206,9 +2957,8 @@ png_image_read_colormapped(png_voidp argument) /* Output must be one channel and one byte per pixel, the output * encoding can be anything. */ - if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE || - info_ptr->color_type == PNG_COLOR_TYPE_GRAY) && - info_ptr->bit_depth == 8) + if ((color_type == PNG_COLOR_TYPE_PALETTE || + color_type == PNG_COLOR_TYPE_GRAY) && bit_depth == 8) break; goto bad_output; @@ -3219,30 +2969,30 @@ png_image_read_colormapped(png_voidp argument) * can be checked with an exact number because it should have been set * to this number above! */ - if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && - info_ptr->bit_depth == 8 && - png_ptr->screen_gamma == PNG_GAMMA_sRGB && - image->colormap_entries == 256) + if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA && bit_depth == 8 && + !png_need_gamma_correction(png_ptr, png_memory_gamma(png_ptr), + 1/*sRGB*/) && + image->colormap_entries == 256) break; goto bad_output; case PNG_CMAP_RGB: /* Output must be 8-bit sRGB encoded RGB */ - if (info_ptr->color_type == PNG_COLOR_TYPE_RGB && - info_ptr->bit_depth == 8 && - png_ptr->screen_gamma == PNG_GAMMA_sRGB && - image->colormap_entries == 216) + if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8 && + !png_need_gamma_correction(png_ptr, png_memory_gamma(png_ptr), + 1/*sRGB*/) && + image->colormap_entries == 216) break; goto bad_output; case PNG_CMAP_RGB_ALPHA: /* Output must be 8-bit sRGB encoded RGBA */ - if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA && - info_ptr->bit_depth == 8 && - png_ptr->screen_gamma == PNG_GAMMA_sRGB && - image->colormap_entries == 244 /* 216 + 1 + 27 */) + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA && bit_depth == 8 && + !png_need_gamma_correction(png_ptr, png_memory_gamma(png_ptr), + 1/*sRGB*/) && + image->colormap_entries == 244 /* 216 + 1 + 27 */) break; /* goto bad_output; */ @@ -3427,11 +3177,11 @@ png_image_read_composite(png_voidp argument) /* The do_local_background case; called when all the following transforms are to * be done: * - * PNG_RGB_TO_GRAY - * PNG_COMPOSITE - * PNG_GAMMA + * PNG_READ_RGB_TO_GRAY + * PNG_READ_COMPOSITE + * PNG_READ_GAMMA * - * This is a work-around for the fact that both the PNG_RGB_TO_GRAY and + * This is a work-around for the fact that both the PNG_READ_RGB_TO_GRAY and * PNG_COMPOSITE code performs gamma correction, so we get double gamma * correction. The fix-up is to prevent the PNG_COMPOSITE operation from * happening inside libpng, so this routine sees an 8 or 16-bit gray+alpha @@ -3454,14 +3204,9 @@ png_image_read_background(png_voidp argument) * left to the png_image_read_background function. The rows libpng produce * might be 8 or 16-bit but should always have two channels; gray plus alpha. */ - if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == 0) - png_error(png_ptr, "lost rgb to gray"); - - if ((png_ptr->transformations & PNG_COMPOSE) != 0) - png_error(png_ptr, "unexpected compose"); - - if (png_get_channels(png_ptr, info_ptr) != 2) - png_error(png_ptr, "lost/gained channels"); + affirm(PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format) == + PNG_COLOR_TYPE_GRAY_ALPHA); + debug(png_get_channels(png_ptr, info_ptr) == 2); /* Expect the 8-bit case to always remove the alpha channel */ if ((image->format & PNG_FORMAT_FLAG_LINEAR) == 0 && @@ -3743,7 +3488,7 @@ png_image_read_direct(png_voidp argument) /* Now check the format to see if it was modified. */ { png_uint_32 base_format = png_image_format(png_ptr) & - ~PNG_FORMAT_FLAG_COLORMAP /* removed by png_set_expand */; + PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP) /* removed by png_set_expand */; png_uint_32 change = format ^ base_format; png_fixed_point output_gamma; int mode; /* alpha mode */ @@ -3777,7 +3522,7 @@ png_image_read_direct(png_voidp argument) PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT); } - change &= ~PNG_FORMAT_FLAG_COLOR; + change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR); } /* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise. @@ -3825,15 +3570,12 @@ png_image_read_direct(png_voidp argument) */ if (do_local_background != 0) { - png_fixed_point gtest; - - /* This is 'png_gamma_threshold' from pngrtran.c; the test used for - * gamma correction, the screen gamma hasn't been set on png_struct - * yet; it's set below. png_struct::gamma, however, is set to the - * final value. + /* This is intended to be a safe check to see if libpng will perform + * gamma work in pngrtran.c; if it will *not* be performed the + * do_local_background flag is cancelled. */ - if (png_muldiv(>est, output_gamma, png_ptr->colorspace.gamma, - PNG_FP_1) != 0 && png_gamma_significant(gtest) == 0) + if (!png_need_gamma_correction(png_ptr, 0/*PNG gamma*/, + output_gamma != PNG_GAMMA_LINEAR)) do_local_background = 0; else if (mode == PNG_ALPHA_STANDARD) @@ -3854,7 +3596,7 @@ png_image_read_direct(png_voidp argument) else /* 8-bit output */ png_set_scale_16(png_ptr); - change &= ~PNG_FORMAT_FLAG_LINEAR; + change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_LINEAR); } /* Now the background/alpha channel changes. */ @@ -3932,7 +3674,7 @@ png_image_read_direct(png_voidp argument) if ((format & PNG_FORMAT_FLAG_AFIRST) != 0) { where = PNG_FILLER_BEFORE; - change &= ~PNG_FORMAT_FLAG_AFIRST; + change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_AFIRST); } else @@ -3943,7 +3685,7 @@ png_image_read_direct(png_voidp argument) } /* This stops the (irrelevant) call to swap_alpha below. */ - change &= ~PNG_FORMAT_FLAG_ALPHA; + change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); } /* Now set the alpha mode correctly; this is always done, even if there is @@ -3962,9 +3704,9 @@ png_image_read_direct(png_voidp argument) png_set_bgr(png_ptr); else - format &= ~PNG_FORMAT_FLAG_BGR; + format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_BGR); - change &= ~PNG_FORMAT_FLAG_BGR; + change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_BGR); } # endif @@ -3986,9 +3728,9 @@ png_image_read_direct(png_voidp argument) } else - format &= ~PNG_FORMAT_FLAG_AFIRST; + format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_AFIRST); - change &= ~PNG_FORMAT_FLAG_AFIRST; + change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_AFIRST); } # endif @@ -4022,54 +3764,47 @@ png_image_read_direct(png_voidp argument) png_read_update_info(png_ptr, info_ptr); { - png_uint_32 info_format = 0; + png_uint_32 out_format = png_memory_format(png_ptr); - if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) - info_format |= PNG_FORMAT_FLAG_COLOR; + /* Swapping is expected for the 16-bit format: */ + out_format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_SWAPPED); - if ((info_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0) + /* The remaining upper bits should never be set: */ + affirm(!(out_format & ~0x3FU)); + + if ((out_format & PNG_FORMAT_FLAG_ALPHA) != 0) { /* do_local_compose removes this channel below. */ - if (do_local_compose == 0) - { - /* do_local_background does the same if required. */ - if (do_local_background != 2 || - (format & PNG_FORMAT_FLAG_ALPHA) != 0) - info_format |= PNG_FORMAT_FLAG_ALPHA; - } + if (do_local_compose != 0 || + /* do_local_background does the same if required. */ + (do_local_background == 2 && + (format & PNG_FORMAT_FLAG_ALPHA) == 0)) + out_format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); } else affirm(do_local_compose == 0 /* else alpha channel lost */); - if (info_ptr->bit_depth == 16) - info_format |= PNG_FORMAT_FLAG_LINEAR; - -# ifdef PNG_FORMAT_BGR_SUPPORTED - if ((png_ptr->transformations & PNG_BGR) != 0) - info_format |= PNG_FORMAT_FLAG_BGR; -# endif + switch (png_memory_channel_depth(png_ptr)) + { + case 16: affirm((out_format & PNG_FORMAT_FLAG_LINEAR) != 0); break; + case 8: affirm((out_format & PNG_FORMAT_FLAG_LINEAR) == 0); break; + default: impossible("unexpected bit depth"); break; + } # ifdef PNG_FORMAT_AFIRST_SUPPORTED if (do_local_background == 2) { + /* do_local_background should be handling the swap: */ + affirm(!(out_format & PNG_FORMAT_FLAG_AFIRST)); + if ((format & PNG_FORMAT_FLAG_AFIRST) != 0) - info_format |= PNG_FORMAT_FLAG_AFIRST; - } - - if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0 || - ((png_ptr->transformations & PNG_ADD_ALPHA) != 0 && - (png_ptr->flags & PNG_FLAG_FILLER_AFTER) == 0)) - { - if (do_local_background == 2) - png_error(png_ptr, "unexpected alpha swap transformation"); - - info_format |= PNG_FORMAT_FLAG_AFIRST; + out_format |= PNG_FORMAT_FLAG_AFIRST; } # endif /* This is actually an internal error. */ - affirm(info_format == format /* else unimplemented transformations */); + affirm(out_format == format /* else unimplemented transformations */); } /* Now read the rows. If do_local_compose is set then it is necessary to use diff --git a/pngrio.c b/pngrio.c index b0ffb6913..625a788f9 100644 --- a/pngrio.c +++ b/pngrio.c @@ -34,11 +34,11 @@ png_read_data(png_structrp png_ptr, png_bytep data, png_size_t length) { png_debug1(4, "reading %d bytes", (int)length); - if (png_ptr->read_data_fn != NULL) - (*(png_ptr->read_data_fn))(png_ptr, data, length); + if (png_ptr->rw_data_fn != NULL) + png_ptr->rw_data_fn(png_ptr, data, length); else - png_error(png_ptr, "Call to NULL read function"); + png_app_error(png_ptr, "No read function"); } #ifdef PNG_STDIO_SUPPORTED @@ -63,7 +63,7 @@ png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) if (check != length) png_error(png_ptr, "Read Error"); } -#endif +#endif /* STDIO */ /* This function allows the application to supply a new input function * for libpng if standard C streams aren't being used. @@ -91,31 +91,19 @@ png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr, if (png_ptr == NULL) return; - png_ptr->io_ptr = io_ptr; - -#ifdef PNG_STDIO_SUPPORTED - if (read_data_fn != NULL) - png_ptr->read_data_fn = read_data_fn; - - else - png_ptr->read_data_fn = png_default_read_data; -#else - png_ptr->read_data_fn = read_data_fn; -#endif - -#ifdef PNG_WRITE_SUPPORTED - /* It is an error to write to a read device */ - if (png_ptr->write_data_fn != NULL) + if (!png_ptr->read_struct) { - png_ptr->write_data_fn = NULL; - png_warning(png_ptr, - "Can't set both read_data_fn and write_data_fn in the" - " same structure"); + png_app_error(png_ptr, "cannot set a read function on a write struct"); + return; } -#endif -#ifdef PNG_WRITE_FLUSH_SUPPORTED - png_ptr->output_flush_fn = NULL; -#endif + if (read_data_fn == NULL) + { + png_app_error(png_ptr, "API change: png_set_read_fn requires a function"); + return; + } + + png_ptr->io_ptr = io_ptr; + png_ptr->rw_data_fn = read_data_fn; } #endif /* READ */ diff --git a/pngrtran.c b/pngrtran.c index d9f5c8aa3..9dd2d277d 100644 --- a/pngrtran.c +++ b/pngrtran.c @@ -19,86 +19,1551 @@ #include "pngpriv.h" #define PNG_SRC_FILE PNG_SRC_FILE_pngrtran -#ifdef PNG_READ_TRANSFORMS_SUPPORTED -/* Is it OK to set a transformation now? Only if png_start_read_image or - * png_read_update_info have not been called. It is not necessary for the IHDR - * to have been read in all cases; the need_IHDR parameter allows for this - * check too. - */ -static int -png_rtran_ok(png_structrp png_ptr, int need_IHDR) +#ifdef PNG_READ_QUANTIZE_SUPPORTED +typedef struct { - if (png_ptr != NULL) + png_transform tr; + png_byte map[256U]; /* Map of palette values */ + png_byte lut[1U << /* LUT for RGB values */ + (PNG_QUANTIZE_RED_BITS+PNG_QUANTIZE_GREEN_BITS+PNG_QUANTIZE_BLUE_BITS)]; +} png_transform_quantize; + +#define PNG_QUANTIZE_MAP 1U /* map is present and not a 1:1 mapping */ +#define PNG_QUANTIZE_LUT 2U /* lut has been built */ + +static void +do_quantize_rgb(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_quantize *tr = png_transform_cast(png_transform_quantize, + *transform); + unsigned int channels = PNG_TC_CHANNELS(*tc); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - channels/*safety*/; + png_bytep dp = png_voidcast(png_bytep, tc->dp); + + affirm(tc->bit_depth == 8 && (channels == 3 || channels == 4) && + !(tc->format & PNG_FORMAT_FLAG_SWAPPED) && + (tr->tr.args & PNG_QUANTIZE_LUT) != 0); + + tc->sp = dp; + tc->format |= PNG_FORMAT_FLAG_COLORMAP; + + while (sp <= ep) { - if ((png_ptr->flags & PNG_FLAG_ROW_INIT) != 0) - png_app_error(png_ptr, - "invalid after png_start_read_image or png_read_update_info"); + unsigned int r = sp[0]; + unsigned int g = sp[1]; + unsigned int b = sp[2]; - else if (need_IHDR && (png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_app_error(png_ptr, "invalid before the PNG header has been read"); + /* This looks real messy, but the compiler will reduce + * it down to a reasonable formula. For example, with + * 5 bits per color, we get: + * p = (((r >> 3) & 0x1f) << 10) | + * (((g >> 3) & 0x1f) << 5) | + * ((b >> 3) & 0x1f); + */ + *dp++ = tr->lut[(((r >> (8 - PNG_QUANTIZE_RED_BITS)) & + ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << + (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | + (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & + ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << + (PNG_QUANTIZE_BLUE_BITS)) | + ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & + ((1 << PNG_QUANTIZE_BLUE_BITS) - 1))]; - else + sp += channels; + } + + affirm(sp == ep+channels); + UNTESTED +# undef png_ptr +} + +static void +do_quantize_pal(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_quantize *tr = png_transform_cast(png_transform_quantize, + *transform); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + + affirm(tc->bit_depth == 8 && (tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0 && + !(tc->format & PNG_FORMAT_FLAG_SWAPPED) && + (tr->tr.args & PNG_QUANTIZE_MAP) != 0); + + tc->sp = dp; + + while (sp < ep) + *dp++ = tr->map[*sp++]; + + UNTESTED +# undef png_ptr +} + +static void +png_init_quantize(png_transformp *transform, png_transform_controlp tc) +{ + if (tc->bit_depth == 8 && (tc->format & PNG_FORMAT_FLAG_COLOR) != 0) + { + /* Either colormapped input, RGB or RGBA: */ + if (!(tc->format & PNG_FORMAT_FLAG_COLORMAP)) /* RGB, RGBA */ { - /* Turn on failure to initialize correctly for all transforms. */ - png_ptr->flags |= PNG_FLAG_DETECT_UNINITIALIZED; + /* This must be a 'palette' lookup */ + if (((*transform)->args & PNG_QUANTIZE_LUT) != 0) + { + /* This changes the format and invalidates pretty much everything in + * the info struct: + */ + tc->format |= PNG_FORMAT_FLAG_COLORMAP; - return 1; /* Ok */ + if (tc->init == PNG_TC_INIT_FINAL) + { + (*transform)->fn = do_quantize_rgb; + tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL+ + PNG_INFO_sBIT+PNG_INFO_bKGD; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = + png_check_byte(tc->png_ptr, tc->bit_depth); + } + + return; + } + } + + else /* colormapped */ + { + /* This must be a 'quantize' lookup */ + if (((*transform)->args & PNG_QUANTIZE_MAP) != 0) + { + /* This doesn't change the format, just the values: */ + if (tc->init == PNG_TC_INIT_FINAL) + { + (*transform)->fn = do_quantize_pal; + tc->invalid_info |= PNG_INFO_sBIT+PNG_INFO_pCAL; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = + png_check_byte(tc->png_ptr, tc->bit_depth); + } + + return; + } } } - return 0; /* no png_error possible! */ + /* Else not applicable */ + (*transform)->fn = NULL; } +/* Dither file to 8-bit. Supply a palette, the current number + * of elements in the palette, the maximum number of elements + * allowed, and a histogram if possible. If the current number + * of colors is greater then the maximum number, the palette will be + * modified to fit in the maximum number. "full_quantize" indicates + * whether we need a quantizing cube set up for RGB images, or if we + * simply are reducing the number of colors in a paletted image. + */ +typedef struct png_dsort_struct +{ + struct png_dsort_struct * next; + png_byte left; + png_byte right; +} png_dsort; +typedef png_dsort * png_dsortp; +typedef png_dsort * * png_dsortpp; + +static void +init_map(png_bytep map) + /* Initialize a mapping table to be 1:1 */ +{ + png_byte b = 0U; + + do + map[b] = b; + while (b++ != 255U); +} + +/* Save typing and make code easier to understand */ +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +void PNGAPI +png_set_quantize(png_structrp png_ptr, png_colorp palette, + int num_palette, int maximum_colors, png_const_uint_16p histogram, + int full_quantize) +{ + png_debug(1, "in png_set_quantize"); + + if (png_ptr != NULL) + { + png_transform_quantize *tr = png_transform_cast(png_transform_quantize, + png_add_transform(png_ptr, sizeof (png_transform_quantize), + png_init_quantize, PNG_TR_QUANTIZE)); + + /* This is weird (consider what happens to png_set_background on a palette + * image with a tRNS chunk). + */ + if (palette == png_ptr->palette) + png_app_warning(png_ptr, "png_set_quantize: PLTE will be damaged"); + + if (maximum_colors <= 0 || num_palette > 256) + { + /* The spuriously allocated transform will be removed by the init + * code. + */ + png_app_error(png_ptr, "png_set_quantize: invalid color count"); + return; + } + + /* The app passed in a palette with too many colors, it's not clear why + * libpng is providing this functionality, it's nothing to do with PNG and + * can be done by the application without any PNG specific knowledge. + */ + if (num_palette > maximum_colors) + { + int map_changed = 0; + + /* The map table must be preset to do no mapping initially: */ + init_map(tr->map); + + if (histogram != NULL) + { + /* This is easy enough, just throw out the least used colors. + * Perhaps not the best solution, but good enough. + */ + int i; + png_byte quantize_sort[256U]; + + /* Initialize an array to sort colors */ + init_map(quantize_sort); + + /* Find the least used palette entries by starting a + * bubble sort, and running it until we have sorted + * out enough colors. Note that we don't care about + * sorting all the colors, just finding which are + * least used. + */ + for (i = num_palette - 1; i >= maximum_colors; i--) + { + int done; /* To stop early if the list is pre-sorted */ + int j; + + done = 1; + for (j = 0; j < i; j++) + { + if (histogram[quantize_sort[j]] < + histogram[quantize_sort[j+1]]) + { + png_byte t = quantize_sort[j]; + quantize_sort[j] = quantize_sort[j+1]; + quantize_sort[j+1] = t; + done = 0; + } + } + + if (done != 0) + break; + } + + /* Swap the palette around, and set up a table, if necessary */ + if (full_quantize) + { + int j = num_palette; + + /* Put all the useful colors within the max, but don't + * move the others. + * + * NOTE: if the app passes in the result of png_get_PLTE it will + * be overwritten at this point, what is the API? + */ + for (i = 0; i < maximum_colors; i++) + { + if (quantize_sort[i] >= maximum_colors) + { + do + j--; + while (quantize_sort[j] >= maximum_colors); + + /* NOTE: NOT swapped, so the original palette[i] has been + * lost. + */ + palette[i] = palette[j]; + } + } + } + + else /* !full_quantize */ + { + int j = num_palette; + + /* Move all the used colors inside the max limit, and + * develop a translation table. + */ + for (i = 0; i < maximum_colors; i++) + { + /* Only move the colors we need to */ + if (quantize_sort[i] >= maximum_colors) + { + png_color tmp_color; + + do + j--; + while (quantize_sort[j] >= maximum_colors); + + tmp_color = palette[j]; + palette[j] = palette[i]; + palette[i] = tmp_color; + /* Indicate where the color went */ + tr->map[j] = png_check_byte(png_ptr, i); + tr->map[i] = png_check_byte(png_ptr, j); + map_changed = 1; + } + } + + /* Find closest color for those colors we are not using */ + for (i = 0; i < num_palette; i++) + { + if (tr->map[i] >= maximum_colors) + { + int min_d, k, min_k, d_index; + + /* Find the closest color to one we threw out */ + d_index = tr->map[i]; + min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); + for (k = 1, min_k = 0; k < maximum_colors; k++) + { + int d; + + d = PNG_COLOR_DIST(palette[d_index], palette[k]); + + if (d < min_d) + { + min_d = d; + min_k = k; + } + } + + /* Point to closest color */ + tr->map[i] = png_check_byte(png_ptr, min_k); + map_changed = 1; + } + } + } /* !full_quantize */ + } /* have a histogram */ + + else /* no histogram */ + { + /* This is much harder to do simply (and quickly). Perhaps + * we need to go through a median cut routine, but those + * don't always behave themselves with only a few colors + * as input. So we will just find the closest two colors, + * and throw out one of them (chosen somewhat randomly). + * [We don't understand this at all, so if someone wants to + * work on improving it, be our guest - AED, GRP] + */ + int max_d; + int num_new_palette; + png_byte index_to_palette[256U]; + png_byte palette_to_index[256U]; + png_dsortp hash[769]; + + /* Initialize palette index sort arrays */ + init_map(index_to_palette); + init_map(palette_to_index); + memset(hash, 0, sizeof hash); + num_new_palette = num_palette; + + /* Initial wild guess at how far apart the farthest pixel + * pair we will be eliminating will be. Larger + * numbers mean more areas will be allocated, Smaller + * numbers run the risk of not saving enough data, and + * having to do this all over again. + * + * I have not done extensive checking on this number. + */ + max_d = 96; + + while (num_new_palette > maximum_colors) + { + int i; + png_dsortp t = NULL; + + for (i = 0; i < num_new_palette - 1; i++) + { + int j; + + for (j = i + 1; j < num_new_palette; j++) + { + int d = PNG_COLOR_DIST(palette[i], palette[j]); + + if (d <= max_d) + { + + t = png_voidcast(png_dsortp, png_malloc_warn(png_ptr, + sizeof (*t))); + + if (t == NULL) + break; + + t->next = hash[d]; + t->left = png_check_byte(png_ptr, i); + t->right = png_check_byte(png_ptr, j); + hash[d] = t; + } + } + if (t == NULL) + break; + } + + if (t != NULL) for (i = 0; i <= max_d; i++) + { + if (hash[i] != NULL) + { + png_dsortp p; + + for (p = hash[i]; p != NULL; p = p->next) + { + if (index_to_palette[p->left] < num_new_palette && + index_to_palette[p->right] < num_new_palette) + { + int j, next_j; + + if (num_new_palette & 0x01) + { + j = p->left; + next_j = p->right; + } + else + { + j = p->right; + next_j = p->left; + } + + num_new_palette--; + /* NOTE: overwrites palette */ + palette[index_to_palette[j]] = + palette[num_new_palette]; + + if (full_quantize == 0) + { + int k; + + for (k = 0; k < num_palette; k++) + { + if (tr->map[k] == index_to_palette[j]) + { + tr->map[k] = index_to_palette[next_j]; + map_changed = 1; + } + + if (tr->map[k] == num_new_palette) + { + tr->map[k] = index_to_palette[j]; + map_changed = 1; + } + } + } + + index_to_palette[palette_to_index[num_new_palette]] = + index_to_palette[j]; + + palette_to_index[index_to_palette[j]] = + palette_to_index[num_new_palette]; + + index_to_palette[j] = + png_check_byte(png_ptr, num_new_palette); + + palette_to_index[num_new_palette] = + png_check_byte(png_ptr, j); + } + + if (num_new_palette <= maximum_colors) + break; + } + + if (num_new_palette <= maximum_colors) + break; + } + } + + for (i = 0; i < 769; i++) + { + if (hash[i] != NULL) + { + png_dsortp p = hash[i]; + + while (p) + { + t = p->next; + png_free(png_ptr, p); + p = t; + } + + hash[i] = NULL; + } + } + + max_d += 96; + } /* while num_new_colors > maximum_colors */ + } /* no histogram */ + + num_palette = maximum_colors; + + if (map_changed) /* else the map is 1:1 */ + tr->tr.args |= PNG_QUANTIZE_MAP; + } /* num_palette > maximum_colors */ + + /* The palette has been reduced to the requested number of colors if it + * was over maximum colors before. + */ + + /* TODO: what is this? Apparently the png_struct::palette member gets + * updated if it didn't originally have a palette, but the update relies + * on the app not freeing the passed in palette. + */ + if (png_ptr->palette == NULL) + png_ptr->palette = palette; + + png_ptr->num_palette = png_check_bits(png_ptr, num_palette, 9); + + if (full_quantize) + { + int i; + png_byte distance[1U << (PNG_QUANTIZE_RED_BITS+PNG_QUANTIZE_GREEN_BITS+ + PNG_QUANTIZE_BLUE_BITS)]; + + memset(distance, 0xff, sizeof distance); + + for (i = 0; i < num_palette; i++) + { + int ir; + int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS)); + int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS)); + int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS)); + + for (ir = 0; ir < (1< r) ? ir - r : r - ir); + int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS + + PNG_QUANTIZE_GREEN_BITS)); + + for (ig = 0; ig < (1< g) ? ig - g : g - ig); + int dt = dr + dg; + int dm = ((dr > dg) ? dr : dg); + int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS); + + for (ib = 0; ib < (1< b) ? ib - b : b - ib); + int dmax = ((dm > db) ? dm : db); + int d = dmax + dt + db; + + if (d < distance[d_index]) + { + distance[d_index] = png_check_byte(png_ptr, d); + tr->lut[d_index] = png_check_byte(png_ptr, i); + } + } /* for blue */ + } /* for green */ + } /* for red */ + } /* num_palette */ + } /* full_quantize */ + } /* png_ptr != NULL */ +} +#endif /* READ_QUANTIZE */ + +#ifdef PNG_READ_PACK_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 + * the numbers 0 or 1. If you would rather they contain 0 and 255, use + * png_set_expand_gray_1_2_4_to_8 instead. + */ +static void +png_do_read_unpack(png_transformp *transform, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = png_voidcast(png_const_bytep, tc->dp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + + sp += PNG_TC_ROWBYTES(*tc) - 1; /* Start from end */ + dp += tc->width; /* output bit depth is 8 */ + +# define png_ptr (tc->png_ptr) + png_debug(1, "in png_do_unpack"); + + switch (tc->bit_depth) + { + case 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 = 7U & -tc->width; + + while (dp > ep) + { + *--dp = (*sp >> shift) & 1U; + shift = 7U & (shift+1U); + if (shift == 0U) + --sp; + } + + debug(shift == 0U); + break; + } + + case 2: + { + unsigned int shift = 7U & -(tc->width << 1); + + while (dp > ep) + { + *--dp = (*sp >> shift) & 3U; + shift = 7U & (shift+2U); + if (shift == 0U) + --sp; + } + + debug(shift == 0U); + break; + } + + case 4: + { + unsigned int shift = 7U & -(tc->width << 2); + + while (dp > ep) + { + *--dp = (*sp >> shift) & 15U; + shift = 7U & (shift+4U); + if (shift == 0U) + --sp; + } + + debug(shift == 0U); + break; + } + + default: + impossible("bit depth"); + } + + debug(dp == ep && sp == png_upcast(png_const_bytep, tc->sp)-1U); + tc->sp = dp; + + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U) + { + tc->range++; + tc->format |= PNG_FORMAT_FLAG_RANGE; + } + + tc->bit_depth = 8U; + PNG_UNUSED(transform) +# undef png_ptr +} + +/* Called from the curiously named png_set_packing API in pngtrans.c; the read + * and write code is separated because read 'unpacks' (from PNG format) and + * write 'packs' (to PNG format.) + */ +void /* PRIVATE */ +png_init_read_pack(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr tc->png_ptr + debug(tc->init); + + if (tc->bit_depth < 8) /* else no packing/unpacking */ + { + /* For indexed images the pack operation does not invalidate the range; in + * fact the corresponding shift operation would! + */ + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U) + { + tc->range++; + tc->format |= PNG_FORMAT_FLAG_RANGE; + } + + tc->bit_depth = 8U; + + if (tc->init == PNG_TC_INIT_FINAL) + (*transform)->fn = png_do_read_unpack/* sic: it unpacks */; + } + + else /* the transform is not applicable */ + (*transform)->fn = NULL; + +# undef png_ptr +} +#endif /* READ_PACK */ + +#if defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) +# ifdef PNG_READ_tRNS_SUPPORTED +static unsigned int +fill_transparent_pixel(png_const_structrp png_ptr, png_byte *trans) + /* Fill a byte array according to the transparent pixel value and return a + * count of the number of bytes. Low bit depth gray values are replicated in + * the first byte. Writes from 1 to 6 bytes. + */ +{ + /* There must be a tRNS chunk and this must not be a palette image: */ + debug(png_ptr->num_trans == 1 && + !(png_ptr->color_type & (PNG_COLOR_MASK_ALPHA+PNG_COLOR_MASK_PALETTE))); + + if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* gray */ + { + unsigned int t = png_ptr->trans_color.gray; + unsigned int depth = png_ptr->bit_depth; + + if (depth < 16U) + { + /* ISO PNG 11.3.2.1 "tRNS Transparency": "If the image bit depth is + * less than 16, the least significant bits are used and the others are + * 0." So mask out the upper bits. + */ + t &= (1U<> 8); + trans[1] = PNG_BYTE(t); + return 2U; + } + + else /* color */ switch (png_ptr->bit_depth) + { + case 8: /* 8-bit RGB */ + trans[0] = PNG_BYTE(png_ptr->trans_color.red); + trans[1] = PNG_BYTE(png_ptr->trans_color.green); + trans[2] = PNG_BYTE(png_ptr->trans_color.blue); + return 3U; + + case 16: /* 16-bit RGB */ + 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); + return 6U; + + default: + NOT_REACHED; + return 0U; /* safe */ + } +} +# endif /* READ_tRNS */ +#endif /* READ_EXPAND || READ_BACKGROUND */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Flags for png_init_expand */ +#define PNG_EXPAND_PALETTE 1U /* palette images only, includes tRNS */ +#define PNG_EXPAND_LBP_GRAY 2U /* grayscale low-bit depth only */ +#define PNG_EXPAND_tRNS 4U /* non-palette images only */ + +/* This struct is only required for tRNS matching, but it is convenient to + * allocated it anyway even if READ_tRNS is not supported. + */ +typedef struct +{ + png_transform tr; + unsigned int ntrans; /* number of bytes below */ + png_byte transparent_pixel[6]; /* the transparent pixel value */ +} png_expand; + +#ifdef PNG_READ_tRNS_SUPPORTED +/* Look for colors matching the trans_color in png_ptr, low bit depth gray is + * covered below so this only need handle 8 abd 16-bit channels. + */ +static void +png_do_expand_tRNS(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_expand *tr = png_transform_cast(png_expand, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp; + const unsigned int spixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3; + unsigned int alpha_size; + + /* We expect opaque and transparent pixels to be interleaved but with long + * sequences of each. Because we are adding an alpha channel we must copy + * down. + */ + debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA)); + debug(spixel_size == tr->ntrans); + sp += PNG_TC_ROWBYTES(*tc); + tc->sp = dp; + tc->format |= PNG_FORMAT_FLAG_ALPHA; + tc->invalid_info |= PNG_INFO_tRNS; + tc->transparent_alpha = 1U; + alpha_size = (PNG_TC_PIXEL_DEPTH(*tc)>>3) - spixel_size; + debug(alpha_size == 1 || alpha_size == 2); + dp += PNG_TC_ROWBYTES(*tc); + + do + { + unsigned int i = spixel_size; + png_byte alpha = 0U; + + dp -= alpha_size; + alpha = 0U; + + /* Copy and check one source pixel (backwards, to avoid any + * overwrite): + */ + do if ((*--dp = *--sp) != tr->transparent_pixel[--i]) /* pixel != tRNS */ + alpha = 0xFFU; + while (i != 0U); + + /* i == 0 */ + do + dp[spixel_size + i] = alpha; + while (++i < alpha_size); + } while (sp > ep); + + debug(sp == ep && dp == tc->dp); /* else overwrite */ +# undef png_ptr +} +#endif /* READ_tRNS */ + +/* Expand grayscale images of less than 8-bit depth to 8 bits. + * libpng 1.7.0: this no longer expands everything, it just expands the low bit + * depth gray row. It does *NOT* expand the tRNS into an alpha channel unless + * it is told to do so. + * + * API CHANGE: the function now does what it was always meant to do. + * + * This is 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. + * + * The tRNS handling is included here too; speed is not important because the + * result will always be cached unless the PNG is very small. + */ +static void +png_do_expand_lbd_gray(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_bytep dp = png_voidcast(png_bytep, tc->dp); + const png_const_bytep ep = dp; + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const unsigned int bit_depth = tc->bit_depth; + unsigned int insignificant_bits = 0U; +# ifdef PNG_READ_tRNS_SUPPORTED + unsigned int gray = 0xffffU; /* doesn't match anything */ + unsigned int do_alpha = 0U; +# endif /* READ_tRNS */ + + sp += PNG_TC_ROWBYTES(*tc); /* last byte +1 */ + tc->bit_depth = 8U; + tc->invalid_info |= PNG_INFO_tRNS; +# ifdef PNG_READ_sBIT_SUPPORTED + if (bit_depth > 1U /* irrelevant for bit depth 1 */ && + !(tc->invalid_info & PNG_INFO_sBIT) && + tc->sBIT_G > 0U/*SAFETY*/ && tc->sBIT_G < bit_depth) + { + insignificant_bits = bit_depth - tc->sBIT_G; + UNTESTED + } +# endif /* READ_sBIT */ + +# ifdef PNG_READ_tRNS_SUPPORTED + if (((*transform)->args & PNG_EXPAND_tRNS) != 0) + { + tc->format |= PNG_FORMAT_FLAG_ALPHA; + tc->transparent_alpha = 1U; + gray = png_ptr->trans_color.gray & ((1U << bit_depth)-1U); + do_alpha = 1U; + } + + /* This helps avoid cluttering the code up with #ifdefs: */ +# define check_tRNS if (do_alpha) *--dp = (pixel != gray) * 255U; +# define UNTESTED_tRNS if (do_alpha) UNTESTED +# else /* !READ_tRNS */ +# define check_tRNS +# define UNTESTED_tRNS +# endif /* READ_tRNS */ + + dp += PNG_TC_ROWBYTES(*tc); /* pre-decremented below */ + + switch (bit_depth) + { + case 1: + { + unsigned int shift = 7U & -tc->width; + unsigned int s = *--sp; + + for(;;) + { + if (shift == 8U) s = *--sp, shift = 0; + + { + const unsigned int pixel = (s >> shift) & 1U; + + check_tRNS + *--dp = PNG_BYTE(pixel * 255U); + if (dp <= ep) break; + } + ++shift; + } + + debug(dp == ep && shift == 7U && sp == tc->sp); + break; + } + + case 2: + { + unsigned int shift = 7U & -(tc->width << 1)/*overflow ok*/; + unsigned int s = *--sp; + + for (;;) + { + if (shift == 8U) s = *--sp, shift = 0; + { + const unsigned int pixel = (s >> shift) & 3U; + + check_tRNS + + /* 'sig_bits' must be 1 or 2 leaving insignificant_bits 0 or + * 1. This may look silly but it allows a compact representation + * of 1 bit gray + 1 bit alpha (transparency): + */ + if (insignificant_bits /* only 1 bit significant */) + { + *--dp = PNG_BYTE((pixel >> 1) * 255U); + UNTESTED + } + + else + *--dp = PNG_BYTE(pixel * 85U); + + if (dp <= ep) break; + } + shift += 2U; + } + + debug(dp == ep && shift == 6U && sp == tc->sp); + break; + } + + case 4: + { + unsigned int shift = 7U & -(tc->width << 2)/*overflow ok*/; + unsigned int s = *--sp; + const unsigned int div = (1U << (4U-insignificant_bits)) - 1U; + + for (;;) + { + if (shift == 8U) s = *--sp, shift = 0; + { + unsigned int pixel = (s >> shift) & 15U; + + check_tRNS + + /* insignifcant_bits may be 0, 1, 2 or 3, requiring a multiply by + * 17, 255/7, 85 or 255. Since this operation is always cached + * we don't much care about the time to do the divide below. + */ + if (insignificant_bits) + { + pixel = ((pixel>>insignificant_bits) * 255U + (div>>1)) / div; + UNTESTED + } + + else + pixel *= 17U; + + *--dp = PNG_BYTE(pixel); + if (dp <= ep) break; + } + + shift += 4U; + } + + debug(dp == ep && shift == 4U && sp == tc->sp); + break; + } + + default: + impossible("bit depth"); + } + + tc->sp = ep; + +# undef check_tRNS +# undef png_ptr +} + +static void +png_init_expand(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* The possible combinations are: + * + * 1) PALETTE: the 'palette' flag should be set on the transform control and + * all that need be done is cancel this to cause the cache code to do the + * expansion. + * + * 2) LBP_GRAY, LBP_GRAY+tRNS: use png_do_expand_lbd_gray to do the required + * expand. Can be cached. + * + * 3) tRNS: scan the row for the relevant tRNS value. + * + * Note that expanding 8 to 16 bits is a byte op in pngtrans.c (it just + * replicates bytes). + */ + if (tc->palette) + { + debug(tc->caching && !(tc->format & PNG_FORMAT_FLAG_COLORMAP)); + + if (((*transform)->args & PNG_EXPAND_PALETTE) != 0U) + { + tc->palette = 0U; + tc->invalid_info |= PNG_INFO_PLTE + PNG_INFO_tRNS; + tc->cost = PNG_CACHE_COST_LIMIT; /* the cache is required! */ + } + + /* Note that this needs to happen when the row is processed (!tc->init) as + * well. + */ + } + + else if (!(tc->format & PNG_FORMAT_FLAG_COLORMAP)) + { + png_uint_32 args = (*transform)->args & PNG_BIC_MASK(PNG_EXPAND_PALETTE); + unsigned int bit_depth = tc->bit_depth; + + debug(tc->init); + + if (bit_depth >= 8U) + args &= PNG_BIC_MASK(PNG_EXPAND_LBP_GRAY); + +# ifdef PNG_READ_tRNS_SUPPORTED + if (png_ptr->num_trans == 0U || + (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0U || + (tc->invalid_info & PNG_INFO_tRNS) != 0U) +# endif + args &= PNG_BIC_MASK(PNG_EXPAND_tRNS); + + (*transform)->args = args; + + switch (args) + { + case PNG_EXPAND_LBP_GRAY: + tc->bit_depth = 8U; + tc->invalid_info |= PNG_INFO_tRNS; + + if (tc->init == PNG_TC_INIT_FINAL) + (*transform)->fn = png_do_expand_lbd_gray; + break; + +# ifdef PNG_READ_tRNS_SUPPORTED + case PNG_EXPAND_LBP_GRAY + PNG_EXPAND_tRNS: + tc->bit_depth = 8U; + tc->format |= PNG_FORMAT_FLAG_ALPHA; + tc->invalid_info |= PNG_INFO_tRNS; + tc->transparent_alpha = 1U; + + /* In this case tRNS must be left unmodified for the expansion code + * to handle. + */ + if (tc->init == PNG_TC_INIT_FINAL) + (*transform)->fn = png_do_expand_lbd_gray; + break; + + case PNG_EXPAND_tRNS: + if (tc->init == PNG_TC_INIT_FINAL) + { + png_expand *tr = png_transform_cast(png_expand, *transform); + + affirm((tc->bit_depth == 8U || tc->bit_depth == 16U) && + (tc->format & + (PNG_FORMAT_FLAG_COLORMAP|PNG_FORMAT_FLAG_ALPHA)) == 0U); + + tr->ntrans = fill_transparent_pixel(png_ptr, + tr->transparent_pixel); + tr->tr.fn = png_do_expand_tRNS; + } /* TC_INIT_FINAL */ + + tc->format |= PNG_FORMAT_FLAG_ALPHA; + tc->invalid_info |= PNG_INFO_tRNS; + tc->transparent_alpha = 1U; + break; +# endif /* READ_tRNS */ + + default: /* transform not applicable */ + (*transform)->fn = NULL; + break; + } + + implies(tc->init == PNG_TC_INIT_FINAL, + (*transform)->fn != png_init_expand); + } + + else /* not applicable */ + { + debug(tc->init); + (*transform)->fn = NULL; + NOT_REACHED; + } +# undef png_ptr +} + +void PNGAPI +png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr) +{ + if (png_ptr != NULL) + png_add_transform(png_ptr, sizeof (png_expand), png_init_expand, + PNG_TR_EXPAND)->args |= PNG_EXPAND_LBP_GRAY; +} + +/* Expand paletted images to 8-bit RGB or, if there is a tRNS chunk, RGBA. + * Note that this is effectively handled by the read code palette optimizations. + * + * API CHANGE: this used to have the completely unexpected side effect of + * turning on the above two optimizations. + */ +void PNGAPI +png_set_palette_to_rgb(png_structrp png_ptr) +{ + if (png_ptr != NULL) + png_add_transform(png_ptr, sizeof (png_expand), png_init_expand, + PNG_TR_EXPAND)->args |= PNG_EXPAND_PALETTE; +} + +/* Expand paletted images to RGB, expand grayscale images of less than 8-bit + * depth to 8-bit depth, and expand tRNS chunks to alpha channels. I.e. all the + * above. + */ +void PNGAPI +png_set_expand(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { + png_set_palette_to_rgb(png_ptr); + png_set_expand_gray_1_2_4_to_8(png_ptr); + png_set_tRNS_to_alpha(png_ptr); + } +} +#endif /* READ_EXPAND */ + +#if defined(PNG_READ_EXPAND_SUPPORTED) ||\ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + +#define PNG_INIT_STRIP_ALPHA 1U +#define PNG_INIT_EXPAND_tRNS 2U +static void +png_init_alpha(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + int required = 0; + +# if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_tRNS_SUPPORTED) + if ((*transform)->args & PNG_INIT_EXPAND_tRNS) + { + /* Prior to 1.7 the alpha channel was stripped after expanding the tRNS + * chunk, so this effectively cancelled out the expand. + */ + if (png_ptr->num_trans > 0 && !tc->palette && + !((*transform)->args & PNG_INIT_STRIP_ALPHA)) + { + debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0); + + required = 1; + tc->expand_tRNS = 1U; + + if (tc->init == PNG_TC_INIT_FORMAT) + png_add_transform(png_ptr, sizeof (png_expand), png_init_expand, + PNG_TR_EXPAND)->args |= PNG_EXPAND_tRNS; + } + + else + (*transform)->args &= PNG_BIC_MASK(PNG_INIT_EXPAND_tRNS); + } +# endif /* READ_EXPAND && READ_tRNS */ + +# ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if ((*transform)->args & PNG_INIT_STRIP_ALPHA) + { + /* When compose is being done tRNS will be expanded regardless of the + * above test. Rather that trying to work out if this will happen the + * code just inserts a strip operation; it will be removed later if it + * is not needed. + */ + required = 1; + tc->strip_alpha = 1U; + + if (tc->init == PNG_TC_INIT_FORMAT) + png_add_strip_alpha_byte_ops(png_ptr); + } +# endif /* READ_STRIP_ALPHA */ + + if (!required) + (*transform)->fn = NULL; +# undef png_ptr +} +#endif /* READ_EXPAND || READ_STRIP_ALPHA */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand tRNS chunks to alpha channels. This only expands the tRNS chunk on + * non-palette formats; call png_set_palette_to_rgb to get the corresponding + * effect for a palette. + * + * Note that this will expand low bit depth gray if there is a tRNS chunk, but + * if not nothing will happen. + * + * API CHANGE: this used to do all the expansions, it was rather pointless + * calling it. + */ +void PNGAPI +png_set_tRNS_to_alpha(png_structrp png_ptr) +{ + if (png_ptr != NULL) + png_add_transform(png_ptr, 0/*size*/, png_init_alpha, PNG_TR_INIT_ALPHA)-> + args |= PNG_INIT_EXPAND_tRNS; +} +#endif /* READ_EXPAND */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +void PNGAPI +png_set_strip_alpha(png_structrp png_ptr) +{ + if (png_ptr != NULL) + png_add_transform(png_ptr, 0/*size*/, png_init_alpha, PNG_TR_INIT_ALPHA)-> + args |= PNG_INIT_STRIP_ALPHA; +} +#endif /* READ_STRIP_ALPHA */ + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +static void +png_do_chop_16_to_8(png_transformp *transform, png_transform_controlp tc) + /* This is actually a repeat of the byte transform, unnecessary code + * replication. + * + * TODO: remove this + */ +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); /* source */ + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); /* end+1 */ + png_bytep dp = png_voidcast(png_bytep, tc->dp); /* destination */ + + debug(tc->bit_depth == 16U); + tc->sp = dp; + tc->bit_depth = 8U; + + while (sp < ep) + *dp++ = *sp, sp += 2; + + debug(sp == ep); +# undef png_ptr + + PNG_UNUSED(transform) +} + +/* A transform containing some useful scaling values... */ +typedef struct +{ + png_transform tr; + png_uint_32 shifts; /* 4 4-bit values preceeded by a shibboleth (1) */ + png_uint_32 channel_scale[4]; +} png_transform_scale_16_to_8; + +/* Scale rows of bit depth 16 down to 8 accurately */ +static void +png_do_scale_16_to_8(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); /* source */ + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); /* end+1 */ + png_bytep dp = png_voidcast(png_bytep, tc->dp); /* destination */ + png_transform_scale_16_to_8 *tr = + png_transform_cast(png_transform_scale_16_to_8, *transform); + png_uint_32p scale = 0; + png_uint_32 shift = 1U; /* set the shibboleth at the start */ + + debug(tc->bit_depth == 16U); + tc->sp = dp; + tc->bit_depth = 8U; + + while (sp < ep) + { + /* The input is an array of 16 bit components, these must be scaled to + * 8 bits each taking into account the sBIT setting. The calculation + * requires that the insignificant bits be stripped from the input value + * via a shift then scaled back to 8 bits: + * + * output = ((input >> shift) * scale + round) >> 24 + * + * The shifts are packed into tr->shifts, with the end of the list marked + * by a shibboleth, 1, which is preset above. + */ + png_uint_32 v = png_get_uint_16(sp); + + sp += 2; + + if (shift == 1U) + { + shift = tr->shifts; + scale = tr->channel_scale; + } + + *dp++ = PNG_BYTE(((v >> (shift & 0xFU)) * *scale++ + 0x800000U) >> 24); + shift >>= 4; + } + + affirm(sp == ep); +# undef png_ptr +} + +static int +add_scale(png_transform_scale_16_to_8 *tr, unsigned int sBIT, unsigned int ch) +{ + /* This is the output max (255) scaled by 2^24 divided by the input max' + * (which is variable) and rounded. It gives the exact 8-bit answer for all + * input sBIT depths when used in the calculation: + * + * output = ((input >> shift) * scale + 0x800000U) >> 24 + */ + tr->channel_scale[ch] = (0xFF000000U + ((1U<>1)) / ((1U<shifts |= ((16U-sBIT) & 0xFU) << (4U*ch); + + /* The result says whether there are 8 or fewer significant bits in the + * input value; if so we can just drop the low byte. + */ + return sBIT <= 8U; +} + +static void +png_init_scale_16_to_8(png_transformp *transform, png_transform_controlp tc) +{ + if (tc->bit_depth == 16U) + { +# define png_ptr (tc->png_ptr) + tc->bit_depth = 8U; + /* But this invalidates tRNS (a 16-bit tRNS cannot be updated to match + * 8-bit data correctly). + */ + tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL; + /* TODO: These need further processing: PNG_INFO_bKGD */ + + if (tc->init == PNG_TC_INIT_FINAL) + { + png_transform_scale_16_to_8 *tr = + png_transform_cast(png_transform_scale_16_to_8, *transform); + + /* Set the scale factors for each channel (up to 4), the factors are + * made so that: + * + * ((channel >> shift) * factor + 0x800000U) >> 24 + * + * Gives the required 8-bit value. The 'shift' is stored in a single + * png_uint_32 with a shibboleth at the end. + */ + unsigned int channels = 0U; + int chop_ok = 1; + + tr->shifts = 0U; + + /* This adds up to four scale factors, the remainder are left as 0 + * which is safe and leads to obvious errors in the output images in + * the event of an (internal) error. + */ + if (tc->format & PNG_FORMAT_FLAG_COLOR) + chop_ok &= add_scale(tr, tc->sBIT_R, channels++); + + chop_ok &= add_scale(tr, tc->sBIT_G, channels++); + + if (tc->format & PNG_FORMAT_FLAG_COLOR) + chop_ok &= add_scale(tr, tc->sBIT_B, channels++); + + if (tc->format & PNG_FORMAT_FLAG_ALPHA) + chop_ok &= add_scale(tr, tc->sBIT_A, channels++); + + if (chop_ok) + tr->tr.fn = png_do_chop_16_to_8; + + else + { + int handled = 1; + + /* Add the shibboleth at the end */ + tr->shifts |= 1U << (4U*channels); + tr->tr.fn = png_do_scale_16_to_8; + + /* sBIT is a little tricky; it has to be processed in the scaling + * operation. The result will have the same number of bits unless + * there were more than 8 before. The sBIT flags in the transform + * control are left unchanged here because the data is still valid, + * unless all the values end up as 8 in which case there is no + * remaining sBIT info. + * + * Note that fields, such as alpha, which are not set for this row + * format will always have max values, so won't reset 'handled': + */ + if (tc->sBIT_R >= 8U) tc->sBIT_R = 8U; else handled = 0; + if (tc->sBIT_G >= 8U) tc->sBIT_G = 8U; else handled = 0; + if (tc->sBIT_B >= 8U) tc->sBIT_B = 8U; else handled = 0; + if (tc->sBIT_A >= 8U) tc->sBIT_A = 8U; else handled = 0; + + /* If all the sBIT values were >= 8U all the bits are now + * significant: + */ + if (handled) + tc->invalid_info |= PNG_INFO_sBIT; + } + } + +# undef png_ptr + } + + else /* not applicable */ + (*transform)->fn = NULL; +} + +void PNGAPI +png_set_scale_16(png_structrp png_ptr) +{ + if (png_ptr != NULL) + png_add_transform(png_ptr, sizeof (png_transform_scale_16_to_8), + png_init_scale_16_to_8, PNG_TR_SCALE_16_TO_8); +} +#endif /* READ_SCALE_16_TO_8 */ + #ifdef PNG_READ_GAMMA_SUPPORTED /* Code that depends on READ_GAMMA support; RGB to gray convertion and * background composition (including the various alpha-mode handling * operations which produce pre-multiplied alpha by composing on 0). */ -#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED -/* A local convenience routine. */ +/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */ static png_fixed_point -png_product2(png_fixed_point a, png_fixed_point b) +png_reciprocal(png_fixed_point a) { - /* The required result is 1/a * 1/b; the following preserves accuracy. */ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = floor(1E10/a+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; +#else png_fixed_point res; - if (png_muldiv(&res, a, b, 100000) != 0) + if (png_muldiv(&res, PNG_FP_1, PNG_FP_1, a) != 0) return res; - - return 0; /* overflow */ -} -#endif /* FLOATING_ARITHMETIC */ - -/* The inverse of png_product2. */ -static png_fixed_point -png_reciprocal2(png_fixed_point a, png_fixed_point b) -{ - /* The required result is 1/a * 1/b; the following preserves accuracy. */ -#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED - if (a != 0 && b != 0) - { - double r = 1E15/a; - r /= b; - r = floor(r+.5); - - if (r <= 2147483647. && r >= -2147483648.) - return (png_fixed_point)r; - } -#else - /* This may overflow because the range of png_fixed_point isn't - * symmetric, but this API is only used for the product of file and - * screen gamma so it doesn't matter that the smallest number it can - * produce is 1/21474, not 1/100000 - */ - png_fixed_point res = png_product2(a, b); - - if (res != 0) - return png_reciprocal(res); #endif - return 0; /* overflow */ + return 0; /* error/overflow */ } +/* This is the shared test on whether a gamma value is 'significant' - whether + * it is worth doing gamma correction. 'significant_bits' is the number of bits + * in the values to be corrected which are significant. + */ +static int +png_gamma_significant(png_const_structrp png_ptr, png_fixed_point gamma_val, + unsigned int sbits) +{ + /* The following table lists the threshold as a difference from PNG_FP_1 at + * which the gamma correction will make a change to at least an 'sbits' + * value. There is no entry for 1 bit values; gamma correction is never + * significant. + */ + static const png_uint_16 gamma_threshold_by_sbit[15][2] = + { + { 36907, 63092 }, /* 2 bits */ + { 17812, 21518 }, /* 3 bits */ + { 8675, 9496 }, /* 4 bits */ + { 4290, 4484 }, /* 5 bits */ + { 2134, 2181 }, /* 6 bits */ + { 1064, 1075 }, /* 7 bits */ + { 531, 534 }, /* 8 bits */ + { 265, 266 }, /* 9 bits */ + { 132, 132 }, /* 10 bits */ + { 66, 66 }, /* 11 bits */ + { 33, 33 }, /* 12 bits */ + { 16, 16 }, /* 13 bits */ + { 8, 8 }, /* 14 bits */ + { 4, 4 }, /* 15 bits */ + { 2, 2 }, /* 16 bits */ + }; + + /* Handle out of range values in release by doing the gamma correction: */ + debug(sbits > 0U && sbits <= 16U); + if (sbits == 0U || sbits > 16U) + return 1; + + /* 1 bit input or zero gamma, no correction possible/required: */ + if (gamma_val == 0 || sbits < 2U) + return 0; + + if (gamma_val < PNG_FP_1 - gamma_threshold_by_sbit[sbits-2U][0U]) + return gamma_val < PNG_FP_1 - png_ptr->gamma_threshold; + + else if (gamma_val > PNG_FP_1 + gamma_threshold_by_sbit[sbits-2U][1U]) + return gamma_val > PNG_FP_1 + png_ptr->gamma_threshold; + + return 0; /* not significant */ +} + +static int +png_gamma_equal(png_const_structrp png_ptr, png_fixed_point g1, + png_fixed_point g2, png_fixed_point *c, unsigned int sbits) + /* Gamma values are equal, or at least one is unknown; c is the correction + * factor from g1 to g2, i.e. g2/g1. + */ +{ + return sbits == 1U || g1 == 0 || g2 == 0 || g1 == g2 || + (png_muldiv(c, g2, PNG_FP_1, g1) && + !png_gamma_significant(png_ptr, *c, sbits)); +} + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +int +png_need_gamma_correction(png_const_structrp png_ptr, png_fixed_point gamma, + int sRGB_output) + /* This is a hook for the simplified code; it just decides whether or not the + * given gamma (which defaults to that of the PNG data) is close enough to + * linear or sRGB not to require gamma correction. + */ +{ + if (gamma == 0) + gamma = png_ptr->colorspace.gamma; + + if (gamma != 0 && + (png_ptr->colorspace.flags & + (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) == + PNG_COLORSPACE_HAVE_GAMMA) + { + + if (sRGB_output && !png_muldiv(&gamma, gamma, PNG_GAMMA_sRGB, PNG_FP_1)) + return 0; /* overflow, so no correction */ + + return png_gamma_significant(png_ptr, gamma, (png_ptr->color_type & + PNG_COLOR_MASK_PALETTE) ? 8U : png_ptr->bit_depth); + } + + return 0; /* no info, no correction */ +} +#endif /* SIMPLIFIED_READ */ + #ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED /* Fixed point gamma. * @@ -143,7 +1608,7 @@ png_8bit_l2[128] = 172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U, 24347096U, 0U -#if 0 +#if 0 /* NOT USED */ /* The following are the values for 16-bit tables - these work fine for the * 8-bit conversions but produce very slightly larger errors in the 16-bit * log (about 1.2 as opposed to 0.7 absolute error in the final value). To @@ -164,31 +1629,34 @@ png_8bit_l2[128] = #endif }; +#if 0 /* UNUSED */ static png_int_32 png_log8bit(unsigned int x) { - unsigned int lg2 = 0; + png_uint_32 lg2 = 0U; + /* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log, * because the log is actually negate that means adding 1. The final * returned value thus has the range 0 (for 255 input) to 7.994 (for 1 * input), return -1 for the overflow (log 0) case, - so the result is * always at most 19 bits. */ - if ((x &= 0xff) == 0) - return -1; + if ((x &= 0xffU) == 0U) /* 0 input, -inf output */ + return -0xfffff; - if ((x & 0xf0) == 0) - lg2 = 4, x <<= 4; + if ((x & 0xf0U) == 0U) + lg2 = 4U, x <<= 4; - if ((x & 0xc0) == 0) - lg2 += 2, x <<= 2; + if ((x & 0xc0U) == 0U) + lg2 += 2U, x <<= 2; - if ((x & 0x80) == 0) - lg2 += 1, x <<= 1; + if ((x & 0x80U) == 0U) + lg2 += 1U, x <<= 1; /* result is at most 19 bits, so this cast is safe: */ - return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128]+32768)>>16)); + return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128U]+32768U)>>16)); } +#endif /* UNUSED */ /* The above gives exact (to 16 binary places) log2 values for 8-bit images, * for 16-bit images we use the most significant 8 bits of the 16-bit value to @@ -219,59 +1687,193 @@ png_log8bit(unsigned int x) * Start (256): -23591 * Zero (257): 0 * End (258): 23499 + * + * In libpng 1.7.0 this is further generalized to return -log2(value/maxval) for + * any maxval up to 65535. This is done by evaluating -log2(value/65535) first + * then adjusting for the required maxval: + * + * ( value) (value 65535) (value) ( 65535) + * -log2(------) = -log2(----- x ------) = -log2(-----)-log2(------) + * (maxval) (65535 maxval) (65535) (maxval) + * + * The extra argument, 'factor', is (2^(16+12))*log2(65535/maxval) (a positive + * value less than 2^32) and this is *subtracted* from the intermediate + * calculation below. */ -#ifdef PNG_16BIT_SUPPORTED static png_int_32 -png_log16bit(png_uint_32 x) +png_log(unsigned int x, png_uint_32 factor) + /* x: a value of up to 16 bits, + * factor: a 4.28 number which is subtracted from the log below + */ { - unsigned int lg2 = 0; + png_uint_32 lg2 = 0U; /* As above, but now the input has 16 bits. */ - if ((x &= 0xffff) == 0) - return -1; + if ((x &= 0xffffU) == 0U) + return -0xfffff; - if ((x & 0xff00) == 0) - lg2 = 8, x <<= 8; + if ((x & 0xff00U) == 0U) + lg2 = 8U, x <<= 8; - if ((x & 0xf000) == 0) - lg2 += 4, x <<= 4; + if ((x & 0xf000U) == 0U) + lg2 += 4U, x <<= 4; - if ((x & 0xc000) == 0) - lg2 += 2, x <<= 2; + if ((x & 0xc000U) == 0U) + lg2 += 2U, x <<= 2; - if ((x & 0x8000) == 0) - lg2 += 1, x <<= 1; + if ((x & 0x8000U) == 0U) + lg2 += 1U, x <<= 1; /* Calculate the base logarithm from the top 8 bits as a 28-bit fractional * value. */ lg2 <<= 28; - lg2 += (png_8bit_l2[(x>>8)-128]+8) >> 4; + lg2 += (png_8bit_l2[(x>>8)-128U]+8U) >> 4; /* Now we need to interpolate the factor, this requires a division by the top * 8 bits. Do this with maximum precision. */ - x = ((x << 16) + (x >> 9)) / (x >> 8); + { + png_uint_32 i = x; - /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24, - * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us exactly - * 16 bits to interpolate to get the low bits of the result. Round the - * answer. Note that the end point values are scaled by 64 to retain overall - * precision and that 'lg2' is current scaled by an extra 12 bits, so adjust - * the overall scaling by 6-12. Round at every step. - */ - x -= 1U << 24; + i = ((i << 16) + (i >> 9)) / (x>> 8); - if (x <= 65536U) /* <= '257' */ - lg2 += ((23591U * (65536U-x)) + (1U << (16+6-12-1))) >> (16+6-12); + /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24, + * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us + * exactly 16 bits to interpolate to get the low bits of the result. + * Round the answer. Note that the end point values are scaled by 64 to + * retain overall precision and that 'lg2' is current scaled by an extra + * 12 bits, so adjust the overall scaling by 6-12. Round at every step. + */ + i -= 1U << 24; - else - lg2 -= ((23499U * (x-65536U)) + (1U << (16+6-12-1))) >> (16+6-12); + if (i <= 65536U) /* <= '257' */ + lg2 += ((23591U * (65536U-i)) + (1U << (16+6-12-1))) >> (16+6-12); - /* Safe, because the result can't have more than 20 bits: */ - return (png_int_32)((lg2 + 2048) >> 12); + else + lg2 -= ((23499U * (i-65536U)) + (1U << (16+6-12-1))) >> (16+6-12); + } + + if (lg2 >= factor) + return (png_int_32)/*SAFE*/((lg2 - factor + 2048U) >> 12); + + else /* the result will be greater than 1.0, so negative: */ + return -(png_int_32)/*SAFE*/((factor - lg2 + 2048U) >> 12); } -#endif /* 16BIT */ + +#if 0 /* UNUSED */ +static png_int_32 +png_log16bit(unsigned int x) +{ + return png_log(x, 0U); +} +#endif /* UNUSED */ + +/* libpng 1.7.0: generalization of png_log{8,16}bit to accept an n-bit input + * value. We want to maintain 1% accuracy in linear light space. This + * corresponds to, approximately, (1*g)% in a gamma encoded space where the + * gamma encoding is 'g' (in the PNG sense, e.g. 0.45455 for sRGB). Apparently + * this requires unbounded accuracy as the gamma encoding value goes down and + * this is a problem for modern HDR data because it may require a high gamma to + * accurately encode image data over a wide dynamic range; the dynamic range of + * 16-bit linear data is only 655:1 if 1% accuracy is needed! + * + * However 16-bit gamma encoded data is still limited because PNG can only + * express gamma encoding. (A log-to-base-1.01 encoding is unlimited; a 12-bit + * value, with 4094 steps, has a dynamic range of more than 1:10^17, which + * exceeds the human eye's range of 1:10^14.) + * + * Notice that sRGB uses a 1/2.4 encoding and CIELab uses a 1/3 encoding. It is + * obvious that, if we assume a maximum D difference in the luminance of + * adjacent pixel values the dynamic range is given by the lowest pixel value + * which is D or less greater than its predecessor, so: + * + * ( P ) (1) + * (---)^(-) = D + * (P-1) (g) + * + * and the maximum dynamic range that can be achieved using M+1 separate values, + * where M+1 is 2^N-1 for an N bit value, reserving the first value for 0, is: + * + * (M) (1) + * range(R) = (-)^(-) + * (P) (g) + * + * So we can eliminate 'P' from the two equations: + * + * P = (P-1) x (D^g) + * + * D^g + * P = ----- + * D^g-1 + * + * (M x (D^g-1)) (1) + * R = (-----------)^(-) + * ( D^g ) (g) + * + * (M x (D^g-1)) ^ (1/g) + * = --------------------- + * D + * + * Which is a function in two variables (R and g) for a given D (maximum delta + * between two adjacent pixel values) and M (number of pixel values, controlled + * by the channel bit depth). + * + * See contrib/tools/dynamic-range.c for code exploring this function. This + * program will output the optimal gamma for a given number of bits and + * precision. + * + * The range of sensitivity of human vision is roughly as follows (this comes + * from the wikipedia article on scotopic vision): + * + * scotopic: 10^-6 to 10^-3.5 cd/m^2 + * mesopic: 10^-3 to 10^0.5 cd/m^2 + * photopic: 10 to 10^8 cd/m^2 + * + * Giving a total range of about 1:10^14. The maximum precision at which this + * range can be achieved using 16-bit channels is about .15% using a gamma of + * 36, higher ranges are possible using higher gammas but precision is reduced. + * The range with 1% precision and 16-bit channels is 1:10^104, using a gamma of + * 240. + * + * In general the optimal gamma for n-bit channels (where 'n' is at least 7 and + * precision is .01 or less) is: + * + * 2^n * precision + * gamma = --------------- + * 2.736 + * + * Or: (24000 * precision) for 16-bit data. + * + * The net effect is that we can't rely on the encoding gamma being limited to + * values around 1/2.5! + */ +static png_int_32 +png_log_nbit(unsigned int x, unsigned int nbits) +{ + static const png_uint_32 factors[16] = + { + 4294961387U, /* 1 bit */ + 3869501255U, /* 2 bit */ + 3541367788U, /* 3 bit */ + 3246213428U, /* 4 bit */ + 2965079441U, /* 5 bit */ + 2690447525U, /* 6 bit */ + 2418950626U, /* 7 bit */ + 2148993476U, /* 8 bit */ + 1879799410U, /* 9 bit */ + 1610985205U, /* 10 bit */ + 1342360514U, /* 11 bit */ + 1073830475U, /* 12 bit */ + 805347736U, /* 13 bit */ + 536888641U, /* 14 bit */ + 268441365U, /* 15 bit */ + 0U /* 16 bit */ + }; + + return png_log(x, factors[nbits-1]); +} + /* The 'exp()' case must invert the above, taking a 20-bit fixed point * logarithmic value and returning a 16 or 8-bit number as appropriate. In @@ -281,7 +1883,7 @@ png_log16bit(png_uint_32 x) * The worst case is the 16-bit distinction between 65535 and 65534. This * requires perhaps spurious accuracy in the decoding of the logarithm to * distinguish log2(65535/65534.5) - 10^-5 or 17 bits. There is little chance - * of getting this accuracy in practice. + * of needing this accuracy in practice. * * To deal with this the following exp() function works out the exponent of the * frational part of the logarithm by using an accurate 32-bit value from the @@ -297,7 +1899,7 @@ png_32bit_exp[16] = }; /* Adjustment table; provided to explain the numbers in the code below. */ -#if 0 +#if 0 /* BC CODE */ for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"} 11 44937.64284865548751208448 10 45180.98734845585101160448 @@ -314,86 +1916,152 @@ for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"} #endif static png_uint_32 -png_exp(png_fixed_point x) +png_exp(png_int_32 x) + /* Utility, the value 'x' must be in the range 0..0x1fffff */ { - if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */ - { - /* Obtain a 4-bit approximation */ - png_uint_32 e = png_32bit_exp[(x >> 12) & 0xf]; + /* Obtain a 4-bit approximation */ + png_uint_32 e = png_32bit_exp[(x >> 12) & 0xf]; - /* Incorporate the low 12 bits - these decrease the returned value by - * multiplying by a number less than 1 if the bit is set. The multiplier - * is determined by the above table and the shift. Notice that the values - * converge on 45426 and this is used to allow linear interpolation of the - * low bits. - */ - if (x & 0x800) - e -= (((e >> 16) * 44938U) + 16U) >> 5; + /* Incorporate the low 12 bits - these decrease the returned value by + * multiplying by a number less than 1 if the bit is set. The multiplier + * is determined by the above table and the shift. Notice that the values + * converge on 45426 and this is used to allow linear interpolation of the + * low bits. + */ + if (x & 0x800) + e -= (((e >> 16) * 44938U) + 16U) >> 5; - if (x & 0x400) - e -= (((e >> 16) * 45181U) + 32U) >> 6; + if (x & 0x400) + e -= (((e >> 16) * 45181U) + 32U) >> 6; - if (x & 0x200) - e -= (((e >> 16) * 45303U) + 64U) >> 7; + if (x & 0x200) + e -= (((e >> 16) * 45303U) + 64U) >> 7; - if (x & 0x100) - e -= (((e >> 16) * 45365U) + 128U) >> 8; + if (x & 0x100) + e -= (((e >> 16) * 45365U) + 128U) >> 8; - if (x & 0x080) - e -= (((e >> 16) * 45395U) + 256U) >> 9; + if (x & 0x080) + e -= (((e >> 16) * 45395U) + 256U) >> 9; - if (x & 0x040) - e -= (((e >> 16) * 45410U) + 512U) >> 10; + if (x & 0x040) + e -= (((e >> 16) * 45410U) + 512U) >> 10; - /* And handle the low 6 bits in a single block. */ - e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9; + /* And handle the low 6 bits in a single block. */ + e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9; - /* Handle the upper bits of x. */ - e >>= x >> 16; - return e; - } - - /* Check for overflow */ - if (x <= 0) - return png_32bit_exp[0]; - - /* Else underflow */ - return 0; + /* Handle the upper bits of x, note that this works for x up to 0x1fffff but + * fails for larger or negative x, where the shift (x >> 16) exceeds 31: + */ + e >>= x >> 16; + return e; } +#if 0 /* UNUSED */ static png_byte -png_exp8bit(png_const_structrp png_ptr, png_fixed_point lg2) +png_exp8bit(png_int_32 lg2) { - /* Get a 32-bit value: */ - png_uint_32 x = png_exp(lg2); - - /* Convert the 32-bit value to 0..255 by multiplying by 256-1. Note that the - * second, rounding, step can't overflow because of the first, subtraction, - * step. + /* The input is a negative fixed point (16:16) logarithm with a useable range + * of [0.0..8.0). Clamp the value so that the output of png_exp is in the + * range (254.5/255..0.5/255): */ - x -= x >> 8; - return png_check_byte(png_ptr, (x + 0x7fffffU) >> 24); - PNG_UNUSEDRC(png_ptr) + if (lg2 <= 185) /* -log2(254.5/255) */ + return 255U; + + else if (lg2 > 589453) /* -log2(0.5/255) */ + return 0U; + + else + { + /* Get a 32-bit value: */ + png_uint_32 x = png_exp(lg2); + + /* Convert the 32-bit value to 0..255 by multiplying by 256-1. Note that + * the second, rounding, step can't overflow because of the first, + * subtraction, step. + */ + x -= x >> 8; + return PNG_BYTE((x + 0x7fffffU) >> 24); + } } static png_uint_16 -png_exp16bit(png_const_structrp png_ptr, png_fixed_point lg2) +png_exp16bit(png_int_32 lg2) { - /* Get a 32-bit value: */ - png_uint_32 x = png_exp(lg2); + if (lg2 <= 0) /* -log2(65534.5/65535) */ + return 65535U; - /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */ - x -= x >> 16; - return png_check_u16(png_ptr, (x + 32767U) >> 16); - PNG_UNUSEDRC(png_ptr) + else if (lg2 > 1114110) /* -log2(0.5/65535) */ + return 0U; + + else + { + /* Get a 32-bit value: */ + png_uint_32 x = png_exp(lg2); + + /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */ + x -= x >> 16; + return PNG_UINT_16((x + 32767U) >> 16); + } } -#endif /* FLOATING_ARITHMETIC */ +#endif /* UNUSED */ -static png_byte -png_gamma_8bit_correct(png_const_structrp png_ptr, png_uint_32 value, - png_fixed_point gamma_val) +static png_uint_32 +png_exp_nbit(png_int_32 lg2, unsigned int n) { - if (value > 0 && value < 255) + /* These pre-computed limits give the low value of lg2 at and below which + * 2^(-lg2/65536) * (2^n-1) gives (2^n-1) and the high value of lg2 above + * which 2(^-lg2/65536) * (2^n-1) gives 0: + */ + static const png_int_32 limits[16][2] = + { + { 65535, 65535 }, /* bits = 1 */ + { 17238, 169408 }, /* bits = 2 */ + { 7006, 249518 }, /* bits = 3 */ + { 3205, 321577 }, /* bits = 4 */ + { 1537, 390214 }, /* bits = 5 */ + { 753, 457263 }, /* bits = 6 */ + { 372, 523546 }, /* bits = 7 */ + { 185, 589453 }, /* bits = 8 */ + { 92, 655175 }, /* bits = 9 */ + { 46, 720803 }, /* bits = 10 */ + { 23, 786385 }, /* bits = 11 */ + { 11, 851944 }, /* bits = 12 */ + { 5, 917492 }, /* bits = 13 */ + { 2, 983034 }, /* bits = 14 */ + { 1, 1048573 }, /* bits = 15 */ + { 0, 1114110 } /* bits = 16 */ + }; + + /* If 'max' is 2^n-1: */ + if (lg2 <= limits[n-1][0]) /* -log2((max-.5)/max) */ + return (1U << n)-1U; + + else if (lg2 > limits[n-1][1]) /* -log2(.5/max) */ + return 0U; + + else /* 'n' will be at least 2 */ + { + /* Get a 32-bit value: */ + png_uint_32 x = png_exp(lg2); + + /* Convert the 32-bit value to 0..(2^n-1) by multiplying by 2^n-1: */ + x -= x >> n; + return (x + ((1U<<(31U-n))-1U)) >> (32U-n); + } +} +#endif /* !FLOATING_ARITHMETIC */ + +#if 0 /* UNUSED */ +static png_byte +png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val) +{ + if (value == 0U) + return 0U; + + else if (value >= 255U) + return 255U; + + else { # ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED /* 'value' is unsigned, ANSI-C90 requires the compiler to correctly @@ -420,582 +2088,1407 @@ png_gamma_8bit_correct(png_const_structrp png_ptr, png_uint_32 value, * range for an (int); that would result in undefined behavior in the * caller if the *argument* ('value') were to be declared (int). */ - double r = floor(255*pow((int)/*SAFE*/value/255.,gamma_val*.00001)+.5); - if (r >= 0 && r <= 255) - return (png_byte)/*SAFE*/r; + double r = 255*pow((int)/*SAFE*/value/255.,gamma_val*.00001); + if (r < .5) + return 0U; + + else if (r >= 254.5) + return 255U; + + r = floor(r+.5); + return (png_byte)/*SAFE*/r; # else png_int_32 lg2 = png_log8bit(value); - png_fixed_point res; + png_int_32 res; - if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1) != 0) - return png_exp8bit(png_ptr, res); + /* Overflow in the muldiv means underflow in the calculation, this is + * OK (it happens for ridiculously high gamma). + */ + if (!png_muldiv(&res, gamma_val, lg2, PNG_FP_1)) + return 0U; /* underflow */ + + return png_exp8bit(res); # endif - - /* Overflow. */ - handled("8-bit gamma overflow"); - return 0; } +} +#endif /* UNUSED */ - return png_check_byte(png_ptr, value); - PNG_UNUSED(png_ptr) /* Only used in non-release builds */ +/* libpng-1.7.0: this internal function converts an n-bit input value to an + * m-bit output value. + */ +static unsigned int +png_gamma_nxmbit_correct(unsigned int value, png_fixed_point gamma_val, + unsigned int n/*input bits*/, unsigned int m/*output bits */) +{ + if (value == 0U) + return 0U; + + else + { + unsigned int min = (1U<= min) + return mout; + + else + { +# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = value; + r /= min; + r = floor(mout * pow(r, gamma_val*.00001)+.5); + if (r < 1) + return 0U; + + else if (r >= mout) + return mout; + + return (unsigned int)/*SAFE*/r; +# else + png_int_32 lg2 = png_log_nbit(value, n); + png_int_32 res; + + if (!png_muldiv(&res, gamma_val, lg2, PNG_FP_1)) + return 0U; /* underflow */ + + return png_exp_nbit(res, m); +# endif + } + } } +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED png_uint_16 -png_gamma_16bit_correct(png_const_structrp png_ptr, png_uint_32 value, +png_gamma_16bit_correct(png_const_structrp png_ptr, unsigned int value, png_fixed_point gamma_val) { - if (value > 0 && value < 65535) + /* This is a hook into this code for use by the simplified API (only) */ + return png_check_u16(png_ptr, + png_gamma_nxmbit_correct(value, gamma_val, 16U, 16U)); + PNG_UNUSED(png_ptr) +} +#endif /* SIMPLIFIED_READ */ + +#undef png_gamma_16bit_correct /* circumvent the prefix handling */ +#define png_gamma_16bit_correct NOT_USED_HERE /* for checking */ + +#if 0 /*UNUSED*/ +static unsigned int +png_gamma_sbit_correct(unsigned int value, png_fixed_point gamma_val, + unsigned int n/*input bits*/, unsigned int sbits, + unsigned int m/*output bits */) + /* As above but the number of significant bits in 'n' is passed in. */ +{ + if (sbits < n) { -# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED - /* The same (unsigned int)->(double) constraints apply here as above, - * however in this case the (unsigned int) to (int) conversion can - * overflow on an ANSI-C90 compliant system so the cast needs to ensure - * that this is not possible. - */ - double r = floor(65535.*pow((png_int_32)value/65535., - gamma_val*.00001)+.5); - if (r >= 0 && r <= 65535) - return (png_uint_16)/*SAFE*/r; -# else - png_int_32 lg2 = png_log16bit(value); - png_fixed_point res; - - if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1) != 0) - return png_exp16bit(png_ptr, res); -# endif - - /* Overflow. */ - handled("16-bit gamma overflow"); - return 0; + value >>= (n-sbits); + n = sbits; } - return png_check_u16(png_ptr, value); - PNG_UNUSED(png_ptr) /* Only used in non-release builds */ + return png_gamma_nxmbit_correct(value, gamma_val, n, m); +} +#endif /*UNUSED*/ + +static int +push_gamma_expand(png_transformp *transform, png_transform_controlp tc, + int need_alpha) + /* Utility to push a transform to expand low-bit-depth gray and, where + * required, tRNS chunks. The caller must return immediately if this + * returns true because the init of the new transform has been run in place + * of the caller's. + */ +{ +# define png_ptr (tc->png_ptr) + unsigned int expand = 0; + + affirm(tc->init == PNG_TC_INIT_FINAL); + + if (tc->bit_depth < 8U) /* low bit gray: expand to 8 bits */ + expand = PNG_EXPAND_LBP_GRAY; + + /* Gamma correction invalidates tRNS, so if it is being expanded and + * alpha is not being stripped expand it now. + */ + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 && !tc->palette && + png_ptr->num_trans == 1 && (tc->invalid_info & PNG_INFO_tRNS) == 0) + { + if (need_alpha || (tc->expand_tRNS && !tc->strip_alpha)) + expand |= PNG_EXPAND_tRNS; + + else + tc->invalid_info |= PNG_INFO_tRNS; + } + + if (expand == 0) + return 0; /* nothing needs to be done */ + + { + png_transformp tr = png_push_transform(png_ptr, sizeof (png_expand), + png_init_expand, transform, NULL/*don't run init*/); + + debug(tr == *transform); + tr->args |= expand; + + /* This must be run immediately, because it just got inserted where this + * transform is; this is safe, the caller must return immediately. + */ + png_init_expand(transform, tc); + affirm(tr->fn != NULL); /* because it should need to do something! */ + } + + return 1; +# undef png_ptr } -#define PNG_GAMMA_TABLE_8 0 /* 8-bit entries in png_byte */ -#define PNG_GAMMA_TABLE_8_IN_16 1 /* 8-bit entries * 257 in png_uint_16 */ -#define PNG_GAMMA_TABLE_16 2 /* 16-bit entries in png_uint_16 */ - +/* Low bit depth gray gamma correction. The 1-bit case is a no-op because 0 and + * 1 always map to 0 and 1. The 2-bit case has the following possiblities: + * + * bits/correction: g0 g1 g2 g3 g4 g5 g6 + * 00 -> 00 00 00 00 00 00 00 + * 01 -> 11 10 10 01 00 00 00 + * 10 -> 11 11 10 10 10 01 00 + * 11 -> 11 11 11 11 11 11 11 + * + * Where the breakpoints are: + * + * g0: correction <= 16595 (1 - log(2.5/3)) + * g1: 16595 < correction <= 44966 (log(2.5/3)/log(2/3)) + * g2: 44966 < correction <= 63092 (1 - log(1.5/3)) + * g3: 63092 < correction <= 163092 (1 - log(.5/3)) + * g4: 163092 < correction <= 170951 (log(1.5/3)/log(2/3)) + * g5: 170951 < correction <= 441902 (log(.5/3)/log(2/3) + * g6 441902 < correction + * + * This can be done by bit-hacking on the byte values (4 pixels), given that + * the correction is fixed (indeed, it can be done on whole 32-bit values!) + * + * g0: B |= B>>1; B &= 0x55U; B |= B<<1; * either bit set + * g1: B ^= B>>1; B &= 0x55U; B += B; * one bit set + * g2: B &= (~B)>>1; B &= 0x55U; B += B; * low bit set, high bit unset + * g3: no-op + * g4: B &= (~B)>>1; B &= 0x55U; B -= B; * low bit set, high bit unset + * g5: B ^= B>>1; B &= 0x55U; B -= B; * one bit set + * g6: B &= B>>1; B &= 0x55U; B |= B<<1; * both bits set + */ typedef struct { - png_fixed_point gamma; - png_uint_32 mult; - unsigned int add; - unsigned int shift; /* input value is (i * mult + add) >> shift */ - int output; /* One of the above values */ - int adjust; /* Divide or multiple output by 257 */ - png_voidp table; /* Lookup table */ -} gamma_table_data; + png_transform tr; + png_fixed_point correct; + png_fixed_point to_gamma; + png_uint_32 shifts; /* 1 followed by up to 4 4-bit shifts */ + png_uint_32 channel_scale[4]; /* up to 4 channel scale factors */ + /* These factors are used: + * + * (input >> (shifts & 0xFU) * channel_scale + SCALE_R) >> SCALE_S + * + * Where the rounding value, SCALE_R and the shift SCALE_S are dependent + * on the bit depth: + * + * SCALE_S = 32 - bit_depth range 16..31 + * SCALE_R = 1 << (SCALE_S-1) + */ + unsigned int to_bit_depth; + unsigned int encode_alpha :1; + unsigned int optimize_alpha :1; +} png_transform_gamma; static unsigned int -write_gamma_table_entry(png_const_structrp png_ptr, - const gamma_table_data *data, png_uint_32 i) - /* Calculate and write a single entry into table[i], the value of the entry - * written is returned. +init_gamma_sBIT(png_transform_gamma *tr, png_transform_controlp tc) + /* Returns true if sBIT processing is required, otherwise all relevant sBIT + * values match the from (tc) bit depth. */ { - png_uint_32 in = (i * data->mult + data->add) >> data->shift; - unsigned int out; - - /* If the output is TABLE_8 with no adjust, or the output is not with an - * adjust, use 8-bit correction. + /* The to_bit_depth and to_gamma fields are already set, but updated values + * are needed for sBIT and the shifts and channel_scale fields must be filled + * in correctly. The do_gamma setting says whether gamma correction will be + * done, but the scale factors are filled in regardless. + * + * The general scaling equation is: + * + * ((in >> shift) * factor + round) >> (32 - to_bit_depth) + * + * 'factor' is then the rounded value of: + * + * out_max + * ------- . (1 << (32-to_bit_depth)) + * in_max */ - if ((data->output == PNG_GAMMA_TABLE_8) != (data->adjust != 0)) - { - out = png_gamma_8bit_correct(png_ptr, in, data->gamma); +# define png_ptr (tc->png_ptr) + const unsigned int to_bit_depth = tr->to_bit_depth; + const png_uint_32 numerator = ((1U<bit_depth; - if (data->adjust != 0) - out *= 257U; + /* The data in the gamma transform is stored in the order of the channels in + * the input row, which is the PNG order. It may be reversed below. + */ + png_uint_32p channel_scale = tr->channel_scale; + png_uint_32 shifts = 0U; + unsigned int count = 0U; + unsigned int need_sBIT = 0U; + + if (tc->format & PNG_FORMAT_FLAG_COLOR) + { + const unsigned int sBIT = tc->sBIT_R; + + if (sBIT < from_bit_depth) + need_sBIT = 1U; + + debug(sBIT > 0U && sBIT <= from_bit_depth); + shifts |= (from_bit_depth - sBIT) << count; + count += 4U; + /* round the scale: */ + *channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U); } - else /* 16-bit correction */ { - out = png_gamma_16bit_correct(png_ptr, in, data->gamma); + const unsigned int sBIT = tc->sBIT_G; - if (data->adjust != 0) - out = PNG_DIV257(out); + if (sBIT < from_bit_depth) + need_sBIT = 1U; + + debug(sBIT > 0U && sBIT <= from_bit_depth); + shifts |= (from_bit_depth - sBIT) << count; + count += 4U; + *channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U); } - PNG_UNUSEDRC(png_ptr) - if (data->output == PNG_GAMMA_TABLE_8) - png_upcast(png_bytep, data->table)[i] = png_check_byte(png_ptr, out); + if (tc->format & PNG_FORMAT_FLAG_COLOR) + { + const unsigned int sBIT = tc->sBIT_B; - else - png_upcast(png_uint_16p, data->table)[i] = png_check_u16(png_ptr, out); + if (sBIT < from_bit_depth) + need_sBIT = 1U; - return out; + debug(sBIT > 0U && sBIT <= from_bit_depth); + shifts |= (from_bit_depth - sBIT) << count; + count += 4U; + /* round the scale: */ + *channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U); + } + + if (tc->format & PNG_FORMAT_FLAG_ALPHA) + { + const unsigned int sBIT = tc->sBIT_A; + + if (sBIT < from_bit_depth) + need_sBIT = 1U; + + debug(sBIT > 0U && sBIT <= from_bit_depth); + shifts |= (from_bit_depth - sBIT) << count; + count += 4U; + /* round the scale: */ + *channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U); + } + + tr->shifts = shifts | (1U << count); + + return need_sBIT; +# undef png_ptr } static void -write_gamma_table(png_const_structrp png_ptr, const gamma_table_data *data, - png_uint_32 lo, unsigned int loval, png_uint_32 hi, unsigned int hival) - /* Fill in gamma table entries between lo and hi, exclusive. The entries at - * table[lo] and table[hi] have already been written, the intervening entries - * are written. - */ +reverse_gamma_sBIT(png_transform_gamma *tr) { - if (hi > lo+1) /* Else nothing to fill in */ + /* This is called for the 'down' gamma implementations, they read the shifts + * and the channel scales in reverse, so: + */ + png_uint_32 shifts = tr->shifts; + png_uint_32 scales[4U]; + unsigned int count = 0U; + + tr->shifts = 1U; + + while (shifts != 1U) { - if (hival == loval) + scales[3U-count] = tr->channel_scale[count]; + ++count; + tr->shifts <<= 4; + tr->shifts |= shifts & 0xFU; + shifts >>= 4; + } + + memcpy(tr->channel_scale, scales+(4U-count), count * sizeof (png_uint_32)); +} + +static void +png_do_gamma8_up(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + const png_fixed_point correct = tr->correct; + const unsigned int bit_depth = tr->to_bit_depth; + const png_uint_32 shifts = tr->shifts; + + affirm(tc->bit_depth == 8U); + affirm(tr->shifts != 0U/*uninitialized*/); + debug((shifts & 0x8888U) == 0U); /* all shifts 7 or less */ + debug(!tr->encode_alpha && !tr->optimize_alpha); /* only set for 16 bits */ + + tc->sp = dp; + tc->bit_depth = bit_depth; + tc->gamma = tr->to_gamma; + + /* Handle the <8 bit output case differently because there can be no alpha + * channel. + */ + if (bit_depth < 8U) + { + const unsigned int shift = shifts & 0xFU; + unsigned int bits = 8U; + unsigned int ob = 0U; + + debug((shifts >> 4) == 1U && shift < 8U); + affirm(PNG_TC_CHANNELS(*tc) == 1); + + do { - /* All intervening entries must be the same. */ - if (data->output == PNG_GAMMA_TABLE_8) - { - png_bytep table8 = png_voidcast(png_bytep, data->table); - - while (++lo < hi) - table8[lo] = png_check_byte(png_ptr, loval); - } - + const unsigned int inb = png_gamma_nxmbit_correct( + *sp++ >> shift, correct, 8U-shift, bit_depth); + bits -= bit_depth; + ob = ob | (inb << bits); + if (bits == 0U) + bits = 8U, *dp++ = PNG_BYTE(ob); else - { - png_uint_16p table16 = png_voidcast(png_uint_16p, data->table); - - while (++lo < hi) - table16[lo] = png_check_u16(png_ptr, loval); - } + bits -= bit_depth; } + while (sp < ep); + + if (bits < 8U) + *dp++ = PNG_BYTE(ob); + + UNTESTED + } + + else /* 8-bit --> 8-bit */ + { + png_uint_32 alpha_scale; + const unsigned int channels = PNG_TC_CHANNELS(*tc); + unsigned int channel, alpha; + + debug(bit_depth == 8U && (shifts >> (4*channels)) == 1U); + + /* The alpha channel is always last, so if present checking against the + * top bits of 'channels' works because of the 1U shibboleth at the end. + */ + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0) + alpha_scale = alpha = 0U; else { - png_uint_32 mid = (lo+hi) >> 1; - unsigned int midval = write_gamma_table_entry(png_ptr, data, mid); + alpha = shifts >> (4U*(channels-1U)); + alpha_scale = tr->channel_scale[channels-1U]; + } - /* The algorithm used is to divide the entries to be written in half - * and fill in the middle. For all practical tables with significant - * gamma this will result in a performance gain because the expensive - * gamma correction arithmetic is avoided for some entries. + channel = 1U; + + do + { + unsigned int inb = *sp++, shift; + + if (channel == 1U) + channel = shifts; + + shift = channel & 0xFU; + inb >>= shift; + + /* The alpha channel is not gamma encoded but it may need some + * appropriate scaling. */ - write_gamma_table(png_ptr, data, lo, loval, mid, midval); - write_gamma_table(png_ptr, data, mid, midval, hi, hival); - } - } -} - -static void * -png_build_gamma_table(png_structrp png_ptr, png_fixed_point gamma_val, - int output/*as above*/, int input_depth, int use_shift) - /* Build a gamma lookup table to encode input_depth bit input values. - * The table will have 2^input_depth entries plus an extra one if use_shift - * is specified. With shift the table is accessed: - * - * table[(original-value + rounding) >> shift] - * - * And an extra entry exists to accomodate overflow of original-value on - * rounding. If use_shift is not specified the table is accessed with an - * input_depth bit value and the original values must have been correctly - * scaled to this range (not using a shift!) - * - * Each table entry contains input-value^gamma_val rounded to the output - * precision. This is 8 bit precision unless output is specified as - * PNG_GAMMA_TABLE_16, in which case it is 16-bit precision. For - * PNG_GAMMA_TABLE_8_IN_16 the 8-bit value is scaled to 16-bits by - * multiplying by 257. - */ -{ - png_uint_32 size; - unsigned int hival; - gamma_table_data data; - - /* If use_shift is true or if the input or output is not 8-bit the gamma - * correction will use the 16-bit correction code. This requires a value in - * the range 0..65535. For use_shift the value is simply: - * - * input << shift - * - * For the scaling case the value is: - * - * round(input * 65535 / ((1<> shift; - * - * With 'mult' and 'add' chosen to minimize the error for all input values - * in the range 0..((1< PNG_GAMMA_TABLE_8; - - if (use_shift != 0) - { - /* The multiplier does the shift: */ - data.mult = 1U << (8-input_depth); - data.add = 0; - data.shift = 0; - if (input_depth < 8) ++size; - } - - else - { - data.mult = multadd255[input_depth-1].mult; - data.add = multadd255[input_depth-1].add; - data.shift = multadd255[input_depth-1].shift; - } - } - - else - { - /* 16-bit correction is used for cases where input or output require more - * than 8 bits. - */ - data.adjust = output == PNG_GAMMA_TABLE_8; - - if (use_shift != 0) - { - data.mult = 1U << (16-input_depth); - data.add = 0; - data.shift = 0; - if (input_depth < 16) ++size; - } - - else - { - data.mult = multadd65535[input_depth-1].mult; - data.add = multadd65535[input_depth-1].add; - data.shift = multadd65535[input_depth-1].shift; - } - } - - if (output == PNG_GAMMA_TABLE_8) - { - data.table = png_malloc(png_ptr, size * sizeof (png_byte)); - ((png_bytep)data.table)[0] = 0; - hival = ((png_bytep)data.table)[size-1] = 255; - } - - else - { - /* Output is 16 bits, although it may only have 8 bits of precision */ - data.table = png_malloc(png_ptr, size * sizeof (png_uint_16)); - ((png_uint_16p)data.table)[0] = 0; - hival = ((png_uint_16p)data.table)[size-1] = 65535; - } - - if (png_gamma_significant(gamma_val) != 0) - write_gamma_table(png_ptr, &data, 0, 0, size-1, hival); - - else /* gamma_val not significant */ - { - if (output == PNG_GAMMA_TABLE_8) - { - png_uint_32 i; - png_bytep table8 = ((png_bytep)data.table); - - if (data.adjust) - for (i=1; i> data.shift)); + if (channel == alpha) + inb = (inb * alpha_scale + 0x800000U) >> 24; else - for (i=1; i> data.shift); + inb = png_gamma_nxmbit_correct(inb, correct, 8U-shift, 8U); + + channel >>= 4; /* for the next channel, or the shibboleth */ + *dp++ = PNG_BYTE(inb); } + while (sp < ep); - else - { - png_uint_32 i; - png_uint_16p table16 = (png_uint_16p)data.table; - - if (data.adjust) - for (i=1; i> data.shift) * 257U); - - else - for (i=1; i> data.shift); - } + debug(channel == 1U); } - - return data.table; +# undef png_ptr } -/* Used from png_read_destroy and below to release the memory used by the gamma - * tables. - */ -void /* PRIVATE */ -png_destroy_gamma_table(png_structrp png_ptr) -{ - png_free(png_ptr, png_ptr->gamma_table); - png_ptr->gamma_table = NULL; - - png_free(png_ptr, png_ptr->gamma_16_table); - png_ptr->gamma_16_table = NULL; - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - png_free(png_ptr, png_ptr->gamma_from_1); - png_ptr->gamma_from_1 = NULL; - png_free(png_ptr, png_ptr->gamma_to_1); - png_ptr->gamma_to_1 = NULL; - - png_free(png_ptr, png_ptr->gamma_16_from_1); - png_ptr->gamma_16_from_1 = NULL; - png_free(png_ptr, png_ptr->gamma_16_to_1); - png_ptr->gamma_16_to_1 = NULL; -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ -} - -/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit - * tables, we don't make a full table if we are reducing to 8-bit in - * the future. Note also how the gamma_16 tables are segmented so that - * we don't need to allocate > 64K chunks for a full 16-bit table. - */ static void -png_build_gamma_tables(png_structrp png_ptr, int bit_depth) +png_do_gamma16_up(png_transformp *transform, png_transform_controlp tc) { - png_debug(1, "in png_build_gamma_table"); +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 1U/*safety*/; + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + const png_fixed_point correct = tr->correct; + const unsigned int bit_depth = tr->to_bit_depth; + const png_uint_32 shifts = tr->shifts; - /* Remove any existing table; this copes with multiple calls to - * png_read_update_info. The warning is because building the gamma tables - * multiple times is a performance hit - it's harmless but the ability to call - * png_read_update_info() multiple times is new in 1.5.6 so it seems sensible - * to warn if the app introduces such a hit. - */ - if (png_ptr->gamma_table != NULL || png_ptr->gamma_16_table != NULL) - { - png_warning(png_ptr, "gamma table being rebuilt"); - png_destroy_gamma_table(png_ptr); - } + affirm(tc->bit_depth == 16U); + affirm(tr->shifts != 0U/*uninitialized*/); + debug(!tr->optimize_alpha); - if (bit_depth <= 8) - { - png_ptr->gamma_table = png_voidcast(png_bytep, png_build_gamma_table( - png_ptr, png_ptr->screen_gamma > 0 ? - png_reciprocal2(png_ptr->colorspace.gamma, png_ptr->screen_gamma) : - PNG_FP_1, PNG_GAMMA_TABLE_8, 8/*input depth*/, 0/*scale*/)); + /* This is exactly the same as above but the input has 16 bits per component, + * not 8. + */ + tc->sp = dp; + tc->bit_depth = bit_depth; + tc->gamma = tr->to_gamma; -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0) - { - /* This sets the accuracy of 8-bit composition and the 8-bit RGB to gray - * conversion - PNG_MAX_GAMMA_8 (the number of bits in the sixteen bit - * value that are considered significant.) - */ - png_ptr->gamma_to_1 = png_voidcast(png_uint_16p, png_build_gamma_table( - png_ptr, png_reciprocal(png_ptr->colorspace.gamma), - PNG_GAMMA_TABLE_16, 8/*input depth*/, 0/*scale*/)); + /* Handle the <8 bit output case differently because there can be no alpha + * channel. + */ + if (bit_depth < 8U) + { + const unsigned int shift = shifts & 0xFU; + unsigned int bits = 8U; + unsigned int ob = 0U; - png_ptr->gamma_from_1 = png_voidcast(png_bytep, png_build_gamma_table( - png_ptr, png_ptr->screen_gamma > 0 ? - png_reciprocal(png_ptr->screen_gamma) : - png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */, - PNG_GAMMA_TABLE_8, PNG_MAX_GAMMA_8/*input depth*/, 1/*shift*/)); + debug((shifts >> 4) == 1U && shift < 16U); + debug(!tr->encode_alpha && !tr->optimize_alpha); + affirm(PNG_TC_CHANNELS(*tc) == 1U); - png_ptr->gamma_shift = 16-PNG_MAX_GAMMA_8; - } -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ - } - else - { - png_byte shift, sig_bit; - int table_type; + do + { + unsigned int inb = *sp++ << 8; /* high bits first */ + inb = png_gamma_nxmbit_correct( + (inb + *sp++) >> shift, correct, 16U-shift, bit_depth); -# ifdef PNG_16BIT_SUPPORTED - table_type = PNG_GAMMA_TABLE_16; -# else - table_type = PNG_GAMMA_TABLE_8_IN_16; -# endif + bits -= bit_depth; + ob = ob | (inb << bits); + if (bits == 0U) + bits = 8U, *dp++ = PNG_BYTE(ob); + else + bits -= bit_depth; + } + while (sp < ep); - if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) - { - sig_bit = png_ptr->sig_bit.red; + if (bits < 8U) + *dp++ = PNG_BYTE(ob); - if (png_ptr->sig_bit.green > sig_bit) - sig_bit = png_ptr->sig_bit.green; + debug(sp == ep+1U); + UNTESTED + } - if (png_ptr->sig_bit.blue > sig_bit) - sig_bit = png_ptr->sig_bit.blue; - } - else - sig_bit = png_ptr->sig_bit.gray; + else + { + png_uint_32 alpha_scale; + const unsigned int channels = PNG_TC_CHANNELS(*tc); + unsigned int channel, alpha; - /* shift == insignificant bits */ - if (sig_bit > 0 && sig_bit < 16U) - shift = png_check_byte(png_ptr, 16U - sig_bit); + debug((bit_depth == 8U || bit_depth == 16U) && + (shifts >> (4*channels)) == 1U); - else - shift = 0; /* keep all 16 bits */ + /* Note that 'encode_alpha' turns on gamma encoding of the alpha + * channel (and this is a really weird operation!) + */ + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 || tr->encode_alpha) + alpha_scale = alpha = 0U; - if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0) - { - /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively - * the significant bits in the *input* when the output will - * eventually be 8 bits. - */ - if (shift < (16U - PNG_MAX_GAMMA_8)) - shift = (16U - PNG_MAX_GAMMA_8); + else + { + alpha = shifts >> (4U*(channels-1U)); + alpha_scale = tr->channel_scale[channels-1U]; + } - table_type = PNG_GAMMA_TABLE_8_IN_16; - } + channel = 1U; - png_ptr->gamma_shift = shift; + if (bit_depth == 16U) + { + do + { + unsigned int inb = *sp++ << 8, shift; + inb += *sp++; - png_ptr->gamma_16_table = png_voidcast(png_uint_16p, png_build_gamma_table( - png_ptr, png_ptr->screen_gamma > 0 ? png_reciprocal2( - png_ptr->colorspace.gamma, png_ptr->screen_gamma) : PNG_FP_1, - table_type, (16-shift)/*input depth*/, 1/*shift*/)); + if (channel == 1U) + channel = shifts; -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0) - { - png_ptr->gamma_16_to_1 = png_voidcast(png_uint_16p, - png_build_gamma_table(png_ptr, - png_reciprocal(png_ptr->colorspace.gamma), PNG_GAMMA_TABLE_16, - (16-shift)/*input depth*/, 1/*shift*/)); + shift = channel & 0xFU; + inb >>= shift; - /* Notice that the '16 from 1' table should be full precision, however - * the lookup on this table still uses gamma_shift, so it can't be. - * TODO: fix this. - */ - png_ptr->gamma_16_from_1 = png_voidcast(png_uint_16p, - png_build_gamma_table(png_ptr, png_ptr->screen_gamma > 0 ? - png_reciprocal(png_ptr->screen_gamma) : - png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */, - PNG_GAMMA_TABLE_16, (16-shift)/*input depth*/, 1/*shift*/)); - } -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ - } + /* The 16-16bit scaling factor equation may be off-by-1 but this + * hardly matters for alpha or for gamma operations. + */ + if (channel == alpha) + inb = (inb * alpha_scale + 0x8000U) >> 16; + + else + inb = png_gamma_nxmbit_correct(inb, correct, 16U-shift, 16U); + + channel >>= 4; /* for the next channel, or the shibboleth */ + *dp++ = PNG_BYTE(inb >> 8); + *dp++ = PNG_BYTE(inb); + } + while (sp < ep); + + debug(channel == 1U && sp == ep+1U); + } + + else /* bit_depth == 8U */ + { + do + { + unsigned int inb = *sp++ << 8, shift; + inb += *sp++; + + if (channel == 1U) + channel = shifts; + + shift = channel & 0xFU; + inb >>= shift; + + if (channel == alpha) + inb = (inb * alpha_scale + 0x800000U) >> 24; + + else + inb = png_gamma_nxmbit_correct(inb, correct, 16U-shift, 8U); + + channel >>= 4; /* for the next channel, or the shibboleth */ + *dp++ = PNG_BYTE(inb); + } + while (sp < ep); + + debug(channel == 1U && sp == ep+1U); + } + } +# undef png_ptr +} + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +static void +png_do_gamma16_up_optimize(png_transformp *transform, png_transform_controlp tc) + /* As above, but the alpha channel is 'optimized' */ +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + const png_fixed_point correct = tr->correct; + + /* The input always as 16 bits, the output 8 or 16. There is always an alpha + * channel and it is converted to the 'optimized' form, where pixels with + * alpha not 0.0 or 1.0 are left in linear form (not gamma corrected.) Where + * bit depth convertion is required it is from 16-bits to 8-bits and the + * DIV257 macro can be used. + * + * The following affirms and NOT_REACHED cases are consequences of the way + * the background (compose) code works: + */ + affirm(tr->optimize_alpha && !tr->encode_alpha && tc->bit_depth == 16U); + + /* TODO: split this into separate functions */ + switch (tr->to_bit_depth) + { + case 8U: /* 16-bit --> 8-bit */ + tc->sp = dp; + tc->bit_depth = 8U; + tc->gamma = tr->to_gamma; + + switch (PNG_TC_CHANNELS(*tc)) + { + case 2:/* GA */ + debug(tr->shifts == 0x100U); + ep -= 3U; /*SAFETY*/ + + do + { + png_uint_32 alpha = PNG_DIV257((sp[2] << 8) + sp[3]); + + switch (alpha) + { + case 0U: + dp[1] = dp[0] = 0U; + break; + + default: /* optimized case: linear color data */ + dp[0] = png_check_byte(png_ptr, + PNG_DIV257((sp[0] << 8) + sp[1])); + dp[1] = PNG_BYTE(alpha); + break; + + case 255U: /* opaque pixels are encoded */ + dp[0] = PNG_BYTE(png_gamma_nxmbit_correct( + (sp[0] << 8) + sp[1], correct, 16U, 8U)); + dp[1] = 255U; + break; + } + + sp += 4U; + dp += 2U; + } + while (sp < ep); + + debug(sp == ep+3U); + break; + + case 4:/* RGBA */ + debug(tr->shifts == 0x10000U); + ep -= 7U; /*SAFETY*/ + + do + { + png_uint_32 alpha = PNG_DIV257((sp[6] << 8) + sp[7]); + + switch (alpha) + { + case 0U: + memset(dp, 0U, 4U); + break; + + default: /* optimized case: linear color data */ + dp[0] = PNG_BYTE(PNG_DIV257((sp[0] << 8) + sp[1])); + dp[1] = PNG_BYTE(PNG_DIV257((sp[2] << 8) + sp[3])); + dp[2] = PNG_BYTE(PNG_DIV257((sp[4] << 8) + sp[5])); + dp[3] = PNG_BYTE(alpha); + break; + + case 255U: /* opaque pixels are encoded */ + dp[0] = PNG_BYTE(png_gamma_nxmbit_correct( + (sp[0] << 8) + sp[1], correct, 16U, 8U)); + dp[1] = PNG_BYTE(png_gamma_nxmbit_correct( + (sp[2] << 8) + sp[3], correct, 16U, 8U)); + dp[2] = PNG_BYTE(png_gamma_nxmbit_correct( + (sp[4] << 8) + sp[5], correct, 16U, 8U)); + dp[3] = 255U; + break; + } + + sp += 8U; + dp += 4U; + } + while (sp < ep); + + debug(sp == ep+7U); + break; + + default: + NOT_REACHED; + break; + } + break; + + case 16: /* 16-bit to 16-bit */ + tc->sp = dp; + tc->bit_depth = 16U; + tc->gamma = tr->to_gamma; + + switch (PNG_TC_CHANNELS(*tc)) + { + case 2:/* GA */ + debug(tr->shifts == 0x100U); + ep -= 3U; /*SAFETY*/ + + do + { + unsigned int alpha = (sp[2] << 8) + sp[3]; + + switch (alpha) + { + case 0U: + memset(dp, 0U, 4U); + break; + + default: /* optimized case: linear color data */ + if (dp != sp) + { + memcpy(dp, sp, 4U); + UNTESTED + } + break; + + case 65535U: /* opaque pixels are encoded */ + { + unsigned int gray = png_gamma_nxmbit_correct( + (sp[0] << 8) + sp[1], correct, 16U, 16U); + dp[0] = PNG_BYTE(gray >> 8); + dp[1] = PNG_BYTE(gray); + } + dp[3] = dp[2] = 255U; + break; + } + + sp += 4U; + dp += 4U; + } + while (sp < ep); + + debug(sp == ep+3U); + break; + + case 4:/* RGBA */ + debug(tr->shifts == 0x10000U); + ep -= 7U; /*SAFETY*/ + + do + { + unsigned int alpha = (sp[6] << 8) + sp[7]; + + switch (alpha) + { + case 0U: + memset(dp, 0U, 8U); + break; + + default: /* optimized case: linear color data */ + if (dp != sp) + { + memcpy(dp, sp, 8U); + UNTESTED + } + break; + + case 65535U: /* opaque pixels are encoded */ + { + unsigned int c = png_gamma_nxmbit_correct( + (sp[0] << 8) + sp[1], correct, 16U, 16U); + dp[0] = PNG_BYTE(c >> 8); + dp[1] = PNG_BYTE(c); + + c = png_gamma_nxmbit_correct( + (sp[2] << 8) + sp[3], correct, 16U, 16U); + dp[2] = PNG_BYTE(c >> 8); + dp[3] = PNG_BYTE(c); + + c = png_gamma_nxmbit_correct( + (sp[4] << 8) + sp[5], correct, 16U, 16U); + dp[4] = PNG_BYTE(c >> 8); + dp[5] = PNG_BYTE(c); + } + dp[7] = dp[6] = 255U; + break; + } + + sp += 8U; + dp += 8U; + } + while (sp < ep); + + debug(sp == ep+7U); + break; + + default: + NOT_REACHED; + break; + } + break; + + default: + NOT_REACHED; + break; + } +# undef png_ptr +} +#endif /* READ_ALPHA_MODE */ + +static void +png_do_scale16_up(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + const unsigned int bit_depth = tr->to_bit_depth; + + affirm(tc->bit_depth == 16U && bit_depth < 8U); + affirm(tr->shifts != 0U/*uninitialized*/); + + /* This is exactly the same as above but without the gamma correction and + * without the 8-bit target support. There can only be one channel: + */ + affirm(PNG_TC_CHANNELS(*tc) == 1U); + + tc->sp = dp; + /* This is a pure scaling operation so sBIT is not invalidated or altered. */ + tc->bit_depth = bit_depth; + + { + const unsigned int shift = tr->shifts & 0xFU; + const png_uint_32 factor = tr->channel_scale[0]; + const png_uint_32 round = 1U << (31U-bit_depth); + unsigned int bits = 8U; + unsigned int ob = 0U; + + do + { + png_uint_32 inb = *sp++ << 8; /* high bits first */ + inb += *sp++; + + inb = ((inb >> shift) * factor + round) >> (32U-bit_depth); + bits -= bit_depth; + ob = ob | (inb << bits); + if (bits == 0U) + bits = 8U, *dp++ = PNG_BYTE(ob); + else + bits -= bit_depth; + } + while (sp < ep); + + if (bits < 8U) + *dp++ = PNG_BYTE(ob); + + UNTESTED + } +# undef png_ptr +} + +static void +png_do_gamma8_down(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep ep = dp + 1U/*safety*/; + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + const png_fixed_point correct = tr->correct; + const png_uint_32 shifts = tr->shifts; + + affirm(tc->bit_depth == 8U && tr->to_bit_depth == 16U); + affirm(tr->shifts != 0U/*uninitialized*/); + debug((shifts & 0x8888U) == 0U); /* all shifts 7 or less */ + debug(!tr->encode_alpha && !tr->optimize_alpha); /* only set for 16 bits */ + + sp += PNG_TC_ROWBYTES(*tc); + tc->sp = dp; + tc->bit_depth = tr->to_bit_depth; + tc->gamma = tr->to_gamma; + dp += PNG_TC_ROWBYTES(*tc); + + { + png_uint_32 alpha_scale; + unsigned int channel, alpha; + + debug((shifts >> (4*PNG_TC_CHANNELS(*tc))) == 1U); + + /* We are going down so alpha, if present, is first. Notice that the init + * routine has to reverse both 'shifts' and 'channel_scale' for the _down + * cases. + */ + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0) + alpha_scale = alpha = 0U; + + else + { + alpha = shifts; + alpha_scale = tr->channel_scale[0U]; + } + + channel = 1U; + + do /* 8-bit --> 16-bit */ + { + unsigned int inb = *--sp, shift; + + if (channel == 1U) + channel = shifts; + + shift = channel & 0xFU; + inb >>= shift; + + if (channel == alpha) /* unencoded alpha, must scale */ + inb = (inb * alpha_scale + 0x8000U) >> 16; + + else + inb = png_gamma_nxmbit_correct(inb, correct, 8U-shift, 16U); + + channel >>= 4; + + *--dp = PNG_BYTE(inb); + *--dp = PNG_BYTE(inb >> 8); + } + while (dp > ep); + + debug(channel == 1U && dp == ep-1U); + } +# undef png_ptr +} + +static void +png_do_expand8_down(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep ep = dp + 1U/*safety*/; + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + const png_uint_32 shifts = tr->shifts; + + affirm(tc->bit_depth == 8U && tr->to_bit_depth == 16U); + affirm(tr->shifts != 0U/*uninitialized*/); + + sp += PNG_TC_ROWBYTES(*tc); + tc->sp = dp; + tc->bit_depth = 16U; + dp += PNG_TC_ROWBYTES(*tc); + + { + png_uint_32 channel = 1U; + png_const_uint_32p scale = 0U; + + do /* 8-bit -> 16-bit */ + { + unsigned int inb = *--sp, shift; + + if (channel == 1U) + channel = shifts, scale = tr->channel_scale; + + shift = channel & 0xFU; + channel >>= 4; + inb >>= shift; + inb = (inb * *scale++ + 0x8000U) >> 16; + /* dp starts beyond the end: */ + *--dp = PNG_BYTE(inb); + *--dp = PNG_BYTE(inb >> 8); + } + while (dp > ep); + + debug(channel == 1U && dp == ep-1U); + } +# undef png_ptr +} + +static void +png_do_expand8_down_fast(png_transformp *transform, png_transform_controlp tc) + /* Optimized version of the above for when the sBIT settings are all a full 8 + * bits (the normal case). + */ +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep ep = dp + 1U/*safety*/; + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + + affirm(tc->bit_depth == 8U && tr->to_bit_depth == 16U); + affirm(tr->shifts != 0U/*uninitialized*/); + + sp += PNG_TC_ROWBYTES(*tc); + tc->sp = dp; + tc->bit_depth = 16U; + dp += PNG_TC_ROWBYTES(*tc); + + do + dp -= 2, dp[0] = dp[1] = *--sp; + while (dp > ep); + + debug(dp == ep-1U); +# undef png_ptr +} + +static void +png_init_gamma_uncached(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + + debug(tc->init == PNG_TC_INIT_FINAL); + + /* Set this first; the result says if the sBIT data is significant, but it is + * ignored here. + */ + (void)init_gamma_sBIT(tr, tc); + + /* If png_set_alpha_mode is called but no background processing needs to be + * done (because there is no alpha channel or tRNS) we get to here with + * potentially spurious alpha mode flags. + */ + if (!(tc->format & PNG_FORMAT_FLAG_ALPHA)) + tr->encode_alpha = tr->optimize_alpha = 0U; + + /* Use separate functions for the two input depths but not for the five + * possible output depths and four channel counts. + */ + if (tc->bit_depth == 8U) + { + if (tr->to_bit_depth <= 8U) + tr->tr.fn = png_do_gamma8_up; + + else + { + debug(tr->to_bit_depth == 16U); + reverse_gamma_sBIT(tr); + tr->tr.fn = png_do_gamma8_down; + } + } + + else + { + affirm(tc->bit_depth == 16U); +# ifdef PNG_READ_ALPHA_MODE_SUPPORTED + if (!tr->optimize_alpha) + tr->tr.fn = png_do_gamma16_up; + else + tr->tr.fn = png_do_gamma16_up_optimize; +# else /* !READ_ALPHA_MODE */ + tr->tr.fn = png_do_gamma16_up; +# endif /* !READ_ALPHA_MODE */ + } + + /* Since the 'do' routines always perform gamma correction they will always + * expand the significant bits to the full output bit depth. + */ + tc->invalid_info |= PNG_INFO_sBIT; + tc->bit_depth = tr->to_bit_depth; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = + png_check_byte(png_ptr, tc->bit_depth); + if (tr->encode_alpha) + tc->sBIT_A = tc->sBIT_G; + tc->gamma = tr->to_gamma; +# undef png_ptr +} + +#ifdef PNG_READ_sBIT_SUPPORTED +static unsigned int +tc_sBIT(png_const_transform_controlp tc) + /* Determine the maximum number of significant bits in the row at this point. + * This uses the png_struct::sig_bit field if it has not been invalidated, + * otherwise it just returns the current bit depth. + */ +{ + const png_structrp png_ptr = tc->png_ptr; + unsigned int bit_depth = tc->bit_depth; + + if ((tc->invalid_info & PNG_INFO_sBIT) == 0U) + { + /* Normally the bit depth will not have been changed from the original PNG + * depth, but it currently is changed by the grayscale expand to 8 bits, + * an operation which doesn't invalidate sBIT. + */ + unsigned int sBIT; + + if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0U) + { + /* Must use the largest of the sBIT depths, except that unset values + * take priority. + */ + sBIT = png_ptr->sig_bit.red && png_ptr->sig_bit.green && + png_ptr->sig_bit.blue; + + if (sBIT != 0U) + { + sBIT = png_ptr->sig_bit.red; + + if (png_ptr->sig_bit.green > sBIT) + sBIT = png_ptr->sig_bit.green; + if (png_ptr->sig_bit.blue > sBIT) + sBIT = png_ptr->sig_bit.blue; + } + } + + else + sBIT = png_ptr->sig_bit.gray; + + if (sBIT > 0U && sBIT < bit_depth) + bit_depth = sBIT; + } + + return bit_depth; +} +#else /* !READ_sBIT */ +# define tc_sBIT(tc) ((tc)->bit_depth) +#endif /* READ_sBIT */ + +static void +png_init_gamma(png_transformp *transform, png_transform_controlp tc) +{ + const png_structrp png_ptr = tc->png_ptr; + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + + if (tc->init == PNG_TC_INIT_FORMAT) + { + /* This should only happen for the final encode gamma transform, which + * never initializes the target bit depth (see png_set_gamma and + * png_set_alpha_mode). The affirm is required here; in we can't continue + * safely if the bit depth has been set somehow. + */ + debug(tr->tr.order == PNG_TR_GAMMA_ENCODE); + affirm(tr->to_gamma > 0 && tr->to_bit_depth == 0U); + + /* At this point the output gamma should not have been set yet: */ + debug(png_ptr->row_gamma == 0); + + /* The following must be true; png_set_gamma and png_set_alpha_mode set + * (or default) the PNG gamma and other routines that insert a gamma + * transform must only do in PNG_TC_INIT_FINAL: + */ + debug(tc->gamma > 0); + + /* At this point the data gamma must be updated so that we get the correct + * png_struct::row_gamma at the end of the init: + */ + tc->gamma = tr->to_gamma; + + /* For safety invalidate the sBIT information too; we don't know yet + * whether a gamma transform will be required but if it is the sBIT + * information becomes invalid. + */ + tc->invalid_info |= PNG_INFO_sBIT; + } + + else /* PNG_TC_INIT_FINAL */ + { + /* It is very bad if we get here when processing a row: */ + affirm(tc->init == PNG_TC_INIT_FINAL && png_ptr->row_bit_depth > 0); + + /* There are three cases: + * + * 1) Gamma correction is required, output bit depth may need to be + * defaulted. + * 2) Gamma correction is not required but a bit depth change is + * necessary. + * 3) Neither is required; the transform can be eliminated. + * + * First default the bit depth if it is not already set. + */ + if (tr->to_bit_depth == 0) + { + if (tc->palette) /* caching a palette */ + tr->to_bit_depth = 8U; + + else + tr->to_bit_depth = png_ptr->row_bit_depth; + } + + /* (1); is gamma correction required? If tc->gamma is 0 at this point it + * is not, but then the png_struct::row_gamma should be 0 too. + */ + implies(tc->gamma == 0, png_ptr->row_gamma == 0); + implies(tr->to_gamma == 0, tc->gamma == 0); + + if (!png_gamma_equal(png_ptr, tc->gamma, tr->to_gamma, &tr->correct, + tc_sBIT(tc))) + { + /* First make sure the input doesn't have a tRNS chunk which needs to + * be expanded now; if it does push_gamma_expand will push an + * appropriate transform *before* this one and we need to return + * immediately (the caller will call back to this function). + */ + if (push_gamma_expand(transform, tc, 0/*need alpha*/)) + { + affirm(tc->bit_depth >= 8U && + (tc->invalid_info & PNG_INFO_tRNS) != 0U && + *transform != &tr->tr); + return; + } + + debug(*transform == &tr->tr && tc->bit_depth >= 8U); + + /* The format is now 8 or 16-bit G, GA, RGB or RGBA and gamma + * correction is required. + */ + png_init_gamma_uncached(transform, tc); + /* TODO: implement caching for the !tc->caching cases! */ + return; + } + + /* The cases where the two gamma values are close enough to be considered + * equal. The code lies about the gamma; this prevents apps and the + * simplified API getting into loops or bad conditions because the gamma + * was not set to the expected value. + * + * Note that png_transform_control::gamma is only set here if both the + * input and output gamma values are known, otherwise the transform + * introduces a spurious know gamma value. + */ + if (tr->to_gamma > 0 && tc->gamma > 0) + tc->gamma = tr->to_gamma; + + if (tr->to_bit_depth > tc->bit_depth) + { + /* This is either the to-linear operation, in which case the expected + * bit depth is 16U, or it is the final encode in the case where an + * 'expand' operation was also specified. + * + * We don't care about the PNG_TR_GAMMA_ENCODE case because we know + * that there has to be an expand operation further down the pipeline. + */ + if (tr->tr.order < PNG_TR_GAMMA_ENCODE) + { + affirm(tr->to_bit_depth == 16U); + + if (push_gamma_expand(transform, tc, 0/*need alpha*/)) + { + affirm(tc->bit_depth == 8U && + (tc->invalid_info & PNG_INFO_tRNS) != 0U && + *transform != &tr->tr); + return; + } + + debug(*transform == &tr->tr); + affirm(tc->bit_depth == 8U); /* if 16U we would not be here! */ + + /* not using byte_ops here, but if there is no sBIT required + * (normally the case) the fast code can be used: + */ + if (init_gamma_sBIT(tr, tc)) + tr->tr.fn = png_do_expand8_down; + + else + tr->tr.fn = png_do_expand8_down_fast; + + tc->bit_depth = 16U; + } + + else /* PNG_TR_GAMMA_ENCODE: nothing need be done */ + tr->tr.fn = NULL; + } + + else if (tr->to_bit_depth < tc->bit_depth) + { + /* No gamma correction but bit depth *reduction* is required. Expect + * the 'from' bit depth to always be 16, otherwise this transform + * should not have been pushed. Also expect this to be the gamma + * 'encode' operation at the end of the arithmetic. + */ + affirm(tc->bit_depth == 16U && tr->tr.order == PNG_TR_GAMMA_ENCODE); + + /* If the target bit depth is 8-bit delay the operation and use the + * standard 16-8-bit scale code. For low bit depth do it now. + */ + if (tr->to_bit_depth == 8U) + { + png_set_scale_16(png_ptr); + tr->tr.fn = NULL; + } + + else /* low bit depth */ + { + (void)init_gamma_sBIT(tr, tc); + tr->tr.fn = png_do_scale16_up; + tc->bit_depth = tr->to_bit_depth; + UNTESTED + } + } + + else /* gamma !significant and nothing to do */ + tr->tr.fn = NULL; + } } static png_fixed_point -translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma, +translate_gamma_flags(png_const_structrp png_ptr, png_fixed_point gamma, int is_screen) + /* If 'is_screen' is set this returns the inverse of the supplied value; i.e. + * this routine always returns an encoding value. + */ { /* Check for flag values. The main reason for having the old Mac value as a * flag is that it is pretty near impossible to work out what the correct * value is from Apple documentation - a working Mac system is needed to * discover the value! */ - if (output_gamma == PNG_DEFAULT_sRGB || - output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB) + switch (gamma) { - /* If there is no sRGB support this just sets the gamma to the standard - * sRGB value. (This is a side effect of using this function!) - */ -# ifdef PNG_READ_sRGB_SUPPORTED - png_ptr->flags |= PNG_FLAG_ASSUME_sRGB; -# else - PNG_UNUSED(png_ptr) -# endif - if (is_screen != 0) - output_gamma = PNG_GAMMA_sRGB; - else - output_gamma = PNG_GAMMA_sRGB_INVERSE; + case PNG_DEFAULT_sRGB: + case PNG_GAMMA_sRGB: + case PNG_FP_1/PNG_GAMMA_sRGB: /* stupid case: -100000 */ + gamma = PNG_GAMMA_sRGB_INVERSE; + break; + + case PNG_GAMMA_MAC_18: + case PNG_FP_1/PNG_GAMMA_MAC_18: /* stupid case: -50000 */ + gamma = PNG_GAMMA_MAC_INVERSE; + break; + + default: + if (is_screen) + { + /* Check for a ridiculously low value; this will result in an + * overflow + * in the reciprocal calculation. + */ + if (gamma < 5) + { + png_app_error(png_ptr, "invalid screen gamma (too low)"); + gamma = 0; + } + + else if (gamma != PNG_FP_1) /* optimize linear */ + gamma = png_reciprocal(gamma); + } + + else if (gamma <= 0) + { + png_app_error(png_ptr, "invalid file gamma (too low)"); + gamma = 0; + } + break; } - else if (output_gamma == PNG_GAMMA_MAC_18 || - output_gamma == PNG_FP_1 / PNG_GAMMA_MAC_18) - { - if (is_screen != 0) - output_gamma = PNG_GAMMA_MAC_OLD; - else - output_gamma = PNG_GAMMA_MAC_INVERSE; - } - - return output_gamma; + return gamma; } -# ifdef PNG_FLOATING_POINT_SUPPORTED +static png_transform_gamma * +add_gamma_transform(png_structrp png_ptr, unsigned int order, + png_fixed_point gamma, unsigned int bit_depth, int force) +{ + /* Add a png_transform_gamma transform at the given position; this is a + * utility which just adds the transform and (unconditionally) overwrites the + * to_gamma field. gamma must be valid. If 'force' is true the gamma value + * in an existing transform will be overwritten, otherwise this is just a + * default value. + */ + png_transform_gamma *tr = png_transform_cast(png_transform_gamma, + png_add_transform(png_ptr, sizeof (png_transform_gamma), png_init_gamma, + order)); + + if (force || tr->to_gamma == 0) + tr->to_gamma = gamma; + + tr->to_bit_depth = bit_depth; + + return tr; +} + +void PNGFAPI +png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma, + png_fixed_point file_gamma) +{ + png_debug(1, "in png_set_gamma_fixed"); + + /* Validate the passed in file gamma value: */ + file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/); + + /* The returned value may be 0, this results in a png_app_error above which + * may be ignored; if that happens simply ignore the setting. + */ + if (file_gamma > 0) + { + /* Set the colorspace gamma value unconditionally - this overrides the + * value in the PNG file if a gAMA chunk was present. png_set_alpha_mode + * provides a different, easier, way to default the file gamma. + */ + png_ptr->colorspace.gamma = file_gamma; + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + png_ptr->colorspace.flags = PNG_COLORSPACE_HAVE_GAMMA; + else + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + } + + /* Do the same thing with the screen gamma; check it and handle it if valid. + * This adds/sets the encoding of the final gamma transform in the chain. + * png_set_alpha_mode does the same thing. + */ + scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/); + + if (scrn_gamma > 0) + (void)add_gamma_transform(png_ptr, PNG_TR_GAMMA_ENCODE, scrn_gamma, + 0/*bit depth*/, 1/*force to_gamma to scrn_gamma*/); +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED static png_fixed_point convert_gamma_value(png_structrp png_ptr, double output_gamma) { @@ -1011,96 +3504,7 @@ convert_gamma_value(png_structrp png_ptr, double output_gamma) return png_fixed(png_ptr, output_gamma, "gamma value"); } -# endif -#ifdef PNG_READ_BACKGROUND_SUPPORTED -/* Handle alpha and tRNS via a background color */ -void PNGFAPI -png_set_background_fixed(png_structrp png_ptr, - png_const_color_16p background_color, int background_gamma_code, - int need_expand, png_fixed_point background_gamma) -{ - png_debug(1, "in png_set_background_fixed"); - - if (png_rtran_ok(png_ptr, 0) == 0 || background_color == NULL) - return; - - if (background_gamma_code != PNG_BACKGROUND_GAMMA_SCREEN && - background_gamma_code != PNG_BACKGROUND_GAMMA_FILE && - background_gamma_code != PNG_BACKGROUND_GAMMA_UNIQUE) - { - png_app_error(png_ptr, "invalid gamma type"); - return; - } - - png_ptr->transformations |= PNG_COMPOSE | PNG_STRIP_ALPHA; - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - - png_ptr->background = *background_color; - png_ptr->background_gamma = background_gamma; - png_ptr->background_gamma_type = png_check_byte(png_ptr, - background_gamma_code); - - if (need_expand != 0) - png_ptr->flags |= PNG_FLAG_BACKGROUND_EXPAND; - - else - png_ptr->flags &= ~PNG_FLAG_BACKGROUND_EXPAND; -} - -# ifdef PNG_FLOATING_POINT_SUPPORTED -void PNGAPI -png_set_background(png_structrp png_ptr, - png_const_color_16p background_color, int background_gamma_code, - int need_expand, double background_gamma) -{ - png_set_background_fixed(png_ptr, background_color, background_gamma_code, - need_expand, png_fixed(png_ptr, background_gamma, "png_set_background")); -} -# endif /* FLOATING_POINT */ -#endif /* READ_BACKGROUND */ - -void PNGFAPI -png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma, - png_fixed_point file_gamma) -{ - png_debug(1, "in png_set_gamma_fixed"); - - if (png_rtran_ok(png_ptr, 0) == 0) - return; - - /* New in libpng-1.5.4 - reserve particular negative values as flags. */ - scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/); - file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/); - - /* Checking the gamma values for being >0 was added in 1.5.4 along with the - * premultiplied alpha support; this actually hides an undocumented feature - * of the previous implementation which allowed gamma processing to be - * disabled in background handling. There is no evidence (so far) that this - * was being used; however, png_set_background itself accepted and must still - * accept '0' for the gamma value it takes, because it isn't always used. - * - * Since this is an API change (albeit a very minor one that removes an - * undocumented API feature) the following checks were only enabled in - * libpng-1.6.0. - */ - if (file_gamma <= 0) - png_error(png_ptr, "invalid file gamma in png_set_gamma"); - - if (scrn_gamma <= 0) - png_error(png_ptr, "invalid screen gamma in png_set_gamma"); - - /* Set the gamma values unconditionally - this overrides the value in the PNG - * file if a gAMA chunk was present. png_set_alpha_mode provides a - * different, easier, way to default the file gamma. - */ - png_ptr->colorspace.gamma = file_gamma; - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; - png_ptr->screen_gamma = scrn_gamma; -} - -#ifdef PNG_FLOATING_POINT_SUPPORTED void PNGAPI png_set_gamma(png_structrp png_ptr, double scrn_gamma, double file_gamma) { @@ -1108,2350 +3512,470 @@ png_set_gamma(png_structrp png_ptr, double scrn_gamma, double file_gamma) convert_gamma_value(png_ptr, file_gamma)); } #endif /* FLOATING_POINT */ - -/* In the case of gamma transformations only do transformations on images where - * the [file] gamma and screen_gamma are not close reciprocals, otherwise it - * slows things down slightly, and also needlessly introduces small errors. - */ -static int /* PRIVATE */ -png_gamma_threshold(png_fixed_point screen_gamma, png_fixed_point file_gamma) -{ - /* PNG_GAMMA_THRESHOLD is the threshold for performing gamma - * correction as a difference of the overall transform from 1.0 - * - * We want to compare the threshold with s*f - 1, if we get - * overflow here it is because of wacky gamma values so we - * turn on processing anyway. - */ - png_fixed_point gtest; - return !png_muldiv(>est, screen_gamma, file_gamma, PNG_FP_1) || - png_gamma_significant(gtest); -} - -/* Initialize everything needed for the read. This includes modifying - * the palette. - */ -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) -static void -gamma_correct_background(png_const_structrp png_ptr, - unsigned int value, unsigned int depth, - png_uint_16p backgroundp, png_uint_16p background_1p, - png_fixed_point gamma_correct, png_fixed_point gamma_to_1) -{ - switch (depth) - { - case 8: - if (gamma_correct != PNG_FP_1) - *backgroundp = png_gamma_8bit_correct(png_ptr, value, - gamma_correct); - - else - *backgroundp = png_check_u16(png_ptr, value); - - if (gamma_to_1 != PNG_FP_1) - *background_1p = png_gamma_16bit_correct(png_ptr, value*257, - gamma_to_1); - - else - *background_1p = png_check_u16(png_ptr, value*257); - - return; - - case 16: - if (gamma_correct != PNG_FP_1) - *backgroundp = png_gamma_16bit_correct(png_ptr, value, - gamma_correct); - - else - *backgroundp = png_check_u16(png_ptr, value); - - if (gamma_to_1 != PNG_FP_1) - *background_1p = png_gamma_16bit_correct(png_ptr, value, - gamma_to_1); - - else - *background_1p = png_check_u16(png_ptr, value); - - return; - - default: - /* Low bit depth gray levels; do no harm. */ - break; - } - - *backgroundp = png_check_u16(png_ptr, value); - *background_1p = 0; /* should not be used */ -} - -static void /* PRIVATE */ -png_init_background_transformations(png_structrp png_ptr) - /* Set the png_ptr->background and png_ptr->background_1 members correctly - * for the bit depth and format. - */ -{ - /* png_ptr->background is only assigned by png_set_background and - * png_set_alpha_mode (which just zeros out the fields.) png_set_background - * can set the PNG_FLAG_BACKGROUND_EXPAND flag if the input value is in the - * file format, for example if it comes from a bKGD chunk. - * - * Under some circumstances deficiencies in the current libpng code mean that - * the bit depth of the values must differ from the final bit depth; the bit - * depth has to match that at which the processing of the image pixels - * happens and this is not always the final bit depth. This is fixed up - * here. - * - * First find the required depth. - */ - unsigned int bit_depth, required_bit_depth; - unsigned int color_type = png_ptr->color_type; - const png_uint_32 transform = png_ptr->transformations; - const int need_expand = (png_ptr->flags & PNG_FLAG_BACKGROUND_EXPAND) != 0; - - if (color_type & PNG_COLOR_MASK_PALETTE) - required_bit_depth = bit_depth = 8; - - else - { - required_bit_depth = bit_depth = png_ptr->bit_depth; - - /* But not PNG_EXPAND_16 at present because it happens after the compose - * operation where the background is used! - */ - if (bit_depth < 8 && (transform & PNG_EXPAND) != 0) - required_bit_depth = 8; - } - - /* bit_depth and color_type now refer to the original file data and - * required_bit_depth is correct for the processing libpng does, however it - * does not necessarily match the output the application gets, fix that and - * the color type here: - */ - if (need_expand == 0) - { - /* The background bit_depth and color_type need correcting */ - if ((transform & PNG_EXPAND) != 0) - color_type &= ~PNG_COLOR_MASK_PALETTE; - - /* The RGB<->gray transformations do the to gray operation first, then the - * from gray. - */ - if ((transform & PNG_RGB_TO_GRAY) != 0) - color_type &= ~PNG_COLOR_MASK_COLOR; - - if ((transform & PNG_GRAY_TO_RGB) != 0) - color_type |= PNG_COLOR_MASK_COLOR; - - bit_depth = required_bit_depth; - - /* The expansion to 16 bits and the scaling back from 16 bits per - * component to only 8 happens after the background processing (at - * present) so these transforms only affect the screen value, not the - * required value. Note that the 16_TO_8 conversions happen before the 8 - * to 16 one, so in theory both could occur - the order of the tests below - * must be correct! - * - * TODO: Note that the second of these changes cause an input 16-bit - * background value to be temporarily crushed to 8-bits per component, - * losing precision. This is a bug and should be fixed. - */ - if (bit_depth == 16 && - (transform & (PNG_16_TO_8|PNG_SCALE_16_TO_8)) != 0) - bit_depth = 8; - - if (bit_depth == 8 && (color_type & PNG_COLOR_MASK_PALETTE) == 0 && - (transform & PNG_EXPAND_16) != 0) - bit_depth = 16; - } - - /* 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 - * png_ptr->background_1 values too. - */ - { - unsigned int mult = 1; - png_fixed_point gamma_to_1, gamma_correct; - - switch (png_ptr->background_gamma_type) - { - case PNG_BACKGROUND_GAMMA_SCREEN: - gamma_to_1 = png_ptr->screen_gamma; - gamma_correct = PNG_FP_1; - break; - - case PNG_BACKGROUND_GAMMA_FILE: - gamma_to_1 = png_reciprocal(png_ptr->colorspace.gamma); - gamma_correct = png_reciprocal2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma); - break; - - case PNG_BACKGROUND_GAMMA_UNIQUE: - gamma_to_1 = png_reciprocal(png_ptr->background_gamma); - gamma_correct = png_reciprocal2(png_ptr->background_gamma, - png_ptr->screen_gamma); - break; - - default: - gamma_to_1 = PNG_FP_1; - gamma_correct = PNG_FP_1; - break; - } - -# define CORRECT(v, c)\ - gamma_correct_background(png_ptr, (v)*mult, bit_depth,\ - &png_ptr->background.c, &png_ptr->background_1.c,\ - gamma_correct, gamma_to_1);\ - if (bit_depth > required_bit_depth)\ - png_ptr->background.c =\ - png_check_u16(png_ptr, PNG_DIV257(png_ptr->background.c)) - - /* The multiplier 'mult' scales the values to 'required_depth', - * 'bit_depth' is the depth of the resultant values. - */ - while (bit_depth < required_bit_depth) - mult += mult << bit_depth, bit_depth <<= 1; - - /* In the event that this still leaves the background bit depth greater - * than the libpng required depth scale the values back to the 8-bit - * range, the test below verifies that this is correct. - */ - affirm(bit_depth <= required_bit_depth || - (bit_depth == 16 && required_bit_depth == 8)); - - if ((color_type & PNG_COLOR_MASK_COLOR) != 0) - { - png_ptr->flags &= ~PNG_FLAG_BACKGROUND_IS_GRAY; /* checked below */ - - /* If need_expand was passed to png_set_background the background value - * was in the file format, therefore if the file is a palette file the - * background will have been an index into the palette. Notice that if - * need_expand was false then the color is RGB even if the output still - * has a palette. - */ - if (need_expand && (color_type & PNG_COLOR_MASK_PALETTE) != 0) - { - unsigned int background_index = png_ptr->background.index; - - if (background_index < png_ptr->num_palette && - png_ptr->palette != NULL) - { - /* In fact 'mult' is always 1 at present in this case */ - CORRECT(png_ptr->palette[background_index].red, red); - CORRECT(png_ptr->palette[background_index].green, green); - CORRECT(png_ptr->palette[background_index].blue, blue); - } - - else - { - png_app_error(png_ptr, "out of range background index"); - memset(&png_ptr->background, 0, sizeof png_ptr->background); - memset(&png_ptr->background_1, 0, sizeof png_ptr->background_1); - } - } - - else - { - CORRECT(png_ptr->background.red, red); - CORRECT(png_ptr->background.green, green); - CORRECT(png_ptr->background.blue, blue); - } - - if (png_ptr->background.red == png_ptr->background.blue && - png_ptr->background.red == png_ptr->background.green) - { - png_ptr->flags |= PNG_FLAG_BACKGROUND_IS_GRAY; - png_ptr->background.gray = png_ptr->background.red; - png_ptr->background_1.gray = png_ptr->background_1.red; - } - - else - png_ptr->background.gray = png_ptr->background_1.gray = 0; - } - - else - { - png_ptr->flags |= PNG_FLAG_BACKGROUND_IS_GRAY; - - CORRECT(png_ptr->background.gray, gray); - - png_ptr->background.red = - png_ptr->background.green = - png_ptr->background.blue = png_ptr->background.gray; - - png_ptr->background_1.red = - png_ptr->background_1.green = - png_ptr->background_1.blue = png_ptr->background_1.gray; - } -# undef CORRECT - } -} - -/* Replace any alpha or transparency with the supplied background color. - * "background" is already in the screen gamma, while "background_1" is - * at a gamma of 1.0. Paletted files have already been taken care of. - */ -static void -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; - png_const_uint_16p gamma_16 = png_ptr->gamma_16_table; - png_const_uint_16p gamma_16_from_1 = png_ptr->gamma_16_from_1; - png_const_uint_16p gamma_16_to_1 = png_ptr->gamma_16_to_1; - PNG_CONST unsigned int shift = png_ptr->gamma_shift; - PNG_CONST unsigned int add = (shift > 0 ? 1U<<(shift-1) : 0); - PNG_CONST int optimize = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0; - - png_bytep sp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - png_debug(1, "in png_do_compose"); - - if (!(row_info->flags & PNG_INDEXED)) { - switch (row_info->channels) - { - case 1 /*GRAY*/: - { - switch (row_info->bit_depth) - { - case 1: - { - int bit_shift = 7; - sp = row; - for (i = 0; i < row_width; i++) - { - if (((*sp >> bit_shift) & 0x01) == - png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0x7f7f >> (7 - bit_shift)); - tmp |= png_ptr->background.gray << bit_shift; - *sp = png_check_byte(png_ptr, tmp); - } - - if (bit_shift == 0) - { - bit_shift = 7; - sp++; - } - - else - bit_shift--; - } - break; - } - - case 2: - { -#if 0 - if (gamma_table != NULL) - { - int bit_shift = 6; - sp = row; - for (i = 0; i < row_width; i++) - { - if (((*sp >> bit_shift) & 0x03) == - png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0x3f3f >> (6 - bit_shift)); - tmp |= png_ptr->background.gray << bit_shift; - *sp = png_check_byte(png_ptr, tmp); - } - - else - { - unsigned int p = (*sp >> bit_shift) & 0x03; - unsigned int g = (gamma_table [p | (p << 2) | - (p << 4) | (p << 6)] >> 6) & 0x03; - unsigned int tmp = *sp & (0x3f3f >> (6 - bit_shift)); - tmp |= g << bit_shift; - *sp = png_check_byte(png_ptr, tmp); - } - - if (bit_shift == 0) - { - bit_shift = 6; - sp++; - } - - else - bit_shift -= 2; - } - } - - else -#endif - { - int bit_shift = 6; - sp = row; - for (i = 0; i < row_width; i++) - { - if (((*sp >> bit_shift) & 0x03) == - png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0x3f3f >> (6 - bit_shift)); - tmp |= png_ptr->background.gray << bit_shift; - *sp = png_check_byte(png_ptr, tmp); - } - - if (bit_shift == 0) - { - bit_shift = 6; - sp++; - } - - else - bit_shift -= 2; - } - } - break; - } - - case 4: - { -#if 0 - if (gamma_table != NULL) - { - int bit_shift = 4; - sp = row; - for (i = 0; i < row_width; i++) - { - if (((*sp >> bit_shift) & 0x0f) == - png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0xf0f >> (4 - bit_shift)); - tmp |= png_ptr->background.gray << bit_shift; - *sp = png_check_byte(png_ptr, tmp); - } - - else - { - unsigned int p = (*sp >> bit_shift) & 0x0f; - unsigned int g = (gamma_table[p | (p << 4)] >> 4) & - 0x0f; - unsigned int tmp = *sp & (0xf0f >> (4 - bit_shift)); - tmp |= g << bit_shift; - *sp = png_check_byte(png_ptr, tmp); - } - - if (bit_shift == 0) - { - bit_shift = 4; - sp++; - } - - else - bit_shift -= 4; - } - } - - else -#endif - { - int bit_shift = 4; - sp = row; - for (i = 0; i < row_width; i++) - { - if (((*sp >> bit_shift) & 0x0f) == - png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0xf0f >> (4 - bit_shift)); - tmp |= png_ptr->background.gray << bit_shift; - *sp = png_check_byte(png_ptr, tmp); - } - - if (bit_shift == 0) - { - bit_shift = 4; - sp++; - } - - else - bit_shift -= 4; - } - } - break; - } - - case 8: - { - if (gamma_table != NULL) - { - sp = row; - 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); - - else - *sp = gamma_table[*sp]; - } - } - - else - { - sp = row; - 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); - } - } - break; - } - - case 16: - { - if (gamma_16 != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 2) - { - png_uint_16 v; - - v = png_check_u16(png_ptr, ((*sp) << 8) + *(sp + 1)); - - 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 + 1) = PNG_BYTE(png_ptr->background.gray); - } - - else - { - v = gamma_16[(v+add) >> shift]; - *sp = png_check_byte(png_ptr, v >> 8); - *(sp + 1) = PNG_BYTE(v); - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 2) - { - png_uint_16 v; - - v = png_check_u16(png_ptr, ((*sp) << 8) + *(sp + 1)); - - if (v == png_ptr->trans_color.gray) - { - *sp = png_check_byte(png_ptr, - png_ptr->background.gray >> 8); - *(sp + 1) = PNG_BYTE(png_ptr->background.gray); - } - } - } - break; - } - - default: - break; - } - break; - } - - case 3 /*RGB*/: - { - if (row_info->bit_depth == 8) - { - if (gamma_table != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 3) - { - if (*sp == png_ptr->trans_color.red && - *(sp + 1) == png_ptr->trans_color.green && - *(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); - } - - else - { - *sp = gamma_table[*sp]; - *(sp + 1) = gamma_table[*(sp + 1)]; - *(sp + 2) = gamma_table[*(sp + 2)]; - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 3) - { - if (*sp == png_ptr->trans_color.red && - *(sp + 1) == png_ptr->trans_color.green && - *(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); - } - } - } - } - - else /* if (row_info->bit_depth == 16) */ - { - if (gamma_16 != NULL) - { - 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)); - - if (r == png_ptr->trans_color.red && - g == png_ptr->trans_color.green && - b == png_ptr->trans_color.blue) - { - /* Background is already in screen gamma */ - *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); - *(sp + 3) = PNG_BYTE(png_ptr->background.green); - *(sp + 4) = png_check_byte(png_ptr, - png_ptr->background.blue >> 8); - *(sp + 5) = PNG_BYTE(png_ptr->background.blue); - } - - else - { - png_uint_16 v = gamma_16[(r+add) >> shift]; - *sp = (png_byte)/*SAFE*/(v >> 8); - *(sp + 1) = PNG_BYTE(v); - - v = gamma_16[(g+add) >> shift]; - *(sp + 2) = (png_byte)/*SAFE*/(v >> 8); - *(sp + 3) = PNG_BYTE(v); - - v = gamma_16[(b+add) >> shift]; - *(sp + 4) = (png_byte)/*SAFE*/(v >> 8); - *(sp + 5) = PNG_BYTE(v); - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 6) - { - 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_byte)/*SAFE*/(png_ptr->background.red >> 8); - *(sp + 1) = PNG_BYTE(png_ptr->background.red); - *(sp + 2) = (png_byte)/*SAFE*/( - png_ptr->background.green >> 8); - *(sp + 3) = PNG_BYTE(png_ptr->background.green); - *(sp + 4) = (png_byte)/*SAFE*/( - png_ptr->background.blue >> 8); - *(sp + 5) = PNG_BYTE(png_ptr->background.blue); - /*UNTESTED*/ - } - } - /*UNTESTED*/ - } - } - break; - } - - case 2 /*GRAY_ALPHA*/: - { - if (row_info->bit_depth == 8) - { - if (gamma_to_1 != NULL && gamma_from_1 != NULL && - gamma_table != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 2) - { - unsigned int a = *(sp + 1); - - if (a == 0xff) - *sp = gamma_table[*sp]; - - else if (a == 0) - { - /* Background is already in screen gamma */ - *sp = png_check_byte(png_ptr, png_ptr->background.gray); - } - - else - { - unsigned int v, w; - - v = gamma_to_1[*sp]; - png_composite_16(w, v, 257*a, - png_ptr->background_1.gray); - - if (optimize == 0) - w = gamma_from_1[(w+add)>>shift]; - - else /* alpha pixels linear and approximate */ - w = PNG_DIV257(w); - - *sp = png_check_byte(png_ptr, w); - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 2) - { - png_byte a = *(sp + 1); - - if (a == 0) - *sp = png_check_byte(png_ptr, png_ptr->background.gray); - - else if (a < 0xff) - png_composite(*sp, *sp, a, png_ptr->background.gray); - } - } - } - - else /* if (png_ptr->bit_depth == 16) */ - { - if (gamma_16 != NULL && gamma_16_from_1 != NULL && - gamma_16_to_1 != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 4) - { - png_uint_16 a = png_check_u16(png_ptr, - ((*(sp + 2)) << 8) + *(sp + 3)); - - if (a == 65535) - { - unsigned int v; - - v = gamma_16[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = (png_byte)/*SAFE*/(v >> 8); - *(sp + 1) = PNG_BYTE(v); - } - - else if (a == 0) - { - /* Background is already in screen gamma */ - *sp = png_check_byte(png_ptr, - png_ptr->background.gray >> 8); - *(sp + 1) = PNG_BYTE(png_ptr->background.gray); - } - - else - { - png_uint_16 g, v, w; - - g = gamma_16_to_1[((sp[0]<<8)+sp[1]+add) >> shift]; - png_composite_16(v, g, a, png_ptr->background_1.gray); - - if (optimize == 0) - w = gamma_16_from_1[(v+add) >> shift]; - - else - w = v; - - *sp = png_check_byte(png_ptr, w >> 8); - *(sp + 1) = PNG_BYTE(w); - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 4) - { - png_uint_16 a = png_check_u16(png_ptr, - ((*(sp + 2)) << 8) + *(sp + 3)); - - if (a == 0) - { - *sp = png_check_byte(png_ptr, - png_ptr->background.gray >> 8); - *(sp + 1) = PNG_BYTE(png_ptr->background.gray); - } - - else if (a < 0xffff) - { - unsigned int g, v; - - g = ((*sp) << 8) + *(sp + 1); - png_composite_16(v, g, a, png_ptr->background.gray); - *sp = png_check_byte(png_ptr, v >> 8); - *(sp + 1) = PNG_BYTE(v); - } - } - } - } - break; - } - - case 4 /*RGB_ALPHA*/: - { - if (row_info->bit_depth == 8) - { - if (gamma_to_1 != NULL && gamma_from_1 != NULL && - gamma_table != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 4) - { - png_byte a = *(sp + 3); - - if (a == 0xff) - { - *sp = gamma_table[*sp]; - *(sp + 1) = gamma_table[*(sp + 1)]; - *(sp + 2) = gamma_table[*(sp + 2)]; - } - - else if (a == 0) - { - /* Background is already in screen gamma */ - *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); - } - - else - { - unsigned int v, w; - unsigned int alpha = a * 257U; - - v = gamma_to_1[*sp]; - png_composite_16(w, v, alpha, - png_ptr->background_1.red); - - if (optimize == 0) - w = gamma_from_1[(w+add)>>shift]; - - else - w = PNG_DIV257(w); - - *sp = png_check_byte(png_ptr, w); - - v = gamma_to_1[*(sp + 1)]; - png_composite_16(w, v, alpha, - png_ptr->background_1.green); - - if (optimize == 0) - w = gamma_from_1[(w+add)>>shift]; - - else - w = PNG_DIV257(w); - - *(sp + 1) = png_check_byte(png_ptr, w); - - v = gamma_to_1[*(sp + 2)]; - png_composite_16(w, v, alpha, - png_ptr->background_1.blue); - - if (optimize == 0) - w = gamma_from_1[(w+add)>>shift]; - - else - w = PNG_DIV257(w); - - *(sp + 2) = png_check_byte(png_ptr, w); - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 4) - { - png_byte a = *(sp + 3); - - if (a == 0) - { - *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); - } - - else if (a < 0xff) - { - png_composite(*sp, *sp, a, png_ptr->background.red); - - png_composite(*(sp + 1), *(sp + 1), a, - png_ptr->background.green); - - png_composite(*(sp + 2), *(sp + 2), a, - png_ptr->background.blue); - } - } - } - } - - else /* if (row_info->bit_depth == 16) */ - { - if (gamma_16 != NULL && gamma_16_from_1 != NULL && - gamma_16_to_1 != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 8) - { - png_uint_16 a = png_check_u16(png_ptr, - ((*(sp + 6)) << 8) + *(sp + 7)); - - if (a == 65535) - { - png_uint_16 v; - - v = gamma_16[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = png_check_byte(png_ptr, v >> 8); - *(sp + 1) = PNG_BYTE(v); - - v = gamma_16[((sp[2]<<8)+sp[3]+add) >> shift]; - *(sp + 2) = png_check_byte(png_ptr, v >> 8); - *(sp + 3) = PNG_BYTE(v); - - v = gamma_16[((sp[4]<<8)+sp[5]+add) >> shift]; - *(sp + 4) = png_check_byte(png_ptr, v >> 8); - *(sp + 5) = PNG_BYTE(v); - } - - else if (a == 0) - { - /* Background is already in screen gamma */ - *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); - *(sp + 3) = PNG_BYTE(png_ptr->background.green); - *(sp + 4) = png_check_byte(png_ptr, - png_ptr->background.blue >> 8); - *(sp + 5) = PNG_BYTE(png_ptr->background.blue); - } - - else - { - png_uint_16 v, w; - - v = gamma_16_to_1[((sp[0]<<8)+sp[1]+add) >> shift]; - png_composite_16(w, v, a, png_ptr->background_1.red); - - if (optimize == 0) - w = gamma_16_from_1[(w+add) >> shift]; - - *sp = png_check_byte(png_ptr, w >> 8); - *(sp + 1) = PNG_BYTE(w); - - v = gamma_16_to_1[((sp[2]<<8)+sp[3]+add) >> shift]; - png_composite_16(w, v, a, png_ptr->background_1.green); - - if (optimize == 0) - w = gamma_16_from_1[(w+add) >> shift]; - - *(sp + 2) = png_check_byte(png_ptr, w >> 8); - *(sp + 3) = PNG_BYTE(w); - - v = gamma_16_to_1[((sp[4]<<8)+sp[5]+add) >> shift]; - png_composite_16(w, v, a, png_ptr->background_1.blue); - - if (optimize == 0) - w = gamma_16_from_1[(w+add) >> shift]; - - *(sp + 4) = png_check_byte(png_ptr, w >> 8); - *(sp + 5) = PNG_BYTE(w); - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 8) - { - png_uint_16 a = png_check_u16(png_ptr, - ((*(sp + 6)) << 8) + *(sp + 7)); - - if (a == 0) - { - *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); - *(sp + 3) = PNG_BYTE(png_ptr->background.green); - *(sp + 4) = png_check_byte(png_ptr, - png_ptr->background.blue >> 8); - *(sp + 5) = PNG_BYTE(png_ptr->background.blue); - } - - else if (a < 0xffff) - { - png_uint_16 v; - - 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)); - - png_composite_16(v, r, a, png_ptr->background.red); - *sp = png_check_byte(png_ptr, v >> 8); - *(sp + 1) = PNG_BYTE(v); - - png_composite_16(v, g, a, png_ptr->background.green); - *(sp + 2) = png_check_byte(png_ptr, v >> 8); - *(sp + 3) = PNG_BYTE(v); - - png_composite_16(v, b, a, png_ptr->background.blue); - *(sp + 4) = png_check_byte(png_ptr, v >> 8); - *(sp + 5) = PNG_BYTE(v); - } - } - } - } - break; - } - - default: - break; - } - } -} -#endif /* READ_BACKGROUND || READ_ALPHA_MODE */ - -/* Gamma correct the image, avoiding the alpha channel. Make sure - * you do this after you deal with the transparency issue on grayscale - * or RGB images. If your bit depth is 8, use gamma_table, if it - * is 16, use gamma_16_table and gamma_shift. Build these with - * build_gamma_table(). - */ -static void -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; - int add = (shift > 0 ? 1U << (shift-1) : 0); - - png_bytep sp; - png_uint_32 i; - png_uint_32 row_width=row_info->width; - - png_debug(1, "in png_do_gamma"); - - /* Prior to libpng 1.7.0 this code would attempt to gamma correct 2 and 4 bit - * gray level values, the results are ridiculously inaccurate. In 1.7.0 the - * code is removed and a warning is introduced to catch cases where an - * application might actually try it. - */ - if (((row_info->bit_depth == 8 && gamma_table != NULL) || - (row_info->bit_depth == 16 && gamma_16_table != NULL))) - { - if (!(row_info->flags & PNG_INDEXED)) switch (row_info->channels) - { - case 3 /*RGB*/: - { - if (row_info->bit_depth == 8) - { - sp = row; - for (i = 0; i < row_width; i++) - { - *sp = gamma_table[*sp]; - sp++; - *sp = gamma_table[*sp]; - sp++; - *sp = gamma_table[*sp]; - sp++; - } - } - - else /* if (row_info->bit_depth == 16) */ - { - sp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 v; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = png_check_byte(png_ptr, v >> 8); - *(sp + 1) = PNG_BYTE(v); - sp += 2; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = png_check_byte(png_ptr, v >> 8); - *(sp + 1) = PNG_BYTE(v); - sp += 2; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = png_check_byte(png_ptr, v >> 8); - *(sp + 1) = PNG_BYTE(v); - sp += 2; - } - } - break; - } - - case 4 /*RGB_ALPHA*/: - { - if (row_info->bit_depth == 8) - { - sp = row; - for (i = 0; i < row_width; i++) - { - *sp = gamma_table[*sp]; - sp++; - - *sp = gamma_table[*sp]; - sp++; - - *sp = gamma_table[*sp]; - sp++; - - sp++; - } - } - - else /* if (row_info->bit_depth == 16) */ - { - sp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 v; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = png_check_byte(png_ptr, v >> 8); - *(sp + 1) = PNG_BYTE(v); - sp += 2; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = png_check_byte(png_ptr, v >> 8); - *(sp + 1) = PNG_BYTE(v); - sp += 2; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = png_check_byte(png_ptr, v >> 8); - *(sp + 1) = PNG_BYTE(v); - sp += 4; - } - } - break; - } - - case 2 /*GRAY_ALPHA*/: - { - if (row_info->bit_depth == 8) - { - sp = row; - for (i = 0; i < row_width; i++) - { - *sp = gamma_table[*sp]; - sp += 2; - } - } - - else /* if (row_info->bit_depth == 16) */ - { - sp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 v; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = png_check_byte(png_ptr, v >> 8); - *(sp + 1) = PNG_BYTE(v); - sp += 4; - } - } - break; - } - - case 1 /*GRAY*/: - { - if (row_info->bit_depth == 8) - { - sp = row; - for (i = 0; i < row_width; i++) - { - *sp = gamma_table[*sp]; - sp++; - } - } - - else /*row_info->bit_depth == 16 */ - { - sp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 v; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = png_check_byte(png_ptr, v >> 8); - *(sp + 1) = PNG_BYTE(v); - sp += 2; - } - } - break; - } - - default: - break; - } - } -} - -#ifdef PNG_READ_ALPHA_MODE_SUPPORTED -void PNGFAPI -png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, - png_fixed_point output_gamma) -{ - int compose = 0; - png_fixed_point file_gamma; - - png_debug(1, "in png_set_alpha_mode"); - - if (png_rtran_ok(png_ptr, 0) == 0) - return; - - output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/); - - /* Validate the value to ensure it is in a reasonable range. The value - * is expected to be 1 or greater, but this range test allows for some - * viewing correction values. The intent is to weed out users of this API - * who use the inverse of the gamma value accidentally! Since some of these - * values are reasonable this may have to be changed. - */ - if (output_gamma < 70000 || output_gamma > 300000) - png_error(png_ptr, "output gamma out of expected range"); - - /* The default file gamma is the inverse of the output gamma; the output - * gamma may be changed below so get the file value first: - */ - file_gamma = png_reciprocal(output_gamma); - - /* There are really 8 possibilities here, composed of any combination - * of: - * - * premultiply the color channels - * do not encode non-opaque pixels - * encode the alpha as well as the color channels - * - * The differences disappear if the input/output ('screen') gamma is 1.0, - * because then the encoding is a no-op and there is only the choice of - * premultiplying the color channels or not. - * - * png_set_alpha_mode and png_set_background interact because both use - * png_compose to do the work. Calling both is only useful when - * png_set_alpha_mode is used to set the default mode - PNG_ALPHA_PNG - along - * with a default gamma value. Otherwise PNG_COMPOSE must not be set. - */ - switch (mode) - { - case PNG_ALPHA_PNG: /* default: png standard */ - /* No compose, but it may be set by png_set_background! */ - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - break; - - case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */ - compose = 1; - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - /* The output is linear: */ - output_gamma = PNG_FP_1; - break; - - case PNG_ALPHA_OPTIMIZED: /* associated, non-opaque pixels linear */ - compose = 1; - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags |= PNG_FLAG_OPTIMIZE_ALPHA; - /* output_gamma records the encoding of opaque pixels! */ - break; - - case PNG_ALPHA_BROKEN: /* associated, non-linear, alpha encoded */ - compose = 1; - png_ptr->transformations |= PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - break; - - default: - png_error(png_ptr, "invalid alpha mode"); - } - - /* Only set the default gamma if the file gamma has not been set (this has - * the side effect that the gamma in a second call to png_set_alpha_mode will - * be ignored.) - */ - if (png_ptr->colorspace.gamma == 0) - { - png_ptr->colorspace.gamma = file_gamma; - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; - } - - /* But always set the output gamma: */ - png_ptr->screen_gamma = output_gamma; - - /* Finally, if pre-multiplying, set the background fields to achieve the - * desired result. - */ - if (compose != 0) - { - /* And obtain alpha pre-multiplication by composing on black: */ - memset(&png_ptr->background, 0, (sizeof png_ptr->background)); - png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */ - png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE; - png_ptr->flags &= ~PNG_FLAG_BACKGROUND_EXPAND; - - if ((png_ptr->transformations & PNG_COMPOSE) != 0) - png_error(png_ptr, - "conflicting calls to set alpha mode and background"); - - png_ptr->transformations |= PNG_COMPOSE; - } -} - -# ifdef PNG_FLOATING_POINT_SUPPORTED -void PNGAPI -png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma) -{ - png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr, - output_gamma)); -} -# endif - -/* Encode the alpha channel to the output gamma (the input channel is always - * linear.) Called only with color types that have an alpha channel. Needs the - * from_1 tables. - */ -static void -png_do_encode_alpha(png_transform_controlp row_info, png_bytep row) -{ - int step = row_info->channels; - - png_debug(1, "in png_do_encode_alpha"); - - 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; - - 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]; - } - } - - else if (row_info->bit_depth == 16) - { - PNG_CONST png_uint_16p gamma_16_from_1 = png_ptr->gamma_16_from_1; - - affirm(gamma_16_from_1 != NULL); - - { - step *= 2; - - /* The alpha channel is the last component: */ - row += step - 2; - - for (; row_width > 0; --row_width, row += step) - { - png_uint_16 v; - - v = gamma_16_from_1[((row[0]<<8)+row[1]+add) >> shift]; - *row = png_check_byte(png_ptr, v >> 8); - *(row + 1) = PNG_BYTE(v); - } - } - } - } -} -#endif /* READ_ALPHA_MODE */ - -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -/* Reduce RGB files to grayscale, with or without alpha - * using the equation given in Poynton's ColorFAQ of 1998-01-04 at - * (THIS LINK IS DEAD June 2008 but - * versions dated 1998 through November 2002 have been archived at - * http://web.archive.org/web/20000816232553/http://www.inforamp.net/ - * ~poynton/notes/colour_and_gamma/ColorFAQ.txt ) - * Charles Poynton poynton at poynton.com - * - * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B - * - * which can be expressed with integers as - * - * Y = (6969 * R + 23434 * G + 2365 * B)/32768 - * - * Poynton's current link (as of January 2003 through July 2011): - * - * has changed the numbers slightly: - * - * Y = 0.2126*R + 0.7152*G + 0.0722*B - * - * which can be expressed with integers as - * - * Y = (6966 * R + 23436 * G + 2366 * B)/32768 - * - * Historically, however, libpng uses numbers derived from the ITU-R Rec 709 - * end point chromaticities and the D65 white point. Depending on the - * precision used for the D65 white point this produces a variety of different - * numbers, however if the four decimal place value used in ITU-R Rec 709 is - * used (0.3127,0.3290) the Y calculation would be: - * - * Y = (6968 * R + 23435 * G + 2366 * B)/32768 - * - * While this is correct the rounding results in an overflow for white, because - * the sum of the rounded coefficients is 32769, not 32768. Consequently - * libpng uses, instead, the closest non-overflowing approximation: - * - * Y = (6968 * R + 23434 * G + 2366 * B)/32768 - * - * Starting with libpng-1.5.5, if the image being converted has a cHRM chunk - * (including an sRGB chunk) then the chromaticities are used to calculate the - * coefficients. See the chunk handling in pngrutil.c for more information. - * - * In all cases the calculation is to be done in a linear colorspace. If no - * gamma information is available to correct the encoding of the original RGB - * values this results in an implicit assumption that the original PNG RGB - * values were linear. - * - * Other integer coefficents can be used via png_set_rgb_to_gray(). Because - * the API takes just red and green coefficients the blue coefficient is - * calculated to make the sum 32768. This will result in different rounding - * to that used above. - */ -static int -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->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->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 - * there is an overall gamma correction). Prior to 1.5.5 this code - * checked the linearized values for equality; this doesn't match - * the documentation, the original values must be checked. - */ - if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) - { - PNG_CONST unsigned int shift = 15 + png_ptr->gamma_shift; - PNG_CONST png_uint_32 add = 1U << (shift-1); - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - png_byte red = *(sp++); - png_byte green = *(sp++); - png_byte blue = *(sp++); - - if (red != green || red != 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[ - /*SAFE*/(rc*red_1 + gc*green_1 + bc*blue_1 + add)>>shift]; - } - - else - { - /* If there is no overall correction the table will not be - * set. - */ - if (png_ptr->gamma_table != NULL) - red = png_ptr->gamma_table[red]; - - *(dp++) = red; - } - - if (have_alpha != 0) - *(dp++) = *(sp++); - } - } - - else - { - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - png_byte red = *(sp++); - png_byte green = *(sp++); - png_byte blue = *(sp++); - - if (red != green || red != blue) - { - rgb_error |= 1; - *(dp++) = (png_byte)/*SAFE*/ - ((rc*red+gc*green+bc*blue+16384) >> 15); - } - - else - *(dp++) = red; - - if (have_alpha != 0) - *(dp++) = *(sp++); - } - } - } - - else /* RGB bit_depth == 16 */ - { - if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL) - { - unsigned int shift = png_ptr->gamma_shift; - unsigned int add = (shift > 0 ? (1U << (shift-1)) : 0); - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - unsigned int red, green, blue; - png_uint_16 w; - - 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[/*SAFE*/(red+add) >> shift]; - - else - w = (png_uint_16)/*SAFE*/red; - } - - else - { - 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_byte)/*SAFE*/(w>>8); - *(dp++) = PNG_BYTE(w); - - if (have_alpha != 0) - { - *(dp++) = *(sp++); - *(dp++) = *(sp++); - } - } - } - - else - { - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - unsigned int red, green, blue, w; - - red = *sp++ << 8, red |= *sp++; - green = *sp++ << 8, green |= *sp++; - blue = *sp++ << 8, blue |= *sp++; - - if (red != green || red != blue) - rgb_error |= 1; - - /* From 1.5.5 in the 16 bit case do the accurate conversion even - * in the 'fast' case - this is because this is where the code - * ends up when handling linear 16 bit data. - */ - w = (rc*red+gc*green+bc*blue+16384) >> 15; - *(dp++) = (png_byte)/*SAFE*/(w>>8); - *(dp++) = PNG_BYTE(w); - - if (have_alpha != 0) - { - *(dp++) = *(sp++); - *(dp++) = *(sp++); - } - } - } - } - - row_info->channels -= 2; - } - - return rgb_error; -} -#endif /* RGB_TO_GRAY */ #endif /* READ_GAMMA */ -/* Scale 16-bit depth files to 8-bit depth. If both of these are set then the - * one that pngrtran does first (scale) happens. This is necessary to allow the - * TRANSFORM and API behavior to be somewhat consistent, and it's simpler. - */ -#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED -void PNGAPI -png_set_scale_16(png_structrp png_ptr) -{ - png_debug(1, "in png_set_scale_16"); - - if (png_rtran_ok(png_ptr, 0) == 0) - return; - - png_ptr->transformations |= PNG_SCALE_16_TO_8; -} -#endif - -#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED -/* Chop 16-bit depth files to 8-bit depth */ -void PNGAPI -png_set_strip_16(png_structrp png_ptr) -{ - png_debug(1, "in png_set_strip_16"); - - if (png_rtran_ok(png_ptr, 0) == 0) - return; - - png_ptr->transformations |= PNG_16_TO_8; -} -#endif - -#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED -void PNGAPI -png_set_strip_alpha(png_structrp png_ptr) -{ - png_debug(1, "in png_set_strip_alpha"); - - if (png_rtran_ok(png_ptr, 0) == 0) - return; - - png_ptr->transformations |= PNG_STRIP_ALPHA; -} -#endif - -#ifdef PNG_READ_QUANTIZE_SUPPORTED -/* Dither file to 8-bit. Supply a palette, the current number - * of elements in the palette, the maximum number of elements - * allowed, and a histogram if possible. If the current number - * of colors is greater then the maximum number, the palette will be - * modified to fit in the maximum number. "full_quantize" indicates - * whether we need a quantizing cube set up for RGB images, or if we - * simply are reducing the number of colors in a paletted image. - */ - -typedef struct png_dsort_struct -{ - struct png_dsort_struct * next; - png_byte left; - png_byte right; -} png_dsort; -typedef png_dsort * png_dsortp; -typedef png_dsort * * png_dsortpp; - -void PNGAPI -png_set_quantize(png_structrp png_ptr, png_colorp palette, - int num_palette, int maximum_colors, png_const_uint_16p histogram, - int full_quantize) -{ - png_debug(1, "in png_set_quantize"); - - if (png_rtran_ok(png_ptr, 0) == 0) - return; - - png_ptr->transformations |= PNG_QUANTIZE; - - if (full_quantize == 0) - { - int i; - - png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, - (png_uint_32)(num_palette * (sizeof (png_byte)))); - for (i = 0; i < num_palette; i++) - png_ptr->quantize_index[i] = png_check_byte(png_ptr, i); - } - - if (num_palette > maximum_colors) - { - if (histogram != NULL) - { - /* This is easy enough, just throw out the least used colors. - * Perhaps not the best solution, but good enough. - */ - - int i; - - /* Initialize an array to sort colors */ - png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr, - (png_uint_32)(num_palette * (sizeof (png_byte)))); - - /* Initialize the quantize_sort array */ - for (i = 0; i < num_palette; i++) - png_ptr->quantize_sort[i] = png_check_byte(png_ptr, i); - - /* Find the least used palette entries by starting a - * bubble sort, and running it until we have sorted - * out enough colors. Note that we don't care about - * sorting all the colors, just finding which are - * least used. - */ - - for (i = num_palette - 1; i >= maximum_colors; i--) - { - int done; /* To stop early if the list is pre-sorted */ - int j; - - done = 1; - for (j = 0; j < i; j++) - { - if (histogram[png_ptr->quantize_sort[j]] - < histogram[png_ptr->quantize_sort[j + 1]]) - { - png_byte t; - - t = png_ptr->quantize_sort[j]; - png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1]; - png_ptr->quantize_sort[j + 1] = t; - done = 0; - } - } - - if (done != 0) - break; - } - - /* Swap the palette around, and set up a table, if necessary */ - if (full_quantize != 0) - { - int j = num_palette; - - /* Put all the useful colors within the max, but don't - * move the others. - */ - for (i = 0; i < maximum_colors; i++) - { - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) - { - do - j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); - - palette[i] = palette[j]; - } - } - } - else - { - int j = num_palette; - - /* Move all the used colors inside the max limit, and - * develop a translation table. - */ - for (i = 0; i < maximum_colors; i++) - { - /* Only move the colors we need to */ - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) - { - png_color tmp_color; - - do - j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); - - tmp_color = palette[j]; - palette[j] = palette[i]; - palette[i] = tmp_color; - /* Indicate where the color went */ - png_ptr->quantize_index[j] = png_check_byte(png_ptr, i); - png_ptr->quantize_index[i] = png_check_byte(png_ptr, j); - } - } - - /* Find closest color for those colors we are not using */ - for (i = 0; i < num_palette; i++) - { - if ((int)png_ptr->quantize_index[i] >= maximum_colors) - { - int min_d, k, min_k, d_index; - - /* Find the closest color to one we threw out */ - d_index = png_ptr->quantize_index[i]; - min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); - for (k = 1, min_k = 0; k < maximum_colors; k++) - { - int d; - - d = PNG_COLOR_DIST(palette[d_index], palette[k]); - - if (d < min_d) - { - min_d = d; - min_k = k; - } - } - /* Point to closest color */ - png_ptr->quantize_index[i] = png_check_byte(png_ptr, min_k); - } - } - } - png_free(png_ptr, png_ptr->quantize_sort); - png_ptr->quantize_sort = NULL; - } - else - { - /* This is much harder to do simply (and quickly). Perhaps - * we need to go through a median cut routine, but those - * don't always behave themselves with only a few colors - * as input. So we will just find the closest two colors, - * and throw out one of them (chosen somewhat randomly). - * [We don't understand this at all, so if someone wants to - * work on improving it, be our guest - AED, GRP] - */ - int i; - int max_d; - int num_new_palette; - png_dsortp t; - png_dsortpp hash; - - t = NULL; - - /* Initialize palette index arrays */ - png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr, - (png_uint_32)(num_palette * (sizeof (png_byte)))); - png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr, - (png_uint_32)(num_palette * (sizeof (png_byte)))); - - /* Initialize the sort array */ - for (i = 0; i < num_palette; i++) - { - png_ptr->index_to_palette[i] = png_check_byte(png_ptr, i); - png_ptr->palette_to_index[i] = png_check_byte(png_ptr, i); - } - - hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 * - (sizeof (png_dsortp)))); - - num_new_palette = num_palette; - - /* Initial wild guess at how far apart the farthest pixel - * pair we will be eliminating will be. Larger - * numbers mean more areas will be allocated, Smaller - * numbers run the risk of not saving enough data, and - * having to do this all over again. - * - * I have not done extensive checking on this number. - */ - max_d = 96; - - while (num_new_palette > maximum_colors) - { - for (i = 0; i < num_new_palette - 1; i++) - { - int j; - - for (j = i + 1; j < num_new_palette; j++) - { - int d; - - d = PNG_COLOR_DIST(palette[i], palette[j]); - - if (d <= max_d) - { - - t = (png_dsortp)png_malloc_warn(png_ptr, - (png_uint_32)(sizeof (png_dsort))); - - if (t == NULL) - break; - - t->next = hash[d]; - t->left = png_check_byte(png_ptr, i); - t->right = png_check_byte(png_ptr, j); - hash[d] = t; - } - } - if (t == NULL) - break; - } - - if (t != NULL) - for (i = 0; i <= max_d; i++) - { - if (hash[i] != NULL) - { - png_dsortp p; - - for (p = hash[i]; p; p = p->next) - { - if ((int)png_ptr->index_to_palette[p->left] - < num_new_palette && - (int)png_ptr->index_to_palette[p->right] - < num_new_palette) - { - int j, next_j; - - if (num_new_palette & 0x01) - { - j = p->left; - next_j = p->right; - } - else - { - j = p->right; - next_j = p->left; - } - - num_new_palette--; - palette[png_ptr->index_to_palette[j]] - = palette[num_new_palette]; - if (full_quantize == 0) - { - int k; - - for (k = 0; k < num_palette; k++) - { - if (png_ptr->quantize_index[k] == - png_ptr->index_to_palette[j]) - png_ptr->quantize_index[k] = - png_ptr->index_to_palette[next_j]; - - if ((int)png_ptr->quantize_index[k] == - num_new_palette) - png_ptr->quantize_index[k] = - png_ptr->index_to_palette[j]; - } - } - - png_ptr->index_to_palette[png_ptr->palette_to_index - [num_new_palette]] = png_ptr->index_to_palette[j]; - - png_ptr->palette_to_index[png_ptr->index_to_palette[j]] - = png_ptr->palette_to_index[num_new_palette]; - - png_ptr->index_to_palette[j] = - png_check_byte(png_ptr, num_new_palette); - - png_ptr->palette_to_index[num_new_palette] = - png_check_byte(png_ptr, j); - } - if (num_new_palette <= maximum_colors) - break; - } - if (num_new_palette <= maximum_colors) - break; - } - } - - for (i = 0; i < 769; i++) - { - if (hash[i] != NULL) - { - png_dsortp p = hash[i]; - while (p) - { - t = p->next; - png_free(png_ptr, p); - p = t; - } - } - hash[i] = 0; - } - max_d += 96; - } - png_free(png_ptr, hash); - png_free(png_ptr, png_ptr->palette_to_index); - png_free(png_ptr, png_ptr->index_to_palette); - png_ptr->palette_to_index = NULL; - png_ptr->index_to_palette = NULL; - } - num_palette = maximum_colors; - } - if (png_ptr->palette == NULL) - { - png_ptr->palette = palette; - } - png_ptr->num_palette = png_check_u16(png_ptr, num_palette); - - if (full_quantize != 0) - { - int i; - png_bytep distance; - int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS + - PNG_QUANTIZE_BLUE_BITS; - int num_red = (1 << PNG_QUANTIZE_RED_BITS); - int num_green = (1 << PNG_QUANTIZE_GREEN_BITS); - int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS); - png_size_t num_entries = ((png_size_t)1 << total_bits); - - png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr, - (png_uint_32)(num_entries * (sizeof (png_byte)))); - - distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries * - (sizeof (png_byte)))); - - memset(distance, 0xff, num_entries * (sizeof (png_byte))); - - for (i = 0; i < num_palette; i++) - { - int ir, ig, ib; - int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS)); - int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS)); - int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS)); - - for (ir = 0; ir < num_red; ir++) - { - /* int dr = abs(ir - r); */ - int dr = ((ir > r) ? ir - r : r - ir); - int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS + - PNG_QUANTIZE_GREEN_BITS)); - - for (ig = 0; ig < num_green; ig++) - { - /* int dg = abs(ig - g); */ - int dg = ((ig > g) ? ig - g : g - ig); - int dt = dr + dg; - int dm = ((dr > dg) ? dr : dg); - int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS); - - for (ib = 0; ib < num_blue; ib++) - { - int d_index = index_g | ib; - /* int db = abs(ib - b); */ - int db = ((ib > b) ? ib - b : b - ib); - int dmax = ((dm > db) ? dm : db); - int d = dmax + dt + db; - - if (d < (int)distance[d_index]) - { - distance[d_index] = png_check_byte(png_ptr, d); - png_ptr->palette_lookup[d_index] = png_check_byte(png_ptr, - i); - } - } - } - } - } - - png_free(png_ptr, distance); - } -} -#endif /* READ_QUANTIZE */ - -#ifdef PNG_READ_EXPAND_SUPPORTED -/* Expand paletted images to RGB, expand grayscale images of - * less than 8-bit depth to 8-bit depth, and expand tRNS chunks - * to alpha channels. - */ -void PNGAPI -png_set_expand(png_structrp png_ptr) -{ - png_debug(1, "in png_set_expand"); - - if (png_rtran_ok(png_ptr, 0) == 0) - return; - - png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); -} - -/* GRR 19990627: the following three functions currently are identical - * to png_set_expand(). However, it is entirely reasonable that someone - * might wish to expand an indexed image to RGB but *not* expand a single, - * fully transparent palette entry to a full alpha channel--perhaps instead - * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace - * the transparent color with a particular RGB value, or drop tRNS entirely. - * IOW, a future version of the library may make the transformations flag - * a bit more fine-grained, with separate bits for each of these three - * functions. - * - * More to the point, these functions make it obvious what libpng will be - * doing, whereas "expand" can (and does) mean any number of things. - * - * GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified - * to expand only the sample depth but not to expand the tRNS to alpha - * and its name was changed to png_set_expand_gray_1_2_4_to_8(). - */ - -/* Expand paletted images to RGB. */ -void PNGAPI -png_set_palette_to_rgb(png_structrp png_ptr) -{ - png_debug(1, "in png_set_palette_to_rgb"); - - if (png_rtran_ok(png_ptr, 0) == 0) - return; - - png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); -} - -/* Expand grayscale images of less than 8-bit depth to 8 bits. */ -void PNGAPI -png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr) -{ - png_debug(1, "in png_set_expand_gray_1_2_4_to_8"); - - if (png_rtran_ok(png_ptr, 0) == 0) - return; - - png_ptr->transformations |= PNG_EXPAND; -} - -/* Expand tRNS chunks to alpha channels. */ -void PNGAPI -png_set_tRNS_to_alpha(png_structrp png_ptr) -{ - png_debug(1, "in png_set_tRNS_to_alpha"); - - if (png_rtran_ok(png_ptr, 0) == 0) - return; - - png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); -} -#endif /* READ_EXPAND */ - -#ifdef PNG_READ_EXPAND_16_SUPPORTED -/* Expand to 16-bit channels, expand the tRNS chunk too (because otherwise - * it may not work correctly.) - */ -void PNGAPI -png_set_expand_16(png_structrp png_ptr) -{ - png_debug(1, "in png_set_expand_16"); - - if (png_rtran_ok(png_ptr, 0) == 0) - return; - - png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS); -} -#endif - -#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED -void PNGAPI -png_set_gray_to_rgb(png_structrp png_ptr) -{ - png_debug(1, "in png_set_gray_to_rgb"); - - if (png_rtran_ok(png_ptr, 0) == 0) - return; - - /* Because rgb must be 8 bits or more: */ - png_set_expand_gray_1_2_4_to_8(png_ptr); - png_ptr->transformations |= PNG_GRAY_TO_RGB; -} -#endif - #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -void PNGFAPI -png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action, - png_fixed_point red, png_fixed_point green) +static void +png_do_rtog_48(png_transformp *transform, png_transform_controlp tc) { - png_debug(1, "in png_set_rgb_to_gray"); +# define png_ptr (tc->png_ptr) + const png_uint_32 r = (*transform)->args >> 16; + const png_uint_32 g = (*transform)->args & 0xFFFFU; + const png_uint_32 b = 65536U - r - g; - /* Need the IHDR here because of the check on color_type below. */ - /* TODO: fix this */ - if (png_rtran_ok(png_ptr, 1) == 0) - return; + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 6U; + png_bytep dp = png_voidcast(png_bytep, tc->dp); - switch (error_action) + debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_FLAG_COLOR && + (tc->gamma == 0U || !png_gamma_significant(png_ptr, tc->gamma, 16U))); + + tc->sp = dp; + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR); + + while (sp <= ep) { - case PNG_ERROR_ACTION_NONE: - png_ptr->transformations |= PNG_RGB_TO_GRAY; - break; + png_uint_32 gray = (((sp[0] << 8) + sp[1]) * r + + ((sp[2] << 8) + sp[3]) * g + + ((sp[4] << 8) + sp[5]) * b + 32767U) >> 16; + debug(gray < 65536U); + *dp++ = PNG_BYTE(gray >> 8); + *dp++ = PNG_BYTE(gray); + sp += 6U; + } +# undef png_ptr +} + +static void +png_do_rtog_64(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + const png_uint_32 r = (*transform)->args >> 16; + const png_uint_32 g = (*transform)->args & 0xFFFFU; + const png_uint_32 b = 65536U - r - g; + + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 8U; + png_bytep dp = png_voidcast(png_bytep, tc->dp); + + debug(tc->bit_depth == 16U && + tc->format == PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA && + (tc->gamma == 0U || !png_gamma_significant(png_ptr, tc->gamma, 16U))); + + tc->sp = dp; + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR); + + while (sp <= ep) + { + png_uint_32 gray = (((sp[0] << 8) + sp[1]) * r + + ((sp[2] << 8) + sp[3]) * g + + ((sp[4] << 8) + sp[5]) * b + 32767U) >> 16; + + debug(gray < 65536U); + *dp++ = PNG_BYTE(gray >> 8); + *dp++ = PNG_BYTE(gray); + sp += 6U; + *dp++ = *sp++; /* alpha */ + *dp++ = *sp++; + } +# undef png_ptr +} + +static void +png_init_rgb_to_gray_arithmetic(png_transformp *transform, + png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* This only gets used in the final init stage: */ + debug(tc->init == PNG_TC_INIT_FINAL && tc->bit_depth == 16U && + (tc->format & PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA)) == + PNG_FORMAT_FLAG_COLOR); + + (*transform)->fn = (tc->format & PNG_FORMAT_FLAG_ALPHA) ? png_do_rtog_64 : + png_do_rtog_48; + + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR); + tc->invalid_info |= PNG_INFO_sBIT; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = + png_check_byte(png_ptr, tc->bit_depth); +# undef png_ptr +} + +typedef struct +{ + png_transform tr; + png_fixed_point red_coefficient; + png_fixed_point green_coefficient; + unsigned int coefficients_set :1; + unsigned int error_action :2; +} png_transform_rgb_to_gray; + +static void +png_update_rgb_status(png_structrp png_ptr, png_transformp *transform) +{ + png_transform_rgb_to_gray *tr = png_transform_cast(png_transform_rgb_to_gray, + *transform); + + png_ptr->rgb_to_gray_status = 1U; + tr->tr.fn = NULL; /* one warning/error only */ + + switch (tr->error_action) + { case PNG_ERROR_ACTION_WARN: - png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; + png_warning(png_ptr, "RGB to gray found nongray pixel"); break; case PNG_ERROR_ACTION_ERROR: - png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; + png_error(png_ptr, "RGB to gray found nongray pixel"); break; default: - png_error(png_ptr, "invalid error action to rgb_to_gray"); break; } +} - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) -#ifdef PNG_READ_EXPAND_SUPPORTED - png_ptr->transformations |= PNG_EXPAND; -#else - { - /* Make this an error in 1.6 because otherwise the application may assume - * that it just worked and get a memory overwrite. - */ - png_error(png_ptr, - "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED"); +static void +png_do_rgb_check24(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue + * channels are not equal. + */ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U; - /* png_ptr->transformations &= ~PNG_RGB_TO_GRAY; */ - } -#endif + debug(tc->bit_depth == 8U && tc->format == PNG_FORMAT_FLAG_COLOR); + + while (sp <= ep) { - if (red >= 0 && green >= 0 && red + green <= PNG_FP_1) + if ((sp[0] ^ sp[1]) | (sp[2] ^ sp[1])) { - png_uint_16 red_int, green_int; - - /* NOTE: this calculation does not round, but this behavior is retained - * for consistency; the inaccuracy is very small. The code here always - * overwrites the coefficients, regardless of whether they have been - * defaulted or set already. - */ - red_int = png_check_u16(png_ptr, - ((png_uint_32)/*SAFE*/red*32768)/100000); - green_int = png_check_u16(png_ptr, - ((png_uint_32)/*SAFE*/green*32768)/100000); - - png_ptr->rgb_to_gray_red_coeff = red_int; - png_ptr->rgb_to_gray_green_coeff = green_int; -# if defined(PNG_COLORS_SPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) - png_ptr->colorspace.flags |= PNG_COLORSPACE_RGB_TO_GRAY_SET; -# endif + png_update_rgb_status(png_ptr, transform); + break; } - else - { - if (red >= 0 && green >= 0) - png_app_warning(png_ptr, - "ignoring out of range rgb_to_gray coefficients"); + sp += 3U; + } +# undef png_ptr +} - /* Use the defaults, from the cHRM chunk if set, else the historical - * values which are close to the sRGB/HDTV/ITU-Rec 709 values. See - * png_do_rgb_to_gray for more discussion of the values. In this case - * the coefficients are not marked as 'set' and are not overwritten if - * something has already provided a default. +static void +png_do_rgb_check32(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue + * channels are not equal and alpha is not zero. + */ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 4U; + + debug(tc->bit_depth == 8U && + tc->format == PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA); + + while (sp <= ep) + { + if (((sp[0] ^ sp[1]) | (sp[2] ^ sp[1])) && sp[3] != 0) + { + png_update_rgb_status(png_ptr, transform); + break; + } + + sp += 4U; + } +# undef png_ptr +} + +static void +png_do_rgb_check48(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue + * channels are not equal. + */ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 6U; + + debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_FLAG_COLOR); + + while (sp <= ep) + { + if ((sp[0] ^ sp[2]) | (sp[4] ^ sp[2]) | + (sp[1] ^ sp[3]) | (sp[5] ^ sp[3])) + { + png_update_rgb_status(png_ptr, transform); + break; + } + + sp += 6U; + } +# undef png_ptr +} + +static void +png_do_rgb_check64(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue + * channels are not equal and alpha is not zero. + */ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 8U; + + debug(tc->bit_depth == 16U && + tc->format == PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA); + + while (sp <= ep) + { + if (((sp[0] ^ sp[2]) | (sp[4] ^ sp[2]) | + (sp[1] ^ sp[3]) | (sp[5] ^ sp[3])) && + (sp[6] | sp[7]) != 0) + { + png_update_rgb_status(png_ptr, transform); + break; + } + + sp += 8U; + } +# undef png_ptr +} + +static void +png_init_rgb_to_gray(png_transformp *transform, png_transform_controlp tc) +{ + png_structrp png_ptr = tc->png_ptr; + + /* Basic checks: if there is no color in the format this transform is not + * applicable. + */ + if ((tc->format & PNG_FORMAT_FLAG_COLOR) != 0) + { + png_transform_rgb_to_gray *tr = png_transform_cast( + png_transform_rgb_to_gray, *transform); + + /* no colormap allowed: */ + affirm(tc->init && !(tc->format & PNG_FORMAT_FLAG_COLORMAP)); + /* no extra flags yet: */ + debug(!(tc->format & + PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA))); + /* at present no non-palette caching: */ + implies(tc->caching, tc->palette); + + if (tc->init == PNG_TC_INIT_FORMAT) + { + /* The convertion should just remove the 'COLOR' flag and do nothing + * else, but if a tRNS chunk is present this would invalidate it. + * Handle this by expanding it now. */ - if (png_ptr->rgb_to_gray_red_coeff == 0 && - png_ptr->rgb_to_gray_green_coeff == 0) + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 && !tc->palette && + png_ptr->num_trans == 1 && !(tc->invalid_info & PNG_INFO_tRNS)) { - png_ptr->rgb_to_gray_red_coeff = 6968; - png_ptr->rgb_to_gray_green_coeff = 23434; - /* png_ptr->rgb_to_gray_blue_coeff = 2366; */ + /* Only if expand was requested and not cancelled: */ + if (tc->expand_tRNS && !tc->strip_alpha) + tc->format |= PNG_FORMAT_FLAG_ALPHA; + + tc->invalid_info |= PNG_INFO_tRNS; /* prevent expansion later */ } + + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR); + } + + else /* PNG_TC_INIT_FINAL */ + { + unsigned int index; /* channel to select (invalid) */ + png_byte sBIT_color; /* sBIT of that channel if valid */ + png_fixed_point r, g; /* Coefficients in range 0..65536 */ + + /* Push a tRNS transform if required. Because this is a push the + * transform the init needs to be run now. This needs to go in + * before the check on r==g==b because a color key might be used. + */ + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 && !tc->palette && + png_ptr->num_trans == 1 && !(tc->invalid_info & PNG_INFO_tRNS)) + { + if (tc->expand_tRNS && !tc->strip_alpha) + { + png_transformp tr_expand = png_push_transform(png_ptr, + sizeof (png_expand), png_init_expand, transform, NULL); + + debug(*transform == tr_expand); + tr_expand->args |= PNG_EXPAND_tRNS; + png_init_expand(transform, tc); + /* Check for the infinite loop possibility: */ + affirm((tc->invalid_info & PNG_INFO_tRNS) != 0); + return; + } + + else + tc->invalid_info |= PNG_INFO_tRNS; + } + + { + png_fixed_point red, green; + + if (tr->coefficients_set) + { + red = tr->red_coefficient; + green = tr->green_coefficient; + } + +# ifdef PNG_COLORSPACE_SUPPORTED + else if ((png_ptr->colorspace.flags & + (PNG_COLORSPACE_HAVE_ENDPOINTS+PNG_COLORSPACE_INVALID)) + == PNG_COLORSPACE_HAVE_ENDPOINTS) + { + red = png_ptr->colorspace.end_points_XYZ.red_Y; + green = png_ptr->colorspace.end_points_XYZ.green_Y; + } +# endif + + else /* no colorspace support, assume sRGB */ + { + /* From IEC 61966-2-1:1999, the reverse transformation from sRGB + * RGB values to XYZ D65 values (not CIEXYZ!). These are not + * exact inverses of the forward transformation; they only have + * four (decimal) digits of precision. + * + * API CHANGE: in 1.7.0 the sRGB values from the official IEC + * specification are used, previously libpng used values from + * Charles Poynton's ColorFAQ of 1998-01-04. The original page + * is gone, however up to date information can be found below: + * + * http://www.poynton.com/ColorFAQ.html + * + * At the time of reading (20150628) this web site quotes the + * same values as below and cites ITU Rec 709 as the source. + */ + red = 21260; + green = 71520; + } + + /* Prior to 1.7 this calculation was done with 15-bit precision, + * this is because the code was written pre-muldiv and tried to + * work round the problems caused by the signs in integer + * calculations. + */ + (void)png_muldiv(&r, red, 65536, PNG_FP_1); + (void)png_muldiv(&g, green, 65536, PNG_FP_1); + } + + /* If the convertion can be deduced to select a single channel do so. + * If the error action is set to error just copy the red channel, if + * the coefficients select just one channel use that. + */ + if (tr->error_action == PNG_ERROR_ACTION_ERROR || r >= 65536) + index = 0U, sBIT_color = tc->sBIT_R; /* select red */ + + else if (g >= 65536) + index = 1U, sBIT_color = tc->sBIT_G; /* select green */ + + else if (r + g == 0) + index = 2U, sBIT_color = tc->sBIT_B; /* select blue */ + + else + index = 3U, sBIT_color = 0U/*UNUSED*/; + + if (index == 3U) + { + /* Arithmetic will have to be done. For this we need linear 16-bit + * data which must then be converted back to the required bit depth, + * png_init_gamma handles this. It may push other expand operations + * (it shouldn't but it can), so give it some space. + * + * The gamma must be restored to the original value, 0U for the bit + * depth means use the output bit depth. + */ + (void)add_gamma_transform(png_ptr, PNG_TR_GAMMA_ENCODE, tc->gamma, + 0U/*bit depth*/, 0/*default*/); + + /* If png_init_gamma is called with tc->gamma 0 it does the right + * thing in PNG_TC_INIT_FINAL; it just does any required bit depth + * adjustment. + */ + (void)add_gamma_transform(png_ptr, tr->tr.order + 0x10U, PNG_FP_1, + 16U, 1/*force: doesn't matter*/); + + { + /* This init routine will update the sBIT information + * appropriately. + */ + png_transformp tr_rtog = png_add_transform(png_ptr, 0/*size*/, + png_init_rgb_to_gray_arithmetic, tr->tr.order + 0x20U); + + /* r and g are known to be in the range 0..65535, so pack them + * into the 'args' argument of a new transform. + */ + tr_rtog->args = (((png_uint_32)r) << 16) + g; + } + } + + else /* index < 3 */ + { + /* TODO: does this need to select the correct sBIT value too? */ + png_add_rgb_to_gray_byte_ops(png_ptr, tc, index, + tr->tr.order + 0x10U); + tc->sBIT_G = sBIT_color; + } + + /* Prior to 1.7 libpng would always check for r!=g!=b. In 1.7 an extra + * error_action setting is added to prevent this overhead. + */ + if (tr->error_action) + tr->tr.fn = tc->bit_depth == 8 ? + ((tc->format & PNG_FORMAT_FLAG_ALPHA) ? + png_do_rgb_check32 : png_do_rgb_check24) : + ((tc->format & PNG_FORMAT_FLAG_ALPHA) ? + png_do_rgb_check64 : png_do_rgb_check48); + + else + tr->tr.fn = NULL; /* PNG_ERROR_ACTION_NO_CHECK */ + } + } + + else /* not color: transform not applicable */ + (*transform)->fn = NULL; +} + +void PNGFAPI +png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action, + png_fixed_point red, png_fixed_point green) + /* API CHANGE: in 1.7 calling this on a palette PNG no longer causes the + * palette to be expanded (unless explicitly requested), rather it converts + * the palette to grayscale. + */ +{ + /* The coefficients must be reasonable, the error handling is to warn (pre + * 1.7) or app error (1.7) and drop back to the cHRM definition of Y. The + * drop back is done in the init routine if relevant flag is unset. Passing + * negative values causes this default to be used without a warning. + */ + int pset = 0; + + if (red >= 0 && green >= 0) + { + if (red <= PNG_FP_1 && green <= PNG_FP_1 && red + green <= PNG_FP_1) + pset = 1; + + else /* overflow */ + png_app_error(png_ptr, "rgb_to_gray coefficients too large (ignored)"); + } + + { + png_transform_rgb_to_gray *tr = + png_transform_cast(png_transform_rgb_to_gray, + png_add_transform(png_ptr, sizeof (png_transform_rgb_to_gray), + png_init_rgb_to_gray, PNG_TR_RGB_TO_GRAY)); + + tr->error_action = 0x3U & error_action; + + if (red < 0 || green < 0) /* use cHRM default */ + tr->coefficients_set = 0U; + + else if (pset) /* else bad coefficients which get ignored */ + { + tr->coefficients_set = 1U; + tr->red_coefficient = red; + tr->green_coefficient = green; } } } @@ -3470,497 +3994,2535 @@ png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red, png_fixed(png_ptr, green, "rgb to gray green coefficient")); } #endif /* FLOATING POINT */ - #endif /* RGB_TO_GRAY */ -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) -void PNGAPI -png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr - read_user_transform_fn) +#ifdef PNG_READ_BACKGROUND_SUPPORTED +typedef struct { - png_debug(1, "in png_set_read_user_transform_fn"); + png_transform tr; + png_color_16 background; /* MUST COME FIRST */ + unsigned int need_expand :1; /* Background matches format of this PNG */ + unsigned int rgb_to_gray :1; /* RGB-to-gray transform found */ + unsigned int compose_background :1; /* png_set_background */ + unsigned int associate_alpha :1; + unsigned int encode_alpha :1; + unsigned int optimize_alpha :1; + unsigned int background_is_gray :1; /* Background color is gray */ + unsigned int background_bit_depth :5; /* bit depth, 1..16 */ + unsigned int ntrans :3; /* 1..6 bytes */ + png_byte transparent_pixel[6]; + png_byte background_pixel[6]; + png_fixed_point background_gamma; +} png_transform_background; -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - png_ptr->transformations |= PNG_USER_TRANSFORM; - png_ptr->read_user_transform_fn = read_user_transform_fn; -#endif -} -#endif - -static void /* PRIVATE */ -png_init_palette_transformations(png_structrp png_ptr) +static void +resolve_background_color(png_transform_background *tr, + png_transform_controlp tc) { - int input_has_alpha = 0; - int input_has_transparency = 0; + png_const_structp png_ptr = tc->png_ptr; - if (png_ptr->num_trans > 0) + /* Deduce the bit depth and color information for the background, the + * special case is when need_expand is set and the PNG has palette format, + * then (and only then) the background value is a palette index. + */ + if (tr->need_expand && tc->palette) { - int i; + unsigned int i = tr->background.index; + png_byte r, g, b; - /* Ignore if all the entries are opaque (unlikely!) */ - for (i=0; inum_trans; ++i) + if (i >= png_ptr->num_palette) { - if (png_ptr->trans_alpha[i] == 255) - continue; - else if (png_ptr->trans_alpha[i] == 0) - input_has_transparency = 1; - else - { - input_has_transparency = 1; - input_has_alpha = 1; - break; - } - } - } - - /* If no alpha we can optimize. */ - if (input_has_alpha == 0) - { - /* Any alpha means background and associative alpha processing is - * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA - * and ENCODE_ALPHA are irrelevant. - */ - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - - if (input_has_transparency == 0) - png_ptr->transformations &= ~PNG_COMPOSE; - } -} - -static void /* PRIVATE */ -png_init_rgb_transformations(png_structrp png_ptr) -{ - /* Added to libpng-1.5.4: check the color type to determine whether there - * is any alpha or transparency in the image and simply cancel the - * background and alpha mode stuff if there isn't. - */ - int input_has_alpha = (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0; - int input_has_transparency = png_ptr->num_trans > 0; - - /* If no alpha we can optimize. */ - if (input_has_alpha == 0) - { - /* Any alpha means background and associative alpha processing is - * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA - * and ENCODE_ALPHA are irrelevant. - */ -# ifdef PNG_READ_ALPHA_MODE_SUPPORTED - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; -# endif - - if (input_has_transparency == 0) - png_ptr->transformations &= ~PNG_COMPOSE; - } -} - -void /* PRIVATE */ -png_init_read_transformations(png_structrp png_ptr) -{ - png_debug(1, "in png_init_read_transformations"); - - /* This internal function is called from png_read_start_row in pngrutil.c - * and it is called before the 'rowbytes' calculation is done, so the code - * in here can change or update the transformations flags. - * - * First do updates that do not depend on the details of the PNG image data - * being processed. - */ - -#ifdef PNG_READ_GAMMA_SUPPORTED - /* Prior to 1.5.4 these tests were performed from png_set_gamma, 1.5.4 adds - * png_set_alpha_mode and this is another source for a default file gamma so - * the test needs to be performed later - here. In addition prior to 1.5.4 - * the tests were repeated for the PALETTE color type here - this is no - * longer necessary (and doesn't seem to have been necessary before.) - */ - { - /* The following temporary indicates if overall gamma correction is - * required. - */ - int gamma_correction = 0; - - if (png_ptr->colorspace.gamma != 0) /* has been set */ - { - if (png_ptr->screen_gamma != 0) /* screen set too */ - gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma, - png_ptr->screen_gamma); - - else - /* Assume the output matches the input; a long time default behavior - * of libpng, although the standard has nothing to say about this. - */ - png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma); + png_app_error(png_ptr, "background index out of range"); + tr->tr.fn = NULL; + return; } - else if (png_ptr->screen_gamma != 0) - /* The converse - assume the file matches the screen, note that this - * perhaps undesireable default can (from 1.5.4) be changed by calling - * png_set_alpha_mode (even if the alpha handling mode isn't required - * or isn't changed from the default.) - */ - png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma); + tr->background_bit_depth = 8U; + r = png_ptr->palette[i].red; + g = png_ptr->palette[i].green; + b = png_ptr->palette[i].blue; - else /* neither are set */ - /* Just in case the following prevents any processing - file and screen - * are both assumed to be linear and there is no way to introduce a - * third gamma value other than png_set_background with 'UNIQUE', and, - * prior to 1.5.4 - */ - png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1; - - /* We have a gamma value now. */ - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; - - /* Now turn the gamma transformation on or off as appropriate. Notice - * that PNG_GAMMA just refers to the file->screen correction. Alpha - * composition may independently cause gamma correction because it needs - * linear data (e.g. if the file has a gAMA chunk but the screen gamma - * hasn't been specified.) In any case this flag may get turned off in - * the code immediately below if the transform can be handled outside the - * row loop. - */ - if (gamma_correction != 0) - png_ptr->transformations |= PNG_GAMMA; + if (r == g && g == b) + { + tr->background_is_gray = 1U; + tr->background.gray = g; + UNTESTED + } else - png_ptr->transformations &= ~PNG_GAMMA; - } -#endif - - /* Certain transformations have the effect of preventing other - * transformations that happen afterward in png_do_read_transformations; - * resolve the interdependencies here. From the code of - * png_do_read_transformations the order is: - * - * 1) PNG_EXPAND (including PNG_EXPAND_tRNS) - * 2) PNG_STRIP_ALPHA (if no compose) - * 3) PNG_RGB_TO_GRAY - * 4) PNG_GRAY_TO_RGB iff !PNG_FLAG_BACKGROUND_IS_GRAY - * 5) PNG_COMPOSE - * 6) PNG_GAMMA - * 7) PNG_STRIP_ALPHA (if compose) - * 8) PNG_ENCODE_ALPHA - * 9) PNG_SCALE_16_TO_8 - * 10) PNG_16_TO_8 - * 11) PNG_QUANTIZE (converts to palette) - * 12) PNG_EXPAND_16 - * 13) PNG_GRAY_TO_RGB iff PNG_FLAG_BACKGROUND_IS_GRAY - * 14) PNG_INVERT_MONO - * 15) PNG_INVERT_ALPHA - * 16) PNG_SHIFT - * 17) PNG_PACK - * 18) PNG_BGR - * 19) PNG_PACKSWAP - * 20) PNG_FILLER (includes PNG_ADD_ALPHA) - * 21) PNG_SWAP_ALPHA - * 22) PNG_SWAP_BYTES - * 23) PNG_USER_TRANSFORM [must be last] - */ -#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED - if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0) - { - if ((png_ptr->transformations & PNG_FILLER) == 0) - png_ptr->transformations &= ~(PNG_INVERT_ALPHA|PNG_SWAP_ALPHA); - - if ((png_ptr->transformations & PNG_COMPOSE) == 0) { - /* Stripping the alpha channel happens immediately after the 'expand' - * transformations, before all other transformations, so it cancels out - * the alpha handling. It has the side effect negating the effect of - * PNG_EXPAND_tRNS too: - */ - png_ptr->transformations &= ~(PNG_ENCODE_ALPHA | PNG_EXPAND_tRNS); - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - - /* Kill the tRNS chunk itself too. Prior to 1.5.4 this did not happen - * so transparency information would remain just so long as it wasn't - * expanded. This produces unexpected API changes if the set of things - * that do PNG_EXPAND_tRNS changes (perfectly possible given the - * documentation - which says ask for what you want, accept what you - * get.) This makes the behavior consistent from 1.5.4: - */ - png_ptr->num_trans = 0; + tr->background_is_gray = 0U; + tr->background.red = r; + tr->background.green = g; + tr->background.blue = b; + UNTESTED } } -#endif /* STRIP_ALPHA supported, no COMPOSE */ + + else /* background is not a palette index */ + { + int use_rgb; + png_uint_16 mask; + + /* First work out the bit depth and whether or not to use the RGB + * fields of the background. + */ + if (tr->need_expand) + { + affirm(!(tc->format & PNG_FORMAT_FLAG_COLORMAP)); + tr->background_bit_depth = + png_check_bits(png_ptr, png_ptr->bit_depth, 5U); + use_rgb = (png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0; + } + + else /* screen format background */ + { + /* If the final output is in palette format assume the background + * is in a matching format. This covers two cases, an original + * COLORMAP PNG and png_set_quantize. + */ + if ((png_ptr->row_format & PNG_FORMAT_FLAG_COLORMAP) != 0) + tr->background_bit_depth = 8U; + + else + tr->background_bit_depth = + png_check_bits(png_ptr, png_ptr->row_bit_depth, 5U); + + use_rgb = (png_ptr->row_format & PNG_FORMAT_FLAG_COLOR) != 0; + } + + /* The PNG spec says to use the low bits of the values, so we mask out + * the high bits here (at present no warning is produced if they are + * set.) + */ + mask = png_check_u16(png_ptr, (1U << tr->background_bit_depth)-1U); + + if (use_rgb) + { + png_uint_16 r, g, b; + + r = tr->background.red & mask; + g = tr->background.green & mask; + b = tr->background.blue & mask; + + if (r == g && g == b) + { + tr->background_is_gray = 1U; + tr->background.gray = g; + } + + else + { + tr->background_is_gray = 0U; + tr->background.red = r; + tr->background.green = g; + tr->background.blue = b; + } + } + + else /* gray */ + { + tr->background_is_gray = 1U; + tr->background.gray = tr->background.gray & mask; + } + } +} + +static void +gamma_correct_background_component(png_const_structrp png_ptr, png_uint_16p cp, + unsigned int bdc, png_fixed_point correction, unsigned int bdout) + /* Utility function for gamma_correct_background. */ +{ + unsigned int c = *cp; + + /* 0.0 and 1.0 are unchanged (and common): */ + if (c > 0U && c < (1U<png_ptr) + png_fixed_point correction = tc->gamma; + const unsigned int bdback = tr->background_bit_depth; + const unsigned int bdrow = tc->bit_depth; + + /* This is harmless if it fails but it will damage the output pixels - they + * won't have the requested color depth accuracy where the background is + * used. + */ + debug(bdback <= bdrow); + debug(tr->background_is_gray || (bdrow >= 8U && bdback >= 8U)); + + /* The background is assumed to be full precision; there is no sBIT + * information for it. The convertion converts from the current depth and + * gamma of the background to that in the transform control. + */ + if (correction != 0 && (tr->background_gamma == 0 || + png_gamma_equal(png_ptr, tr->background_gamma, correction, &correction, + bdback))) + correction = 0; /* no correction! */ + + if (tr->background_is_gray) + gamma_correct_background_component(png_ptr, &tr->background.gray, bdback, + correction, bdrow); + + else + { + gamma_correct_background_component(png_ptr, &tr->background.red, bdback, + correction, bdrow); + gamma_correct_background_component(png_ptr, &tr->background.green, bdback, + correction, bdrow); + gamma_correct_background_component(png_ptr, &tr->background.blue, bdback, + correction, bdrow); + } + + if (correction != 0) + tr->background_gamma = tc->gamma; + + tr->background_bit_depth = png_check_bits(png_ptr, bdrow, 5U); +# undef png_ptr +} + +static void +fill_background_pixel(png_transform_background *tr, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* Fill in 'background_pixel' if the appropriate sequence of bytes for the + * format given in the transform control. + */ + const unsigned int bdtc = tc->bit_depth; + + /* If necessary adjust the background pixel to the current row format (it is + * important to do this as late as possible to avoid spurious + * interconvertions). + */ + gamma_correct_background(tr, tc); + + if (tr->background_is_gray) + { + unsigned int g = tr->background.gray; + + /* 'g' now has enough bits for the destination, note that in the case of + * low bit depth gray this causes the pixel to be replicated through the + * written byte. Fill all six bytes: + */ + memset(tr->background_pixel, PNG_BYTE(g), 6U); + if (bdtc == 16U) + tr->background_pixel[0] = tr->background_pixel[2] = + tr->background_pixel[4] = PNG_BYTE(g >> 8); + /* Must not include the alpha channel here: */ + tr->ntrans = png_check_bits(png_ptr, + ((tc->format & PNG_FORMAT_FLAG_COLOR)+1U) << (bdtc == 16U), 3U); + } + + else + { + unsigned int r = tr->background.red; + unsigned int g = tr->background.green; + unsigned int b = tr->background.blue; + + debug((tc->format & PNG_FORMAT_FLAG_COLOR) != 0); + + switch (bdtc) + { + case 8U: + tr->background_pixel[0] = PNG_BYTE(r); + tr->background_pixel[1] = PNG_BYTE(g); + tr->background_pixel[2] = PNG_BYTE(b); + tr->ntrans = 3U; + break; + + case 16U: + tr->background_pixel[0] = PNG_BYTE(r>>8); + tr->background_pixel[1] = PNG_BYTE(r); + tr->background_pixel[2] = PNG_BYTE(g>>8); + tr->background_pixel[3] = PNG_BYTE(g); + tr->background_pixel[4] = PNG_BYTE(b>>8); + tr->background_pixel[5] = PNG_BYTE(b); + tr->ntrans = 6U; + break; + + default: + NOT_REACHED; + } + } +# undef png_ptr +} + +/* Look for colors matching the trans_color in png_ptr and replace them. This + * must handle all the non-alpha formats. + */ +static void +png_do_replace_tRNS_multi(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + const unsigned int cbytes = tr->ntrans; + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - cbytes/*safety*/; + const int copy = dp != sp; + + /* We expect opaque and transparent pixels to be interleaved but with long + * sequences of each. + */ + debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA) && + PNG_TC_PIXEL_DEPTH(*tc) == cbytes << 3); + tc->invalid_info |= PNG_INFO_tRNS; + tc->sp = dp; + + /* Look for pixels that match the transparent value, copying opaque ones as + * required. + */ + do + { + const png_const_bytep opaque_start = sp; + size_t cb; + + /* Find a transparent pixel, or the end: */ + do + { + if (memcmp(sp, tr->transparent_pixel, cbytes) == 0) /* transparent */ + break; + sp += cbytes; + } + while (sp <= ep); + + cb = sp - opaque_start; + + /* Copy any opaque pixels: */ + if (cb > 0) + { + if (copy) + memcpy(dp, opaque_start, cb); + dp += cb; + } + + /* Set transparent pixels to the background (this has to be done one-by + * one; the case where all the bytes in the background are equal is not + * optimized.) + */ + if (sp <= ep) do + { + memcpy(dp, tr->background_pixel, cbytes); + sp += cbytes; + dp += cbytes; + } + while (sp <= ep && memcmp(sp, tr->transparent_pixel, cbytes) == 0); + } while (sp <= ep); + + debug(sp == ep+cbytes); +# undef png_ptr +} + +static void +png_do_replace_tRNS_8(png_transformp *transform, png_transform_controlp tc) + /* The single byte version: 8-bit gray */ +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_alloc_size_t row_bytes = tc->width; + const int copy = dp != sp; + const int transparent_pixel = tr->transparent_pixel[0]; + const int background_pixel = tr->background_pixel[0]; + + /* We expect opaque and transparent pixels to be interleaved but with long + * sequences of each. + */ + debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA) && + PNG_TC_PIXEL_DEPTH(*tc) == 8 && tr->ntrans == 1); + tc->invalid_info |= PNG_INFO_tRNS; + tc->sp = dp; + + /* Now search for a byte that matches the transparent pixel. */ + do + { + const png_const_bytep tp = png_voidcast(png_const_bytep, + memchr(sp, transparent_pixel, row_bytes)); + png_alloc_size_t cb; + + if (tp == NULL) /* all remaining pixels are opaque */ + { + if (copy) + memcpy(dp, sp, row_bytes); + return; + } + + cb = tp - sp; + if (cb > 0) /* some opaque pixels found */ + { + if (copy) + memcpy(dp, sp, cb); + sp = tp; + dp += cb; + debug(row_bytes > cb); + row_bytes -= cb; + } + + /* Now count the transparent pixels, this could use strspn but for the + * moment does not. + */ + debug(row_bytes > 0); + ++sp; /* next to check, may be beyond the last */ + while (--row_bytes > 0 && *sp == transparent_pixel) ++sp; + + cb = sp - tp; + memset(dp, background_pixel, cb); + dp += cb; + } while (row_bytes > 0); + UNTESTED +# undef png_ptr +} + +static void +png_do_set_row(png_transformp *transform, png_transform_controlp tc) + /* This is a no-op transform that both invalidates INFO from args and sets + * the entire row to the byte given in the top bits. + */ +{ + png_bytep dp = png_voidcast(png_bytep, tc->dp); + + tc->sp = dp; + memset(dp, (*transform)->args >> 24, PNG_TC_ROWBYTES(*tc)); +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr +} + +static void +png_do_replace_tRNS_lbd(png_transformp *transform, png_transform_controlp tc) +{ + /* This is the 2 or 4 bit depth grayscale case; the 1 bit case is handled by + * the two routines above and the 8-bit and 16-bit cases by the two before + * that. + * + * The transform contains pixel values that have been expanded to one byte, + * the code needs to match the tRNS pixel and substitute the background one + * in each byte. + */ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + const unsigned int transparent_pixel = tr->transparent_pixel[0]; + const unsigned int background_pixel = tr->background_pixel[0]; + + /* We expect opaque and transparent pixels to be interleaved but with long + * sequences of each. + */ + debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA) && + PNG_TC_PIXEL_DEPTH(*tc) < 8 && tr->ntrans == 1); + tc->sp = dp; + + /* Now search for a byte that contains the transparent pixel + * + * NOTE: this is the "strlen" algorithm, I first saw a variant implemented in + * Acorn RISC iX (strlen) around 1991, almost certainly derived from a + * suggestion by Alan Mycroft dating from April 27, 1987 (Mycroft was one of + * the authors of the 'Norcroft' compiler used for RISC iX, and well known to + * the RISC iX implementors.) See, e.g.: + * + * http://bits.stephan-brumme.com/null.html. + * + * The exact form used here is the one reported by Brumme; I haven't been + * able to find the original Mycroft posting, it was probably on comp.arch. + * + * The 4-bit and 2-bit versions (probably slower in the 4-bit case than the + * do-it-by-pixel version, but definately faster once 32-bit handling is + * implemented): + * + * 4 bit: (byte - 0x11) & ~byte & 0x88 + * 2 bit: (byte - 0x55) & ~byte & 0xcc + * + * The generalizations to 32 bits (8 and 16 pixels per step) should be + * obvious. + * + * This algorithm reads pixels within a byte beyond the end of the row and, + * potentially, changes the non-existent pixels. This is harmless and not + * a security risk. + */ + if (tc->bit_depth == 4U) + { + do + { + unsigned int b = *sp++; + unsigned int m = b; + + m ^= transparent_pixel; /* Set transparent pixels to 0 */ + m = 0x88U & ((m - 0x11U) & ~m); /* Top bit set for transparent pixel */ + m |= m >> 1; /* Set all four bits */ + m |= m >> 2; + + *dp++ = PNG_BYTE((b & ~m) | (background_pixel & m)); + } while (sp < ep); + UNTESTED + } + + else + { + affirm(tc->bit_depth == 2U); + + do + { + unsigned int b = *sp++; + unsigned int m = b; + + m ^= transparent_pixel; /* Set transparent pixels to 0 */ + m = 0xCCU & ((m - 0x55U) & ~m); /* Top bit set for transparent pixel */ + m |= m >> 1; /* Set both bits */ + + *dp++ = PNG_BYTE((b & ~m) | (background_pixel & m)); + } while (sp < ep); + UNTESTED + } + + UNTESTED +# undef png_ptr +} + +static void +png_do_background_with_transparent_GA8(png_transformp *transform, + png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 1U/*safety*/; + const png_byte background_pixel = tr->background_pixel[0]; + + /* Because this is an alpha format and we are removing the alpha channel we + * can copy up. + */ + debug(tc->bit_depth == 8U && tc->format == PNG_FORMAT_GA && + tr->ntrans == 1U); + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + tc->sp = dp; + + /* Look for pixels that have alpha 0; all others should have alpha 1.0, + * however they are simply treated as opaque regardless. + */ + do + { + *dp++ = (sp[1] == 0U) ? background_pixel : sp[0]; + sp += 2U; + } while (sp < ep); + + debug(sp == ep+1U); +# undef png_ptr +} + +static void +png_do_background_with_transparent_GA16(png_transformp *transform, + png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U/*safety*/; + + debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_GA && + tr->ntrans == 2U); + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + tc->sp = dp; + + do + { + if (sp[2] == 0U && sp[3] == 0U) /* transparent */ + dp[0] = tr->background_pixel[0], dp[1] = tr->background_pixel[1]; + + else + dp[0] = sp[0], dp[1] = sp[1]; + + dp += 2U; + sp += 4U; + } while (sp < ep); + + debug(sp == ep+3U); +# undef png_ptr +} + +static void +png_do_background_with_transparent_RGBA8(png_transformp *transform, + png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U/*safety*/; + + debug(tc->bit_depth == 8U && tc->format == PNG_FORMAT_RGBA && + tr->ntrans == 3U); + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + tc->sp = dp; + + do + { + if (sp[3] == 0U) /* transparent */ + memcpy(dp, tr->background_pixel, 3U); + + else + memmove(dp, sp, 3U); + + dp += 3U; + sp += 4U; + } while (sp < ep); + + debug(sp == ep+3U); +# undef png_ptr +} + +static void +png_do_background_with_transparent_RGBA16(png_transformp *transform, + png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 7U/*safety*/; + + debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_RGBA && + tr->ntrans == 6U); + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + tc->sp = dp; + + do + { + if (sp[6] == 0U && sp[7] == 0U) /* transparent */ + memcpy(dp, tr->background_pixel, 6U); + + else + memmove(dp, sp, 6U); + + dp += 6U; + sp += 8U; + } while (sp < ep); + + debug(sp == ep+7U); +# undef png_ptr +} + +static void +png_init_background_transparent(png_transformp *transform, + png_transform_controlp tc) + /* Select the correct version of the above routines. */ +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + + debug(tc->init == PNG_TC_INIT_FINAL /* never called in 'FORMAT' */ && + (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0); + + /* Now we know the format on which processing will happen so it is possible + * to generate the correct fill pixel value to use. + */ + fill_background_pixel(tr, tc); + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + tc->invalid_info |= PNG_INFO_sBIT; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = + png_check_byte(png_ptr, tc->bit_depth); + + if (!(tc->format & PNG_FORMAT_FLAG_COLOR)) + { + if (tc->bit_depth == 8U) + tr->tr.fn = png_do_background_with_transparent_GA8; + + else + { + debug(tc->bit_depth == 16U); + tr->tr.fn = png_do_background_with_transparent_GA16; + } + } + + else /* color */ + { + if (tc->bit_depth == 8U) + tr->tr.fn = png_do_background_with_transparent_RGBA8; + + else + { + debug(tc->bit_depth == 16U); + tr->tr.fn = png_do_background_with_transparent_RGBA16; + } + } +# undef png_ptr +} + +/* The calculated values below have the range 0..65535*65535, the output has the + * range 0..65535, so divide by 65535. Two approaches are given here, one + * modifies the value in place, the other uses a more complex expression. With + * gcc on an AMD64 system the in-place approach is very slightly faster. + * + * The two expressions are slightly different in what they calculate but both + * give the exact answer (verified by exhaustive testing.) + * + * The macro must be given a png_uint_32 variable (lvalue), normally an auto + * variable. + */ +#ifndef PNG_COMPOSE_DIV_65535 +# ifdef PNG_COMPOSE_DIV_EXPRESSION_SUPPORTED +# define PNG_COMPOSE_DIV_65535(v)\ + (v = ((v + (v>>16) + (v>>31) + 32768U) >> 16)) +# else +# define PNG_COMPOSE_DIV_65535(v)\ + (v += v >> 16, v += v >> 31, v += 32768U, v >>= 16) +# endif +#endif + +static void +png_do_background_alpha_GA(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U/*safety*/; + const unsigned int background = tr->background.gray; + const int copy = (sp != dp); + const int compose = tr->compose_background; + + affirm(tc->bit_depth == 16U && tc->format == PNG_FORMAT_GA && + tr->background_bit_depth == 16U && + (tr->background_gamma == tc->gamma || tr->background_gamma == 0)); + + tc->sp = tc->dp; /* nothing else changes */ + + do + { + const png_uint_32 alpha = (sp[2] << 8) + sp[3]; + + switch (alpha) + { + case 0U: /* transparent */ + memset(dp, 0U, 4U); + break; + + default: + { + png_uint_32 v = ((sp[0] << 8) + sp[1]) * alpha + + background * (65535U - alpha); + + PNG_COMPOSE_DIV_65535(v); + debug(v <= 65535U); + dp[0] = PNG_BYTE(v >> 8); + dp[1] = PNG_BYTE(v); + } + + if (compose) + dp[3] = dp[2] = 0xFFU; /* alpha; set to 1.0 */ + + else if (copy) + { + dp[2] = PNG_BYTE(alpha >> 8); + dp[3] = PNG_BYTE(alpha); + } + break; + + case 65535U: /* opaque */ + if (copy) + { + memcpy(dp, sp, 4U); + UNTESTED + } + break; + } + + sp += 4U; + dp += 4U; + } + while (sp < ep); + + debug(sp == ep+3U); +# undef png_ptr +} + +static void +png_do_background_alpha_RGBA(png_transformp *transform, + png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 7U/*safety*/; + const unsigned int bred = tr->background.red; + const unsigned int bgreen = tr->background.green; + const unsigned int bblue = tr->background.blue; + const int copy = (sp != dp); + const int compose = tr->compose_background; + + affirm(tc->bit_depth == 16U && tc->format == PNG_FORMAT_RGBA && + tr->background_bit_depth == 16U && + (tr->background_gamma == tc->gamma || tr->background_gamma == 0)); + + tc->sp = tc->dp; /* nothing else changes */ + + do + { + const png_uint_32 alpha = (sp[6] << 8) + sp[7]; + + switch (alpha) + { + case 0U: /* transparent */ + memset(dp, 0U, 8U); + break; + + default: + { + const png_uint_32 balpha = (65535U - alpha); + png_uint_32 r = ((sp[0] << 8) + sp[1]) * alpha + bred * balpha; + png_uint_32 g = ((sp[2] << 8) + sp[3]) * alpha + bgreen * balpha; + png_uint_32 b = ((sp[4] << 8) + sp[5]) * alpha + bblue * balpha; + + PNG_COMPOSE_DIV_65535(r); + PNG_COMPOSE_DIV_65535(g); + PNG_COMPOSE_DIV_65535(b); + debug(r <= 65535U && g <= 65535U && b <= 65535U); + dp[0] = PNG_BYTE(r >> 8); + dp[1] = PNG_BYTE(r); + dp[2] = PNG_BYTE(g >> 8); + dp[3] = PNG_BYTE(g); + dp[4] = PNG_BYTE(b >> 8); + dp[5] = PNG_BYTE(b); + } + + if (compose) + dp[7] = dp[6] = 0xFFU; + + else if (copy) + { + dp[6] = PNG_BYTE(alpha >> 8); + dp[7] = PNG_BYTE(alpha); + } + break; + + case 65535U: /* opaque */ + if (copy) + { + memcpy(dp, sp, 8U); + UNTESTED + } + break; + } + + sp += 8U; + dp += 8U; + } + while (sp < ep); + + debug(sp == ep+7U); +# undef png_ptr +} + +static void +png_init_background_alpha_end(png_transformp *transform, + png_transform_controlp tc) + /* This is just the last part of png_init_background_alpha (below) */ +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + + debug(tc->init == PNG_TC_INIT_FINAL); + + /* Repeat the tests at the end of png_init_background_alpha: */ + affirm(tc->bit_depth == 16U && (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0); + debug(tc->gamma == 0 || + !png_gamma_significant(png_ptr, tc->gamma, tc_sBIT(tc))); + + /* tr->background_is_gray was filled in by resolve_background_color and + * records if either the background was a gray value or it was a color + * value with all the channels equal. + */ + if (!tr->background_is_gray && !(tc->format & PNG_FORMAT_FLAG_COLOR)) + { +# ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* Color background with gray data: this happens when there is a + * gray to RGB transform in the pipeline but it hasn't happened + * yet. Unfortunately it has to happen now to be able to do the + * compose against the colored background. + */ + png_push_gray_to_rgb_byte_ops(transform, tc); + affirm((tc->format & PNG_FORMAT_FLAG_COLOR) != 0); + return; +# else /* !GRAY_TO_RGB */ + impossible("gray to RGB"); /* how can this happen? */ +# endif /* !GRAY_TO_RGB */ + } + + /* The transform happens in two parts, a part to do the arithmetic on + * pixels where it is required followed by a part to replace transparent + * pixels. These two parts require different versions of the background + * pixel. Set up the second part first. + * + * This only happens with background composition, otherwise the + * transparent pixels are already 0 and nothing needs to be done. + */ + if (tr->compose_background) + { + /* The transparent pixel handling happens *after* the data has been + * re-encoded to the output gamma: + */ + png_transform_background *tr_alpha = + png_transform_cast(png_transform_background, + png_add_transform(png_ptr, sizeof (png_transform_background), + png_init_background_transparent, PNG_TR_GAMMA_ENCODE+0xF0U)); + + /* Copy the current state into the new png_transform_background: */ + memcpy(&tr_alpha->background, &tr->background, + (sizeof *tr) - offsetof(png_transform_background, background)); + } + + /* Now it is possible to overwrite tr->background with the linear version. + */ + gamma_correct_background(tr, tc); + + /* sBIT informationmust also be invalidated here, because a gamma + * transform may run before the transparent pixel handling. + */ + tc->invalid_info |= PNG_INFO_sBIT; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = + png_check_byte(png_ptr, tc->bit_depth); + + /* And select an appropriate function; there are only two choices: */ + switch (tc->format) + { + case PNG_FORMAT_GA: + /* If the background format is color this indicates that there is a + * gray to RGB transform missing and we need it to happen before + * this point! + */ + affirm(tr->background_is_gray); + tr->tr.fn = png_do_background_alpha_GA; + break; + + case PNG_FORMAT_RGBA: + if (tr->background_is_gray) + tr->background.blue = tr->background.green = tr->background.red = + tr->background.gray; + tr->tr.fn = png_do_background_alpha_RGBA; + break; + + default: + NOT_REACHED; + } +# undef png_ptr +} + +static void +png_init_background_alpha(png_transformp *transform, png_transform_controlp tc) + /* This is used when alpha composition is required because the alpha channel + * may contain values that are between 0 and 1. Because doing alpha + * composition requires linear arithmetic the data is converted to 16-bit + * linear, however this means that the background pixel gets converted too + * and, for 16-bit output, this tends to smash the value. Consequently the + * algorithm used here is to skip those pixels and use the 'transparent + * alpha' routines to replace them after the gamma correction step. + */ +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + + debug(tc->init == PNG_TC_INIT_FINAL); + /* png_init_background ensures this is true: */ + debug((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0); + + /* Always push gamma transforms; don't try to optimize the case when they + * aren't needed because that would be an attempt to duplicate the tests in + * png_init_gamma and it might now work reliably. + * + * Need to push the to-linear transform *before* this transform and add gamma + * correction afterward to get back to the screen format. Do the afterward + * bit first to avoid complexity over *transform: + */ + { + png_transform_gamma *tr_end = add_gamma_transform(png_ptr, + PNG_TR_GAMMA_ENCODE, tc->gamma, 0U/*bit depth*/, 0/*default*/); + + /* Encoding the alpha channel happens in the last step, so this needs to + * be set here. Notice that in C++ terms we are very friendly with + * png_transform_gamma. + */ + tr_end->encode_alpha = tr->encode_alpha; + tr_end->optimize_alpha = tr->optimize_alpha; + } + + { + /* Now add tr_gamma before this transform, expect it to go in at + * *transform or the whole thing won't work: + */ + png_transform_gamma *tr_gamma = png_transform_cast(png_transform_gamma, + png_push_transform(png_ptr, sizeof (png_transform_gamma), + png_init_gamma, transform, NULL/*don't run init*/)); + + /* This must happen before we run png_gamma_init: */ + tr_gamma->to_gamma = PNG_FP_1; + tr_gamma->to_bit_depth = 16U; + + /* Now run the this transform; it was pushed before this one, so it gets + * to do its init first and this function must return as the caller will + * immediately call here again. + */ + debug(*transform == &tr_gamma->tr); + png_init_gamma(transform, tc); + affirm(tc->bit_depth == 16U && + (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0); + /* This is only a 'debug' because it needs to replicate the test in + * png_init_gamma and that is easy to get wrong (a harmless mistake). + */ + debug(tc->gamma == 0 || + !png_gamma_significant(png_ptr, tc->gamma, tc_sBIT(tc))); + } + + /* A transform was pushed, so this transform init will be run again: */ + tr->tr.fn = png_init_background_alpha_end; +# undef png_ptr +} + +/* Handle alpha and tRNS via a background color */ +static void +png_init_background(png_transformp *transform, png_transform_controlp tc) +{ + /* This init function is called right at the start, this means it can get at + * the tRNS values if appropriate. If not the RGB to gray transform comes + * next followed by PNG_TR_COMPOSE_ALPHA, which actually does the non-tRNS + * work. + */ + png_structp png_ptr = tc->png_ptr; + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + + if (tc->init == PNG_TC_INIT_FORMAT) + { + /* Background composition removes the alpha channel, so the other + * operations become irrelevant: + */ + if (tr->compose_background) + tr->associate_alpha = tr->encode_alpha = tr->optimize_alpha = 0U; + + else if (!tr->associate_alpha) + { + /* There is nothing to do, delete the whole transform. */ + tr->tr.fn = NULL; + return; + } + + /* Else alpha association ('pre-multiplication') which is achieved by + * composing on a 0 background. The background color will be black (all + * zeros) and the background gamma will be zero. + */ + + /* Because we are in PNG_TC_INIT_FORMAT no other transforms will have been + * inserted between this one and an rgb-to-gray transform, so we can find + * out if rgb-to-gray has been requested: + */ + tr->rgb_to_gray = tr->tr.next != NULL && + tr->tr.next->order == PNG_TR_RGB_TO_GRAY; + + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + /* Associated alpha does not strip the alpha channel! */ + if (tr->compose_background) + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + } + + else if (!tc->palette && + png_ptr->num_trans == 1 && !(tc->invalid_info & PNG_INFO_tRNS)) + { + /* tRNS will be expanded, or handled */ + tc->invalid_info |= PNG_INFO_tRNS; + if (!tr->compose_background) + tc->format |= PNG_FORMAT_FLAG_ALPHA; + } + + else /* no transparent pixels to change */ + tr->tr.fn = NULL; + } + + else /* PNG_TC_INIT_FINAL */ + { + png_fixed_point correction; + + debug(tc->init == PNG_TC_INIT_FINAL && + ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 || + (!tc->palette && png_ptr->num_trans == 1 && + !(tc->invalid_info & PNG_INFO_tRNS)))); + + /* The screen gamma is known, so the background gamma can be found, note + * that both the gamma values used below will be 0 if no gamma information + * was in the PNG and no gamma information has been provided by + * png_set_gamma or png_set_alpha_mode. + */ + switch (tr->background_gamma) + { + case PNG_BACKGROUND_GAMMA_FILE: + /* png_init_transform_control has already found the file gamma, + * and because this is the first arithmetic transformation + * nothing has changed it. + */ + tr->background_gamma = tc->gamma; + break; + + case PNG_BACKGROUND_GAMMA_SCREEN: + tr->background_gamma = png_ptr->row_gamma; + break; + + default: + /* already set */ + break; + } + + /* Work out what the background color is, this only depends on 'tc' for + * palette information, so it can be done now before we know the actual + * bit_depth/format that will be required: + */ + resolve_background_color(tr, tc); + + /* Is this format compatible with the current row data? If it is then it + * is possible to avoid the arithmetic if no alpha processing is required. + * This is a useful optimization because PNG files with just transparent + * pixels and no alpha are common. + * + * NOTE: if an RGB-to-gray transform is present this is fine so long as + * the background is gray, otherwise (non-gray background) there is a + * following gray-to-RGB transform and the now gray image must be + * composited on a color background. + */ + if (tr->compose_background /* alpha channel stripped */ && + (tr->background_is_gray || + ((tc->format & PNG_FORMAT_FLAG_COLOR) != 0 && !tr->rgb_to_gray)) + /* color compatible */ && + tc->bit_depth >= tr->background_bit_depth + /* bit depth compatible */ && + (tc->transparent_alpha || + (!tc->palette && png_ptr->num_trans == 1 && + !(tc->invalid_info & PNG_INFO_tRNS))) + /* no alpha processing */ && + png_gamma_equal(png_ptr, tc->gamma, png_ptr->row_gamma, &correction, + tc->bit_depth) /* gamma compatible (so no gamma processing) */) + { + /* How the operation gets performed depends on whether the current data + * has an alpha channel or not. + */ + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + /* TODO: it may be impossible to get here! */ + affirm(tc->transparent_alpha); + /* This init routine does the sBIT handling: */ + png_init_background_transparent(transform, tc); + UNTESTED + } + + else if (!tc->palette && png_ptr->num_trans == 1 && + !(tc->invalid_info & PNG_INFO_tRNS)) + { + /* The background pixel needs to be filled in now; no more init + * routines are called in this case. It is important to delay this + * as late as possible because it needs to know the actual tc format + * that must be used. + */ + fill_background_pixel(tr, tc); + + debug(!(png_ptr->color_type & PNG_COLOR_MASK_PALETTE)); + + /* The pixel depth should not have been changed yet: */ + debug(PNG_PIXEL_DEPTH(*png_ptr) == PNG_TC_PIXEL_DEPTH(*tc)); + + /* The transparent_pixel value needs to be filled in. */ + affirm(tr->ntrans == + fill_transparent_pixel(png_ptr, tr->transparent_pixel)); + + /* The whole operation is a no-op if the transparent pixel and the + * background pixel match, even in the associated alpha case where + * both will be 0 throughout. + * + * NOTE: for palette images this test happens in the caching + * operation, so the answer is still correct. + */ + if (memcmp(tr->transparent_pixel, tr->background_pixel, tr->ntrans) + == 0) + { + tr->tr.fn = NULL; + UNTESTED + } + + /* Then the processing function depends on the pixel size: */ + else if (tr->ntrans > 1U) + tr->tr.fn = png_do_replace_tRNS_multi; + + else if (tc->bit_depth == 8U) + tr->tr.fn = png_do_replace_tRNS_8; + + else if (tc->bit_depth == 1U) + { + /* This is the silly case: the replacement pixel does not match + * the transparent pixel (handled above) so either all the '0' + * bits are replaced by '1' or all the '1' bits are replaced by + * '0': + */ + png_uint_32 args = tr->background_pixel[0]; + + args <<= 24; + args |= PNG_INFO_tRNS | PNG_INFO_sRGB; + tr->tr.args = args; + tr->tr.fn = png_do_set_row; + UNTESTED + } + + else + { + tr->tr.fn = png_do_replace_tRNS_lbd; + UNTESTED + } + + tc->invalid_info |= PNG_INFO_tRNS | PNG_INFO_sBIT; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = + png_check_byte(png_ptr, tc->bit_depth); + } + + else + { + /* Nothing to do; should have been eliminated before! */ + tr->tr.fn = NULL; + NOT_REACHED; + } + } + + else /* alpha, or maybe gamma, processing required */ + { + /* Alpha case, add an appropriate transform; this has to be done + * *after* the RGB-to-gray case so move the transform info there: + */ + png_transform_background *tr_alpha = + png_transform_cast(png_transform_background, + png_add_transform(png_ptr, sizeof (png_transform_background), + png_init_background_alpha, PNG_TR_COMPOSE_ALPHA)); + + /* Copy the current state into the new png_transform_background: */ + memcpy(&tr_alpha->background, &tr->background, + (sizeof *tr) - offsetof(png_transform_background, background)); + + /* The rest of the init occurs later; this transform is no longer + * needed. + */ + tr->tr.fn = NULL; + + /* Ensure that png_init_background_alpha gets an alpha channel, this + * needs to happen here because otherwise intervening transforms can + * invalidate tRNS. + */ + tc->expand_tRNS = 1U; + if (tr->compose_background) + tc->strip_alpha = 0U; + + /* And push the expand: */ + (void)push_gamma_expand(transform, tc, 1/*need alpha*/); + + /* Regardless of whether anything got pushed the following should now + * be true: + */ + affirm((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 && + tc->bit_depth >= 8U); + } + } +} + +void PNGFAPI +png_set_background_fixed(png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma) +{ + if (png_ptr != NULL) + { + if (background_color != NULL) + { + png_transform_background *tr = + png_transform_cast(png_transform_background, + png_add_transform(png_ptr, sizeof (png_transform_background), + png_init_background, PNG_TR_COMPOSE)); + + /* This silently overwrites the information if png_set_background is + * called more than once. + */ + tr->background = *background_color; + tr->need_expand = need_expand != 0; + tr->compose_background = 1U; /* png_set_background called */ + switch (background_gamma_code) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + case PNG_BACKGROUND_GAMMA_FILE: + tr->background_gamma = background_gamma_code; + break; + + case PNG_BACKGROUND_GAMMA_UNIQUE: + if (background_gamma >= 16 && background_gamma <= 625000000) + { + tr->background_gamma = background_gamma; + break; + } + + png_app_error(png_ptr, "gamma value out of range"); + /* FALL THROUGH */ + default: + png_app_error(png_ptr, "invalid gamma information"); + tr->background_gamma = (need_expand ? + PNG_BACKGROUND_GAMMA_FILE : PNG_BACKGROUND_GAMMA_SCREEN); + break; + } + } + + else + png_app_error(png_ptr, "missing background color"); + } +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_background(png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma) +{ + png_set_background_fixed(png_ptr, background_color, background_gamma_code, + need_expand, png_fixed(png_ptr, background_gamma, "png_set_background")); +} +# endif /* FLOATING_POINT */ +#endif /* READ_BACKGROUND */ #ifdef PNG_READ_ALPHA_MODE_SUPPORTED - /* If the screen gamma is about 1.0 then the OPTIMIZE_ALPHA and ENCODE_ALPHA - * settings will have no effect. - */ - if (png_gamma_significant(png_ptr->screen_gamma) == 0) +void PNGFAPI +png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, + png_fixed_point output_gamma) +{ + if (png_ptr != NULL) { - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - } -#endif + /* Check the passed in output_gamma value; it must be valid and it must be + * converted to the reciprocal for use below: + */ + output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/); -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - /* Make sure the coefficients for the rgb to gray conversion are set - * appropriately. - */ - if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0) - png_colorspace_set_rgb_coefficients(png_ptr); -#endif - - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - png_init_palette_transformations(png_ptr); - - else - png_init_rgb_transformations(png_ptr); - -#ifdef PNG_READ_BACKGROUND_SUPPORTED - /* Set up the background information if required. It is only used if - * PNG_COMPOSE is specified. - */ - if ((png_ptr->transformations & PNG_COMPOSE) != 0) - png_init_background_transformations(png_ptr); -#endif - - /* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations - * can be performed directly on the palette, and some (such as rgb to gray) - * can be optimized inside the palette. This is particularly true of the - * composite (background and alpha) stuff, which can be pretty much all done - * in the palette even if the result is expanded to RGB or gray afterward. - * - * NOTE: this is Not Yet Implemented, the code behaves as in 1.5.1 and - * earlier and the palette stuff is actually handled on the first row. This - * leads to the reported bug that the palette returned by png_get_PLTE is not - * updated. - */ -#if 0 /* NYI */ - png_do_palette_transformations(png_ptr); -#endif - -#ifdef PNG_READ_GAMMA_SUPPORTED - /* This needs to change - in the palette image case a whole set of tables are - * built when it would be quicker to just calculate the correct value for - * each palette entry directly. Also, the test is too tricky - why check - * PNG_RGB_TO_GRAY if PNG_GAMMA is not set? The answer seems to be that - * PNG_GAMMA is cancelled even if the gamma is known? The test excludes the - * PNG_COMPOSE case, so apparently if there is no *overall* gamma correction - * the gamma tables will not be built even if composition is required on a - * gamma encoded value. - * - * In 1.5.4 this is addressed below by an additional check on the individual - * file gamma - if it is not 1.0 both RGB_TO_GRAY and COMPOSE need the - * tables. - */ - if ((png_ptr->transformations & PNG_GAMMA) != 0 - || ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0 - && (png_gamma_significant(png_ptr->colorspace.gamma) != 0 || - png_gamma_significant(png_ptr->screen_gamma) != 0)) - || ((png_ptr->transformations & PNG_COMPOSE) != 0 - && (png_gamma_significant(png_ptr->colorspace.gamma) != 0 - || png_gamma_significant(png_ptr->screen_gamma) != 0 -# ifdef PNG_READ_BACKGROUND_SUPPORTED - || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE - && png_gamma_significant(png_ptr->background_gamma) != 0) -# endif - )) || ((png_ptr->transformations & PNG_ENCODE_ALPHA) != 0 - && png_gamma_significant(png_ptr->screen_gamma) != 0) - ) - { - png_build_gamma_tables(png_ptr, png_ptr->bit_depth); - -#ifdef PNG_READ_BACKGROUND_SUPPORTED - if ((png_ptr->transformations & PNG_COMPOSE) != 0) + if (output_gamma > 0) /* Else an app_error has been signalled. */ { - /* Issue a warning about this combination: because RGB_TO_GRAY is - * optimized to do the gamma transform if present yet do_background has - * to do the same thing if both options are set a - * double-gamma-correction happens. This is true in all versions of - * libpng to date. + /* Only set the colorspace gamma if it has not already been set (this + * has the side effect that the gamma in a second call to + * png_set_alpha_mode will be ignored.) */ - if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0) - png_warning(png_ptr, - "libpng does not support gamma+background+rgb_to_gray"); - - if ((png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) != 0) + if ((png_ptr->colorspace.flags & + (PNG_COLORSPACE_INVALID | PNG_COLORSPACE_HAVE_GAMMA)) != + PNG_COLORSPACE_HAVE_GAMMA) { - unsigned int i, num_palette = png_ptr->num_palette; - png_color back; - png_color_16 back_1 = png_ptr->background_1; - png_colorp palette = png_ptr->palette; + /* The default file gamma is the output gamma encoding: */ + png_ptr->colorspace.gamma = output_gamma; + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + png_ptr->colorspace.flags = PNG_COLORSPACE_HAVE_GAMMA; + else + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + } - back.red = png_check_byte(png_ptr, png_ptr->background.red); - back.green = png_check_byte(png_ptr, png_ptr->background.green); - back.blue = png_check_byte(png_ptr, png_ptr->background.blue); + /* Always set the output gamma, note that it may be changed to PNG_FP_1 + * for the associated alpha support. This means that the last call to + * png_set_gamma[_fixed] or png_set_alpha_mode sets the output gamma, + * which is probably what is expected. + */ + { + png_transform_gamma *tr_gamma = add_gamma_transform(png_ptr, + PNG_TR_GAMMA_ENCODE, + mode == PNG_ALPHA_ASSOCIATED ? PNG_FP_1 : output_gamma, 0U, + 1/*force*/); - for (i = 0; i < num_palette; i++) - { - if (i < png_ptr->num_trans && png_ptr->trans_alpha[i] != 0xff) - { - if (png_ptr->trans_alpha[i] == 0) - { - palette[i] = back; - } - else /* if (png_ptr->trans_alpha[i] != 0xff) */ - { - png_uint_16 v, w; - unsigned int alpha = png_ptr->trans_alpha[i] * 257U; - unsigned int shift = png_ptr->gamma_shift; - unsigned int add = (shift > 0 ? 1U<<(shift-1) : 0); - - if (png_ptr->gamma_to_1 != NULL) - { - v = png_ptr->gamma_to_1[palette[i].red]; - png_composite_16(w, v, alpha, back_1.red); - palette[i].red = png_ptr->gamma_from_1[(w+add)>>shift]; - - v = png_ptr->gamma_to_1[palette[i].green]; - png_composite_16(w, v, alpha, back_1.green); - palette[i].green = - png_ptr->gamma_from_1[(w+add)>>shift]; - - v = png_ptr->gamma_to_1[palette[i].blue]; - png_composite_16(w, v, alpha, back_1.blue); - palette[i].blue = png_ptr->gamma_from_1[(w+add)>>shift]; - } - } - } - else if (png_ptr->gamma_table != NULL) - { - palette[i].red = png_ptr->gamma_table[palette[i].red]; - palette[i].green = png_ptr->gamma_table[palette[i].green]; - palette[i].blue = png_ptr->gamma_table[palette[i].blue]; - } - } - - /* Prevent the transformations being done again. + /* Get a background transform and set the appropriate fields. * - * NOTE: this is highly dubious; it removes the transformations in - * place. This seems inconsistent with the general treatment of the - * transformations elsewhere. + * png_set_background removes the alpha channel so it effectively + * disbles png_set_alpha_mode however png_set_alpha_mode is still + * useful to set a default gamma value. */ - png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA); - } /* color_type == PNG_COLOR_TYPE_PALETTE */ - }/* png_ptr->transformations & PNG_BACKGROUND */ + png_transform_background *tr = + png_transform_cast(png_transform_background, + png_add_transform(png_ptr, sizeof (png_transform_background), + png_init_background, PNG_TR_COMPOSE)); - else - /* Transformation does not include PNG_BACKGROUND */ -#endif /* READ_BACKGROUND */ - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - /* RGB_TO_GRAY needs to have non-gamma-corrected values! */ - && ((png_ptr->transformations & PNG_EXPAND) == 0 || - (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0) -#endif - ) - { - png_colorp palette = png_ptr->palette; - int num_palette = png_ptr->num_palette; - int i; - - /* NOTE: there are other transformations that should probably be in - * here too. - */ - if (png_ptr->gamma_table != NULL) - { - for (i = 0; i < num_palette; i++) + /* There are really 8 possibilities here, composed of any + * combination of: + * + * premultiply the color channels + * do not encode non-opaque pixels (leave as linear) + * encode the alpha as well as the color channels + * + * The differences disappear if the input/output ('screen') gamma is + * 1.0, because then the encoding is a no-op and there is only the + * choice of premultiplying the color channels or not. + */ + switch (mode) { - palette[i].red = png_ptr->gamma_table[palette[i].red]; - palette[i].green = png_ptr->gamma_table[palette[i].green]; - palette[i].blue = png_ptr->gamma_table[palette[i].blue]; - } - } + case PNG_ALPHA_PNG: /* default: png standard */ + /* No compose, but it may be set by png_set_background! This + * is the only mode that doesn't interfere with what + * png_set_background does. + */ + tr->associate_alpha = 0U; + tr_gamma->encode_alpha = tr->encode_alpha = 0U; + tr_gamma->optimize_alpha = tr->optimize_alpha = 0U; + break; - /* Done the gamma correction. */ - png_ptr->transformations &= ~PNG_GAMMA; - } /* color_type == PALETTE && !PNG_BACKGROUND transformation */ - } -#ifdef PNG_READ_BACKGROUND_SUPPORTED - else -#endif -#endif /* READ_GAMMA */ + case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */ + tr->associate_alpha = 1U; + tr_gamma->encode_alpha = tr->encode_alpha = 0U; + tr_gamma->optimize_alpha = tr->optimize_alpha = 0U; + break; -#ifdef PNG_READ_BACKGROUND_SUPPORTED - /* No GAMMA transformation (see the hanging else 4 lines above) */ - if ((png_ptr->transformations & PNG_COMPOSE) != 0 && - (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) + case PNG_ALPHA_OPTIMIZED: + /* associated with opaque pixels having the given gamma and + * non-opaque pixels being linear. + */ + tr->associate_alpha = 1U; + tr_gamma->encode_alpha = tr->encode_alpha = 0U; + tr_gamma->optimize_alpha = tr->optimize_alpha = 1U; + /* output_gamma records the encoding of opaque pixels! */ + break; + + case PNG_ALPHA_BROKEN: + /* associated+non-linear+alpha encoded */ + tr->associate_alpha = 1U; + tr_gamma->encode_alpha = tr->encode_alpha = 1U; + tr_gamma->optimize_alpha = tr->optimize_alpha = 0U; + break; + + default: + png_app_error(png_ptr, "invalid alpha mode"); + /* A return at this point is safe; if a background transform + * was created the init routine will remove it because + * nothing is set. + */ + break; + } /* alpha mode switch */ + } /* add gamma and background transforms */ + } /* valid output gamma */ + } /* png_ptr != NULL */ +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma) +{ + png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr, + output_gamma)); +} +#endif /* FLOATING_POINT */ +#endif /* READ_ALPHA_MODE */ + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +typedef struct +{ + png_transform tr; + png_transform_control tc; + union { - int i; - int istop = (int)png_ptr->num_trans; - png_color back; - png_colorp palette = png_ptr->palette; + png_uint_32 u32[1]; /* ensure alignment */ + png_uint_16 u16[1]; + png_byte b8[1]; + } cache; +} png_transform_cache; - back.red = png_check_byte(png_ptr, png_ptr->background.red); - back.green = png_check_byte(png_ptr, png_ptr->background.green); - back.blue = png_check_byte(png_ptr, png_ptr->background.blue); +#define png_transform_cache_size(size)\ + (offsetof(png_transform_cache, cache)+(size)) +#define png_transform_cache_cast(pointer,size)\ + png_voidcast(png_transform_cache*,\ + png_transform_cast_check(png_ptr, PNG_SRC_LINE, (pointer),\ + png_transform_cache_size(size))) + /* This is like png_transform_cast except that 'size' is the size of the + * cache part in the above structure and the type returned is always + * 'png_transform_cache*'. + */ - for (i = 0; i < istop; i++) +/* Functions to handle the cache operation. These don't do any initialization; + * that happens below when PNG_TC_INIT_FINAL is being run on the whole list. + * These functions are only implemented for read so the transform control + * source and destination are always aligned. + * + * First some utility functions: + */ +static void +png_transform_control_cp(png_transform_controlp tcDest, + png_const_transform_controlp tcSrc) +{ + /* Copy tcSrc over tcDest without overwriting the information specific to the + * row being transformed. + */ + png_structp png_ptr = tcDest->png_ptr; + png_const_voidp sp = tcDest->sp; + png_voidp dp = tcDest->dp; + png_uint_32 width = tcDest->width; + unsigned int init = tcDest->init; + + *tcDest = *tcSrc; + + tcDest->png_ptr = png_ptr; + tcDest->sp = sp; + tcDest->dp = dp; + tcDest->width = width; + tcDest->init = png_check_bits(tcDest->png_ptr, init, 2); +} + +#if !PNG_RELEASE_BUILD +static int +png_transform_control_eq(png_const_transform_controlp tc1, + png_const_transform_controlp tc2) +{ + /* Say if *tc1 == *tc2, ignoring differences in uncopied fields and 'cost': + */ + return +# ifdef PNG_READ_GAMMA_SUPPORTED + tc1->gamma == tc2->gamma && +# endif + tc1->format == tc2->format && + tc1->range == tc2->range && + tc1->bit_depth == tc2->bit_depth && + tc1->caching == tc2->caching && + tc1->palette == tc2->palette; + /* invalid_info, cost, interchannel and channel_add are only set during + * init, so don't do the compare. + */ +} +#endif /* !RELEASE_BUILD */ + +/* Now the routines that actually perform the transform. There are two basic + * cases: + * + * 1) A cached transform that does not change the pixel size and where the pixel + * size 8 bits or less. This can be done by a 256-entry single byte lookup + * table, regardless of the bit depth. Two versions of the code exist, one + * which just transforms the row, the other which transforms and records the + * maximum pixel depth. + * + * 2) A cached transform that increases pixel depth. The destination pixel + * depth will always be a multiple of 8 bits, the source pixel will be less + * than or equal to 8 bits and will be in the PNG native (big endian) layout. + */ +#define png_ptr (tc->png_ptr) /* Used in all functions below */ +/* (1): single-byte cached transforms: */ +static void +do_transform_cache_byte(png_transformp *trIn, png_transform_controlp tc) +{ + png_transform_cache *tr = png_transform_cache_cast(*trIn, 256U); + + /* Copy the bytes through the 256-byte LUT: */ + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep ep = dp + PNG_TC_ROWBYTES(*tc); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + + tc->sp = dp; + + do + *dp++ = tr->cache.b8[*sp++]; + while (dp < ep); + + png_transform_control_cp(tc, &tr->tc); +} + +/* (2) A cached transform that increases pixel depth. + * + * There are six output depth possibilites, all a whole number of bytes: + * + * 1 byte, 8 bits: palette or grayscale + * 2 bytes, 16 bits: 16-bit grayscale or 8-bit gray+alpa + * 3 bytes, 24 bits: 8-bit RGB + * 4 bytes, 32 bits: 16-bit gray+alpha or 8-bit RGBA + * 6 bytes, 48 bits: 16-bit RGB + * 8 bytes, 64 bits: 16-bit RGBA + * + * The input must be 1, 2, 4 or 8-bit gray or palette. The first 1-byte case is + * handled for 8-bit gray/palette above, so there are 22 possibilities. The + * function names below are: + * + * do_transform_cache__ + */ +#define transform_cache_size(ipd,opd) ((((1U << (ipd)) * (opd))+7U) >> 3) +static void +do_transform_cache_(png_transformp *trIn, png_transform_controlp tc, + unsigned int ipd, unsigned int opd) + /* This is the implementation for unknown ipd, opd, below it is called with + * fixed values. The purpose of this is to allow the compiler/system builder + * to decide how to optimize for size vs space vs speed. Note that this + * implementation, while it would work for 8 bit ipd, is not used in that + * case. + */ +{ + png_transform_cache *tr = + png_transform_cache_cast(*trIn, transform_cache_size(ipd, opd)); + + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep ep = dp; + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + unsigned int s, shift, mask; + + sp += PNG_TC_ROWBYTES(*tc); /* One byte beyond the end */ + + png_transform_control_cp(tc, &tr->tc); + dp += PNG_TC_ROWBYTES(*tc); + + shift = 7U & -(tc->width * ipd); + /* MSB: shift right required to get last pixel */ + mask = (1U << ipd) - 1U; + /* Mask to extract a single pixel from the low bits of a byte */ + opd >>= 3; + /* Output pixel size in bytes */ + s = *--sp; + /* The first byte; the last byte of the input row */ + + for (;;) + { + png_const_bytep opixel = (((s >> shift) & mask)+1U) * opd + tr->cache.b8; + /* Points to the byte after last byte of the output value */ + unsigned int i; + + for (i=0; isp); + tc->sp = ep; /* start of row, safe even if the above fails */ +} + +#define do_transform_cache(ipd,opd)\ +static void \ +do_transform_cache_##ipd##_##opd(png_transformp *tr, png_transform_controlp tc)\ +{\ + do_transform_cache_(tr, tc, ipd, opd);\ +} + +#define TCLOW(opd)\ +do_transform_cache(1,opd)\ +do_transform_cache(2,opd)\ +do_transform_cache(4,opd) + +TCLOW(8) +TCLOW(16) +TCLOW(24) +TCLOW(32) +TCLOW(48) +TCLOW(64) + +#undef TCLOW +#undef do_transform_cache + +static void +do_transform_cache_8_(png_transformp *trIn, png_transform_controlp tc, + unsigned int opd) + /* This is the 8-bit input implementation. */ +{ + png_transform_cache *tr = + png_transform_cache_cast(*trIn, transform_cache_size(8, opd)); + + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep ep = dp; + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + + sp += PNG_TC_ROWBYTES(*tc); /* One byte beyond the end */ + + png_transform_control_cp(tc, &tr->tc); + dp += PNG_TC_ROWBYTES(*tc); + + opd >>= 3; /* Output pixel size in bytes */ + do + { + png_const_bytep opixel = (*--sp + 1U) * opd + tr->cache.b8; + /* Points to the byte after last byte of the output value */ + unsigned int i; + + for (i=0; i ep); + + debug(dp == ep && sp == tc->sp); + tc->sp = ep; /* start of row, safe even if the above fails */ +} + +#define do_transform_cache(opd)\ +static void \ +do_transform_cache_8_##opd(png_transformp *tr, png_transform_controlp tc)\ +{\ + do_transform_cache_8_(tr, tc, opd);\ +} + +/* The 8-bit to 8-bit case uses the byte transform code */ +do_transform_cache(16) +do_transform_cache(24) +do_transform_cache(32) +do_transform_cache(48) +do_transform_cache(64) + +#undef do_transform_cache + +#define do_transform_cache(ipd,opd) do_transform_cache_##ipd##_##opd + +#undef png_ptr + +typedef struct +{ + png_transformp *start; + /* This is a pointer to the pointer to the start of the list being cached, + * i.e. *start is the first transform in the list. + */ + png_transform_control tstart; + /* This is the transform control at the start; i.e. before (*start)->fn is + * called. Note that for palette data it will contain the original + * palette format/bit-depth, not that passed to (*start)->fn which will + * represent the palette. + */ + png_transformp *end; + png_transform_control tend; + /* The same data from the end of the run to be cached, i.e. after the + * function of the transform which *contains* '*end' (end points to + * tr->next). + */ +} png_cache_params, *png_cache_paramsp; + +static void +init_caching(png_structp png_ptr, png_cache_paramsp cp) + /* Given an already initialized cp->tend turn on caching if appropriate. */ +{ + /* Handle the colormap case, where a cache is always required: */ + if (cp->tend.format & PNG_FORMAT_FLAG_COLORMAP) + { + /* This turns starts the palette caching with the next transform: */ + cp->tend.palette = cp->tend.caching = 1U; + cp->tend.transparent_alpha = png_ptr->transparent_palette; + cp->tend.format = PNG_FORMAT_FLAG_COLOR; +# ifdef PNG_READ_tRNS_SUPPORTED + if (png_ptr->num_trans > 0 && !(cp->tend.invalid_info & PNG_INFO_tRNS)) + { + cp->tend.format |= PNG_FORMAT_FLAG_ALPHA; + } +# endif /* READ_tRNS */ + cp->tend.bit_depth = 8U; + } + + else if (PNG_TC_PIXEL_DEPTH(cp->tend) <= 8) + { + /* Cacheable pixel transforms; the pixel is less than 8 bits in size so + * the cache makes sense. + * + * TODO: check the cost estimate and the image size to avoid expensive + * caches of very small images. + */ + cp->tend.caching = 1U; + } + + /* TODO: handle handle 8-bit GA/RGB/RGBA */ +} + +static void +add_cache_transform(png_structp png_ptr, unsigned int order, + png_transform_fn fn, png_cache_paramsp cp, + png_const_bytep cache, unsigned int size) + /* Add a transform from the input format cp->tstart to the output format + * stored in cp->tend. + */ +{ + affirm(size <= 2048U); /* 256 8-byte pixels at most */ + { + png_transform_cache *tr = png_transform_cache_cast( + png_add_transform(png_ptr, png_transform_cache_size(size), fn, order), + size); + + /* This must have replaced the transform in *cp->start: */ + affirm(&tr->tr == *cp->start); + + /* Fill in the respective members: */ + tr->tc = cp->tend; + memcpy(tr->cache.b8, cache, size); + + /* Skip this transform, because the calling routine has already executed + * the cache (it could be executed again, just to verify that it works; + * cp->tstart should be correct.) + */ + cp->start = &tr->tr.next; + } +} + +static unsigned int +setup_palette_cache(png_structp png_ptr, png_byte cache[8*256]) + /* This returns the number of entries in the cache; the width */ +{ + const unsigned int num_palette = png_ptr->num_palette; +# ifdef PNG_READ_tRNS_SUPPORTED + unsigned int num_trans = png_ptr->num_trans; +# endif /* READ_tRNS */ + const png_colorp palette = png_ptr->palette; + png_bytep p; + unsigned int i; +# ifdef PNG_READ_tRNS_SUPPORTED + const png_bytep trans_alpha = png_ptr->trans_alpha; +# endif /* READ_tRNS */ + + for (i=0, p=cache; i 0) + { + if (i < num_trans) + *p++ = trans_alpha[i]; + + else + *p++ = 0xFFU; + } +# endif /* READ_tRNS */ + } + + return num_palette; +} + +static void +png_remove_PLTE_and_tRNS(png_structrp png_ptr) +{ + if ((png_ptr->free_me & PNG_FREE_PLTE) != 0) + { + png_ptr->free_me &= PNG_BIC_MASK(PNG_FREE_PLTE); + png_free(png_ptr, png_ptr->palette); + } + + png_ptr->palette = NULL; + png_ptr->num_palette = 0; + +# ifdef PNG_READ_tRNS_SUPPORTED + if ((png_ptr->free_me & PNG_FREE_TRNS) != 0) { - if (png_ptr->trans_alpha[i] == 0) - { - palette[i] = back; - } - - else if (png_ptr->trans_alpha[i] != 0xff) - { - /* The png_composite() macro is defined in png.h */ - png_composite(palette[i].red, palette[i].red, - png_ptr->trans_alpha[i], back.red); - - png_composite(palette[i].green, palette[i].green, - png_ptr->trans_alpha[i], back.green); - - png_composite(palette[i].blue, palette[i].blue, - png_ptr->trans_alpha[i], back.blue); - } + png_ptr->free_me &= PNG_BIC_MASK(PNG_FREE_TRNS); + png_free(png_ptr, png_ptr->trans_alpha); } - png_ptr->transformations &= ~PNG_COMPOSE; - } -#endif /* READ_BACKGROUND */ + png_ptr->trans_alpha = NULL; + png_ptr->num_trans = 0; +# endif /* READ_tRNS */ +} -#ifdef PNG_READ_SHIFT_SUPPORTED - if ((png_ptr->transformations & PNG_SHIFT) != 0 && - (png_ptr->transformations & PNG_EXPAND) == 0 && - (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) +static void +update_palette(png_structp png_ptr, png_cache_paramsp cp, + unsigned int max_depth) +{ + union { - int i; - int istop = png_ptr->num_palette; - int shift = 8 - png_ptr->sig_bit.red; + png_uint_32 u32[1]; + png_uint_16 u16[1]; /* For alignment */ + png_byte b8[8*256]; /* For 16-bit RGBA intermediate */ + } cache; - png_ptr->transformations &= ~PNG_SHIFT; + /* The caller only calls this function if the initial transform control had + * the palette flag set, implying that the original 'format' was a COLORMAP + * one. Also this can only happen (at present) when starting the transform + * list, so: + */ + affirm((cp->tstart.format & PNG_FORMAT_FLAG_COLORMAP) != 0); /* required */ + debug(cp->start == &png_ptr->transform_list); /* should be harmless */ - /* significant bits can be in the range 1 to 7 for a meaninful result, if - * the number of significant bits is 0 then no shift is done (this is an - * error condition which is silently ignored.) + /* Run the whole of the given list on the palette data. PNG_TC_INIT_FINAL + * has already been run; this is a full run (with init == 0). + */ + { + unsigned int check_depth; + only_deb(png_transform_control orig = cp->tend;) + + cp->tend = cp->tstart; + init_caching(png_ptr, cp); + /* And set up tend to actually work out the palette: */ + cp->tend.init = 0U; + cp->tend.width = setup_palette_cache(png_ptr, cache.b8); + cp->tend.sp = cache.b8; + cp->tend.dp = cache.b8; + + check_depth = + png_run_this_transform_list_forwards(&cp->tend, cp->start, cp->end); + + /* If we get here these two things must be true or there are been some + * buggy difference of opinion between the INIT code and the actual run: */ - if (shift > 0 && shift < 8) - for (i=0; ipalette[i].red; + affirm(check_depth == max_depth && cp->tend.palette); - component >>= shift; - png_ptr->palette[i].red = png_check_byte(png_ptr, component); - } + /* This should match the passed in final format obtained before, this + * debug statement detects discrepancies between the init code and the + * run code: + */ + debug(png_transform_control_eq(&cp->tend, &orig)); - shift = 8 - png_ptr->sig_bit.green; - if (shift > 0 && shift < 8) - for (i=0; ipalette[i].green; - - component >>= shift; - png_ptr->palette[i].green = png_check_byte(png_ptr, component); - } - - shift = 8 - png_ptr->sig_bit.blue; - if (shift > 0 && shift < 8) - for (i=0; ipalette[i].blue; - - component >>= shift; - png_ptr->palette[i].blue = png_check_byte(png_ptr, component); - } + /* Also, expect the palette to still be valid: */ + debug((cp->tend.invalid_info & PNG_INFO_PLTE) == 0); } -#endif /* READ_SHIFT */ + + /* The result must be compatible with a PNG palette with respect to bit + * depth; specifically the expand-16 transform has no effect on palette data. + * + * The colormap setting must not have been re-introduced here either; there + * may be some quantize interactions here, neither can unexpected flags be + * handled; just COLOR and ALPHA. + */ + affirm(cp->tend.bit_depth == 8 && + (cp->tend.format & PNG_FORMAT_FLAG_COLORMAP) == 0); + + /* Remove all the transforms between start(inclusive) and end(exclusive); + * they have been processed. The effect they had on the transform control + * is irrelevant because the caller re-instates the settings from tstart. + */ + { + png_transformp list = *cp->start; /* list to free */ + + *cp->start = *cp->end; /* part of list not to be freed */ + *cp->end = NULL; /* terminate the list to be freed */ + cp->end = cp->start; /* else cp->end points to the end of the list! */ + + png_transform_free(png_ptr, &list); + } + + /* Adjust the PNG palette and, if required, the tRNS entries. Note that + * if the transforms stripped the alpha channel from the palette num_trans + * will get set to 0 here. + * + * This is the point where the gamma gets frozen too. The alternative + * design is to pass palette, tRNS and gamma up the transform chain, but + * that doesn't work because the palette change would, apparently, have to + * be repeated on each row. This seems simpler at the cost of a little + * obscurity; the answer to the question, "Where does the palette get + * updated?", is "Here!" + * + * API CHANGE: (fix): previously the init code would silently overwrite + * the palette information shared with png_info, breaking the API for + * png_read_update_info, which doesn't update the info if it isn't called, + * by changing the palette and maybe tRNS when the first row was read! + * + * NOTE: PNG_FORMAT_FLAG_RANGE is lost at this point, even if the palette + * entries were shifted or inverted. This could be fixed, but it would + * complicate the libpng API to expose the information. + */ + png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr, + PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)))); + png_ptr->free_me |= PNG_FREE_PLTE; + + /* Write the transformed palette: */ + { + png_colorp palette = png_ptr->palette; + png_const_bytep p; + const int is_color = (cp->tend.format & PNG_FORMAT_FLAG_COLOR) != 0; + unsigned int i; +# ifdef PNG_READ_tRNS_SUPPORTED + unsigned int num_trans = 0; + const int do_trans = (cp->tend.format & PNG_FORMAT_FLAG_ALPHA) != 0; + png_byte trans_alpha[PNG_MAX_PALETTE_LENGTH]; +# endif /* READ_tRNS */ + + for (i=0, p=cache.b8; itend.width; ++i) + { + if (is_color) + { + palette[i].red = *p++; + palette[i].green = *p++; + palette[i].blue = *p++; + } + + else + palette[i].blue = palette[i].green = palette[i].red = *p++; + +# ifdef PNG_READ_tRNS_SUPPORTED + if (do_trans) + { + png_byte a = *p++; + trans_alpha[i] = a; + + /* Strip opaque entries from the end: */ + if (a < 0xFFU) + num_trans = i+1; + } +# endif /* READ_tRNS */ + } + + png_ptr->num_palette = png_check_bits(png_ptr, cp->tend.width, 9); + +# ifdef PNG_READ_tRNS_SUPPORTED + if (num_trans > 0) + { + png_ptr->trans_alpha = png_voidcast(png_bytep, png_malloc(png_ptr, + PNG_MAX_PALETTE_LENGTH)); + png_ptr->free_me |= PNG_FREE_TRNS; + memcpy(png_ptr->trans_alpha, trans_alpha, num_trans); + memset(png_ptr->trans_alpha+num_trans, 0xFFU, + PNG_MAX_PALETTE_LENGTH-num_trans); + png_ptr->num_trans = png_check_bits(png_ptr, num_trans, 9); + } +# endif /* READ_tRNS */ + } + + /* NOTE: the caller sets cp->start to cp->end and cp->tend to cp->tstart, + * this causes processing to continue with the palette format and the + * first unprocessed transform. The reset of the transform control loses the + * gamma information as well, of course, as any information about the palette + * and tRNS changes (such as the RANGE flags). + */ +} + +/* These structure and the save/restore routines that follow it exist to save + * data from a png_transform_control that is specific to the sample encoding of + * the PNG data, rather than the row format itself. + */ +typedef struct +{ +# ifdef PNG_READ_GAMMA_SUPPORTED + png_fixed_point gamma; +# endif + png_byte sBIT_R; + png_byte sBIT_G; + png_byte sBIT_B; + png_byte sBIT_A; /* Signnificant bits in the row channels. */ + unsigned int invalid_info; /* PNG_INFO_* for invalidated chunks */ +} png_tc_channel_data; + +static void +save_cp_channel_data(png_tc_channel_data *save, png_const_transform_controlp tc) +{ +# ifdef PNG_READ_GAMMA_SUPPORTED + save->gamma = tc->gamma; +# endif /* READ_GAMMA */ + + /* The sBIT information and the list of invalidated chunks must also be + * preserved: + */ + save->sBIT_R = tc->sBIT_R; + save->sBIT_G = tc->sBIT_G; + save->sBIT_B = tc->sBIT_B; + save->sBIT_A = tc->sBIT_A; + save->invalid_info = tc->invalid_info; +} + +static void +restore_cp_channel_data(png_transform_controlp tc, + const png_tc_channel_data *save) + /* Reverse the above */ +{ +# ifdef PNG_READ_GAMMA_SUPPORTED + tc->gamma = save->gamma; +# endif /* READ_GAMMA */ + + tc->sBIT_R = save->sBIT_R; + tc->sBIT_G = save->sBIT_G; + tc->sBIT_B = save->sBIT_B; + tc->sBIT_A = save->sBIT_A; + tc->invalid_info = save->invalid_info; +} + +static void +make_cache(png_structp png_ptr, png_cache_paramsp cp, unsigned int max_depth) +{ + /* At present the cache is just a byte lookup table. We need the original + * pixel depth to work out how big the working buffer needs to be. + */ + unsigned int ipd = PNG_TC_PIXEL_DEPTH(cp->tstart); + unsigned int opd = PNG_TC_PIXEL_DEPTH(cp->tend); + unsigned int order; /* records position of start transform */ + unsigned int width; /* width of cache in pixels */ + png_tc_channel_data save; /* Record of the final channel info */ + union + { + png_uint_32 u32[1]; + png_uint_16 u16[1]; /* For alignment */ + png_byte b8[8*256]; /* For 16-bit RGBA */ + } cache; + + debug(cp->tend.init == PNG_TC_INIT_FINAL); + affirm(opd <= 64 && max_depth <= 64); /* or the cache is not big enough */ + + if ((cp->tstart.format & PNG_FORMAT_FLAG_COLORMAP) != 0) + width = setup_palette_cache(png_ptr, cache.b8); + + else switch (ipd) + { + /* The input to the cache is the full range of possible pixel values: */ + case 1: + /* 2 1-bit pixels, MSB first */ + cache.b8[0] = 0x40U; + width = 2; + break; + + case 2: + /* 4 2-bit pixels, MSB first */ + cache.b8[0] = 0x1BU; + width = 4; + break; + + case 4: + /* 16 4-bit pixels, MSB first */ + cache.b8[0] = 0x01U; + cache.b8[1] = 0x23U; + cache.b8[2] = 0x45U; + cache.b8[3] = 0x67U; + cache.b8[4] = 0x89U; + cache.b8[5] = 0xABU; + cache.b8[6] = 0xCDU; + cache.b8[7] = 0xEFU; + width = 16; + break; + + case 8: + /* 256 8-bit pixels */ + { + unsigned int i; + + for (i=0; i<256; ++i) + cache.b8[i] = PNG_BYTE(i); + } + width = 256; + break; + + default: + impossible("cache input bit depth"); + } + + /* Reset the transform control to run the transforms on this data, but save + * the channel info because the row processing functions do not always + * write it. + */ + save_cp_channel_data(&save, &cp->tend); + cp->tend = cp->tstart; + init_caching(png_ptr, cp); + /* And set tend to work out the result of transforming each possible pixel + * value: + */ + cp->tend.init = 0U; + cp->tend.width = width; + cp->tend.sp = cache.b8; + cp->tend.dp = cache.b8; + + { + unsigned int check_depth = + png_run_this_transform_list_forwards(&cp->tend, cp->start, cp->end); + + /* This must not change: */ + affirm(PNG_TC_PIXEL_DEPTH(cp->tend) == opd && check_depth == max_depth); + } + + /* Restore the potentially lost channel data. */ + restore_cp_channel_data(&cp->tend, &save); + + /* This is all the information required to cache the set of transforms + * between 'start' and 'end'. We take the transformed pixels and make a + * cache transform of them. The cache transform skips the work, transforms + * the row, and sets the tranform_control to (a copy of) cp->tend. + * + * Remove all the transforms between start(inclusive) and end(exclusive); + * they have been processed. The effect they had on the transform control + * is irrelevant because the caller re-instates the settings from tstart. + */ + { + png_transformp list = *cp->start; /* list to free */ + + *cp->start = *cp->end; /* part of list not to be freed */ + *cp->end = NULL; /* terminate the list to be freed */ + cp->end = NULL; /* reset below */ + + order = list->order; /* used below when adding the cache transform */ + png_transform_free(png_ptr, &list); + } + + /* Make the required cache, as enumerated above there are 22 possibilities, + * this selects between them, fixes up the cache for the 'byte' cases (where + * multiple pixels can be handled byte-by-byte) and selects the correct + * transform function. + */ + if (ipd == opd) + { + /* We already know that ipd is <= 8 bits, so we can expand this case to + * the byte transform. The complexity is that for ipd < 8 bits we only + * have information for individual pixel values and these may be + * pixel-swapped within the byte. + */ + if (ipd < 8) + { + const int lsb = (cp->tend.format & PNG_FORMAT_FLAG_SWAPPED) != 0; + unsigned int ishift, b; + png_byte bcache[256]; + + switch (ipd) + { + case 1: ishift = 3U; break; + case 2: ishift = 2U; break; + case 4: ishift = 1U; break; + default: impossible("ipd"); + } + + /* Work out the right answer for each byte of pixels: */ + for (b=0U; b<256U; ++b) + { + unsigned int o = 0U; /* output byte */ + unsigned int p = 8U; /* right shift to find input pixel */ + + do + { + unsigned int q = ((1U<> (p-=ipd)); + /* The input pixel. For a palette this value might be outside + * the range of palette indices, in which case simply insert + * '0': + */ + if (q < width) + { + unsigned int r = cache.b8[q >> ishift]; + r >>= ((lsb ? q : ~q) & ((1U<tend); + + affirm(cachebytes <= sizeof cache.b8); + + if (cachebytes < size) + memset(cache.b8+cachebytes, 0, size - cachebytes); + + add_cache_transform(png_ptr, order, fn, cp, cache.b8, size); + } + } + + /* Because a transform was inserted cp->end needs to be set to the new + * pointer to the original end. add_cache_transform sets cp->start to this, + * so: + */ + cp->end = cp->start; + + /* This invalidates the palette if that is what was cached because the + * palette and, if present, tRNS chunk did not get updated above. + */ + if (cp->tstart.palette) + png_remove_PLTE_and_tRNS(png_ptr); +} + +static void restore_cp(png_cache_paramsp cp) +{ + /* A utility to restore cp->tstart by copying it into cp->tend. This is used + * both in the palette case when restoring the transform control for the + * indexed data and in the case where no transforms were cached. It + * preserves the color-channel-specific data from cp->tend because in either + * case it is possible for this data to be modified without preserving any + * transforms, e.g. if only the gamma is changed but no gamma transform is + * retained because the change was not significant. + */ + png_tc_channel_data save; + + save_cp_channel_data(&save, &cp->tend); + cp->tend = cp->tstart; + restore_cp_channel_data(&cp->tend, &save); +} + +static void +handle_cache(png_structp png_ptr, png_cache_paramsp cp, unsigned int max_depth) +{ + /* There is nothing to do if there are no transforms between 'start' and + * 'end': + */ + if (cp->start != cp->end) + { + only_deb(png_transformp tr_check = *cp->end;) + + /* libpng doesn't currently implement any pixel size of more than 64 bits + * so: + */ + affirm(max_depth <= 64); + + if (cp->tend.palette) + { + /* The transforms being cached apply to the palette, the following + * transforms will apply to the original index data and the transformed + * data must be used to update the palette: + */ + if (cp->tend.init == PNG_TC_INIT_FINAL) + update_palette(png_ptr, cp, max_depth); + + cp->start = cp->end; + restore_cp(cp); /* reset to palette data */ + } + + else + { + /* Continue with the transform control in cp.tend; even if there was + * palette data in cp.tstart it has been expanded. + */ + if (cp->tend.init == PNG_TC_INIT_FINAL) + make_cache(png_ptr, cp, max_depth); + + cp->tstart = cp->tend; /* keep current context */ + } + + debug(tr_check == *cp->end); + } + + else /* no transforms cached */ + restore_cp(cp); /* removes any palette caching info */ +} + +#ifdef PNG_READ_tRNS_SUPPORTED +static void +check_tRNS_for_alpha(png_structrp png_ptr) +{ + unsigned int num_trans = png_ptr->num_trans; + + debug(png_ptr->color_type == PNG_COLOR_TYPE_PALETTE); + + while (num_trans > 0) + { + { + const png_byte trans = png_ptr->trans_alpha[--num_trans]; + + if (trans == 0xFFU) + continue; + + if (trans > 0U) + return; /* Palette has at least one entry >0, <0xff */ + } + + /* There is some point to the tRNS chunk; it has a non-opaque entry, this + * code could truncate it but there is no obvious performance advantage to + * doing this. + */ + while (num_trans > 0) + { + const png_byte trans = png_ptr->trans_alpha[--num_trans]; + + if (trans > 0U && trans < 0xFFU) + return; + } + + /* Here if the above did not find an entry >0 && <0xFFU but did find a + * transparent entry (0u). Record this. + */ + png_ptr->transparent_palette = 1U; + return; + } + + /* All entries opaque; remove the tRNS data: */ + png_ptr->num_trans = 0U; +} +#endif /* READ_tRNS */ + +unsigned int /* PRIVATE */ +png_read_init_transform_mech(png_structp png_ptr, png_transform_controlp tc) + /* This is called once for each init stage (PNG_TC_INIT_FORMAT and + * PNG_TC_INIT_FINAL) to run the transform list forwards, returning the + * maximum depth required to process the row. It handles caching of the + * transforms and the processing of the palette for color-mapped PNG data. + */ +{ + png_transformp *list = &png_ptr->transform_list; + unsigned int max_depth = 0U; + png_cache_params cp; + + /* PNG color-mapped data must be handled here so that the palette is updated + * correctly. png_set_palette_to_rgb causes the palette flag to be removed + * from the transform control but does no other change. png_set_quantize + * causes 8-bit RGB, RGBA or palette data to be converted into palette + * indices, setting the palette flag. + */ +# ifdef PNG_READ_tRNS_SUPPORTED + /* This happens once at the start to find out if the tRNS chunk consisted + * entirely of opaque (255) and/or transparent (0) entries. + */ + if (tc->init == PNG_TC_INIT_FORMAT && + png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + check_tRNS_for_alpha(png_ptr); +# endif /* READ_tRNS */ + cp.end = cp.start = list; + cp.tend = cp.tstart = *tc; + init_caching(png_ptr, &cp); + + while (*cp.end != NULL) + { + png_transformp tr = *cp.end; + + /* The user transform cannot be cached. */ + if (tr->order >= PNG_TR_USER) + break; + + /* If the 'palette' flag is set and the next transform has order + * PNG_TR_ENCODING or later cache the results so far and continue with the + * original palette data (cp.tstart). + */ + if (cp.tend.palette && tr->order >= PNG_TR_ENCODING) + { + handle_cache(png_ptr, &cp, max_depth); + + /* The cache handling function must maintain cp.end; */ + affirm(tr == *cp.end); + max_depth = PNG_TC_PIXEL_DEPTH(cp.tend); + } + + /* Now run the transform list entry: */ + if (tr->fn != NULL) + { + tr->fn(cp.end, &cp.tend); + tr = *cp.end; /* in case something was inserted */ + } + + if (tr->fn == NULL) /* delete this transform */ + png_remove_transform(png_ptr, cp.end); + + else + { + /* Handle the initialization of the maximum pixel depth. */ + unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(cp.tend); + + if (tc_depth > max_depth) + max_depth = tc_depth; + + /* Advance to the next transform. */ + cp.end = &tr->next; + } + } + + /* At the end if still caching record the cache information (this is common; + * this is generally the case for an expanded palette.) + */ + if (cp.tend.caching) + { + handle_cache(png_ptr, &cp, max_depth); + max_depth = PNG_TC_PIXEL_DEPTH(cp.tend); + } + + /* At the end run the init on the user transform: */ + if (*cp.end != NULL) + { + png_transformp tr = *cp.end; + affirm(tr->order == PNG_TR_USER); + if (tr->fn != NULL) + tr->fn(cp.end, &cp.tend); + /* This cannot insert anything, so: */ + affirm(tr == *cp.end && tr->next == NULL); + + if (tr->fn == NULL) /* delete this transform */ + png_remove_transform(png_ptr, cp.end); + + else + { + unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(cp.tend); + + if (tc_depth > max_depth) + max_depth = tc_depth; + } + } + + /* And write the input transform control: */ + *tc = cp.tend; + + return max_depth; } /* Modify the info structure to reflect the transformations. The @@ -3972,1733 +6534,63 @@ png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr) { png_debug(1, "in png_read_transform_info"); -#ifdef PNG_READ_EXPAND_SUPPORTED - if ((png_ptr->transformations & PNG_EXPAND) != 0) - { - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - { - /* This check must match what actually happens in - * png_do_expand_palette; if it ever checks the tRNS chunk to see if - * it is all opaque we must do the same (at present it does not.) + /* WARNING: this is very basic at present. It just updates the format + * information. It should update the palette (and will eventually) as well + * as invalidating chunks that the transforms break. + */ +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + info_ptr->format = png_ptr->row_format; + info_ptr->bit_depth = png_ptr->row_bit_depth; +# ifdef PNG_READ_GAMMA_SUPPORTED + /* If an info struct is used with a different png_ptr in a call to + * png_set_gAMA then the png_struct information won't be updated, this + * doesn't matter on write, but don't zap the value in the info on read + * unless it is known: + * + * TODO: review this whole mess. */ - if (png_ptr->num_trans > 0) - info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; - - else - info_ptr->color_type = PNG_COLOR_TYPE_RGB; - - info_ptr->bit_depth = 8; - info_ptr->num_trans = 0; - - if (png_ptr->palette == NULL) - png_error (png_ptr, "Palette is NULL in indexed image"); - } - else - { - if (png_ptr->num_trans != 0) - { - if ((png_ptr->transformations & PNG_EXPAND_tRNS) != 0) - info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; - } - if (info_ptr->bit_depth < 8) - info_ptr->bit_depth = 8; - - info_ptr->num_trans = 0; - } - } -#endif - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) - /* The following is almost certainly wrong unless the background value is in - * the screen space! - */ - if ((png_ptr->transformations & PNG_COMPOSE) != 0) - info_ptr->background = png_ptr->background; -#endif - -#ifdef PNG_READ_GAMMA_SUPPORTED - /* The following used to be conditional on PNG_GAMMA (prior to 1.5.4), - * however it seems that the code in png_init_read_transformations, which has - * been called before this from png_read_update_info->png_read_start_row - * sometimes does the gamma transform and cancels the flag. - * - * TODO: this looks wrong; the info_ptr should end up with a gamma equal to - * the screen_gamma value. The following probably results in weirdness if - * the info_ptr is used by the app after the rows have been read. - */ - info_ptr->colorspace.gamma = png_ptr->colorspace.gamma; -#endif - - if (info_ptr->bit_depth == 16) - { -# ifdef PNG_READ_16BIT_SUPPORTED -# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED - if ((png_ptr->transformations & PNG_SCALE_16_TO_8) != 0) - info_ptr->bit_depth = 8; + if (png_ptr->row_gamma > 0) + info_ptr->colorspace.gamma = png_ptr->row_gamma; # endif -# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED - if ((png_ptr->transformations & PNG_16_TO_8) != 0) - info_ptr->bit_depth = 8; -# endif + /* Invalidate chunks marked as invalid: */ +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + info_ptr->valid &= ~png_ptr->invalid_info; -# else - /* No 16 bit support: force chopping 16-bit input down to 8, in this case - * the app program can chose if both APIs are available by setting the - * correct scaling to use. - */ -# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED - /* For compatibility with previous versions use the strip method by - * default. This code works because if PNG_SCALE_16_TO_8 is already - * set the code below will do that in preference to the chop. + /* If the palette or tRNS chunk was changed copy them over to the info + * structure; this may actually re-validate the PLTE or tRNS chunks, + * but only if png_ptr has a new version, otherwise the invalid_info + * settings from above can still invalidate the chunk. */ - png_ptr->transformations |= PNG_16_TO_8; - info_ptr->bit_depth = 8; -# else - -# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED - png_ptr->transformations |= PNG_SCALE_16_TO_8; - info_ptr->bit_depth = 8; -# else - - CONFIGURATION ERROR: you must enable at least one 16 to 8 method -# endif -# endif -#endif /* !READ_16BIT */ - } - -#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED - if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0) - info_ptr->color_type = png_check_byte(png_ptr, info_ptr->color_type | - PNG_COLOR_MASK_COLOR); -#endif - -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0) - info_ptr->color_type = png_check_byte(png_ptr, info_ptr->color_type & - ~PNG_COLOR_MASK_COLOR); -#endif - -#ifdef PNG_READ_QUANTIZE_SUPPORTED - if ((png_ptr->transformations & PNG_QUANTIZE) != 0) - { - if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || - (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && - png_ptr->palette_lookup != 0 && info_ptr->bit_depth == 8) - { - info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; - } - } -#endif - -#ifdef PNG_READ_EXPAND_16_SUPPORTED - if ((png_ptr->transformations & PNG_EXPAND_16) != 0 && - info_ptr->bit_depth == 8 && - info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) - { - info_ptr->bit_depth = 16; - } -#endif - -#ifdef PNG_READ_PACK_SUPPORTED - if ((png_ptr->transformations & PNG_PACK) != 0 && - (info_ptr->bit_depth < 8)) - info_ptr->bit_depth = 8; -#endif - - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - info_ptr->channels = 1; - - else if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) - info_ptr->channels = 3; - - else - info_ptr->channels = 1; - -#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED - if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0) - { - info_ptr->color_type = png_check_byte(png_ptr, info_ptr->color_type & - ~PNG_COLOR_MASK_ALPHA); - info_ptr->num_trans = 0; - } -#endif - - if ((info_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0) - info_ptr->channels++; - -#ifdef PNG_READ_FILLER_SUPPORTED - /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ - if ((png_ptr->transformations & PNG_FILLER) != 0 && - (info_ptr->color_type == PNG_COLOR_TYPE_RGB || - info_ptr->color_type == PNG_COLOR_TYPE_GRAY)) - { - info_ptr->channels++; - /* If adding a true alpha channel not just filler */ - if ((png_ptr->transformations & PNG_ADD_ALPHA) != 0) - info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; - } -#endif - -#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ -defined(PNG_READ_USER_TRANSFORM_SUPPORTED) - if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0) - { - if (info_ptr->bit_depth < png_ptr->user_transform_depth) - info_ptr->bit_depth = png_ptr->user_transform_depth; - - if (info_ptr->channels < png_ptr->user_transform_channels) - info_ptr->channels = png_ptr->user_transform_channels; - } -#endif - - info_ptr->pixel_depth = png_check_byte(png_ptr, info_ptr->channels * - info_ptr->bit_depth); - - info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width); - - /* Adding in 1.5.4: cache the above value in png_struct so that we can later - * check in png_rowbytes that the user buffer won't get overwritten. Note - * that the field is not always set - if png_read_update_info isn't called - * the application has to either not do any transforms or get the calculation - * right itself. - */ - png_ptr->info_rowbytes = info_ptr->rowbytes; - -#ifndef PNG_READ_EXPAND_SUPPORTED - if (png_ptr != NULL) - return; -#endif -} - -#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 - * the numbers 0 or 1. If you would rather they contain 0 and 255, use - * png_do_shift() after this. - */ -static void -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) - { - switch (row_info->bit_depth) - { - case 1: + if (png_ptr->palette != info_ptr->palette) { - 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 = (*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; - - row_info->flags |= PNG_BITS_SHIFTED; - row_info->bit_depth = 8; - dp = row + png_transform_rowbytes(row_info); - - while (dp > row) - { - *--dp = (*sp >> shift) & 0x03; - 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->flags |= PNG_BITS_SHIFTED; - row_info->bit_depth = 8; - dp = row + png_transform_rowbytes(row_info); - - while (dp > row) - { - *--dp = (*sp >> shift) & 0x0f; - shift = 0x7 & (shift+4); - if (shift == 0) - --sp; - } - - debug(dp == row && shift == 0 && sp == row-1); - break; - } - - default: - break; - } - } -# undef png_ptr -} -#endif /* READ_PACK || READ_EXPAND */ - -#ifdef PNG_READ_SHIFT_SUPPORTED -/* Reverse the effects of png_do_shift. This routine merely shifts the - * pixels back to their significant bits values. Thus, if you have - * a row of bit depth 8, but only 5 are significant, this will shift - * the values back to 0 through 31. - */ -static void -png_do_unshift(png_transform_controlp row_info, png_bytep row) -{ - png_debug(1, "in png_do_unshift"); - - /* The palette case has already been handled in the _init routine. */ - if (!(row_info->flags & PNG_INDEXED)) - { - 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 (row_info->channels > 2) /* at least three channels: color */ - { - 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 - png_ptr->shift.gray; - } - - if (row_info->channels > channels) /* one more channel: alpha */ - shift[channels++] = bit_depth - png_ptr->shift.alpha; - - debug(row_info->channels == channels); - - { - unsigned int c, have_shift; - - for (c = have_shift = 0; c < channels; ++c) - { - /* A shift of more than the bit depth is an error condition but it - * gets ignored here. - */ - if (shift[c] <= 0 || shift[c] >= bit_depth) - shift[c] = 0; - - else - have_shift = 1; - } - - if (have_shift == 0) - return; - } - - switch (bit_depth) - { - default: - /* Must be 1bpp gray: should not be here! */ - impossible("unshift bit depth"); - /* NOTREACHED */ - break; - - case 2: - /* Must be 2bpp gray */ - debug(channels == 1 && shift[0] == 1); - { - png_bytep bp = row; - png_bytep bp_end = bp + png_transform_rowbytes(row_info); - - while (bp < bp_end) - *bp = (*bp >> 1) & 0x55, ++bp; - - row_info->flags |= PNG_BITS_SHIFTED; - break; - } - - case 4: - /* Must be 4bpp gray */ - debug(channels == 1); - { - png_bytep bp = row; - 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; /* <= 8 bits */ - - while (bp < bp_end) - *bp = (png_byte)/*SAFE*/((*bp >> gray_shift) & mask), ++bp; - - row_info->flags |= PNG_BITS_SHIFTED; - break; - } - - case 8: - /* Single byte components, G, GA, RGB, RGBA */ - { - png_bytep bp = row; - png_bytep bp_end = bp + png_transform_rowbytes(row_info); - unsigned int channel = 0; - - while (bp < bp_end) - { - *bp = (png_byte)/*SAFE*/(*bp >> shift[channel]), ++bp; - if (++channel >= channels) - channel = 0; - } - - row_info->flags |= PNG_BITS_SHIFTED; - break; - } - - case 16: - /* Double byte components, G, GA, RGB, RGBA */ - { - png_bytep bp = row; - png_bytep bp_end = bp + png_transform_rowbytes(row_info); - unsigned int channel = 0; - - while (bp < bp_end) - { - unsigned int value = bp[0]; - - value = (value << 8) + bp[1]; /* <= 16 bits */ - value >>= shift[channel]; - if (++channel >= channels) - channel = 0; - *bp++ = (png_byte)/*SAFE*/(value >> 8); - *bp++ = PNG_BYTE(value); - } - - row_info->flags |= PNG_BITS_SHIFTED; - break; - } - } - } -} -#endif - -#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_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_const_bytep sp = row; /* source */ - png_bytep dp = row; /* destination */ - png_bytep ep = dp + png_transform_rowbytes(row_info); /* end+1 */ - - while (sp < ep) - { - /* The input is an array of 16 bit components, these must be scaled to - * 8 bits each. For a 16 bit value V the required value (from the PNG - * specification) is: - * - * (V * 255) / 65535 - * - * This reduces to round(V / 257), or floor((V + 128.5)/257) - * - * Represent V as the two byte value vhi.vlo. Make a guess that the - * result is the top byte of V, vhi, then the correction to this value - * is: - * - * error = floor(((V-vhi.vhi) + 128.5) / 257) - * = floor(((vlo-vhi) + 128.5) / 257) - * - * This can be approximated using integer arithmetic (and a signed - * shift): - * - * error = (vlo-vhi+128) >> 8; - * - * The approximate differs from the exact answer only when (vlo-vhi) is - * 128; it then gives a correction of +1 when the exact correction is - * 0. This gives 128 errors. The exact answer (correct for all 16 bit - * input values) is: - * - * error = (vlo-vhi+128)*65535 >> 24; - * - * An alternative arithmetic calculation which also gives no errors is: - * - * (V * 255 + 32895) >> 16 - */ - png_int_32 tmp = *sp++; /* must be signed! */ - tmp += ((*sp++ - tmp + 128) * 65535) >> 24; - *dp++ = png_check_byte(png_ptr, tmp); - } - - row_info->bit_depth = 8; - } -# undef png_ptr -} -#endif - -#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED -static void -/* Simply discard the low byte. This was the default behavior prior - * to libpng-1.5.4. - */ -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_const_bytep sp = row; /* source */ - png_const_bytep ep = sp + png_transform_rowbytes(row_info); /* end+1 */ - png_bytep dp = row; /* destination */ - - while (sp < ep) - { - *dp++ = *sp; - sp += 2; /* skip low byte */ - } - - row_info->bit_depth = 8; - } -# undef png_ptr -} -#endif - -#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED -static void -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)) - { - if (row_info->channels == 4) - { - /* This converts from RGBA to ARGB */ - if (row_info->bit_depth == 8) - { - png_bytep dp = row + png_transform_rowbytes(row_info); - - while (dp >= row+4/*safe*/) - { - 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; - } - - /* This converts from RRGGBBAA to AARRGGBB */ - else - { - png_bytep dp = row + png_transform_rowbytes(row_info); - - while (dp >= row+8/*safe*/) - { - 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; - } - } - - else if (row_info->channels == 2) - { - /* This converts from GA to AG */ - if (row_info->bit_depth == 8) - { - png_bytep dp = row + png_transform_rowbytes(row_info); - - while (dp >= row+1/*safe*/) - { - png_byte alpha = *--dp; - - *dp = dp[-1], --dp; - *dp = alpha; - } - - debug(dp == row); - row_info->flags ^= PNG_ALPHA_SWAPPED; - } - - /* This converts from GGAA to AAGG */ - else - { - png_bytep dp = row + png_transform_rowbytes(row_info); - - while (dp >= row+4/*safe*/) - { - 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; - } - } - } -# undef png_ptr -} -#endif - -#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED -static void -png_do_read_invert_alpha(png_transform_controlp row_info, png_bytep row) -{ - png_debug(1, "in png_do_read_invert_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 fourth channel in RGBA */ - png_bytep ep = row + png_transform_rowbytes(row_info); - - for (row += 3; row < ep; row += 4) - *row ^= 0xff; - - row_info->flags ^= PNG_ALPHA_INVERTED; - } - -#ifdef PNG_READ_16BIT_SUPPORTED - /* This inverts the alpha channel in RRGGBBAA */ - else - { - /* Need 2 bytes for each pixel, so subtract 1 from ep here: */ - png_bytep ep = row + png_transform_rowbytes(row_info) - 1; - - for (row += 6; row < ep; row += 8) - { - row[0] ^= 0xff; - row[1] ^= 0xff; - } - - row_info->flags ^= PNG_ALPHA_INVERTED; - } -#endif - } - else if (row_info->channels == 2) - { - if (row_info->bit_depth == 8) - { - /* This inverts the alpha channel in GA */ - png_bytep ep = row + png_transform_rowbytes(row_info); - - 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 */ - /* Need 2 bytes for each pixel, so subtract 1 from ep here: */ - png_bytep ep = row + png_transform_rowbytes(row_info) - 1; - - for (row += 2; row < ep; row += 4) - { - 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 to 1 and 3 channel non-indexed data */ -static void -png_do_read_filler(png_transform_controlp row_info, png_bytep row) -{ - png_debug(1, "in png_do_read_filler"); - - /* 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) - { - 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) - { - if (row_info->channels == 2) - { - while (dp >= row+2) + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); + info_ptr->palette = png_ptr->palette; + info_ptr->num_palette = png_ptr->num_palette; + if (info_ptr->palette != NULL && info_ptr->num_palette > 0) + info_ptr->valid |= PNG_INFO_PLTE; + +# ifdef PNG_READ_tRNS + /* ONLY do this if the palette was changed above because, in + * fact, the tRNS data is not shared (yes, this is inconsistent, + * perhaps fix it?) + */ + if ((info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) != 0 && + png_ptr->trans_alpha != info_ptr->trans_alpha) { - *--dp = fb; - *--dp = *--sp; + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); + /* NOTE: it is shared now! */ + info_ptr->trans_alpha = png_ptr->trans_alpha; + info_ptr->num_trans = png_ptr->num_trans; + if (info_ptr->trans_alpha != NULL && info_ptr->num_trans > 0) + info_ptr->valid |= PNG_INFO_tRNS; } - - 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); - } +# endif /* READ_tRNS */ } - - else /* filler before */ - { - if (row_info->channels == 2) - { - 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); - } - } - } - -# ifdef PNG_READ_16BIT_SUPPORTED - else /* bit_depth == 16 */ - { - /* 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) - { - if (row_info->channels == 2) - { - 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); - } - } - - else /* filler before */ - { - if (row_info->channels == 2) - { - 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); - } - } - - if (!(png_ptr->transformations & PNG_ADD_ALPHA)) - row_info->flags |= PNG_FILLER_IN_ALPHA; - } -# 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_transform_controlp row_info, png_bytep row) -{ - png_debug(1, "in png_do_gray_to_rgb"); - -# 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)) - { - 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 */ - while (sp > row) - { - *--dp = *--sp; - *--dp = *sp; - *--dp = *sp; - } - - debug(dp == row && sp == row); - } - - else - { - /* This changes GG to RRGGBB */ - while (sp > row) - { - 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 - { - debug(row_info->channels == 4); - - if (row_info->bit_depth == 8) - { - /* This changes GA to RGBA */ - while (sp > row) - { - *--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 */ - while (sp > row) - { - *--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); - } - } - } -# undef png_ptr -} -#endif - -#ifdef PNG_READ_EXPAND_SUPPORTED -/* Expands a palette row to an RGB or RGBA row depending - * upon whether you supply trans and num_trans. - */ -static void -png_do_expand_palette(png_transform_controlp row_info, png_bytep row) -{ - png_debug(1, "in png_do_expand_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) - { - png_do_unpack(row_info, row); - debug(row_info->flags & PNG_BITS_SHIFTED); - row_info->flags &= ~PNG_BITS_SHIFTED; - } - - 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 */ - { - png_const_bytep trans_alpha = png_ptr->trans_alpha; - - row_info->channels = 4; - dp = row + png_transform_rowbytes(row_info); - - while (dp >= row+4) - { - const int index = *--sp; - - if (index < num_trans) - *--dp = trans_alpha[index]; - - else - *--dp = 0xff; - - 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(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_transform_controlp row_info, png_bytep row) -{ - 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)) - { - /* 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 = png_ptr->trans_color.gray; - - switch (row_info->bit_depth) - { - 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; - { - 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) - { - const png_byte g = *--sp; - - *--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; - - { - 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); - } - } - - debug(dp == row && sp == row); - } - break; - - default: - impossible("bit depth invalid"); - } - } - - 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) - { - 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); - - row_info->channels = 4; - 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); - } - } -} -#endif - -#ifdef PNG_READ_EXPAND_16_SUPPORTED -/* If the bit depth is 8 and the color type is not a palette type expand the - * whole row to 16 bits. Has no effect otherwise. - */ -static void -png_do_expand_16(png_transform_controlp row_info, png_bytep row) -{ - 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 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: - * - * (input / 255) * 65535 - * - * Which happens to be exactly input * 257 and this can be achieved - * simply by byte replication in place (copying backwards). - */ - 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; - - debug(dp == row && sp == row); - } -# undef png_ptr -} -#endif - -#ifdef PNG_READ_QUANTIZE_SUPPORTED -static void -png_do_quantize(png_transform_controlp row_info, png_bytep row) -{ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width=row_info->width; - - png_debug(1, "in png_do_quantize"); - - if (row_info->bit_depth == 8) - { - 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; - dp = row; - for (i = 0; i < row_width; i++) - { - r = *sp++; - g = *sp++; - b = *sp++; - - /* This looks real messy, but the compiler will reduce - * it down to a reasonable formula. For example, with - * 5 bits per color, we get: - * p = (((r >> 3) & 0x1f) << 10) | - * (((g >> 3) & 0x1f) << 5) | - * ((b >> 3) & 0x1f); - */ - p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & - ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << - (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | - (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & - ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << - (PNG_QUANTIZE_BLUE_BITS)) | - ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & - ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); - - *dp++ = palette_lookup[p]; - } - - row_info->flags |= PNG_INDEXED; - row_info->channels = 1; - } - - else if (row_info->channels == 4 && palette_lookup != NULL) - { - int r, g, b, p; - sp = row; - dp = row; - for (i = 0; i < row_width; i++) - { - r = *sp++; - g = *sp++; - b = *sp++; - sp++; - - p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & - ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << - (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | - (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & - ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << - (PNG_QUANTIZE_BLUE_BITS)) | - ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & - ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); - - *dp++ = palette_lookup[p]; - } - - row_info->flags |= PNG_INDEXED; - row_info->channels = 1; - } - - 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++) - { - *sp = quantize_lookup[*sp]; - } - } - } -} -#endif /* READ_QUANTIZE */ - -/* Transform the row. The order of transformations is significant, - * and is very touchy. If you add a transformation, take care to - * 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_in) -{ - png_transform_control display; - - png_debug(1, "in png_do_read_transformations"); - - 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 - * PNG_WARN_UNINITIALIZED_ROW removed. In 1.6 the new flag is set only for - * all transformations, however in practice the ROW_INIT always gets done on - * demand, if necessary. - */ - if ((png_ptr->flags & PNG_FLAG_DETECT_UNINITIALIZED) != 0 && - (png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) - { - /* Application has failed to call either png_read_start_image() or - * 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, "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 (display.flags & PNG_INDEXED) - png_do_expand_palette(&display, png_ptr->row_buf + 1); - - else - 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 && - (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(&display, png_ptr->row_buf + 1); - - if (rgb_error != 0) - { - png_ptr->rgb_to_gray_status=1; - if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == - PNG_RGB_TO_GRAY_WARN) - png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); - - if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == - PNG_RGB_TO_GRAY_ERR) - png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); - } - } -#endif - -/* From Andreas Dilger e-mail to png-implement, 26 March 1998: - * - * In most cases, the "simple transparency" should be done prior to doing - * gray-to-RGB, or you will have to test 3x as many bytes to check if a - * pixel is transparent. You would also need to make sure that the - * transparency information is upgraded to RGB. - * - * To summarize, the current flow is: - * - Gray + simple transparency -> compare 1 or 2 gray bytes and composite - * with background "in place" if transparent, - * convert to RGB if necessary - * - Gray + alpha -> composite with gray background and remove alpha bytes, - * convert to RGB if necessary - * - * To support RGB backgrounds for gray images we need: - * - Gray + simple transparency -> convert to RGB + simple transparency, - * compare 3 or 6 bytes and composite with - * background "in place" if transparent - * (3x compare/pixel compared to doing - * composite with gray bkgrnd) - * - Gray + alpha -> convert to RGB + alpha, composite with background and - * remove alpha bytes (3x float - * operations/pixel compared with composite - * on gray background) - * - * Greg's change will do this. The reason it wasn't done before is for - * performance, as this increases the per-pixel operations. If we would check - * in advance if the background was gray or RGB, and position the gray-to-RGB - * transform appropriately, then it would save a lot of work/time. - */ - -#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED - /* If gray -> RGB, do so now only if background is non-gray; else do later - * for performance reasons - */ - if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0 && - (png_ptr->flags & PNG_FLAG_BACKGROUND_IS_GRAY) == 0) - 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(&display, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_GAMMA_SUPPORTED - if ((png_ptr->transformations & PNG_GAMMA) != 0 && -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - /* Because RGB_TO_GRAY does the gamma transform. */ - (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0 && -#endif -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - 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 || - display.channels == 2 || display.channels == 4)) && -#endif - /* Because png_init_read_transformations transforms the palette, unless - * 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(&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 && - (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 && - (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(&display, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED - /* There is no harm in doing both of these because only one has any effect, - * by putting the 'scale' option first if the app asks for scale (either by - * calling the API or in a TRANSFORM flag) this is what happens. - */ - if ((png_ptr->transformations & PNG_16_TO_8) != 0) - 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(&display, png_ptr->row_buf + 1); -#endif /* READ_QUANTIZE */ - -#ifdef PNG_READ_EXPAND_16_SUPPORTED - /* Do the expansion now, after all the arithmetic has been done. Notice - * that previous transformations can handle the PNG_EXPAND_16 flag if this - * is efficient (particularly true in the case of gamma correction, where - * better accuracy results faster!) - */ - if ((png_ptr->transformations & PNG_EXPAND_16) != 0) - 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(&display, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_INVERT_SUPPORTED - if ((png_ptr->transformations & PNG_INVERT_MONO) != 0) - 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(&display, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_SHIFT_SUPPORTED - if ((png_ptr->transformations & PNG_SHIFT) != 0) - 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(&display, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED - /* Added at libpng-1.5.10 */ - 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(&display, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_PACKSWAP_SUPPORTED - if ((png_ptr->transformations & PNG_PACKSWAP) != 0) - 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(&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(&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(&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_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 */ - /* png_byte bit_depth; bit depth of samples */ - /* 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_in->bit_depth = png_ptr->user_transform_depth; - - if (png_ptr->user_transform_channels != 0) - row_info_in->channels = png_ptr->user_transform_channels; -#endif - row_info_in->pixel_depth = png_check_byte(png_ptr, - row_info_in->bit_depth * row_info_in->channels); - - row_info_in->rowbytes = - PNG_ROWBYTES(row_info_in->pixel_depth, row_info_in->width); - } -#endif +# endif /* READ_TRANSFORMS */ +# else /* !TRANSFORM_MECH */ + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +# endif /* !TRANSFORM_MECH */ } #endif /* READ_TRANSFORMS */ diff --git a/pngrutil.c b/pngrutil.c index 49132f002..6e6f643ae 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -127,45 +127,6 @@ png_read_sig(png_structrp png_ptr, png_inforp info_ptr) png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; } -/* Read the chunk header (length + type name). - * Put the type name into png_ptr->chunk_name, and return the length. - */ -png_uint_32 /* PRIVATE */ -png_read_chunk_header(png_structrp png_ptr) -{ - png_byte buf[8]; - png_uint_32 length; - -#ifdef PNG_IO_STATE_SUPPORTED - png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR; -#endif - - /* Read the length and the chunk name. - * This must be performed in a single I/O call. - */ - png_read_data(png_ptr, buf, 8); - length = png_get_uint_31(png_ptr, buf); - - /* Put the chunk name into png_ptr->chunk_name. */ - png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4); - - png_debug2(0, "Reading %lx chunk, length = %lu", - (unsigned long)png_ptr->chunk_name, (unsigned long)length); - - /* Reset the crc and run it over the chunk name. */ - png_reset_crc(png_ptr); - png_calculate_crc(png_ptr, buf + 4, 4); - - /* Check to see if chunk name is valid. */ - png_check_chunk_name(png_ptr, png_ptr->chunk_name); - -#ifdef PNG_IO_STATE_SUPPORTED - png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA; -#endif - - return length; -} - /* Read data, and (optionally) run it through the CRC. */ void /* PRIVATE */ png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length) @@ -177,6 +138,46 @@ png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length) png_calculate_crc(png_ptr, buf, length); } +/* Compare the CRC stored in the PNG file with that calculated by libpng from + * the data it has read thus far. + */ +static int +png_crc_error(png_structrp png_ptr) +{ + png_byte crc_bytes[4]; + png_uint_32 crc; + int need_crc = 1; + + if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)) + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC; +#endif + + /* The chunk CRC must be serialized in a single I/O call. */ + png_read_data(png_ptr, crc_bytes, 4); + + if (need_crc != 0) + { + crc = png_get_uint_32(crc_bytes); + return ((int)(crc != png_ptr->crc)); + } + + else + return (0); +} + /* Optionally skip data and then check the CRC. Depending on whether we * are reading an ancillary or critical chunk, and how the program has set * things up, we may calculate the CRC on the data and print a message. @@ -219,46 +220,6 @@ png_crc_finish(png_structrp png_ptr, png_uint_32 skip) return (0); } -/* Compare the CRC stored in the PNG file with that calculated by libpng from - * the data it has read thus far. - */ -int /* PRIVATE */ -png_crc_error(png_structrp png_ptr) -{ - png_byte crc_bytes[4]; - png_uint_32 crc; - int need_crc = 1; - - if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)) - { - if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == - (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) - need_crc = 0; - } - - else /* critical */ - { - if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) - need_crc = 0; - } - -#ifdef PNG_IO_STATE_SUPPORTED - png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC; -#endif - - /* The chunk CRC must be serialized in a single I/O call. */ - png_read_data(png_ptr, crc_bytes, 4); - - if (need_crc != 0) - { - crc = png_get_uint_32(crc_bytes); - return ((int)(crc != png_ptr->crc)); - } - - else - return (0); -} - #if defined(PNG_READ_iCCP_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) ||\ defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_READ_sCAL_SUPPORTED) ||\ defined(PNG_READ_sPLT_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) ||\ @@ -269,14 +230,13 @@ png_crc_error(png_structrp png_ptr) * it will call png_error (via png_malloc) on failure. (warn == 2 means * 'silent'). */ -static png_bytep +png_bytep /* PRIVATE */ png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn) { png_bytep buffer = png_ptr->read_buffer; if (buffer != NULL && new_size > png_ptr->read_buffer_size) { - png_ptr->read_buffer = NULL; png_ptr->read_buffer = NULL; png_ptr->read_buffer_size = 0; png_free(png_ptr, buffer); @@ -373,7 +333,10 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner) png_ptr->zstream.next_out = NULL; png_ptr->zstream.avail_out = 0; - if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) + /* If png_struct::zstream has been used before for decompression it does + * not need to be re-initialized, just reset. + */ + if (png_ptr->zstream.state != NULL) { # if PNG_ZLIB_VERNUM < 0x1240 ret = inflateReset(&png_ptr->zstream); @@ -389,16 +352,19 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner) # else ret = inflateInit2(&png_ptr->zstream, window_bits); # endif - - if (ret == Z_OK) - png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED; } - if (ret == Z_OK) + if (ret == Z_OK && png_ptr->zstream.state != NULL) + { png_ptr->zowner = owner; + png_ptr->zstream_ended = 0; + } else + { png_zstream_error(png_ptr, ret); + png_ptr->zstream_ended = 1; + } return ret; } @@ -408,28 +374,27 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner) # endif } -#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED -/* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to - * allow the caller to do multiple calls if required. If the 'finish' flag is - * set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must - * be returned or there has been a problem, otherwise Z_SYNC_FLUSH is used and - * Z_OK or Z_STREAM_END will be returned on success. +/* This is a wrapper for the zlib deflate call which will handle larger buffer + * sizes than uInt. The input is limited to png_uint_32, because invariably + * the input comes from a chunk which has a 31-bit length, the output can be + * anything that fits in a png_alloc_size_t. * - * The input and output sizes are updated to the actual amounts of data consumed - * or written, not the amount available (as in a z_stream). The data pointers - * are not changed, so the next input is (data+input_size) and the next - * available output is (output+output_size). + * This internal function sets png_struct::zstream_ended when the end of the + * decoded data has been encountered; this includes both a normal end and + * error conditions. */ static int -png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, - /* INPUT: */ png_const_bytep input, png_uint_32p input_size_ptr, - /* OUTPUT: */ png_bytep output, png_alloc_size_t *output_size_ptr) +png_zlib_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, + /* INPUT: */ png_const_bytep *next_in_ptr, png_uint_32p avail_in_ptr, + /* OUTPUT: */ png_bytep *next_out_ptr, png_alloc_size_t *avail_out_ptr) { if (png_ptr->zowner == owner) /* Else not claimed */ { int ret; - png_alloc_size_t avail_out = *output_size_ptr; - png_uint_32 avail_in = *input_size_ptr; + png_alloc_size_t avail_out = *avail_out_ptr; + png_uint_32 avail_in = *avail_in_ptr; + png_bytep output = *next_out_ptr; + png_const_bytep input = *next_in_ptr; /* zlib can't necessarily handle more than 65535 bytes at once (i.e. it * can't even necessarily handle 65536 bytes) because the type uInt is @@ -465,7 +430,6 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, * every ZLIB_IO_MAX input bytes. */ avail_in += png_ptr->zstream.avail_in; /* not consumed last time */ - avail = ZLIB_IO_MAX; if (avail_in < avail) @@ -476,7 +440,6 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, /* zlib OUTPUT BUFFER */ avail_out += png_ptr->zstream.avail_out; /* not written last time */ - avail = ZLIB_IO_MAX; /* maximum zlib can process */ if (output == NULL) @@ -516,13 +479,20 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, /* Update the input and output sizes; the updated values are the amount * consumed or written, effectively the inverse of what zlib uses. */ - if (avail_out > 0) - *output_size_ptr -= avail_out; + *avail_out_ptr = avail_out; + if (output != NULL) + *next_out_ptr = png_ptr->zstream.next_out; - if (avail_in > 0) - *input_size_ptr -= avail_in; + *avail_in_ptr = avail_in; + *next_in_ptr = png_ptr->zstream.next_in; + + /* Ensure png_ptr->zstream.msg is set, ret can't be Z_OK at this point. + */ + debug(ret != Z_OK); + + if (ret != Z_BUF_ERROR) + png_ptr->zstream_ended = 1; - /* Ensure png_ptr->zstream.msg is set (even in the success case!) */ png_zstream_error(png_ptr, ret); return ret; } @@ -531,15 +501,44 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, { /* This is a bad internal error. The recovery assigns to the zstream msg * pointer, which is not owned by the caller, but this is safe; it's only - * used on errors! + * used on errors! (The {next,avail}_{in,out} values are not changed.) */ png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed"); return Z_STREAM_ERROR; } } -/* - * Decompress trailing data in a chunk. The assumption is that read_buffer +#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED +/* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to + * allow the caller to do multiple calls if required. If the 'finish' flag is + * set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must + * be returned or there has been a problem, otherwise Z_SYNC_FLUSH is used and + * Z_OK or Z_STREAM_END will be returned on success. + * + * The input and output sizes are updated to the actual amounts of data consumed + * or written, not the amount available (as in a z_stream). The data pointers + * are not changed, so the next input is (data+input_size) and the next + * available output is (output+output_size). + */ +static int +png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, + /* INPUT: */ png_const_bytep input, png_uint_32p input_size_ptr, + /* OUTPUT: */ png_bytep output, png_alloc_size_t *output_size_ptr) +{ + png_uint_32 avail_in = *input_size_ptr; + png_alloc_size_t avail_out = *output_size_ptr; + int ret = png_zlib_inflate(png_ptr, owner, finish, + &input, &avail_in, &output, &avail_out); + + /* And implement the non-zlib semantics (the size values are updated to the + * amounts consumed and written, not the amount remaining.) + */ + *input_size_ptr -= avail_in; + *output_size_ptr -= avail_out; + return ret; +} + +/* Decompress trailing data in a chunk. The assumption is that read_buffer * points at an allocated area holding the contents of a chunk with a * trailing compressed part. What we get back is an allocated area * holding the original prefix part and an uncompressed version of the @@ -769,27 +768,56 @@ png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size, return Z_STREAM_ERROR; } } -#endif +#endif /* READ_iCCP */ + +/* Chunk handling error handlers and utilities: */ +/* Utility to read the chunk data from the start without processing it; + * a skip function. + */ +static void +png_handle_skip(png_structrp png_ptr) + /* Skip the entire chunk after the name,length header has been read: */ +{ + png_crc_finish(png_ptr, png_ptr->chunk_length); +} + +static void +png_handle_error(png_structrp png_ptr +# ifdef PNG_ERROR_TEXT_SUPPORTED + , png_const_charp error +# else +# define png_handle_error(pp,e) png_handle_error(pp) +# endif + ) + /* Handle an error detected immediately after the chunk header has been + * read; this skips the rest of the chunk data and the CRC then signals + * a *benign* chunk error. + */ +{ + png_handle_skip(png_ptr); + png_chunk_benign_error(png_ptr, error); +} + +static void +png_handle_bad_length(png_structrp png_ptr) +{ + png_handle_error(png_ptr, "invalid length"); +} /* Read and check the IDHR chunk */ -void /* PRIVATE */ -png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr) { png_byte buf[13]; png_uint_32 width, height; - png_byte bit_depth, color_type, compression_type, filter_type; + png_byte bit_depth, color_type, compression_type, filter_method; png_byte interlace_type; png_debug(1, "in png_handle_IHDR"); - if (png_ptr->mode & PNG_HAVE_IHDR) - png_chunk_error(png_ptr, "out of place"); - - /* Check the length */ - if (length != 13) - png_chunk_error(png_ptr, "invalid"); - - png_ptr->mode |= PNG_HAVE_IHDR; + /* Check the length (this is a chunk error; not benign) */ + if (png_ptr->chunk_length != 13) + png_chunk_error(png_ptr, "invalid length"); png_crc_read(png_ptr, buf, 13); png_crc_finish(png_ptr, 0); @@ -799,7 +827,7 @@ png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) bit_depth = buf[8]; color_type = buf[9]; compression_type = buf[10]; - filter_type = buf[11]; + filter_method = buf[11]; interlace_type = buf[12]; /* Set internal variables */ @@ -808,90 +836,34 @@ png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_ptr->bit_depth = bit_depth; png_ptr->interlaced = interlace_type; png_ptr->color_type = color_type; -#ifdef PNG_MNG_FEATURES_SUPPORTED - png_ptr->filter_type = filter_type; -#endif + png_ptr->filter_method = filter_method; png_ptr->compression_type = compression_type; - /* Find number of channels */ - switch (png_ptr->color_type) - { - default: /* invalid, png_set_IHDR calls png_error */ - case PNG_COLOR_TYPE_GRAY: - case PNG_COLOR_TYPE_PALETTE: - png_ptr->channels = 1; - break; - - case PNG_COLOR_TYPE_RGB: - png_ptr->channels = 3; - break; - - case PNG_COLOR_TYPE_GRAY_ALPHA: - png_ptr->channels = 2; - break; - - case PNG_COLOR_TYPE_RGB_ALPHA: - png_ptr->channels = 4; - break; - } - - /* Set up other useful info */ - png_ptr->pixel_depth = png_check_byte(png_ptr, png_ptr->bit_depth * - png_ptr->channels); - png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width); - png_debug1(3, "bit_depth = %d", png_ptr->bit_depth); - png_debug1(3, "channels = %d", png_ptr->channels); - png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes); png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, - color_type, interlace_type, compression_type, filter_type); + color_type, interlace_type, compression_type, filter_method); } /* Read and check the palette */ -void /* PRIVATE */ -png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr) { png_color palette[PNG_MAX_PALETTE_LENGTH]; + png_uint_32 length = png_ptr->chunk_length; int num, i; -#ifdef PNG_POINTER_INDEXING_SUPPORTED png_colorp pal_ptr; -#endif png_debug(1, "in png_handle_PLTE"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - /* Moved to before the 'after IDAT' check below because otherwise duplicate - * PLTE chunks are potentially ignored (the spec says there shall not be more - * than one PLTE, the error is not treated as benign, so this check trumps - * the requirement that PLTE appears before IDAT.) - */ - else if (png_ptr->mode & PNG_HAVE_PLTE) - png_chunk_error(png_ptr, "duplicate"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) - { - /* This is benign because the non-benign error happened before, when an - * IDAT was encountered in a color-mapped image with no PLTE. - */ - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - png_ptr->mode |= PNG_HAVE_PLTE; - if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "ignored in grayscale PNG"); + png_handle_error(png_ptr, "ignored in grayscale PNG"); return; } #ifndef PNG_READ_OPT_PLTE_SUPPORTED if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) { - png_crc_finish(png_ptr, length); + png_handle_skip(png_ptr); return; } #endif @@ -899,20 +871,15 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) { png_crc_finish(png_ptr, length); - - if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) - png_chunk_benign_error(png_ptr, "invalid"); - - else - png_chunk_error(png_ptr, "invalid"); - + png_chunk_report(png_ptr, "invalid length", + ((png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) ? PNG_CHUNK_ERROR : + PNG_CHUNK_FATAL)); return; } /* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */ - num = (int)length / 3; + num = (int)/*SAFE*/length / 3; -#ifdef PNG_POINTER_INDEXING_SUPPORTED for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) { png_byte buf[3]; @@ -922,18 +889,6 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) pal_ptr->green = buf[1]; pal_ptr->blue = buf[2]; } -#else - for (i = 0; i < num; i++) - { - png_byte buf[3]; - - png_crc_read(png_ptr, buf, 3); - /* Don't depend upon png_color being any order */ - palette[i].red = buf[0]; - palette[i].green = buf[1]; - palette[i].blue = buf[2]; - } -#endif /* If we actually need the PLTE chunk (ie for a paletted image), we do * whatever the normal CRC configuration tells us. However, if we @@ -943,9 +898,7 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) #ifndef PNG_READ_OPT_PLTE_SUPPORTED if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) #endif - { png_crc_finish(png_ptr, 0); - } #ifndef PNG_READ_OPT_PLTE_SUPPORTED else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ @@ -974,7 +927,7 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_chunk_warning(png_ptr, "CRC error"); } } -#endif +#endif /* READ_OPT_PLTE */ /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its * own copy of the palette. This has the side effect that when png_start_row @@ -1010,60 +963,45 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_chunk_benign_error(png_ptr, "tRNS must be after"); } -#endif +#endif /* READ_tRNS */ #ifdef PNG_READ_hIST_SUPPORTED if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0) png_chunk_benign_error(png_ptr, "hIST must be after"); -#endif +#endif /* READ_hIST */ #ifdef PNG_READ_bKGD_SUPPORTED if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0) png_chunk_benign_error(png_ptr, "bKGD must be after"); -#endif +#endif /* READ_bKGD */ } -void /* PRIVATE */ -png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr) { png_debug(1, "in png_handle_IEND"); - if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) - png_chunk_error(png_ptr, "out of place"); + png_crc_finish(png_ptr, png_ptr->chunk_length); - png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); - - png_crc_finish(png_ptr, length); - - if (length != 0) - png_chunk_benign_error(png_ptr, "invalid"); + /* Treat this as benign and terminate the PNG anyway: */ + if (png_ptr->chunk_length != 0) + png_chunk_benign_error(png_ptr, "invalid length"); PNG_UNUSED(info_ptr) } #ifdef PNG_READ_gAMA_SUPPORTED -void /* PRIVATE */ -png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr) { png_fixed_point igamma; png_byte buf[4]; png_debug(1, "in png_handle_gAMA"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) + if (png_ptr->chunk_length != 4) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - if (length != 4) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -1077,11 +1015,13 @@ png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma); png_colorspace_sync(png_ptr, info_ptr); } -#endif +#else +# define png_handle_gAMA NULL +#endif /* READ_gAMA */ #ifdef PNG_READ_sBIT_SUPPORTED -void /* PRIVATE */ -png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr) { unsigned int truelen, i; png_byte sample_depth; @@ -1089,20 +1029,9 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_sBIT"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); + png_handle_error(png_ptr, "duplicate"); return; } @@ -1114,14 +1043,14 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else { - truelen = png_ptr->channels; + truelen = PNG_CHANNELS(*png_ptr); sample_depth = png_ptr->bit_depth; + affirm(truelen <= 4); } - if (length != truelen || length > 4) + if (png_ptr->chunk_length != truelen) { - png_chunk_benign_error(png_ptr, "invalid"); - png_crc_finish(png_ptr, length); + png_handle_bad_length(png_ptr); return; } @@ -1157,31 +1086,22 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); } -#endif +#else +# define png_handle_sBIT NULL +#endif /* READ_sBIT */ #ifdef PNG_READ_cHRM_SUPPORTED -void /* PRIVATE */ -png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr) { png_byte buf[32]; png_xy xy; png_debug(1, "in png_handle_cHRM"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) + if (png_ptr->chunk_length != 32) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - if (length != 32) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -1208,7 +1128,7 @@ png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) xy.bluex == PNG_FIXED_ERROR || xy.bluey == PNG_FIXED_ERROR) { - png_chunk_benign_error(png_ptr, "invalid values"); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -1229,30 +1149,21 @@ png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 1/*prefer cHRM values*/); png_colorspace_sync(png_ptr, info_ptr); } -#endif +#else +# define png_handle_cHRM NULL +#endif /* READ_cHRM */ #ifdef PNG_READ_sRGB_SUPPORTED -void /* PRIVATE */ -png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr) { png_byte intent; png_debug(1, "in png_handle_sRGB"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) + if (png_ptr->chunk_length != 1) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - if (length != 1) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -1279,28 +1190,21 @@ png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) (void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent); png_colorspace_sync(png_ptr, info_ptr); } +#else +# define png_handle_sRGB NULL #endif /* READ_sRGB */ #ifdef PNG_READ_iCCP_SUPPORTED -void /* PRIVATE */ -png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr) /* Note: this does not properly handle profiles that are > 64K under DOS */ { png_const_charp errmsg = NULL; /* error message output, or no error */ + png_uint_32 length = png_ptr->chunk_length; int finished = 0; /* crc checked */ png_debug(1, "in png_handle_iCCP"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - /* Consistent with all the above colorspace handling an obviously *invalid* * chunk is just ignored, so does not invalidate the color space. An * alternative is to set the 'invalid' flags at the start of this routine @@ -1311,8 +1215,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ if (length < 9) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too short"); + png_handle_bad_length(png_ptr); return; } @@ -1336,7 +1239,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ read_length = 81; /* maximum */ if (read_length > length) - read_length = (uInt)length; + read_length = (uInt)/*SAFE*/length; png_crc_read(png_ptr, (png_bytep)keyword, read_length); length -= read_length; @@ -1384,7 +1287,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ if (png_icc_check_header(png_ptr, &png_ptr->colorspace, keyword, profile_length, profile_header, - png_ptr->color_type)) + (png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)) { /* Now read the tag table; a variable size buffer is * needed at this point, allocate one for the whole @@ -1554,13 +1457,16 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (errmsg != NULL) /* else already output */ png_chunk_benign_error(png_ptr, errmsg); } +#else +# define png_handle_iCCP NULL #endif /* READ_iCCP */ #ifdef PNG_READ_sPLT_SUPPORTED -void /* PRIVATE */ -png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr) /* Note: this does not properly handle chunks that are > 64K under DOS */ { + png_uint_32 length = png_ptr->chunk_length; png_bytep entry_start, buffer; png_sPLT_t new_palette; png_sPLT_entryp pp; @@ -1589,26 +1495,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) return; } } -#endif - - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - -#ifdef PNG_MAX_MALLOC_64K - if (length > 65535U) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too large to fit in memory"); - return; - } -#endif +#endif /* USER_LIMITS */ buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); if (buffer == NULL) @@ -1618,7 +1505,6 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) return; } - /* WARNING: this may break if size_t is less than 32 bits; it is assumed * that the PNG_MAX_MALLOC_64K test is enabled in this case, but this is a * potential breakage point if the types in pngconf.h aren't exactly right. @@ -1638,7 +1524,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* A sample depth should follow the separator, and we should be on it */ if (entry_start > buffer + length - 2) { - png_warning(png_ptr, "malformed sPLT chunk"); + png_chunk_benign_error(png_ptr, "malformed"); return; } @@ -1652,7 +1538,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* Integrity-check the data length */ if (data_length % entry_size) { - png_warning(png_ptr, "sPLT chunk has bad length"); + png_chunk_benign_error(png_ptr, "invalid length"); return; } @@ -1661,22 +1547,21 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (dl > max_dl) { - png_warning(png_ptr, "sPLT chunk too long"); + png_chunk_benign_error(png_ptr, "exceeds system limits"); return; } new_palette.nentries = (png_int_32)(data_length / entry_size); - new_palette.entries = (png_sPLT_entryp)png_malloc_warn( - png_ptr, new_palette.nentries * (sizeof (png_sPLT_entry))); + new_palette.entries = png_voidcast(png_sPLT_entryp, png_malloc_base( + png_ptr, new_palette.nentries * (sizeof (png_sPLT_entry)))); if (new_palette.entries == NULL) { - png_warning(png_ptr, "sPLT chunk requires too much memory"); + png_chunk_benign_error(png_ptr, "out of memory"); return; } -#ifdef PNG_POINTER_INDEXING_SUPPORTED for (i = 0; i < new_palette.nentries; i++) { pp = new_palette.entries + i; @@ -1699,31 +1584,6 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) pp->frequency = png_get_uint_16(entry_start); entry_start += 2; } -#else - pp = new_palette.entries; - - for (i = 0; i < new_palette.nentries; i++) - { - - if (new_palette.depth == 8) - { - pp[i].red = *entry_start++; - pp[i].green = *entry_start++; - pp[i].blue = *entry_start++; - pp[i].alpha = *entry_start++; - } - - else - { - pp[i].red = png_get_uint_16(entry_start); entry_start += 2; - pp[i].green = png_get_uint_16(entry_start); entry_start += 2; - pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; - pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; - } - - pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2; - } -#endif /* Discard all chunk data except the name and stash that */ new_palette.name = (png_charp)buffer; @@ -1732,30 +1592,27 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_free(png_ptr, new_palette.entries); } +#else +# define png_handle_sPLT NULL #endif /* READ_sPLT */ #ifdef PNG_READ_tRNS_SUPPORTED -void /* PRIVATE */ -png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr) { png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; png_debug(1, "in png_handle_tRNS"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); + /* libpng 1.7.0: this used to be a benign error, but it doesn't look very + * benign because it has security implications; libpng ignores the second + * tRNS, so if you can find something that ignores the first instead you + * can choose which image the user sees depending on the PNG decoder. + */ + png_crc_finish(png_ptr, png_ptr->chunk_length); + png_chunk_error(png_ptr, "duplicate"); return; } @@ -1763,10 +1620,9 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[2]; - if (length != 2) + if (png_ptr->chunk_length != 2) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -1779,14 +1635,13 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[6]; - if (length != 6) + if (png_ptr->chunk_length != 6) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } - png_crc_read(png_ptr, buf, length); + png_crc_read(png_ptr, buf, 6); png_ptr->num_trans = 1; png_ptr->trans_color.red = png_get_uint_16(buf); png_ptr->trans_color.green = png_get_uint_16(buf + 2); @@ -1795,31 +1650,26 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - if (!(png_ptr->mode & PNG_HAVE_PLTE)) - { - /* TODO: is this actually an error in the ISO spec? */ - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } + png_uint_32 length; - if (length > png_ptr->num_palette || - length > (unsigned int) PNG_MAX_PALETTE_LENGTH || - length == 0) + /* png_find_chunk_op checks this: */ + debug(png_ptr->mode & PNG_HAVE_PLTE); + + length = png_ptr->chunk_length; + + if (length > png_ptr->num_palette || length == 0) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } png_crc_read(png_ptr, readbuf, length); - png_ptr->num_trans = png_check_u16(png_ptr, length); + png_ptr->num_trans = length & 0x1FF; } else { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid with alpha channel"); + png_handle_error(png_ptr, "invalid"); return; } @@ -1839,11 +1689,13 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (info_ptr != NULL) png_ptr->trans_alpha = info_ptr->trans_alpha; } -#endif +#else +# define png_handle_tRNS NULL +#endif /* READ_tRNS */ #ifdef PNG_READ_bKGD_SUPPORTED -void /* PRIVATE */ -png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr) { unsigned int truelen; png_byte buf[6]; @@ -1851,22 +1703,9 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_bKGD"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) || - (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - !(png_ptr->mode & PNG_HAVE_PLTE))) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); + png_handle_error(png_ptr, "duplicate"); return; } @@ -1879,10 +1718,9 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else truelen = 2; - if (length != truelen) + if (png_ptr->chunk_length != truelen) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -1941,40 +1779,30 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_set_bKGD(png_ptr, info_ptr, &background); } -#endif +#else +# define png_handle_bKGD NULL +#endif /* READ_bKGD */ #ifdef PNG_READ_hIST_SUPPORTED -void /* PRIVATE */ -png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr) { unsigned int num, i; png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; png_debug(1, "in png_handle_hIST"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) || !(png_ptr->mode & PNG_HAVE_PLTE)) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); + png_handle_error(png_ptr, "duplicate"); return; } - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } + num = png_ptr->chunk_length / 2; - num = length / 2 ; - - if (num != png_ptr->num_palette || num > PNG_MAX_PALETTE_LENGTH) + if (num != png_ptr->num_palette || 2*num != png_ptr->chunk_length) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -1991,11 +1819,13 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_set_hIST(png_ptr, info_ptr, readbuf); } -#endif +#else +# define png_handle_hIST NULL +#endif /* READ_hIST */ #ifdef PNG_READ_pHYs_SUPPORTED -void /* PRIVATE */ -png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr) { png_byte buf[9]; png_uint_32 res_x, res_y; @@ -2003,27 +1833,15 @@ png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_pHYs"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); + png_handle_error(png_ptr, "duplicate"); return; } - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + if (png_ptr->chunk_length != 9) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - if (length != 9) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -2037,11 +1855,13 @@ png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) unit_type = buf[8]; png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); } -#endif +#else +# define png_handle_pHYs NULL +#endif /* READ_pHYs */ -#ifdef PNG_READ_oFFs_SUPPORTED -void /* PRIVATE */ -png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +#ifdef PNG_READ_oFFs_SUPPORTED /* EXTENSION, before IDAT, no duplicates */ +static void +png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr) { png_byte buf[9]; png_int_32 offset_x, offset_y; @@ -2049,27 +1869,15 @@ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_oFFs"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); + png_handle_error(png_ptr, "duplicate"); return; } - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + if (png_ptr->chunk_length != 9) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - if (length != 9) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -2083,12 +1891,13 @@ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) unit_type = buf[8]; png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); } -#endif +#else +# define png_handle_oFFs NULL +#endif /* READ_oFFs */ -#ifdef PNG_READ_pCAL_SUPPORTED -/* Read the pCAL chunk (described in the PNG Extensions document) */ -void /* PRIVATE */ -png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +#ifdef PNG_READ_pCAL_SUPPORTED /* EXTENSION: before IDAT, no duplicates */ +static void +png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr) { png_int_32 X0, X1; png_byte type, nparams; @@ -2098,47 +1907,35 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_pCAL"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); + png_handle_error(png_ptr, "duplicate"); return; } png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)", - length + 1); + png_ptr->chunk_length + 1); - buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + buffer = png_read_buffer(png_ptr, png_ptr->chunk_length+1, 2/*silent*/); if (buffer == NULL) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of memory"); + png_handle_error(png_ptr, "out of memory"); return; } - png_crc_read(png_ptr, buffer, length); + png_crc_read(png_ptr, buffer, png_ptr->chunk_length); if (png_crc_finish(png_ptr, 0)) return; - buffer[length] = 0; /* Null terminate the last string */ + buffer[png_ptr->chunk_length] = 0; /* Null terminate the last string */ png_debug(3, "Finding end of pCAL purpose string"); for (buf = buffer; *buf; buf++) /* Empty loop */ ; - endptr = buffer + length; + endptr = buffer + png_ptr->chunk_length; /* We need to have at least 12 bytes after the purpose string * in order to get the parameter information. @@ -2172,6 +1969,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else if (type >= PNG_EQUATION_LAST) { png_chunk_benign_error(png_ptr, "unrecognized equation type"); + return; } for (buf = units; *buf; buf++) @@ -2179,7 +1977,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(3, "Allocating pCAL parameters array"); - params = png_voidcast(png_charpp, png_malloc_warn(png_ptr, + params = png_voidcast(png_charpp, png_malloc_base(png_ptr, nparams * (sizeof (png_charp)))); if (params == NULL) @@ -2212,41 +2010,32 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_free(png_ptr, params); } -#endif +#else +# define png_handle_pCAL NULL +#endif /* READ_pCAL */ #ifdef PNG_READ_sCAL_SUPPORTED /* Read the sCAL chunk */ -void /* PRIVATE */ -png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr) { + png_uint_32 length = png_ptr->chunk_length; png_bytep buffer; png_size_t i; int state; png_debug(1, "in png_handle_sCAL"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); + png_handle_error(png_ptr, "duplicate"); return; } /* Need unit type, width, \0, height: minimum 4 bytes */ - else if (length < 4) + if (length < 4) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -2257,8 +2046,7 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (buffer == NULL) { - png_chunk_benign_error(png_ptr, "out of memory"); - png_crc_finish(png_ptr, length); + png_handle_error(png_ptr, "out of memory"); return; } @@ -2306,34 +2094,28 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) (png_charp)buffer+1, (png_charp)buffer+heighti); } } -#endif +#else +# define png_handle_sCAL NULL +#endif /* READ_sCAL */ #ifdef PNG_READ_tIME_SUPPORTED -void /* PRIVATE */ -png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr) { png_byte buf[7]; png_time mod_time; png_debug(1, "in png_handle_tIME"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); + png_handle_error(png_ptr, "duplicate"); return; } - if (png_ptr->mode & PNG_HAVE_IDAT) - png_ptr->mode |= PNG_AFTER_IDAT; - - if (length != 7) + if (png_ptr->chunk_length != 7) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -2351,13 +2133,15 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_set_tIME(png_ptr, info_ptr, &mod_time); } -#endif +#else +# define png_handle_tIME NULL +#endif /* READ_tIME */ #ifdef PNG_READ_tEXt_SUPPORTED -/* Note: this does not properly handle chunks that are > 64K under DOS */ -void /* PRIVATE */ -png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr) { + png_uint_32 length = png_ptr->chunk_length; png_text text_info; png_bytep buffer; png_charp key; @@ -2377,33 +2161,17 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (--png_ptr->user_chunk_cache_max == 1) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "no space in chunk cache"); + png_handle_error(png_ptr, "no space in chunk cache"); return; } } -#endif - - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - if (png_ptr->mode & PNG_HAVE_IDAT) - png_ptr->mode |= PNG_AFTER_IDAT; - -#ifdef PNG_MAX_MALLOC_64K - if (length > 65535U) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too large to fit in memory"); - return; - } -#endif +#endif /* USER_LIMITS */ buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); if (buffer == NULL) { - png_chunk_benign_error(png_ptr, "out of memory"); + png_handle_error(png_ptr, "out of memory"); return; } @@ -2432,13 +2200,15 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (png_set_text_2(png_ptr, info_ptr, &text_info, 1)) png_warning(png_ptr, "Insufficient memory to process text chunk"); } -#endif +#else +# define png_handle_tEXt NULL +#endif /* READ_tEXt */ #ifdef PNG_READ_zTXt_SUPPORTED -/* Note: this does not correctly handle chunks that are > 64K under DOS */ -void /* PRIVATE */ -png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr) { + png_uint_32 length = png_ptr->chunk_length; png_const_charp errmsg = NULL; png_bytep buffer; png_uint_32 keyword_length; @@ -2456,25 +2226,17 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (--png_ptr->user_chunk_cache_max == 1) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "no space in chunk cache"); + png_handle_error(png_ptr, "no space in chunk cache"); return; } } -#endif - - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - if (png_ptr->mode & PNG_HAVE_IDAT) - png_ptr->mode |= PNG_AFTER_IDAT; +#endif /* USER_LIMITS */ buffer = png_read_buffer(png_ptr, length, 2/*silent*/); if (buffer == NULL) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of memory"); + png_handle_error(png_ptr, "out of memory"); return; } @@ -2541,13 +2303,15 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (errmsg != NULL) png_chunk_benign_error(png_ptr, errmsg); } -#endif +#else +# define png_handle_zTXt NULL +#endif /* READ_zTXt */ #ifdef PNG_READ_iTXt_SUPPORTED -/* Note: this does not correctly handle chunks that are > 64K under DOS */ -void /* PRIVATE */ -png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr) { + png_uint_32 length = png_ptr->chunk_length; png_const_charp errmsg = NULL; png_bytep buffer; png_uint_32 prefix_length; @@ -2565,25 +2329,17 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (--png_ptr->user_chunk_cache_max == 1) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "no space in chunk cache"); + png_handle_error(png_ptr, "no space in chunk cache"); return; } } -#endif - - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - if (png_ptr->mode & PNG_HAVE_IDAT) - png_ptr->mode |= PNG_AFTER_IDAT; +#endif /* USER_LIMITS */ buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); if (buffer == NULL) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of memory"); + png_handle_error(png_ptr, "out of memory"); return; } @@ -2691,78 +2447,97 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (errmsg != NULL) png_chunk_benign_error(png_ptr, errmsg); } -#endif +#else +# define png_handle_iTXt NULL +#endif /* READ_iTXt */ + +/* UNSUPPORTED CHUNKS */ +#define png_handle_sTER NULL +#define png_handle_fRAc NULL +#define png_handle_gIFg NULL +#define png_handle_gIFt NULL +#define png_handle_gIFx NULL +#define png_handle_dSIG NULL + +/* IDAT has special treatment below */ +#define png_handle_IDAT NULL + +/****************************************************************************** + * UNKNOWN HANDLING LOGIC + * + * There are three ways an unknown chunk may arise: + * + * 1) Chunks not in the spec. + * 2) Chunks in the spec where libpng support doesn't exist or has been compiled + * out. These are recognized, for a very small performance benefit at the + * cost of maintaining a png_known_chunks entry for each one. + * 3) Chunks supported by libpng which have been marked as 'unknown' by the + * application. + * + * Prior to 1.7.0 all three cases are handled the same way, in 1.7.0 some + * attempt is made to optimize (2) and (3) by storing flags in + * png_struct::known_unknown for chunks in the spec which have been marked for + * unknown handling. + * + * There are three things libpng can do with an unknown chunk, in order of + * preference: + * + * 1) If PNG_READ_USER_CHUNKS_SUPPORTED call an application supplied callback + * with all the chunk data. If this doesn't handle the chunk in prior + * versions of libpng the chunk would be stored if safe otherwise skipped. + * In 1.7.0 the specified chunk unknown handling is used. + * 2) If PNG_SAVE_UNKNOWN_CHUNKS_SUPPOPRTED the chunk may be saved in the + * info_struct (if there is one.) + * 3) The chunk can be skipped. + * + * In effect libpng tries each option in turn. (2) looks at any per-chunk + * unknown handling then, if one wasn't specified, the overall default. + * + * IHDR and IEND cannot be treated as unknown. PLTE and IDAT can. Prior to + * 1.7.0 they couldn't be skipped without a png_error. 1.7.0 adds an extension + * which allows any critical chunk to be skipped so long as IDAT is skipped; the + * logic for failing on critical chunks only applies if the image data is being + * processed. + * + * The default behavior is (3); unknown chunks are simply skipped. 1.7.0 uses + * this to optimize the read code when possible. + * + * In the read code PNG_READ_UNKNOWN_CHUNKS_SUPPORTED is set only if either (1) + * or (2) or both are supported. + * + *****************************************************************************/ +#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +static int +png_chunk_unknown_handling(png_const_structrp png_ptr, png_uint_32 chunk_name) +{ + png_byte chunk_string[5]; + + PNG_CSTRING_FROM_CHUNK(chunk_string, chunk_name); + return png_handle_as_unknown(png_ptr, chunk_string); +} +#endif /* SAVE_UNKNOWN_CHUNKS */ #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED /* Utility function for png_handle_unknown; set up png_ptr::unknown_chunk */ -static int -png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length) +static void +png_make_unknown_chunk(png_structrp png_ptr, png_unknown_chunkp chunk, + png_bytep data) { - png_alloc_size_t limit = PNG_SIZE_MAX; - - if (png_ptr->unknown_chunk.data != NULL) - { - png_free(png_ptr, png_ptr->unknown_chunk.data); - png_ptr->unknown_chunk.data = NULL; - } - -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - if (png_ptr->user_chunk_malloc_max > 0 && - png_ptr->user_chunk_malloc_max < limit) - limit = png_ptr->user_chunk_malloc_max; - -# elif PNG_USER_CHUNK_MALLOC_MAX > 0 - if (PNG_USER_CHUNK_MALLOC_MAX < limit) - limit = PNG_USER_CHUNK_MALLOC_MAX; -# endif - - if (length <= limit) - { - PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name); - /* The following is safe because of the PNG_SIZE_MAX init above */ - png_ptr->unknown_chunk.size = (png_size_t)/*SAFE*/length; - /* 'mode' is a flag array, only the bottom four bits matter here */ - png_ptr->unknown_chunk.location = PNG_BYTE(png_ptr->mode & 0xf); - - if (length == 0) - png_ptr->unknown_chunk.data = NULL; - - else - { - /* Do a 'warn' here - it is handled below. */ - png_ptr->unknown_chunk.data = png_voidcast(png_bytep, - png_malloc_warn(png_ptr, length)); - } - } - - if (png_ptr->unknown_chunk.data == NULL && length > 0) - { - /* This is benign because we clean up correctly */ - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "unknown chunk exceeds memory limits"); - return 0; - } - - else - { - if (length > 0) - png_crc_read(png_ptr, png_ptr->unknown_chunk.data, length); - png_crc_finish(png_ptr, 0); - return 1; - } + chunk->data = data; + chunk->size = png_ptr->chunk_length; + PNG_CSTRING_FROM_CHUNK(chunk->name, png_ptr->chunk_name); + /* 'mode' is a flag array, only three of the bottom four bits are public: */ + chunk->location = + png_ptr->mode & (PNG_HAVE_IHDR+PNG_HAVE_PLTE+PNG_AFTER_IDAT); } -#endif /* READ_UNKNOWN_CHUNKS */ /* Handle an unknown, or known but disabled, chunk */ void /* PRIVATE */ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, - png_uint_32 length, int keep) + png_bytep chunk_data) { - int handled = 0; /* the chunk was handled */ - png_debug(1, "in png_handle_unknown"); -#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED /* NOTE: this code is based on the code in libpng-1.4.12 except for fixing * the bug which meant that setting a non-default behavior for a specific * chunk would be ignored (the default was always used unless a user @@ -2773,75 +2548,47 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, * will always be PNG_HANDLE_CHUNK_AS_DEFAULT and it needs to be set here. * This is just an optimization to avoid multiple calls to the lookup * function. - */ -# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED -# ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED - keep = png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name); -# endif -# endif - - /* One of the following methods will read the chunk or skip it (at least one + * + * One of the following methods will read the chunk or skip it (at least one * of these is always defined because this is the only way to switch on * PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) */ # ifdef PNG_READ_USER_CHUNKS_SUPPORTED - /* The user callback takes precedence over the chunk keep value, but the - * keep value is still required to validate a save of a critical chunk. - */ + /* The user callback takes precedence over the chunk handling option: */ if (png_ptr->read_user_chunk_fn != NULL) { - if (png_cache_unknown_chunk(png_ptr, length)) - { - /* Callback to user unknown chunk handler */ - int ret = (*(png_ptr->read_user_chunk_fn))(png_ptr, - &png_ptr->unknown_chunk); + png_unknown_chunk unknown_chunk; + int ret; - /* ret is: - * negative: An error occurred; png_chunk_error will be called. - * zero: The chunk was not handled, the chunk will be discarded - * unless png_set_keep_unknown_chunks has been used to set - * a 'keep' behavior for this particular chunk, in which - * case that will be used. A critical chunk will cause an - * error at this point unless it is to be saved. - * positive: The chunk was handled, libpng will ignore/discard it. - */ - if (ret < 0) - png_chunk_error(png_ptr, "error in user chunk"); + /* Callback to user unknown chunk handler */ + png_make_unknown_chunk(png_ptr, &unknown_chunk, chunk_data); + ret = png_ptr->read_user_chunk_fn(png_ptr, &unknown_chunk); - else if (ret == 0) - { - /* Use the default handling, note that if there is per-chunk - * handling specified it has already been set into 'keep'. - * - * NOTE: this is an API change in 1.7.0. Prior to 1.7.0 libpng - * would force keep to PNG_HANDLE_CHUNK_IF_SAFE at this point, - * and 1.6.0 would issue a warning if this caused a default of - * discarding the chunk to be changed. - */ - if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT) - keep = png_ptr->unknown_default; - } + /* ret is: + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be discarded + * unless png_set_keep_unknown_chunks has been used to set + * a 'keep' behavior for this particular chunk, in which + * case that will be used. A critical chunk will cause an + * error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + */ + if (ret > 0) + return; - else /* chunk was handled */ - { - handled = 1; - /* Critical chunks can be safely discarded at this point. */ - keep = PNG_HANDLE_CHUNK_NEVER; - } - } + else if (ret < 0) + png_chunk_error(png_ptr, "application error"); - else - keep = PNG_HANDLE_CHUNK_NEVER; /* insufficient memory */ + /* Else: use the default handling. */ } - - else - /* Use the SAVE_UNKNOWN_CHUNKS code or skip the chunk */ # endif /* READ_USER_CHUNKS */ # ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED { + int keep = png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name); + /* keep is currently just the per-chunk setting, if there was no - * setting change it to the global default now (not that this may + * setting change it to the global default now (note that this may * still be AS_DEFAULT) then obtain the cache of the chunk if required, * if not simply skip the chunk. */ @@ -2851,93 +2598,52 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, if (keep == PNG_HANDLE_CHUNK_ALWAYS || (keep == PNG_HANDLE_CHUNK_IF_SAFE && PNG_CHUNK_ANCILLARY(png_ptr->chunk_name))) - { - if (!png_cache_unknown_chunk(png_ptr, length)) - keep = PNG_HANDLE_CHUNK_NEVER; - } +# ifdef PNG_USER_LIMITS_SUPPORTED + switch (png_ptr->user_chunk_cache_max) + { + case 2: + png_ptr->user_chunk_cache_max = 1; + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + /* FALL THROUGH */ + case 1: + /* NOTE: prior to 1.6.0 this case resulted in an unknown + * critical chunk being skipped, now there will be a hard + * error below. + */ + break; - else - png_crc_finish(png_ptr, length); + default: /* not at limit */ + --(png_ptr->user_chunk_cache_max); + /* FALL THROUGH */ + case 0: /* no limit */ +# endif /* USER_LIMITS */ + /* Here when the limit isn't reached or when limits are + * compiled out; store the chunk. + */ + { + png_unknown_chunk unknown_chunk; + + png_make_unknown_chunk(png_ptr, &unknown_chunk, + chunk_data); + png_set_unknown_chunks(png_ptr, info_ptr, &unknown_chunk, + 1); + return; + } +# ifdef PNG_USER_LIMITS_SUPPORTED + } +# endif /* USER_LIMITS */ } -# else -# ifndef PNG_READ_USER_CHUNKS_SUPPORTED -# error no method to support READ_UNKNOWN_CHUNKS -# endif - - { - /* If here there is no read callback pointer set and no support is - * compiled in to just save the unknown chunks, so simply skip this - * chunk. If 'keep' is something other than AS_DEFAULT or NEVER then - * the app has erroneously asked for unknown chunk saving when there - * is no support. - */ - if (keep > PNG_HANDLE_CHUNK_NEVER) - png_app_error(png_ptr, "no unknown chunk support available"); - - png_crc_finish(png_ptr, length); - } -# endif - -# ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED - /* Now store the chunk in the chunk list if appropriate, and if the limits - * permit it. - */ - if (keep == PNG_HANDLE_CHUNK_ALWAYS || - (keep == PNG_HANDLE_CHUNK_IF_SAFE && - PNG_CHUNK_ANCILLARY(png_ptr->chunk_name))) - { -# ifdef PNG_USER_LIMITS_SUPPORTED - switch (png_ptr->user_chunk_cache_max) - { - case 2: - png_ptr->user_chunk_cache_max = 1; - png_chunk_benign_error(png_ptr, "no space in chunk cache"); - /* FALL THROUGH */ - case 1: - /* NOTE: prior to 1.6.0 this case resulted in an unknown critical - * chunk being skipped, now there will be a hard error below. - */ - break; - - default: /* not at limit */ - --(png_ptr->user_chunk_cache_max); - /* FALL THROUGH */ - case 0: /* no limit */ -# endif /* USER_LIMITS */ - /* Here when the limit isn't reached or when limits are compiled - * out; store the chunk. - */ - png_set_unknown_chunks(png_ptr, info_ptr, - &png_ptr->unknown_chunk, 1); - handled = 1; -# ifdef PNG_USER_LIMITS_SUPPORTED - break; - } -# endif - } -# else /* no store support: the chunk must be handled by the user callback */ +# else /* !SAVE_UNKNOWN_CHUNKS */ PNG_UNUSED(info_ptr) -# endif +# endif /* !SAVE_UNKNOWN_CHUNKS */ - /* Regardless of the error handling below the cached data (if any) can be - * freed now. Notice that the data is not freed if there is a png_error, but - * it will be freed by destroy_read_struct. + /* This is the 'skip' case, where the read callback (if any) returned 0 and + * the save code did not save the chunk. */ - if (png_ptr->unknown_chunk.data != NULL) - png_free(png_ptr, png_ptr->unknown_chunk.data); - png_ptr->unknown_chunk.data = NULL; - -#else /* !READ_UNKNOWN_CHUNKS */ - /* There is no support to read an unknown chunk, so just skip it. */ - png_crc_finish(png_ptr, length); - PNG_UNUSED(info_ptr) - PNG_UNUSED(keep) -#endif /* !READ_UNKNOWN_CHUNKS */ - - /* Check for unhandled critical chunks */ - if (!handled && PNG_CHUNK_CRITICAL(png_ptr->chunk_name)) + if (PNG_CHUNK_CRITICAL(png_ptr->chunk_name)) png_chunk_error(png_ptr, "unhandled critical chunk"); } +#endif /* READ_UNKNOWN_CHUNKS */ /* This function is called to verify that a chunk name is valid. * This function can't have the "critical chunk check" incorporated @@ -2951,7 +2657,7 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, * ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) */ -void /* PRIVATE */ +static void png_check_chunk_name(png_structrp png_ptr, png_uint_32 chunk_name) { int i; @@ -2962,6 +2668,9 @@ png_check_chunk_name(png_structrp png_ptr, png_uint_32 chunk_name) { int c = chunk_name & 0xff; + /* This is unrecoverable at present because it most likely indicates + * a broken stream. + */ if (c < 65 || c > 122 || (c > 90 && c < 97)) png_chunk_error(png_ptr, "invalid chunk type"); @@ -2969,806 +2678,864 @@ png_check_chunk_name(png_structrp png_ptr, png_uint_32 chunk_name) } } -/* Combines the row recently read in with the existing pixels in the row. This - * routine takes care of alpha and transparency if requested. This routine also - * handles the two methods of progressive display of interlaced images, - * depending on the 'display' value; if 'display' is true then the whole row - * (dp) is filled from the start by replicating the available pixels. If - * 'display' is false only those pixels present in the pass are filled in. +/* This is the known chunk table; it contains an entry for each supported + * chunk. */ -void /* PRIVATE */ -png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) +static const struct { - unsigned int pixel_depth = png_ptr->transformed_pixel_depth; - png_const_bytep sp = png_ptr->row_buf + 1; - png_alloc_size_t row_width = png_ptr->width; - unsigned int pass = png_ptr->pass; - png_bytep end_ptr = 0; - png_byte end_byte = 0; - unsigned int end_mask; + void (*handle)(png_structrp png_ptr, png_infop info_ptr); + png_uint_32 name; + unsigned int before :5; + unsigned int after :5; +} +png_known_chunks[] = +/* To make the code easier to write the following defines are used, note that + * before_end should never trip - it would indicate that libpng attempted to + * read beyond the IEND chunk. + * + * 'within_IDAT' is used for IDAT chunks; PNG_AFTER_IDAT must not be set, but + * PNG_HAVE_IDAT may be set. + */ +#define before_end PNG_HAVE_IEND /* Should be impossible */ +#define within_IDAT (before_end+PNG_AFTER_IDAT) +#define before_IDAT (within_IDAT+PNG_HAVE_IDAT) +#define before_PLTE (before_IDAT+PNG_HAVE_PLTE) +#define before_start (before_PLTE+PNG_HAVE_IHDR) +#define at_start 0 +#define after_start PNG_HAVE_IHDR +#define after_PLTE (after_start+PNG_HAVE_PLTE) /* NOTE: PLTE optional */ +#define after_IDAT (after_PLTE+PNG_AFTER_IDAT) /* NOTE: PLTE optional */ - png_debug(1, "in png_combine_row"); +/* See scripts/chunkdesc.h for how this works: */ +#define PNG_CHUNK_END(n, c1, c2, c3, c4, before, after)\ + { png_handle_ ## n, png_ ##n, before, after } +#define PNG_CHUNK(n, c1, c2, c3, c4, before, after)\ + PNG_CHUNK_END(n, c1, c2, c3, c4, before, after), +#define PNG_CHUNK_BEGIN(n, c1, c2, c3, c4, before, after)\ + PNG_CHUNK_END(n, c1, c2, c3, c4, before, after), +{ +# include "scripts/chunkdesc.h" +}; +#undef PNG_CHUNK_START +#undef PNG_CHUNK +#undef PNG_CHUNK_END - /* Added in 1.5.6: it should not be possible to enter this routine until at - * least one row has been read from the PNG data and transformed. +#define C_KNOWN ((sizeof png_known_chunks)/(sizeof png_known_chunks[0])) + +/* See: scripts/chunkhash.c for code to generate this. This reads the same + * description file (scripts/chunkdesc.h) as is included above. Whenever + * that file is changed chunkhash needs to be re-run to generate the lines + * following this comment. + * + * PNG_CHUNK_HASH modifes its argument and returns an index. png_chunk_index is + * a function which does the same thing without modifying the value of the + * argument. Both macro and function always return a valid index; to detect + * known chunks it is necessary to check png_known_chunks[index].name against + * the hashed name. + */ +static const png_byte png_chunk_lut[64] = +{ + 10, 20, 7, 3, 0, 23, 8, 0, 0, 11, 24, 0, 0, 0, 0, 4, + 12, 0, 0, 0, 13, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0, + 0, 6, 17, 0, 15, 0, 5, 19, 26, 0, 0, 0, 18, 0, 0, 9, + 1, 0, 21, 0, 22, 14, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0 +}; + +#define PNG_CHUNK_HASH(n)\ + png_chunk_lut[0x3f & (((n += n >> 2),n += n >> 8),n += n >> 16)] + +static png_byte +png_chunk_index(png_uint_32 name) +{ + name += name >> 2; + name += name >> 8; + name += name >> 16; + return png_chunk_lut[name & 0x3f]; +} + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +/* Mark a known chunk to be handled as unknown. */ +void /*PRIVATE*/ +png_cache_known_unknown(png_structrp png_ptr, png_const_bytep add, int keep) + /* Update the png_struct::known_unknown bit cache which stores whether each + * known chunk should be treated as unknown. + * + * This cache exists to avoid doing the search loop on every chunk while + * handling chunks. This code is only ever used if unknown handling is + * invoked, and the loop is isolated code; the function is called from + * add_one_chunk in pngset.c once for each unknown and while this is + * happening no other code is being run in this thread. */ - if (pixel_depth == 0) - png_error(png_ptr, "internal row logic error"); - - /* Added in 1.5.4: the pixel depth should match the information returned by - * any call to png_read_update_info at this point. Do not continue if we got - * this wrong. +{ + /* The cache only stores whether or not to handle the chunk; specifically + * whether or not keep is 0. */ - if (png_ptr->info_rowbytes != 0 && png_ptr->info_rowbytes != - PNG_ROWBYTES(pixel_depth, row_width)) - png_error(png_ptr, "internal row size calculation error"); + png_uint_32 name = PNG_CHUNK_FROM_STRING(add); - /* Don't expect this to ever happen: */ - if (row_width == 0) - png_error(png_ptr, "internal row width error"); + debug(PNG_HANDLE_CHUNK_AS_DEFAULT == 0 && C_KNOWN <= 32); - /* Preserve the last byte in cases where only part of it will be overwritten, - * the multiply below may overflow, we don't care because ANSI-C guarantees - * we get the low bits. + /* But do not treat IHDR or IEND as unknown. This is historical; it + * always was this way, it's not clear if PLTE can always safely be + * treated as unknown, but it is allowed. */ - end_mask = (pixel_depth * row_width) & 7; - if (end_mask != 0) + if (name != png_IHDR && name != png_IEND) { - /* end_ptr == NULL is a flag to say do nothing */ - end_ptr = dp + PNG_ROWBYTES(pixel_depth, row_width) - 1; - end_byte = *end_ptr; -# ifdef PNG_READ_PACKSWAP_SUPPORTED - if (png_ptr->transformations & PNG_PACKSWAP) /* little-endian byte */ - end_mask = 0xff << end_mask; + png_byte i = png_chunk_index(name); - else /* big-endian byte */ -# endif - end_mask = 0xff >> end_mask; - /* end_mask is now the bits to *keep* from the destination row */ - } - - /* For non-interlaced images this reduces to a memcpy(). A memcpy() - * will also happen if interlacing isn't supported or if the application - * does not call png_set_interlace_handling(). In the latter cases the - * caller just gets a sequence of the unexpanded rows from each interlace - * pass. - */ -#ifdef PNG_READ_INTERLACING_SUPPORTED - if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE) && - pass < 6 && (display == 0 || - /* The following copies everything for 'display' on passes 0, 2 and 4. */ - (display == 1 && (pass & 1) != 0))) - { - /* Narrow images may have no bits in a pass; the caller should handle - * this, but this test is cheap: - */ - if (row_width <= PNG_PASS_START_COL(pass)) - return; - - if (pixel_depth < 8) + if (png_known_chunks[i].name == name) { - /* For pixel depths up to 4 bpp the 8-pixel mask can be expanded to fit - * into 32 bits, then a single loop over the bytes using the four byte - * values in the 32-bit mask can be used. For the 'display' option the - * expanded mask may also not require any masking within a byte. To - * make this work the PACKSWAP option must be taken into account - it - * simply requires the pixels to be reversed in each byte. - * - * The 'regular' case requires a mask for each of the first 6 passes, - * the 'display' case does a copy for the even passes in the range - * 0..6. This has already been handled in the test above. - * - * The masks are arranged as four bytes with the first byte to use in - * the lowest bits (little-endian) regardless of the order (PACKSWAP or - * not) of the pixels in each byte. - * - * NOTE: the whole of this logic depends on the caller of this function - * only calling it on rows appropriate to the pass. This function only - * understands the 'x' logic; the 'y' logic is handled by the caller. - * - * The following defines allow generation of compile time constant bit - * masks for each pixel depth and each possibility of swapped or not - * swapped bytes. Pass 'p' is in the range 0..6; 'x', a pixel index, - * is in the range 0..7; and the result is 1 if the pixel is to be - * copied in the pass, 0 if not. 'S' is for the sparkle method, 'B' - * for the block method. - * - * With some compilers a compile time expression of the general form: - * - * (shift >= 32) ? (a >> (shift-32)) : (b >> shift) - * - * Produces warnings with values of 'shift' in the range 33 to 63 - * because the right hand side of the ?: expression is evaluated by - * the compiler even though it isn't used. Microsoft Visual C (various - * versions) and the Intel C compiler are known to do this. To avoid - * this the following macros are used in 1.5.6. This is a temporary - * solution to avoid destabilizing the code during the release process. - */ -# if PNG_USE_COMPILE_TIME_MASKS -# define PNG_LSR(x,s) ((x)>>((s) & 0x1f)) -# define PNG_LSL(x,s) ((x)<<((s) & 0x1f)) -# else -# define PNG_LSR(x,s) ((x)>>(s)) -# define PNG_LSL(x,s) ((x)<<(s)) -# endif -# define S_COPY(p,x) (((p)<4 ? PNG_LSR(0x80088822,(3-(p))*8+(7-(x))) :\ - PNG_LSR(0xaa55ff00,(7-(p))*8+(7-(x)))) & 1) -# define B_COPY(p,x) (((p)<4 ? PNG_LSR(0xff0fff33,(3-(p))*8+(7-(x))) :\ - PNG_LSR(0xff55ff00,(7-(p))*8+(7-(x)))) & 1) - - /* Return a mask for pass 'p' pixel 'x' at depth 'd'. The mask is - * little endian - the first pixel is at bit 0 - however the extra - * parameter 's' can be set to cause the mask position to be swapped - * within each byte, to match the PNG format. This is done by XOR of - * the shift with 7, 6 or 4 for bit depths 1, 2 and 4. - */ -# define PIXEL_MASK(p,x,d,s) \ - (PNG_LSL(((PNG_LSL(1U,(d)))-1),(((x)*(d))^((s)?8-(d):0)))) - - /* Hence generate the appropriate 'block' or 'sparkle' pixel copy mask. - */ -# define S_MASKx(p,x,d,s) (S_COPY(p,x)?PIXEL_MASK(p,x,d,s):0) -# define B_MASKx(p,x,d,s) (B_COPY(p,x)?PIXEL_MASK(p,x,d,s):0) - - /* Combine 8 of these to get the full mask. For the 1-bpp and 2-bpp - * cases the result needs replicating, for the 4-bpp case the above - * generates a full 32 bits. - */ -# define MASK_EXPAND(m,d) ((m)*((d)==1?0x01010101:((d)==2?0x00010001:1))) - -# define S_MASK(p,d,s) MASK_EXPAND(S_MASKx(p,0,d,s) + S_MASKx(p,1,d,s) +\ - S_MASKx(p,2,d,s) + S_MASKx(p,3,d,s) + S_MASKx(p,4,d,s) +\ - S_MASKx(p,5,d,s) + S_MASKx(p,6,d,s) + S_MASKx(p,7,d,s), d) - -# define B_MASK(p,d,s) MASK_EXPAND(B_MASKx(p,0,d,s) + B_MASKx(p,1,d,s) +\ - B_MASKx(p,2,d,s) + B_MASKx(p,3,d,s) + B_MASKx(p,4,d,s) +\ - B_MASKx(p,5,d,s) + B_MASKx(p,6,d,s) + B_MASKx(p,7,d,s), d) - -#if PNG_USE_COMPILE_TIME_MASKS - /* Utility macros to construct all the masks for a depth/swap - * combination. The 's' parameter says whether the format is PNG - * (big endian bytes) or not. Only the three odd-numbered passes are - * required for the display/block algorithm. - */ -# define S_MASKS(d,s) { S_MASK(0,d,s), S_MASK(1,d,s), S_MASK(2,d,s),\ - S_MASK(3,d,s), S_MASK(4,d,s), S_MASK(5,d,s) } - -# define B_MASKS(d,s) { B_MASK(1,d,s), B_MASK(3,d,s), B_MASK(5,d,s) } - -# define DEPTH_INDEX(d) ((d)==1?0:((d)==2?1:2)) - - /* Hence the pre-compiled masks indexed by PACKSWAP (or not), depth and - * then pass: - */ - static PNG_CONST png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] = { - /* Little-endian byte masks for PACKSWAP */ - { S_MASKS(1,0), S_MASKS(2,0), S_MASKS(4,0) }, - /* Normal (big-endian byte) masks - PNG format */ - { S_MASKS(1,1), S_MASKS(2,1), S_MASKS(4,1) } - }; + if (keep != PNG_HANDLE_CHUNK_AS_DEFAULT) + { + png_ptr->known_unknown |= 1U << i; - /* display_mask has only three entries for the odd passes, so index by - * pass>>1. - */ - static PNG_CONST png_uint_32 display_mask[2][3][3] = - { - /* Little-endian byte masks for PACKSWAP */ - { B_MASKS(1,0), B_MASKS(2,0), B_MASKS(4,0) }, - /* Normal (big-endian byte) masks - PNG format */ - { B_MASKS(1,1), B_MASKS(2,1), B_MASKS(4,1) } - }; - -# define MASK(pass,depth,display,png)\ - ((display)?display_mask[png][DEPTH_INDEX(depth)][pass>>1]:\ - row_mask[png][DEPTH_INDEX(depth)][pass]) - -#else /* !USE_COMPILE_TIME_MASKS */ - /* This is the runtime alternative: it seems unlikely that this will - * ever be either smaller or faster than the compile time approach. - */ -# define MASK(pass,depth,display,png)\ - ((display)?B_MASK(pass,depth,png):S_MASK(pass,depth,png)) -#endif /* !USE_COMPILE_TIME_MASKS */ - - /* Use the appropriate mask to copy the required bits. In some cases - * the byte mask will be 0 or 0xff; optimize these cases. row_width is - * the number of pixels, but the code copies bytes, so it is necessary - * to special case the end. - */ - png_uint_32 pixels_per_byte = 8 / pixel_depth; - png_uint_32 mask; - -# ifdef PNG_READ_PACKSWAP_SUPPORTED - if (png_ptr->transformations & PNG_PACKSWAP) - mask = MASK(pass, pixel_depth, display, 0); +# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + if (keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_IF_SAFE && + PNG_CHUNK_ANCILLARY(name))) + png_ptr->save_unknown |= 1U << i; + + else /* PNG_HANDLE_CHUNK_NEVER || !SAFE */ + png_ptr->save_unknown &= ~(1U << i); +# endif /* SAVE_UNKNOWN_CHUNKS */ + } else -# endif - mask = MASK(pass, pixel_depth, display, 1); + png_ptr->known_unknown &= ~(1U << i); + } + } + + /* else this is not a known chunk */ + } + + else /* 1.7.0: inform the app writer; */ + png_app_warning(png_ptr, "IHDR, IEND cannot be treated as unknown"); + +} +#endif /* HANDLE_AS_UNKNOWN */ + +/* Handle chunk position requirements in a consistent way. The chunk must + * come after 'after' and before 'before', either of which may be 0. If it + * does the function returns true, if it does not an appropriate chunk error + * is issued; benign for non-critical chunks, fatal for critical ones. + */ +static int +png_handle_position(png_const_structrp png_ptr, unsigned int chunk) +{ + unsigned int before = png_known_chunks[chunk].before; + unsigned int after = png_known_chunks[chunk].after; + +# ifdef PNG_ERROR_TEXT_SUPPORTED + png_const_charp error = NULL; +# endif /* ERROR_TEXT */ + + /* PLTE is optional with all color types except PALETTE, so for the other + * color types clear it from the 'after' bits. + * + * TODO: find some better way of recognizing the case where there is a PLTE + * and it follows after_PLTE chunks (see the complex stuff in handle_PLTE.) + */ + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + after &= PNG_BIC_MASK(PNG_HAVE_PLTE); + + if ((png_ptr->mode & before) == 0 && + (png_ptr->mode & after) == after) + return 1; + + /* The error case; do before first (it is normally more important) */ +# ifdef PNG_ERROR_TEXT_SUPPORTED + switch (before & -before) /* Lowest set bit */ + { + case 0: + /* Check 'after'; only one bit set. */ + switch (after) + { + case PNG_HAVE_IHDR: + error = "missing IHDR"; + break; + + case PNG_HAVE_PLTE: + error = "must occur after PLTE"; + break; + + case PNG_AFTER_IDAT: + error = "must come after IDAT"; + break; + + default: + impossible("invalid 'after' position"); + } + break; + + case PNG_HAVE_IHDR: + error = "must occur first"; + break; + + case PNG_HAVE_PLTE: + error = "must come before PLTE"; + break; + + case PNG_HAVE_IDAT: + error = "must come before IDAT"; + break; + + default: + impossible("invalid 'before' position"); + } +# endif /* ERROR_TEXT */ + + png_chunk_report(png_ptr, error, PNG_CHUNK_CRITICAL(png_ptr->chunk_name) ? + PNG_CHUNK_FATAL : PNG_CHUNK_ERROR); + return 0; +} + +/* This is the shared chunk handling function, used for both the sequential and + * progressive reader. + */ +png_chunk_op /* PRIVATE */ +png_find_chunk_op(png_structrp png_ptr) +{ + /* Given a chunk in png_struct::{chunk_name,chunk_length} validate the name + * and work out how it should be handled. This function checks the chunk + * location using png_struct::mode and will set the PNG_AFTER_IDAT bit if + * appropriate but otherwise makes no changes to the stream read state. + * + * png_chunk_skip Skip this chunk + * png_chunk_unknown This is an unknown chunk which can't be skipped; + * the unknown handler must be called with all the + * chunk data. + * png_chunk_process_all The caller must call png_chunk_handle to handle + * the chunk, when this call is made all the chunk + * data must be available to the handler. + * png_chunk_process_part The handler expects data in png_struct::zstream. + * {next,avail}_in and does not require all of the + * data at once (as png_read_process_IDAT). + */ + png_uint_32 chunk_name = png_ptr->chunk_name; + unsigned int mode = png_ptr->mode; + unsigned int index; + + /* This function should never be called if IEND has been set: + */ + debug((mode & PNG_HAVE_IEND) == 0); + + /* IDAT logic: we are only *after* IDAT when we start reading the first + * following (non-IDAT) chunk, this may already have been set in the IDAT + * handling code, but if IDAT is handled as unknown this doesn't happen. + */ + if (chunk_name != png_IDAT && (mode & PNG_HAVE_IDAT) != 0) + mode = png_ptr->mode |= PNG_AFTER_IDAT; + + index = png_chunk_index(chunk_name); + + if (png_known_chunks[index].name == chunk_name) + { + /* Known chunks have a position requirement; check it, badly positioned + * chunks that do not error out in png_handle_position are simply skipped. + * + * API CHANGE: libpng 1.7.0: prior versions of libpng did not check + * ordering requirements for known chunks where the support for reading + * them had been configured out of libpng. This seems dangerous; the + * user chunk callback could still see them and crash as a result. + */ + if (!png_handle_position(png_ptr, index)) + return png_chunk_skip; + + /* Do the mode update. + * + * API CHANGE 1.7.0: the 'HAVE' flags are now consistently set *before* + * the chunk is handled. Previously only IDAT was handled this way. This + * can only affect an app that was previously handling PLTE itself in a + * callback, however this seems to be impossible. + */ + switch (chunk_name) + { + case png_IHDR: png_ptr->mode |= PNG_HAVE_IHDR; break; + case png_PLTE: png_ptr->mode |= PNG_HAVE_PLTE; break; + case png_IDAT: png_ptr->mode |= PNG_HAVE_IDAT; break; + case png_IEND: png_ptr->mode |= PNG_HAVE_IEND; break; + default: break; + } + +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + /* A known chunk may still be treated as unknown. Check for that. */ + if (!((png_ptr->known_unknown >> index) & 1U)) +# endif /* HANDLE_AS_UNKNOWN */ + { + /* This is a known chunk that is not being treated as unknown. If + * it is IDAT then partial processing is done, otherwise (at present) + * the whole thing is processed in one shot + * + * TODO: this is a feature of the legacy use of the sequential read + * code in the handlers, fix this. + */ + if (chunk_name == png_IDAT) + return png_chunk_process_part; + + /* Check for a known chunk where support has been compiled out of + * libpng. We know it cannot be a critical chunk; support for those + * cannot be removed. + */ + if (png_known_chunks[index].handle != NULL) + return png_chunk_process_all; + +# ifdef PNG_READ_USER_CHUNKS_SUPPORTED + if (png_ptr->read_user_chunk_fn != NULL) + return png_chunk_unknown; +# endif /* READ_USER_CHUNKS */ + +# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + /* There is no per-chunk special handling set for this chunk + * (because of the test on known_unknown above) so only the + * default unknown handling behavior matters. We skip the chunk + * if the behavior is 'NEVER' or 'DEFAULT'. This is irrelevant + * if SAVE_UNKNOWN_CHUNKS is not supported. + */ + if (png_ptr->unknown_default > PNG_HANDLE_CHUNK_NEVER) + return png_chunk_unknown; +# endif /* SAVE_UNKNOWN_CHUNKS */ + + return png_chunk_skip; + } + +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else + { + /* Else this is a known chunk that is being treated as unknown. If + * there is a user callback the whole shebang is required: + */ +# ifdef PNG_READ_USER_CHUNKS_SUPPORTED + if (png_ptr->read_user_chunk_fn != NULL) + return png_chunk_unknown; +# endif /* READ_USER_CHUNKS */ + + /* No user callback, there is a possibility that we can skip this + * chunk: + */ +# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + if ((png_ptr->save_unknown >> index) & 1U) + return png_chunk_unknown; +# endif /* SAVE_UNKNOWN_CHUNKS */ + + /* If this is a critical chunk and IDAT is not being skipped then + * this is an error. The only possibility here is PLTE on an + * image which is palette mapped. If the app ignores this error + * then there will be a more definate one in png_handle_unknown. + */ + if (chunk_name == png_PLTE && + png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_app_error(png_ptr, "skipping PLTE on palette image"); + + return png_chunk_skip; + } +# endif /* HANDLE_AS_UNKNOWN */ + } + + else /* unknown chunk */ + { + /* The code above implicitly validates the chunk name, however if a chunk + * name/type is not recognized it is necessary to validate it to ensure + * that the PNG stream isn't hopelessly damaged: + */ + png_check_chunk_name(png_ptr, chunk_name); + +# ifdef PNG_READ_USER_CHUNKS_SUPPORTED + if (png_ptr->read_user_chunk_fn != NULL) + return png_chunk_unknown; +# endif /* READ_USER_CHUNKS */ + +# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + /* There may be per-chunk handling, otherwise the default is used, this + * is the one place where the list needs to be searched: + */ + { + int keep = png_chunk_unknown_handling(png_ptr, chunk_name); + + if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT) + keep = png_ptr->unknown_default; + + if (keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_IF_SAFE && + PNG_CHUNK_ANCILLARY(chunk_name))) + return png_chunk_unknown; + } +# endif /* SAVE_UNKNOWN_CHUNKS */ + + /* The chunk will be skipped so it must not be a critical chunk, unless + * IDATs are being skipped too. + */ + if (PNG_CHUNK_CRITICAL(chunk_name) +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + && !png_IDATs_skipped(png_ptr) +# endif /* HANDLE_AS_UNKNOWN */ + ) + png_chunk_error(png_ptr, "unhandled critical chunk"); + + return png_chunk_skip; + } +} + +void /* PRIVATE */ +png_handle_chunk(png_structrp png_ptr, png_inforp info_ptr) + /* The chunk to handle is in png_struct::chunk_name,chunk_length. + * + * NOTE: at present it is only valid to call this after png_find_chunk_op + * has returned png_chunk_process_all and all the data is available for + * png_handle_chunk (via the libpng read callback.) + */ +{ + png_uint_32 chunk_name = png_ptr->chunk_name; + unsigned int index = png_chunk_index(chunk_name); + + /* So this must be true: */ + affirm(png_known_chunks[index].name == chunk_name && + png_known_chunks[index].handle != NULL); + + png_known_chunks[index].handle(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_copy_row(png_const_structrp png_ptr, png_bytep dp) + /* Copy the row in row_buffer; this is the 'simple' case of png_combine_row + * where no adjustment to the pixel spacing is required. + */ +{ + unsigned int pixel_depth = +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + png_ptr->row_bit_depth * PNG_FORMAT_CHANNELS(png_ptr->row_format); +# else + PNG_PIXEL_DEPTH(*png_ptr); +# endif + png_alloc_size_t cb = png_ptr->width; + unsigned int remaining; /* remaining bits in a partial byte */ + + /* 1.7.0: png_combine_row used to copy data equal to the whole row even if + * the deinterlace transform had not been performed. This must be an error + * and possibly a security issue: + */ + if (png_ptr->interlaced) + cb = PNG_PASS_COLS(cb, png_ptr->pass); + + /* Copy 'cb' pixels, but take care with the last byte because it may + * be partially written. + */ + switch (pixel_depth) + { + case 1: remaining = cb & 7U; cb >>= 3; break; + case 2: remaining = (cb << 1) & 6U; cb >>= 2; break; + case 4: remaining = (cb << 2) & 4U; cb >>= 1; break; + default: remaining = 0U; cb *= pixel_depth >> 3; break; + } + + memcpy(dp, png_ptr->row_buffer, cb); + + if (remaining > 0) + { + /* 'remaining' is the number of bits still to be copied. */ +# ifdef PNG_READ_PACKSWAP_SUPPORTED + /* Format may be little endian; bits to copy in the bottom of 's' */ + if ((png_ptr->row_format & PNG_FORMAT_FLAG_SWAPPED) != 0) + remaining = 0xffU << remaining; + + else +# endif /* READ_PACKSWAP */ + remaining = 0xffU >> remaining; + + /* remaining is now the bits to *keep* from the destination byte */ + dp[cb] = png_check_byte(png_ptr, + (dp[cb] & remaining) | (png_ptr->row_buffer[cb] & ~remaining)); + } +} + +#ifdef PNG_READ_DEINTERLACE_SUPPORTED +void /* PRIVATE */ +png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) + /* 1.7.0: API CHANGE: prior to 1.7.0 read de-interlace was done in two steps, + * the first would expand a narrow pass by replicating pixels according to + * the inter-pixel spacing of the pixels from the pass in the image. It did + * not take account of any offset from the start of the image row of the + * first pixel. The second step happened in png_combine_row where the result + * was merged into the output rows. + * + * In 1.7.0 this is no longer done. Instead all the work happens here. This + * is only an API change for the progressive reader if the app didn't call + * png_combine_row, but rather expected an expanded row. It's not obvious + * why any user of the progressive reader would want this, particularly given + * the weird non-offseting of the start in the original + * 'png_do_read_interlace'; the behavior was completely undocumented. + * + * In 1.7.0 png_combine_row does all the work. It expects a raw + * uncompressed, de-filtered, transformed row and it either copies it if: + * + * 1) It is not interlaced. + * 2) libpng isn't handling the de-interlace. + * 3) This is pass 7 (i.e. '6' using the libpng 0-based numbering). + * + * The input data comes from png_struct: + * + * png_struct::row_buffer; the row data + * png_struct::pass; the pass + * png_struct::row_number; the row number in the *image* + * png_struct::row_bit_depth, + * png_struct::row_format; the pixel format, if TRANSFORM_MECH, else: + * png_struct::bit_depth, + * png_struct::color_type; the pixel format otherwise + * + * The destination pointer (but not size) and how to handle intermediate + * passes are arguments to the API. 'display' is interpreted as: + * + * 0: only overwrite destination pixels that will correspond to the source + * pixel in the final image. 'sparkle' mode. + * 1: overwrite the corresponding destination pixel and all following + * pixels (horizontally and, eventually, vertically) that will come + * from *later* passes. 'block' mode. + */ +{ + png_debug(1, "in png_combine_row"); + + /* Factor out the copy case first, the 'display' argument is irrelevant in + * these cases: + */ + if (!png_ptr->do_interlace || png_ptr->pass == 6) + { + png_copy_row(png_ptr, dp); + return; + } + + else /* not a simple copy */ + { + unsigned int pixel_depth = +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + png_ptr->row_bit_depth * PNG_IMAGE_PIXEL_CHANNELS(png_ptr->row_format); +# else + PNG_PIXEL_DEPTH(*png_ptr); +# endif + const unsigned int pass = png_ptr->pass; + png_const_bytep sp = png_ptr->row_buffer; + png_uint_32 row_width = png_ptr->width; /* output width */ + /* The first source pixel is written to PNG_PASS_START_COL of the + * destination: + */ + unsigned int dstart = PNG_PASS_START_COL(pass); /* in pixels */ + /* Subsequent pixels are written PNG_PASS_COL_OFFSET further on: */ + unsigned int doffset = PNG_PASS_COL_OFFSET(pass); /* in pixels */ + /* In 'block' mode when dstart is 0 (PNG passes 1,3,5,7) the same pixel is + * replicated doffset times, when dstart is non-zero (PNG passes 2,4,6) it + * is replicated dstart times. For 'sparkle' mode only one copy of the + * pixel is written: + */ + unsigned int drep = display ? (dstart ? dstart : doffset) : 1; + + /* The caller should have excluded the narrow cases: */ + affirm(row_width > dstart); + row_width -= dstart; + + /* So each source pixel sp[i] is written to: + * + * dp[dstart + i*doffset]..dp[dstart + i*doffset + (drep-1)] + * + * Until we get to row_width. This is easy for pixels that are 8 or more + * bits deep; whole bytes are read and written, slightly more difficult + * when pixel_depth * drep is at least 8 bits, because then dstart * + * pixel_depth will always be a whole byte and most complex when source + * and destination require sub-byte addressing. + * + * Cherry pick the easy cases: + */ + if (pixel_depth > 8) + { + affirm((pixel_depth & 7) == 0); + /* Convert to bytes: */ + pixel_depth >>= 3; + dp += dstart * pixel_depth; for (;;) { - png_uint_32 m; + unsigned int c; - /* It doesn't matter in the following if png_uint_32 has more than - * 32 bits because the high bits always match those in m<<24; it is, - * however, essential to use OR here, not +, because of this. - */ - m = mask; - mask = (m >> 8) | (m << 24); /* rotate right to good compilers */ - m &= 0xff; + if (drep > row_width) + drep = row_width; - if (m != 0) /* something to copy */ - { - if (m != 0xff) - *dp = png_check_byte(png_ptr, (*dp & ~m) | (*sp & m)); - else - *dp = *sp; - } + for (c=0; c= row_width) + break; - row_width -= pixels_per_byte; - ++dp; - ++sp; + row_width -= doffset; + dp += (doffset-drep) * pixel_depth; + sp += pixel_depth; } } - else /* pixel_depth >= 8 */ + else if (pixel_depth == 8) { - unsigned int bytes_to_copy, bytes_to_jump; - - /* Validate the depth - it must be a multiple of 8 */ - if (pixel_depth & 7) - png_error(png_ptr, "invalid user transform pixel depth"); - - pixel_depth >>= 3; /* now in bytes */ - row_width *= pixel_depth; - - /* Regardless of pass number the Adam 7 interlace always results in a - * fixed number of pixels to copy then to skip. There may be a - * different number of pixels to skip at the start though. + /* Optimize the common 1-byte per pixel case (typical case for palette + * mapped images): */ - { - unsigned int offset = PNG_PASS_START_COL(pass) * pixel_depth; + dp += dstart; - row_width -= offset; - dp += offset; - sp += offset; + for (;;) + { + if (drep > row_width) + drep = row_width; + + memset(dp, *sp++, drep); + + if (doffset >= row_width) + break; + + row_width -= doffset; + dp += doffset; } + } - /* Work out the bytes to copy. */ - if (display != 0) + else /* pixel_depth < 8 */ + { + /* Pixels are 1, 2 or 4 bits in size. */ + unsigned int spixel = *sp++; + unsigned int dbrep = pixel_depth * drep; + unsigned int spos = 0; +# ifdef PNG_READ_PACKSWAP_SUPPORTED + const int lsb = + (png_ptr->row_format & PNG_FORMAT_FLAG_SWAPPED) != 0; +# endif /* READ_PACKSWAP */ + + if (dbrep >= 8) { - /* When doing the 'block' algorithm the pixel in the pass gets - * replicated to adjacent pixels. This is why the even (0,2,4,6) - * passes are skipped above - the entire expanded row is copied. + /* brep must be greater than 1, the destination does not require + * sub-byte addressing except, maybe, at the end. + * + * db is the count of bytes required to replicate the source pixel + * drep times. */ - bytes_to_copy = (1<<((6-pass)>>1)) * pixel_depth; + affirm((dbrep & 7) == 0); + dbrep >>= 3; + affirm((dstart * pixel_depth & 7) == 0); + dp += (dstart * pixel_depth) >> 3; - /* But don't allow this number to exceed the actual row width. */ - if (bytes_to_copy > row_width) - bytes_to_copy = (unsigned int)/*SAFE*/row_width; - } + for (;;) + { + /* Fill a byte with copies of the next pixel: */ + unsigned int spixel_rep = spixel; + +# ifdef PNG_READ_PACKSWAP_SUPPORTED + if (lsb) + spixel_rep >>= spos; + else +# endif /* READ_PACKSWAP */ + spixel_rep >>= (8-pixel_depth)-spos; - else /* normal row; Adam7 only ever gives us one pixel to copy. */ - bytes_to_copy = pixel_depth; - - /* In Adam7 there is a constant offset between where the pixels go. */ - bytes_to_jump = PNG_PASS_COL_OFFSET(pass) * pixel_depth; - - /* And simply copy these bytes. Some optimization is possible here, - * depending on the value of 'bytes_to_copy'. Special case the low - * byte counts, which we know to be frequent. - * - * Notice that these cases all 'return' rather than 'break' - this - * avoids an unnecessary test on whether to restore the last byte - * below. - */ - switch (bytes_to_copy) - { - case 1: - for (;;) + switch (pixel_depth) { - *dp = *sp; - - if (row_width <= bytes_to_jump) - return; - - dp += bytes_to_jump; - sp += bytes_to_jump; - row_width -= bytes_to_jump; + case 1: spixel_rep &= 1; spixel_rep |= spixel_rep << 1; + case 2: spixel_rep &= 3; spixel_rep |= spixel_rep << 2; + case 4: spixel_rep &= 15; spixel_rep |= spixel_rep << 4; + default: + break; } - case 2: - /* There is a possibility of a partial copy at the end here; this - * slows the code down somewhat. + /* This may leave some pixels unwritten when there is a partial + * byte write required at the end: */ - do + if (drep > row_width) + drep = row_width, dbrep = (pixel_depth * drep) >> 3; + + memset(dp, spixel_rep, dbrep); + + if (doffset >= row_width) { - dp[0] = sp[0], dp[1] = sp[1]; - - if (row_width <= bytes_to_jump) - return; - - sp += bytes_to_jump; - dp += bytes_to_jump; - row_width -= bytes_to_jump; - } - while (row_width > 1); - - /* And there can only be one byte left at this point: */ - *dp = *sp; - return; - - case 3: - /* This can only be the RGB case, so each copy is exactly one - * pixel and it is not necessary to check for a partial copy. - */ - for (;;) - { - dp[0] = sp[0], dp[1] = sp[1], dp[2] = sp[2]; - - if (row_width <= bytes_to_jump) - return; - - sp += bytes_to_jump; - dp += bytes_to_jump; - row_width -= bytes_to_jump; - } - - default: -#if PNG_ALIGN_TYPE != PNG_ALIGN_NONE - /* Check for double byte alignment and, if possible, use a - * 16-bit copy. Don't attempt this for narrow images - ones that - * are less than an interlace panel wide. Don't attempt it for - * wide bytes_to_copy either - use the memcpy there. - */ - if (bytes_to_copy < 16 /*else use memcpy*/ && - png_isaligned(dp, png_uint_16) && - png_isaligned(sp, png_uint_16) && - bytes_to_copy % (sizeof (png_uint_16)) == 0 && - bytes_to_jump % (sizeof (png_uint_16)) == 0) - { - /* Everything is aligned for png_uint_16 copies, but try for - * png_uint_32 first. + /* End condition; were all 'drep' pixels written at the end? */ - if (png_isaligned(dp, png_uint_32) && - png_isaligned(sp, png_uint_32) && - bytes_to_copy % (sizeof (png_uint_32)) == 0 && - bytes_to_jump % (sizeof (png_uint_32)) == 0) + drep = (pixel_depth * drep - (dbrep << 3)); + + if (drep) { - png_uint_32p dp32 = png_aligncast(png_uint_32p,dp); - png_const_uint_32p sp32 = png_aligncastconst( - png_const_uint_32p, sp); - size_t skip = (bytes_to_jump-bytes_to_copy) / - (sizeof (png_uint_32)); + unsigned int mask; - do - { - size_t c = bytes_to_copy; - do - { - *dp32++ = *sp32++; - c -= (sizeof (png_uint_32)); - } - while (c > 0); + affirm(drep < 8); + dp += dbrep; - if (row_width <= bytes_to_jump) - return; - - dp32 += skip; - sp32 += skip; - row_width -= bytes_to_jump; - } - while (bytes_to_copy <= row_width); - - /* Get to here when the row_width truncates the final copy. - * There will be 1-3 bytes left to copy, so don't try the - * 16-bit loop below. + /* Set 'mask' to have 0's where *dp must be overwritten + * with spixel_rep: */ - dp = (png_bytep)dp32; - sp = (png_const_bytep)sp32; - do - *dp++ = *sp++; - while (--row_width > 0); - return; +# ifdef PNG_READ_PACKSWAP_SUPPORTED + if (lsb) + mask = 0xff << drep; + else +# endif /* READ_PACKSWAP */ + mask = 0xff >> drep; + + *dp = png_check_byte(png_ptr, + (*dp & mask) | (spixel_rep & ~mask)); } - /* Else do it in 16-bit quantities, but only if the size is - * not too large. - */ + break; + } + + row_width -= doffset; + dp += (doffset * pixel_depth) >> 3; + spos += pixel_depth; + if (spos == 8) + spixel = *sp++, spos = 0; + } /* for (;;) */ + } /* pixel_depth * drep >= 8 */ + + else /* pixel_depth * drep < 8 */ + { + /* brep may be 1, pixel_depth may be 1, 2 or 4, dbrep is the number + * of bits to set. + */ + unsigned int dpixel; + + dstart *= pixel_depth; + dp += dstart >> 3; + dstart &= 7; + dpixel = *dp; + + /* dpixel: current *dp, being modified + * dstart: bit offset within dpixel + * drep: pixel size to write (used as a check against row_width) + * doffset: pixel step to next written destination + * + * spixel: current *sp, being read, and: + * spixel_rep: current pixel, replicated to fill a byte + * spos: bit offset within spixel + * + * Set dbrep to a mask for the bits to set: + */ + dbrep = (1<>= spos; else - { - png_uint_16p dp16 = png_aligncast(png_uint_16p, dp); - png_const_uint_16p sp16 = png_aligncastconst( - png_const_uint_16p, sp); - size_t skip = (bytes_to_jump-bytes_to_copy) / - (sizeof (png_uint_16)); +# endif /* READ_PACKSWAP */ + spixel_rep >>= (8-pixel_depth)-spos; - do - { - size_t c = bytes_to_copy; - do - { - *dp16++ = *sp16++; - c -= (sizeof (png_uint_16)); - } - while (c > 0); - - if (row_width <= bytes_to_jump) - return; - - dp16 += skip; - sp16 += skip; - row_width -= bytes_to_jump; - } - while (bytes_to_copy <= row_width); - - /* End of row - 1 byte left, bytes_to_copy > row_width: */ - dp = (png_bytep)dp16; - sp = (png_const_bytep)sp16; - do - *dp++ = *sp++; - while (--row_width > 0); - return; - } - } -#endif /* ALIGN_ code */ - - /* The true default - use a memcpy: */ - for (;;) + switch (pixel_depth) { - memcpy(dp, sp, bytes_to_copy); - - if (row_width <= bytes_to_jump) - return; - - sp += bytes_to_jump; - dp += bytes_to_jump; - row_width -= bytes_to_jump; - if (bytes_to_copy > row_width) - bytes_to_copy = (unsigned int)/*SAFE*/row_width; + case 1: spixel_rep &= 1; spixel_rep |= spixel_rep << 1; + case 2: spixel_rep &= 3; spixel_rep |= spixel_rep << 2; + case 4: spixel_rep &= 15; spixel_rep |= spixel_rep << 4; + default: + break; } - } - /* NOT REACHED*/ - } /* pixel_depth >= 8 */ + /* This may leave some pixels unwritten when there is a partial + * byte write required at the end: + */ + if (drep > row_width) + drep = row_width, dbrep = (1<<(pixel_depth*drep))-1; - /* Here if pixel_depth < 8 to check 'end_ptr' below. */ - } - else -#endif + { + unsigned int mask; - /* If here then the switch above wasn't used so just memcpy the whole row - * from the temporary row buffer (notice that this overwrites the end of the - * destination row if it is a partial byte.) - */ - memcpy(dp, sp, PNG_ROWBYTES(pixel_depth, row_width)); + /* Mask dbrep bits at dstart: */ +# ifdef PNG_READ_PACKSWAP_SUPPORTED + if (lsb) + mask = dstart; + else +# endif /* READ_PACKSWAP */ + mask = (8-pixel_depth)-dstart; + mask = dbrep << mask; - /* Restore the overwritten bits from the last byte if necessary. */ - if (end_ptr != NULL) - *end_ptr = png_check_byte(png_ptr, - (end_byte & end_mask) | (*end_ptr & ~end_mask)); + dpixel &= ~mask; + dpixel |= spixel_rep & mask; + } + + if (doffset >= row_width) + { + *dp = png_check_byte(png_ptr, dpixel); + break; + } + + row_width -= doffset; + dstart += doffset * pixel_depth; + + if (dstart >= 8) + { + *dp = png_check_byte(png_ptr, dpixel); + dp += dstart >> 3; + dstart &= 7; + dpixel = *dp; + } + + spos += pixel_depth; + if (spos == 8) + spixel = *sp++, spos = 0; + } /* for (;;) */ + } /* pixel_depth * drep < 8 */ + } /* pixel_depth < 8 */ + } /* not a simple copy */ } - -#ifdef PNG_READ_INTERLACING_SUPPORTED -void /* PRIVATE */ -png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, - png_uint_32 transformations /* Because these may affect the byte layout */) -{ - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - /* Offset to next interlace block */ - static PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - png_debug(1, "in png_do_read_interlace"); - if (row != NULL && row_info != NULL) - { - png_uint_32 final_width; - - final_width = row_info->width * png_pass_inc[pass]; - - switch (row_info->pixel_depth) - { - case 1: - { - png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); - png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); - int sshift, dshift; - int s_start, s_end, s_inc; - int jstop = png_pass_inc[pass]; - png_byte v; - png_uint_32 i; - int j; - -#ifdef PNG_READ_PACKSWAP_SUPPORTED - if (transformations & PNG_PACKSWAP) - { - sshift = (int)((row_info->width + 7) & 0x07); - dshift = (int)((final_width + 7) & 0x07); - s_start = 7; - s_end = 0; - s_inc = -1; - } - - else -#endif - { - sshift = 7 - (int)((row_info->width + 7) & 0x07); - dshift = 7 - (int)((final_width + 7) & 0x07); - s_start = 0; - s_end = 7; - s_inc = 1; - } - - for (i = 0; i < row_info->width; i++) - { - v = PNG_BYTE((*sp >> sshift) & 0x01); - for (j = 0; j < jstop; j++) - { - unsigned int tmp = *dp & (0x7f7f >> (7 - dshift)); - tmp |= v << dshift; - *dp = png_check_byte(0/*TODO:fixme*/, tmp); - - if (dshift == s_end) - { - dshift = s_start; - dp--; - } - - else - dshift += s_inc; - } - - if (sshift == s_end) - { - sshift = s_start; - sp--; - } - - else - sshift += s_inc; - } - break; - } - - case 2: - { - png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); - png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2); - int sshift, dshift; - int s_start, s_end, s_inc; - int jstop = png_pass_inc[pass]; - png_uint_32 i; - -#ifdef PNG_READ_PACKSWAP_SUPPORTED - if (transformations & PNG_PACKSWAP) - { - sshift = (int)(((row_info->width + 3) & 0x03) << 1); - dshift = (int)(((final_width + 3) & 0x03) << 1); - s_start = 6; - s_end = 0; - s_inc = -2; - } - - else -#endif - { - sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1); - dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1); - s_start = 0; - s_end = 6; - s_inc = 2; - } - - for (i = 0; i < row_info->width; i++) - { - png_byte v; - int j; - - v = PNG_BYTE((*sp >> sshift) & 0x03); - for (j = 0; j < jstop; j++) - { - unsigned int tmp = *dp & (0x3f3f >> (6 - dshift)); - tmp |= v << dshift; - *dp = PNG_BYTE(tmp); - - if (dshift == s_end) - { - dshift = s_start; - dp--; - } - - else - dshift += s_inc; - } - - if (sshift == s_end) - { - sshift = s_start; - sp--; - } - - else - sshift += s_inc; - } - break; - } - - case 4: - { - png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); - png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); - int sshift, dshift; - int s_start, s_end, s_inc; - png_uint_32 i; - int jstop = png_pass_inc[pass]; - -#ifdef PNG_READ_PACKSWAP_SUPPORTED - if (transformations & PNG_PACKSWAP) - { - sshift = (int)(((row_info->width + 1) & 0x01) << 2); - dshift = (int)(((final_width + 1) & 0x01) << 2); - s_start = 4; - s_end = 0; - s_inc = -4; - } - - else -#endif - { - sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2); - dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2); - s_start = 0; - s_end = 4; - s_inc = 4; - } - - for (i = 0; i < row_info->width; i++) - { - png_byte v = PNG_BYTE((*sp >> sshift) & 0x0f); - int j; - - for (j = 0; j < jstop; j++) - { - unsigned int tmp = *dp & (0xf0f >> (4 - dshift)); - tmp |= v << dshift; - *dp = png_check_byte(0/*TODO:fixme*/, tmp); - - if (dshift == s_end) - { - dshift = s_start; - dp--; - } - - else - dshift += s_inc; - } - - if (sshift == s_end) - { - sshift = s_start; - sp--; - } - - else - sshift += s_inc; - } - break; - } - - default: - { - png_size_t pixel_bytes = (row_info->pixel_depth >> 3); - - png_bytep sp = row + (png_size_t)(row_info->width - 1) - * pixel_bytes; - - png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; - - int jstop = png_pass_inc[pass]; - png_uint_32 i; - - for (i = 0; i < row_info->width; i++) - { - png_byte v[8]; /* SAFE; pixel_depth does not exceed 64 */ - int j; - - memcpy(v, sp, pixel_bytes); - - for (j = 0; j < jstop; j++) - { - memcpy(dp, v, pixel_bytes); - dp -= pixel_bytes; - } - - sp -= pixel_bytes; - } - break; - } - } - - row_info->width = final_width; - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width); - } -#ifndef PNG_READ_PACKSWAP_SUPPORTED - PNG_UNUSED(transformations) /* Silence compiler warning */ -#endif -} -#endif /* READ_INTERLACING */ +#endif /* READ_DEINTERLACE */ static void -png_read_filter_row_sub(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_sub(png_alloc_size_t istop, unsigned int bpp, + png_bytep row, png_const_bytep prev_row) { - png_size_t i; - png_size_t istop = row_info->rowbytes; - unsigned int bpp = (row_info->pixel_depth + 7) >> 3; + png_alloc_size_t i; png_bytep rp = row + bpp; PNG_UNUSED(prev_row) for (i = bpp; i < istop; i++) { - *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + *rp = PNG_BYTE(*rp + *(rp-bpp)); rp++; } } static void -png_read_filter_row_up(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_up(png_alloc_size_t istop, unsigned int bpp, + png_bytep row, png_const_bytep prev_row) { - png_size_t i; - png_size_t istop = row_info->rowbytes; + png_alloc_size_t i; png_bytep rp = row; png_const_bytep pp = prev_row; for (i = 0; i < istop; i++) { - *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + *rp = PNG_BYTE(*rp + *pp++); rp++; } + + PNG_UNUSED(bpp) } static void -png_read_filter_row_avg(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_avg(png_alloc_size_t istop, unsigned int bpp, + png_bytep row, png_const_bytep prev_row) { - png_size_t i; + png_alloc_size_t i; png_bytep rp = row; png_const_bytep pp = prev_row; - unsigned int bpp = (row_info->pixel_depth + 7) >> 3; - png_size_t istop = row_info->rowbytes - bpp; + istop -= bpp; for (i = 0; i < bpp; i++) { - *rp = (png_byte)(((int)(*rp) + - ((int)(*pp++) / 2 )) & 0xff); - + *rp = PNG_BYTE(*rp + (*pp++ / 2)); rp++; } for (i = 0; i < istop; i++) { - *rp = (png_byte)(((int)(*rp) + - (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + *rp = PNG_BYTE(*rp + (*pp++ + *(rp-bpp)) / 2); rp++; } } static void -png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_paeth_1byte_pixel(png_alloc_size_t row_bytes, + unsigned int bpp, png_bytep row, png_const_bytep prev_row) { - png_bytep rp_end = row + row_info->rowbytes; + png_bytep rp_end = row + row_bytes; int a, c; /* First pixel/byte */ @@ -3810,13 +3577,14 @@ png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row, a += *row; *row++ = (png_byte)a; } + + PNG_UNUSED(bpp) } static void -png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_paeth_multibyte_pixel(png_alloc_size_t row_bytes, + unsigned int bpp, png_bytep row, png_const_bytep prev_row) { - int bpp = (row_info->pixel_depth + 7) >> 3; png_bytep rp_end = row + bpp; /* Process the first pixel in the row completely (this is the same as 'up' @@ -3825,11 +3593,11 @@ png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row, while (row < rp_end) { int a = *row + *prev_row++; - *row++ = (png_byte)(a&0xFF); + *row++ = PNG_BYTE(a); } /* Remainder */ - rp_end += row_info->rowbytes - bpp; + rp_end += row_bytes - bpp; while (row < rp_end) { @@ -3856,12 +3624,12 @@ png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row, if (pc < pa) a = c; a += *row; - *row++ = (png_byte)(a&0xFF); + *row++ = PNG_BYTE(a); } } static void -png_init_filter_functions(png_structrp pp) +png_init_filter_functions(png_structrp pp, unsigned int bpp) /* This function is called once for every PNG image (except for PNG images * that only use PNG_FILTER_VALUE_NONE for all rows) to set the * implementations required to reverse the filtering of PNG rows. Reversing @@ -3872,8 +3640,6 @@ png_init_filter_functions(png_structrp pp) * interlacing causes the actual row width to vary. */ { - unsigned int bpp = (pp->pixel_depth + 7) >> 3; - pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub; pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up; pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg; @@ -3897,562 +3663,841 @@ png_init_filter_functions(png_structrp pp) #endif } -void /* PRIVATE */ -png_read_filter_row(png_structrp pp, png_row_infop row_info, png_bytep row, - png_const_bytep prev_row, int filter) +/* This is an IDAT specific wrapper for png_zlib_inflate; the input is already + * in png_ptr->zstream.{next,avail}_in however the output uses the full + * capabilities of png_zlib_inflate, returning a byte count of bytes read. + * This is just a convenience for IDAT processing. + * + * NOTE: this function works just fine after the zstream has ended, it just + * fills the buffer with zeros (outputing an error message once.) + */ +static png_alloc_size_t +png_inflate_IDAT(png_structrp png_ptr, int finish, + /* OUTPUT: */ png_bytep output, png_alloc_size_t output_size) { - /* OPTIMIZATION: DO NOT MODIFY THIS FUNCTION, instead #define - * PNG_FILTER_OPTIMIZATIONS to a function that overrides the generic - * implementations. See png_init_filter_functions above. + /* Expect Z_OK if !finsh and Z_STREAM_END if finish; if Z_STREAM_END is + * delivered when finish is not set the IDAT stream is truncated, if Z_OK is + * delivered when finish is set this is harmless and indicates that the + * stream end code has not been read. + * + * finish should be set as follows: + * + * 0: not reading the last row, stream not expected to end + * 1: reading the last row, stream expected to end + * 2: looking for stream end after the last row has been read, expect no + * more output and stream end. */ - if (filter > PNG_FILTER_VALUE_NONE && filter < PNG_FILTER_VALUE_LAST) + png_alloc_size_t original_size = output_size; + int ret = Z_STREAM_END; /* In case it ended ok before. */ + + if (!png_ptr->zstream_ended) { - if (pp->read_filter[0] == NULL) - png_init_filter_functions(pp); + png_const_bytep next_in = png_ptr->zstream.next_in; + png_uint_32 avail_in = png_ptr->zstream.avail_in; - pp->read_filter[filter-1](row_info, row, prev_row); - } -} + ret = png_zlib_inflate(png_ptr, png_IDAT, finish, + &next_in, &avail_in, &output, &output_size/*remaining*/); -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED -void /* PRIVATE */ -png_read_IDAT_data(png_structrp png_ptr, png_bytep output, - png_alloc_size_t avail_out) -{ - /* Loop reading IDATs and decompressing the result into output[avail_out] */ - png_ptr->zstream.next_out = output; - png_ptr->zstream.avail_out = 0; /* safety: set below */ + debug(next_in == png_ptr->zstream.next_in); + debug(avail_in == png_ptr->zstream.avail_in); + debug(output == png_ptr->zstream.next_out); + /* But zstream.avail_out may be truncated to uInt */ - if (output == NULL) - avail_out = 0; - - do - { - int ret; - png_byte tmpbuf[PNG_INFLATE_BUF_SIZE]; - - if (png_ptr->zstream.avail_in == 0) + switch (ret) { - uInt avail_in; - png_bytep buffer; - - while (png_ptr->idat_size == 0) - { - png_crc_finish(png_ptr, 0); - - png_ptr->idat_size = png_read_chunk_header(png_ptr); - /* This is an error even in the 'check' case because the code just - * consumed a non-IDAT header. + case Z_STREAM_END: + /* The caller must set finish on the last row of the image (not + * the last row of the pass!) */ - if (png_ptr->chunk_name != png_IDAT) - png_error(png_ptr, "Not enough image data"); - } + debug(png_ptr->zstream_ended); - avail_in = png_ptr->IDAT_read_size; + if (!finish) /* early end */ + break; + + if (output_size > 0) /* incomplete read */ + { + if (finish == 2) /* looking for end; it has been found */ + return original_size - output_size; - if (avail_in > png_ptr->idat_size) - avail_in = (uInt)png_ptr->idat_size; + /* else those bytes are really needed: */ + break; + } - /* A PNG with a gradually increasing IDAT size will defeat this attempt - * to minimize memory usage by causing lots of re-allocs, but - * realistically doing IDAT_read_size re-allocs is not likely to be a - * big problem. - */ - buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/); + /* else: FALL THROUGH: success */ - png_crc_read(png_ptr, buffer, avail_in); - png_ptr->idat_size -= avail_in; + case Z_BUF_ERROR: + /* this is the success case: output or input is empty: */ + original_size -= output_size; /* bytes written */ - png_ptr->zstream.next_in = buffer; - png_ptr->zstream.avail_in = avail_in; + if (output_size > 0) + { + /* Some output still needed; if the next chunk is known + * to not be an IDAT then this is the truncation case. + */ + affirm(avail_in == 0); + + if ((png_ptr->mode & PNG_AFTER_IDAT) != 0) + { + /* Zlib doesn't know we are out of data, so this must be + * done here: + */ + png_ptr->zstream_ended = 1; + break; + } + } + + return original_size; /* bytes written */ + + default: + /* error */ + break; } - /* And set up the output side. */ - if (output != NULL) /* standard read */ - { - uInt out = ZLIB_IO_MAX; - - if (out > avail_out) - out = (uInt)avail_out; - - avail_out -= out; - png_ptr->zstream.avail_out = out; - } - - else /* after last row, checking for end */ - { - png_ptr->zstream.next_out = tmpbuf; - png_ptr->zstream.avail_out = (sizeof tmpbuf); - } - - /* Use NO_FLUSH; this gives zlib the maximum opportunity to optimize the - * process. If the LZ stream is truncated the sequential reader will - * terminally damage the stream, above, by reading the chunk header of the - * following chunk (it then exits with png_error). - * - * TODO: deal more elegantly with truncated IDAT lists. + /* The 'ended' flag should always be set if we get here, the success + * cases where the LZ stream hasn't reached an end or an error leave + * the function at the return above. */ - ret = inflate(&png_ptr->zstream, Z_NO_FLUSH); - - /* Take the unconsumed output back. */ - if (output != NULL) - avail_out += png_ptr->zstream.avail_out; - - else /* avail_out counts the extra bytes */ - avail_out += (sizeof tmpbuf) - png_ptr->zstream.avail_out; - - png_ptr->zstream.avail_out = 0; - - if (ret == Z_STREAM_END) - { - /* Do this for safety; we won't read any more into this row. */ - png_ptr->zstream.next_out = NULL; - - png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; - - if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0) - png_chunk_benign_error(png_ptr, "Extra compressed data"); - break; - } - - if (ret != Z_OK) - { - png_zstream_error(png_ptr, ret); - - if (output != NULL) - png_chunk_error(png_ptr, png_ptr->zstream.msg); - - else /* checking */ - { - png_chunk_benign_error(png_ptr, png_ptr->zstream.msg); - return; - } - } - } while (avail_out > 0); - - if (avail_out > 0) - { - /* The stream ended before the image; this is the same as too few IDATs so - * should be handled the same way. - */ - if (output != NULL) - png_error(png_ptr, "Not enough image data"); - - else /* the deflate stream contained extra data */ - png_chunk_benign_error(png_ptr, "Too much image data"); + debug(png_ptr->zstream_ended); } + + /* This is the error return case; there was missing data, or an error. + * Either continue with a warning (once; hence the zstream_error flag) + * or png_error. The 'warn' setting has to be turned on and benign errors + * have to be turned off (made warnings.) The logic of this is that this + * is a pretty serious error; PNG is about images and we don't know that the + * image is correct. + */ + if (!png_ptr->zstream_error) /* first time */ + { + if ((png_ptr->flags & PNG_FLAG_IDAT_ERRORS_WARN) != 0) + png_chunk_benign_error(png_ptr, png_ptr->zstream.msg); + else + png_chunk_error(png_ptr, png_ptr->zstream.msg); + + /* And prevent the report about too many IDATs on streams with internal + * LZ errors: + */ + png_ptr->zstream_error = 1; + } + + /* This is the error recovery case; fill the buffer with zeros. This is + * safe because it makes the filter byte 'NONE' and the row fairly innocent. + */ + memset(output, 0, output_size); + return original_size; } +/* SHARED IDAT HANDLING. + * + * This is the 1.7+ common read code; shared by both the progressive and + * sequential readers. + */ +/* Initialize the row buffers, etc. */ void /* PRIVATE */ +png_read_start_IDAT(png_structrp png_ptr) +{ +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + /* This won't work at all if the app turned on unknown handling for IDAT + * chunks; the first IDAT has already been consumed! + */ + if (png_ptr->known_unknown & 1U) + png_error(png_ptr, "Attempt to read image with unknown IDAT"); +# endif /* HANDLE_AS_UNKNOWN */ + + /* This is a missing read of the header information; we still haven't + * countered the first IDAT chunk. This can only happen in the sequential + * reader if the app didn't call png_read_info. + */ + if (png_ptr->chunk_name != png_IDAT) + png_error(png_ptr, "Missing call to png_read_info"); + + /* Two things need to happen: first work out the effect of any + * transformations (if supported) on the row size, second, allocate + * row_buffer and claim the zstream. + */ + png_init_row_info(png_ptr); + + /* Now allocate the row buffer and, if that succeeds, claim the zstream. + */ + png_ptr->row_buffer = png_voidcast(png_bytep, + png_malloc(png_ptr, png_ptr->row_allocated_bytes)); + + if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); +} + +/* The process function gets called when there is some IDAT data to process + * and it just does the right thing with it. The zstream must have been claimed + * (owner png_IDAT) and the input data is in zstream.{next,avail}_in. The + * output next_{in,out} must not be changed by the caller; it is used + * internally. + * + * Result codes are as follows: + * + * png_row_incomplete: Insufficient IDAT data (from zstream) was present to + * process the next row. zstream.avail_in will be 0. + * png_row_process: A new row is available in the input buffer, it should be + * handled before the next call (if any) to this function. + * png_row_repeat: For interlaced images (only) this row is not in the pass, + * however the existing buffer may be displayed in lieu; if doing the + * 'blocky' (not 'sparkle') display the row should be displayed, + * otherwise treat as: + * png_row_skip: For interlaced images (only) the interlace pass has no data + * appropriate to this row, it should be skipped. + * + * In both of the two cases zstream.avail_in may be non-0, indicating that some + * IDAT data at zstream.next_in remains to be consumed. This data must be + * preserved and preset at the next call to the function. + * + * The function may also call png_error if an unrecoverable error occurs. + */ +png_row_op /*PRIVATE*/ +png_read_process_IDAT(png_structrp png_ptr) +{ + png_uint_32 width = png_ptr->width; + png_uint_32 row_number = png_ptr->row_number; + unsigned int pass = png_ptr->pass; + const unsigned int interlaced = png_ptr->interlaced != PNG_INTERLACE_NONE; + enum anonymous { + start_of_pass = 0U, /* at start of pass, no filter byte */ + need_filter_byte = 1U, /* need the filter byte *after* this row */ + need_row_bytes = 2U, /* reading the row */ + processing_row = 3U, /* control returned to caller to process the row */ + start_read_row_bytes, /* start read a row (internal) */ + transform_row /* transform an unfiltered row (internal) */ + } state = png_upcast(enum anonymous, png_ptr->row_state); + + /* The caller is responsible for calling png_read_start_IDAT: */ + affirm(png_ptr->zowner == png_IDAT); + + for (;;) switch (state) + { + case processing_row: + /* When there was a previous row (not at the start of the image) the + * row number needs to be updated and, possibly, the pass number. + */ + if (++row_number == png_ptr->height) + { + affirm(interlaced && pass < 6); /* else too many calls */ + + /* Start a new pass: there never is a pending filter byte so it + * is always necessary to read the filter byte of the next row. + */ + png_ptr->pass = ++pass & 0x7; + row_number = 0; + } + + png_ptr->row_number = row_number; + + /* This is a new row, but it may not be in the pass data so it + * may be possible to simply return control to the caller to + * skip it or use the previous row as appropriate. + */ + if (interlaced) + { + png_uint_32 pass_width = width; + + debug(pass <= 6); + + /* This macro cannot overflow because the PNG width (and height) + * have already been checked to ensure that they are less than + * 2^31 (i.e. they are 31-bit values, not 32-bit values.) + */ + pass_width = PNG_PASS_COLS(pass_width, pass); + + /* On average most rows are skipped, so do this first: */ + if (pass_width == 0 || + !PNG_ROW_IN_INTERLACE_PASS(row_number, pass)) + { + /* Using the PNG specification numbering (pass+1), passes 1, + * 2, 4, 6 contribute to all the rows in 'block' interlaced + * filling mode. Pass 3 contributes to four rows (5,6,7,8), + * pass 5 to two rows (3,4 then 7,8) and pass 7 only to one + * (the one on which it is processed). have_row must be set + * appropriately; it is set when a row is processed (end of + * this function) and remains set while the 'block' mode of + * interlace handling should reuse the previous row for this + * row. + * + * Each pass row can be used in a fixed number of rows, shown + * in 'rows' below, the '*' indicates that the row is actually + * in the pass, the '^' that the previous '*' row is used in + * block display update and the '@' that the pass doesn't + * contribte at all to that row in block display mode: + * + * PASS: 0 1 2 3 4 5 6 + * rows: 8 8 4 4 2 2 1 + * 0: * * @ * @ * @ + * 1: ^ ^ @ ^ @ ^ * + * 2: ^ ^ @ ^ * * @ + * 3: ^ ^ @ ^ ^ ^ * + * 4: ^ ^ * * @ * @ + * 5: ^ ^ ^ ^ @ ^ * + * 6: ^ ^ ^ ^ * * @ + * 7: ^ ^ ^ ^ ^ ^ * + * + * The '@' signs are the interesting thing, since we know that + * this row isn't present in the pass data. Rewriting the + * above table with '1' for '@', little endian (i.e. row 0 at + * the LSB end): + * + * row: 76543210 + * Pass 0: 00000000 0x00 [bit 3, 0x8 of row unset (always)] + * Pass 1: 00000000 0x00 + * Pass 2: 00001111 0x0F [bit 2, 0x4 of row unset] + * Pass 3: 00000000 0x00 + * Pass 4: 00110011 0x33 [bit 1, 0x2 of row unset] + * Pass 5: 00000000 0x00 + * Pass 6: 01010101 0x55 [bit 0, 0x1 of row unset] + * + * PNG_PASS_BLOCK_SKIP(pass, row) can be written two ways; + * + * As a shift and a mask: + * (0x55330F00 >> ((pass >> 1) + (row & 7))) & ~pass & 1 + * + * And, somewhat simpler, as a bit check on the low bits of + * row: + * + * ~((row) >> (3-(pass >> 1))) & ~pass & 1 + */ +# define PNG_PASS_BLOCK_SKIP(pass, row)\ + (~((row) >> (3U-((pass) >> 1))) & ~(pass) & 0x1U) + + /* Hence: */ + png_ptr->row_state = processing_row; + return pass_width == 0 || PNG_PASS_BLOCK_SKIP(pass, + row_number) ? png_row_skip : png_row_repeat; + } /* skipped row */ + + /* processed; fall through to start_read_row_bytes unless this is + * the first row in this pass, in which case the filter byte has + * not been read. + */ + if (row_number == PNG_PASS_START_ROW(pass)) + { + state = start_of_pass; + continue; + } + } /* interlaced */ + + else /* not interlaced */ if (row_number == 0) + { + /* On the first row it is necessary to read a filter byte: */ + state = start_of_pass; + continue; /* get the filter byte */ + } + + /* FALL THROUGH */ + + case start_read_row_bytes: + /* The row is always read into png_struct::row_buffer, however if the + * row filter (png_struct::next_filter) requires the previous row it + * is necessary to make sure that the read does not overwrite it (the + * previous row:) + */ + if (png_ptr->next_filter > PNG_FILTER_VALUE_SUB && + !png_ptr->prev_in_alt) + { + /* Swap the buffers: */ + png_bytep pb = png_ptr->alt_buffer; + + /* Check this first before assignment, otherwise the same buffer + * will be stored in two members of png_struct and we will do a + * double free on an OOM in png_malloc: + */ + if (pb == NULL) + { + pb = png_voidcast(png_bytep, + png_malloc(png_ptr, png_ptr->row_allocated_bytes)); + /* SECURITY: hide the heap contents: */ + memset(pb, 0, png_ptr->row_allocated_bytes); + } + + png_ptr->alt_buffer = png_ptr->row_buffer; + png_ptr->row_buffer = pb; + png_ptr->prev_in_alt = 1; /* until the filter has been undone */ + } + + /* Now png_ptr::zstream can be set, the code below sets avail_out each + * time, but next_out is used as a progress pointer so must be reset + * once at the start: + */ + png_ptr->zstream.next_out = png_ptr->row_buffer; + state = need_row_bytes; + + case need_row_bytes: + { + png_alloc_size_t row_bytes; + png_uint_32 pass_width = width; + int last_pass_row; + png_byte row_filter; + + if (interlaced) + pass_width = PNG_PASS_COLS(pass_width, pass); + + /* Find out how many bytes are expected for this row, this relies on + * color_type, bit_depth and png_ptr->width having been validated + * for potential overflow in png_read_start_IDAT: + */ + row_bytes = PNG_ROWBYTES(PNG_PIXEL_DEPTH(*png_ptr), pass_width); + + /* Check this every time, it's fast and safe: */ + affirm(row_bytes <= png_ptr->row_allocated_bytes); + + { /* get expanded row bytes until the row is full */ + png_alloc_size_t avail_out; + png_bytep next_out = png_ptr->zstream.next_out; + + /* The affirm will fire if something tampers with next_out and + * sets it to somewhere other than row_buffer, or if it is not + * reset between passes or at the end of a row. + */ + avail_out = next_out - png_ptr->row_buffer; /*unsigned*/ + affirm(avail_out < row_bytes); + avail_out = row_bytes - avail_out; /* 1..row_bytes */ + + { /* expand (deflate) the available IDAT input */ + int finish; + png_alloc_size_t cb; + + { /* calculate 'finish' and 'last_pass_row' */ + png_uint_32 height = png_ptr->height; + + /* last_pass_row indicates that this is the last row in + * this pass and, therefore, that the filter byte for the + * next row is irrelevant (or, indeed, may not be there if + * this is the last pass.) This is trival for + * non-interlaced images and more complex for interlaced + * ones. + * + * The test is optimized for the non-interlaced case. + */ + last_pass_row = row_number+1 >= height || (interlaced && + PNG_LAST_PASS_ROW(row_number, pass, height)); + + /* Set 'finish' if this is the last row in the last pass + * of the image. + */ + finish = last_pass_row && (!interlaced || pass >= + PNG_LAST_PASS(width, height)); + } /* calculate 'finish' and 'last_pass_row' */ + + cb = png_inflate_IDAT(png_ptr, finish, next_out, avail_out); + + if (cb < avail_out) + { + png_ptr->row_state = need_row_bytes; + return png_row_incomplete; + } + } /* expand (inflate) the availble IDAT input */ + } /* get expanded row bytes until the row is full */ + + /* The row is now complete; the return immediately above ended this + * function call if insufficient IDAT data was available. + * + * At this point all the required information to process the row has + * been read from the input stream and the original, filtered, row + * data is held in png_struct::row_buffer. + * + * png_struct::next_filter must contain the filter for *this* row, + * use this to reverse the filter: + */ + row_filter = png_ptr->next_filter; + + if (row_filter > PNG_FILTER_VALUE_NONE) + { + const unsigned int bpp = (PNG_PIXEL_DEPTH(*png_ptr)+0x7)>>3; + + /* This is checked in the read code below: */ + debug(row_filter < PNG_FILTER_VALUE_LAST); + + if (png_ptr->read_filter[0] == NULL) + png_init_filter_functions(png_ptr, bpp); + + /* If the filter code needs the previous row, it must have been + * saved previously: + */ + affirm(row_filter <= PNG_FILTER_SUB || + (png_ptr->prev_in_alt && png_ptr->alt_buffer != NULL)); + + png_ptr->read_filter[row_filter-1](row_bytes, bpp, + png_ptr->row_buffer, png_ptr->alt_buffer); + } + + /* The row has been read and is now the 'previous' row for the + * next line, we need the next filter byte to determine whether + * this needs to be saved or can be overwritten if there are row + * transformations. + */ + png_ptr->prev_in_alt = 0; + + if (last_pass_row) + { + /* No next line, so no need to store this row: */ + png_ptr->next_filter = PNG_FILTER_VALUE_NONE; + state = transform_row; + continue; + } + } /* need_row_bytes */ + + state = need_filter_byte; + /* FALL THROUGH */ + + case need_filter_byte: /* for the next row */ + case start_of_pass: /* so the first byte is for the upcoming row */ + { /* read filter byte */ + png_byte row_filter; + + /* This is the filter byte that precedes the *next* row, or, at + * start_of_pass, the first row: + */ + png_alloc_size_t cb = png_inflate_IDAT(png_ptr, 0/*finish*/, + &png_ptr->next_filter, 1); + + /* This can be temporary; it verifies the invariants on how + * png_inflate_IDAT updates the {next,avail}_out fields: + */ + debug(png_ptr->zstream.avail_out == 1-cb && + png_ptr->zstream.next_out == cb + &png_ptr->next_filter); + + /* next_out points into png_struct, for security do this: */ + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0; + + /* One byte, so we either got it or have to get more input data: */ + if (cb != 1) + { + affirm(cb == 0 && png_ptr->zstream.avail_in == 0); + png_ptr->row_state = state & 3U; + return png_row_incomplete; + } + + /* Check the filter byte. */ + row_filter = png_ptr->next_filter; + + if (row_filter >= PNG_FILTER_VALUE_LAST) + png_chunk_error(png_ptr, "invalid PNG filter"); + + if (state == start_of_pass) + { + /* The filter is followed by the row data, but first check the + * filter byte; the spec requires that we invent an empty row + * if the first row of a pass requires it. + */ + if (row_filter >= PNG_FILTER_VALUE_UP) + { + /* x-0 == x, so do this optimization: */ + if (row_filter == PNG_FILTER_VALUE_UP) + png_ptr->next_filter = PNG_FILTER_VALUE_NONE; + + /* The Paeth predictor is always the preceding (leftwards) + * value, so this is the same as sub: + */ + else if (row_filter == PNG_FILTER_VALUE_PAETH) + png_ptr->next_filter = PNG_FILTER_VALUE_SUB; + + else /* PNG_FILTER_VALUE_AVG */ + { + /* It would be possible to 'invent' a new filter that did + * AVG using only the previous byte; it's 'SUB' of half the + * preceding value, but this seems pointless. + */ + png_bytep pb = png_ptr->alt_buffer; + + if (pb == NULL) + { + png_ptr->alt_buffer = pb = png_voidcast(png_bytep, + png_malloc(png_ptr, png_ptr->row_allocated_bytes)); + /* SECURITY: hide the heap contents: */ + memset(pb, 0, png_ptr->row_allocated_bytes); + } + + else + { + png_uint_32 pass_width = width; + png_alloc_size_t row_bytes; + + if (interlaced) + pass_width = PNG_PASS_COLS(pass_width, pass); + + /* Be safe here: this avoids a memory overwrite in a + * place where we are relying on previously validated + * values (NOTE: the row_bytes value may be truncated, + * that's a safe bug!) + */ + row_bytes = PNG_ROWBYTES(PNG_PIXEL_DEPTH(*png_ptr), + pass_width); + affirm(row_bytes <= png_ptr->row_allocated_bytes); + + /* Just zero the bytes that are needed; */ + memset(pb, 0, row_bytes); + } + + png_ptr->prev_in_alt = 1; + } + } /* silly first line filter */ + + /* Proceed to read the row bytes: */ + state = start_read_row_bytes; + continue; + } /* start_of_pass */ + + /* else state == need_filter_byte: + * png_struct::next_filter is the filter byte for the next row. + */ + } /* read filter byte */ + + case transform_row: + /* The entire row has been read and png_struct::next_filter is the + * filter for the next line or PNG_FILTER_VALUE_NONE if there is no + * next line. Do we have read transforms to perform? + */ +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + if (png_ptr->transform_list != NULL) + { + unsigned int max_depth; + png_transform_control tc; + + png_init_transform_control(&tc, png_ptr); + + if (interlaced) + tc.width = PNG_PASS_COLS(width, pass); + else + tc.width = width; + + tc.sp = tc.dp = png_ptr->row_buffer; /* assume overwrite ok */ + + /* Look at the filter for the *next* row, if it uses the previous + * row (this row) then row_buffer must be preserved. + */ + if (png_ptr->next_filter > PNG_FILTER_VALUE_SUB) + { + /* 'row_buffer' is the location of what will become the + * *previous* row. Depending on the transforms it may or may + * not also be the transformed row. + */ + tc.dp = png_ptr->alt_buffer; /* Transformed row */ + + if (tc.dp == NULL) + { + /* Lazy allocation; this is where alt_buffer is + * allocated if there *are* transforms to perform. + */ + png_ptr->alt_buffer = png_voidcast(png_bytep, tc.dp = + png_malloc(png_ptr, png_ptr->row_allocated_bytes)); + } + } + + /* Run the list. It is ok if it doesn't end up doing anything; + * this can happen with a lazy init, but that may mean that + * alt_buffer is allocated when it doesn't need to be. + */ + max_depth = png_run_transform_list_forwards(png_ptr, &tc); + + /* This is too late, a memory overwrite has already happened, but + * it may still prevent exploits: + */ + affirm(max_depth <= png_ptr->row_max_pixel); + + /* This check used to be performed in png_combine_row, above; + * do it here to detect the bug earlier on (this is quite common + * while making changes to the transform code!) + */ + affirm(png_ptr->row_format == tc.format && + png_ptr->row_range == tc.range && + png_ptr->row_bit_depth == tc.bit_depth); +# ifdef PNG_READ_GAMMA_SUPPORTED + affirm(png_ptr->row_gamma == tc.gamma); +# endif /* READ_GAMMA */ + + /* If the transformed data ended up in alt_buffer then swap it + * back to row_buffer; this allows the caller to always look in + * row_buffer for the output data. + */ + if (tc.sp == png_ptr->alt_buffer) + { + png_bytep pb = png_ptr->alt_buffer; + + png_ptr->alt_buffer = png_ptr->row_buffer; + png_ptr->row_buffer = pb; + png_ptr->prev_in_alt = 1; /* else it is in row_buffer */ + } + } +# endif + + png_ptr->row_state = processing_row; + return png_row_process; + + default: + impossible("bad row state"); + } /* forever switch */ +} + +/* Complete reading of the IDAT chunks. This returns 0 if more data is to + * be read, 1 if the zlib stream has terminated. Call this routine with + * zstream.avail_in greater than zero unless there is no more input data. + * When zstream_avail_in is 0 on entry and the stream does not terminate + * an "IDAT truncated" error will be output. + */ +int /* PRIVATE */ png_read_finish_IDAT(png_structrp png_ptr) { + enum + { + no_error = 0, + LZ_too_long, + IDAT_too_long, + IDAT_truncated + } error = no_error; + + /* Release row_buffer and alt_buffer first; they can use considerable + * amounts of memory. + */ + if (png_ptr->row_buffer != NULL) + { + if (png_ptr->alt_buffer != NULL) + { + png_free(png_ptr, png_ptr->alt_buffer); + png_ptr->alt_buffer = NULL; + } + + png_free(png_ptr, png_ptr->row_buffer); + png_ptr->row_buffer = NULL; + png_ptr->row_allocated_bytes = 0; + } + + affirm(png_ptr->zowner == png_IDAT); /* else this should not be called */ + /* We don't need any more data and the stream should have ended, however the * LZ end code may actually not have been processed. In this case we must * read it otherwise stray unread IDAT data or, more likely, an IDAT chunk * may still remain to be consumed. */ - if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + if (!png_ptr->zstream_ended) { - /* The NULL causes png_read_IDAT_data to swallow any remaining bytes in - * the compressed stream, but the stream may be damaged too, so even after - * this call we may need to terminate the zstream ownership. - */ - png_read_IDAT_data(png_ptr, NULL, 0); - png_ptr->zstream.next_out = NULL; /* safety */ + int end_of_IDAT = png_ptr->zstream.avail_in == 0; + png_byte b; + png_alloc_size_t cb = png_inflate_IDAT(png_ptr, 2/*finish*/, &b, 1); - /* Now clear everything out for safety; the following may not have been - * done. - */ - if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + debug(png_ptr->zstream.avail_out == 1-cb && + png_ptr->zstream.next_out == cb + &b); + + /* As above, for safety do this: */ + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0; + + /* No data is expected, either compressed or in the IDAT: */ + if (cb != 0) + error = LZ_too_long; + + else if (png_ptr->zstream.avail_in == 0 /* && cb == 0 */) { - png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + /* This is the normal case but there may still be some waiting codes + * (including the adler32 that follow the LZ77 end code; so we can + * have at least 5 bytes after the end of the row data before the + * end of the stream. + */ + if (!png_ptr->zstream_ended) + { + if (!end_of_IDAT) + return 0; /* keep reading, no detectable error yet */ + + error = IDAT_truncated; + } + + /* Else there may still be an error; too much IDAT, but we can't + * tell. + */ } } - /* If the zstream has not been released do it now *and* terminate the reading - * of the final IDAT chunk. + /* If there is still pending zstream input then there was too much IDAT + * data: + */ + if (!error && png_ptr->zstream.avail_in > 0) + error = IDAT_too_long; + + /* Either this is the success case or an error has been detected and + * warned about. */ - if (png_ptr->zowner == png_IDAT) { - /* Always do this; the pointers otherwise point into the read buffer. */ - png_ptr->zstream.next_in = NULL; - png_ptr->zstream.avail_in = 0; - - /* Now we no longer own the zstream. */ - png_ptr->zowner = 0; - - /* The slightly weird semantics of the sequential IDAT reading is that we - * are always in or at the end of an IDAT chunk, so we always need to do a - * crc_finish here. If idat_size is non-zero we also need to read the - * spurious bytes at the end of the chunk now. + int ret = inflateEnd(&png_ptr->zstream); + + /* In fact we expect this to always succeed, so it is a good idea to + * catch it in pre-release builds: */ - (void)png_crc_finish(png_ptr, png_ptr->idat_size); - } -} + debug(ret == Z_OK); -void /* PRIVATE */ -png_read_finish_row(png_structrp png_ptr) -{ -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - - /* Start of interlace block */ - static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - /* Start of interlace block in the y direction */ - static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; - - /* Offset to next interlace block in the y direction */ - static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; -#endif /* READ_INTERLACING */ - - png_debug(1, "in png_read_finish_row"); - png_ptr->row_number++; - if (png_ptr->row_number < png_ptr->num_rows) - return; - -#ifdef PNG_READ_INTERLACING_SUPPORTED - if (png_ptr->interlaced) - { - png_ptr->row_number = 0; - - /* TO DO: don't do this if prev_row isn't needed (requires - * read-ahead of the next row's filter byte. - */ - memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); - - do + if (ret != Z_OK) { - png_ptr->pass++; + /* This is just a warning; it's safe, and the zstream_error flag is + * not set. + */ + png_zstream_error(png_ptr, ret); + png_chunk_warning(png_ptr, png_ptr->zstream.msg); + } + } - if (png_ptr->pass >= 7) + /* Output an error message if required: */ + if (error && !png_ptr->zstream_error) + { + switch (error) + { + case LZ_too_long: + png_benign_error(png_ptr, "compressed data too long"); break; - png_ptr->iwidth = (png_ptr->width + - png_pass_inc[png_ptr->pass] - 1 - - png_pass_start[png_ptr->pass]) / - png_pass_inc[png_ptr->pass]; + case IDAT_too_long: + png_benign_error(png_ptr, "uncompressed data too long"); + break; - if (!(png_ptr->transformations & PNG_INTERLACE)) - { - png_ptr->num_rows = (png_ptr->height + - png_pass_yinc[png_ptr->pass] - 1 - - png_pass_ystart[png_ptr->pass]) / - png_pass_yinc[png_ptr->pass]; - } + case IDAT_truncated: + png_benign_error(png_ptr, "data truncated"); + break; - else /* if (png_ptr->transformations & PNG_INTERLACE) */ - break; /* libpng deinterlacing sees every row */ + default: + case no_error: /* Satisfy the compiler */ + break; + } - } while (png_ptr->num_rows == 0 || png_ptr->iwidth == 0); - - if (png_ptr->pass < 7) - return; + png_ptr->zstream_error = 1; } -#endif /* READ_INTERLACING */ - /* Here after at the end of the last row of the last pass. */ - png_read_finish_IDAT(png_ptr); + /* WARNING: leave {next,avail}_in set here, the progressive reader uses these + * to complete the PNG chunk CRC calculation. + */ + png_ptr->zstream_ended = 1; + png_ptr->zowner = 0; + + return 1; /* end of stream */ } -#endif /* SEQUENTIAL_READ */ -void /* PRIVATE */ -png_read_start_row(png_structrp png_ptr) +/* Optional call to update the users info_ptr structure, can be used from both + * the progressive and sequential reader, but the app must call it. + */ +void PNGAPI +png_read_update_info(png_structrp png_ptr, png_inforp info_ptr) { -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + png_debug(1, "in png_read_update_info"); - /* Start of interlace block */ - static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - /* Start of interlace block in the y direction */ - static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; - - /* Offset to next interlace block in the y direction */ - static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; -#endif - - unsigned int max_pixel_depth; - png_size_t row_bytes; - - png_debug(1, "in png_read_start_row"); - -#ifdef PNG_READ_TRANSFORMS_SUPPORTED - png_init_read_transformations(png_ptr); -#endif -#ifdef PNG_READ_INTERLACING_SUPPORTED - if (png_ptr->interlaced) + if (png_ptr != NULL) { - if (!(png_ptr->transformations & PNG_INTERLACE)) - png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - - png_pass_ystart[0]) / png_pass_yinc[0]; + if (png_ptr->zowner != png_IDAT) + { + png_read_start_IDAT(png_ptr); +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + png_read_transform_info(png_ptr, info_ptr); +# else + PNG_UNUSED(info_ptr) +# endif + } + + /* New in 1.6.0 this avoids the bug of doing the initializations twice */ else - png_ptr->num_rows = png_ptr->height; - - png_ptr->iwidth = (png_ptr->width + - png_pass_inc[png_ptr->pass] - 1 - - png_pass_start[png_ptr->pass]) / - png_pass_inc[png_ptr->pass]; + png_app_error(png_ptr, + "png_read_update_info/png_start_read_image: duplicate call"); } - - else -#endif /* READ_INTERLACING */ - { - png_ptr->num_rows = png_ptr->height; - png_ptr->iwidth = png_ptr->width; - } - - max_pixel_depth = png_ptr->pixel_depth; - - /* WARNING: * png_read_transform_info (pngrtran.c) performs a simpler set of - * calculations to calculate the final pixel depth, then - * png_do_read_transforms actually does the transforms. This means that the - * code which effectively calculates this value is actually repeated in three - * separate places. They must all match. Innocent changes to the order of - * transformations can and will break libpng in a way that causes memory - * overwrites. - * - * TODO: fix this. - */ -#ifdef PNG_READ_PACK_SUPPORTED - if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8) - max_pixel_depth = 8; -#endif - -#ifdef PNG_READ_EXPAND_SUPPORTED - if (png_ptr->transformations & PNG_EXPAND) - { - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - { - if (png_ptr->num_trans) - max_pixel_depth = 32; - - else - max_pixel_depth = 24; - } - - else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) - { - if (max_pixel_depth < 8) - max_pixel_depth = 8; - - if (png_ptr->num_trans) - max_pixel_depth *= 2; - } - - else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) - { - if (png_ptr->num_trans) - { - max_pixel_depth *= 4; - max_pixel_depth /= 3; - } - } - } -#endif - -#ifdef PNG_READ_EXPAND_16_SUPPORTED - if (png_ptr->transformations & PNG_EXPAND_16) - { -# ifdef PNG_READ_EXPAND_SUPPORTED - /* In fact it is an error if it isn't supported, but checking is - * the safe way. - */ - if (png_ptr->transformations & PNG_EXPAND) - { - if (png_ptr->bit_depth < 16) - max_pixel_depth *= 2; - } - else -# endif - png_ptr->transformations &= ~PNG_EXPAND_16; - } -#endif - -#ifdef PNG_READ_FILLER_SUPPORTED - if (png_ptr->transformations & (PNG_FILLER)) - { - if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) - { - if (max_pixel_depth <= 8) - max_pixel_depth = 16; - - else - max_pixel_depth = 32; - } - - else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB || - png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - { - if (max_pixel_depth <= 32) - max_pixel_depth = 32; - - else - max_pixel_depth = 64; - } - } -#endif - -#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED - if (png_ptr->transformations & PNG_GRAY_TO_RGB) - { - if ( -#ifdef PNG_READ_EXPAND_SUPPORTED - (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || -#endif -#ifdef PNG_READ_FILLER_SUPPORTED - (png_ptr->transformations & (PNG_FILLER)) || -#endif - png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - { - if (max_pixel_depth <= 16) - max_pixel_depth = 32; - - else - max_pixel_depth = 64; - } - - else - { - if (max_pixel_depth <= 8) - { - if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - max_pixel_depth = 32; - - else - max_pixel_depth = 24; - } - - else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - max_pixel_depth = 64; - - else - max_pixel_depth = 48; - } - } -#endif - -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ -defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) - if (png_ptr->transformations & PNG_USER_TRANSFORM) - { - unsigned int user_pixel_depth = png_ptr->user_transform_depth * - png_ptr->user_transform_channels; - - if (user_pixel_depth > max_pixel_depth) - max_pixel_depth = user_pixel_depth; - } -#endif - - /* This value is stored in png_struct and double checked in the row read - * code. - */ - 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 - * for interlacing - */ - row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7)); - /* Calculate the maximum bytes needed, adding a byte and a pixel - * for safety's sake - */ - row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) + - 1 + ((max_pixel_depth + 7) >> 3); - -#ifdef PNG_MAX_MALLOC_64K - if (row_bytes > (png_uint_32)65536L) - png_error(png_ptr, "This image requires a row greater than 64KB"); -#endif - - if (row_bytes + 48 > png_ptr->big_row_buf_size) - { - png_free(png_ptr, png_ptr->big_row_buf); - png_free(png_ptr, png_ptr->big_prev_row); - - if (png_ptr->interlaced) - png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr, - row_bytes + 48); - - else - png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48); - - png_ptr->big_prev_row = (png_bytep)png_malloc(png_ptr, row_bytes + 48); - -#ifdef PNG_ALIGNED_MEMORY_SUPPORTED - /* Use 16-byte aligned memory for row_buf with at least 16 bytes - * of padding before and after row_buf; treat prev_row similarly. - * NOTE: the alignment is to the start of the pixels, one beyond the start - * of the buffer, because of the filter byte. Prior to libpng 1.5.6 this - * was incorrect; the filter byte was aligned, which had the exact - * opposite effect of that intended. - */ - { - png_bytep temp = png_ptr->big_row_buf + 32; - int extra = (int)((temp - (png_bytep)0) & 0x0f); - png_ptr->row_buf = temp - extra - 1/*filter byte*/; - - temp = png_ptr->big_prev_row + 32; - extra = (int)((temp - (png_bytep)0) & 0x0f); - png_ptr->prev_row = temp - extra - 1/*filter byte*/; - } - -#else - /* Use 31 bytes of padding before and 17 bytes after row_buf. */ - png_ptr->row_buf = png_ptr->big_row_buf + 31; - png_ptr->prev_row = png_ptr->big_prev_row + 31; -#endif - png_ptr->big_row_buf_size = row_bytes + 48; - } - -#ifdef PNG_MAX_MALLOC_64K - if (png_ptr->rowbytes > 65535) - png_error(png_ptr, "This image requires a row greater than 64KB"); - -#endif - if (png_ptr->rowbytes > (PNG_SIZE_MAX - 1)) - png_error(png_ptr, "Row has too many bytes to allocate in memory"); - - memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); - - png_debug1(3, "width = %u,", png_ptr->width); - png_debug1(3, "height = %u,", png_ptr->height); - png_debug1(3, "iwidth = %u,", png_ptr->iwidth); - png_debug1(3, "num_rows = %u,", png_ptr->num_rows); - png_debug1(3, "rowbytes = %lu,", (unsigned long)png_ptr->rowbytes); - png_debug1(3, "irowbytes = %lu", - (unsigned long)PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1); - - /* The sequential reader needs a buffer for IDAT, but the progressive reader - * does not, so free the read buffer now regardless; the sequential reader - * reallocates it on demand. - */ - if (png_ptr->read_buffer) - { - png_bytep buffer = png_ptr->read_buffer; - - png_ptr->read_buffer_size = 0; - png_ptr->read_buffer = NULL; - png_free(png_ptr, buffer); - } - - /* Finally claim the zstream for the inflate of the IDAT data, use the bits - * value from the stream (note that this will result in a fatal error if the - * IDAT stream has a bogus deflate header window_bits value, but this should - * not be happening any longer!) - */ - if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK) - png_error(png_ptr, png_ptr->zstream.msg); - - png_ptr->flags |= PNG_FLAG_ROW_INIT; } + #endif /* READ */ diff --git a/pngset.c b/pngset.c index 91448ee99..1933a7b34 100644 --- a/pngset.c +++ b/pngset.c @@ -164,8 +164,6 @@ void PNGAPI png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr, png_const_uint_16p hist) { - int i; - png_debug1(1, "in %s storage function", "hIST"); if (png_ptr == NULL || info_ptr == NULL) @@ -197,8 +195,12 @@ png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr, info_ptr->free_me |= PNG_FREE_HIST; - for (i = 0; i < info_ptr->num_palette; i++) - info_ptr->hist[i] = hist[i]; + { + unsigned int i; + + for (i = 0; i < info_ptr->num_palette; i++) + info_ptr->hist[i] = hist[i]; + } info_ptr->valid |= PNG_INFO_hIST; } @@ -217,32 +219,16 @@ png_set_IHDR(png_const_structrp png_ptr, png_inforp info_ptr, info_ptr->width = width; info_ptr->height = height; - info_ptr->bit_depth = png_check_byte(png_ptr, bit_depth); - info_ptr->color_type = png_check_byte(png_ptr, color_type); + info_ptr->bit_depth = png_check_bits(png_ptr, bit_depth, 6); + info_ptr->format = png_check_bits(png_ptr, + PNG_FORMAT_FROM_COLOR_TYPE(color_type), PNG_RF_BITS); info_ptr->compression_type = png_check_byte(png_ptr, compression_type); info_ptr->filter_type = png_check_byte(png_ptr, filter_type); info_ptr->interlace_type = png_check_byte(png_ptr, interlace_type); png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, - info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->bit_depth, color_type, info_ptr->interlace_type, info_ptr->compression_type, info_ptr->filter_type); - - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - info_ptr->channels = 1; - - else if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) - info_ptr->channels = 3; - - else - info_ptr->channels = 1; - - if ((info_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0) - info_ptr->channels++; - - info_ptr->pixel_depth = png_check_byte(png_ptr, info_ptr->channels * - info_ptr->bit_depth); - - info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); } #ifdef PNG_oFFs_SUPPORTED @@ -523,26 +509,23 @@ png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr, if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) { - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - png_error(png_ptr, "Invalid palette length"); + if ((info_ptr->format == PNG_FORMAT_FLAG_COLORMAP) != 0) + png_chunk_error(png_ptr, "Invalid palette length"); else { - png_warning(png_ptr, "Invalid palette length"); - + png_chunk_report(png_ptr, "Invalid palette length", PNG_CHUNK_ERROR); return; } } if ((num_palette > 0 && palette == NULL) || (num_palette == 0 -# ifdef PNG_MNG_FEATURES_SUPPORTED - && (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 -# endif +# ifdef PNG_MNG_FEATURES_SUPPORTED + && (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 +# endif /* MNG_FEATURES */ )) - { png_error(png_ptr, "Invalid palette"); - } /* It may not actually be necessary to set png_ptr->palette here; * we do it for backward compatibility with the way the png_handle_tRNS @@ -563,8 +546,8 @@ png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr, if (num_palette > 0) memcpy(png_ptr->palette, palette, num_palette * (sizeof (png_color))); info_ptr->palette = png_ptr->palette; - info_ptr->num_palette = png_ptr->num_palette = - png_check_u16(png_ptr, num_palette); + info_ptr->num_palette = png_ptr->num_palette = png_check_bits(png_ptr, + num_palette, 9); info_ptr->free_me |= PNG_FREE_PLTE; @@ -647,7 +630,7 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, */ { int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name, - proflen, profile, info_ptr->color_type); + proflen, profile, (info_ptr->format & PNG_FORMAT_FLAG_COLOR) != 0); png_colorspace_sync_info(png_ptr, info_ptr); @@ -730,6 +713,11 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr, int max_text; png_textp new_text = NULL; + /* The code below goes horribly wrong if old_num_text ever ends up + * negative, so: + */ + affirm(old_num_text >= 0); + /* Calculate an appropriate max_text, checking for overflow. */ max_text = old_num_text; if (num_text <= INT_MAX - max_text) @@ -908,17 +896,30 @@ png_set_tIME(png_const_structrp png_ptr, png_inforp info_ptr, { png_debug1(1, "in %s storage function", "tIME"); - if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL || - (png_ptr->mode & PNG_WROTE_tIME) != 0) + if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL) return; + /* It is valid to do set the tIME chunk after the IDAT has been written, but + * not if one has already been written. This was ignored before - the + * previous time was used - this is a bad thing. + */ + if ((info_ptr->valid & PNG_INFO_tIME) != 0 /* Changing a time chunk */ && + (png_ptr->mode & PNG_HAVE_IHDR) != 0 /* after writing started */) + { + /* So it can be *set* but it can't be *changed* after the info before PLTE + * has been written. (Note that putting tIME into an unknown chunk + * currently gets round this; to be fixed.) + */ + png_app_error(png_ptr, "cannot change tIME after writing starts"); + return; + } + if (mod_time->month == 0 || mod_time->month > 12 || mod_time->day == 0 || mod_time->day > 31 || mod_time->hour > 23 || mod_time->minute > 59 || mod_time->second > 60) { - png_warning(png_ptr, "Ignoring invalid time value"); - + png_app_error(png_ptr, "Ignoring invalid time value"); return; } @@ -937,11 +938,11 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, if (png_ptr == NULL || info_ptr == NULL) return; - if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + if ((info_ptr->format & PNG_FORMAT_FLAG_ALPHA) != 0) png_chunk_report(png_ptr, "png_set_tRNS: invalid on PNG with alpha channel", PNG_CHUNK_ERROR); - else if (info_ptr->color_type & PNG_COLOR_MASK_PALETTE) + else if ((info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) != 0) { int max_num; @@ -949,7 +950,7 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); /* Do this just in case the old data was not owned by libpng: */ - info_ptr->valid &= ~PNG_INFO_tRNS; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS); info_ptr->trans_alpha = NULL; info_ptr->num_trans = 0; @@ -988,7 +989,7 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, memcpy(info_ptr->trans_alpha, trans_alpha, (unsigned)/*SAFE*/num_trans); info_ptr->valid |= PNG_INFO_tRNS; - info_ptr->num_trans = png_check_u16(png_ptr, num_trans); + info_ptr->num_trans = png_check_bits(png_ptr, num_trans, 9); } } @@ -997,19 +998,18 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, /* Invalidate any prior transparent color and set num_trans. It is not * used internally in this case but png_get_tRNS still returns it. */ - info_ptr->valid &= ~PNG_INFO_tRNS; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS); info_ptr->num_trans = 0; /* for png_get_tRNS */ if (trans_color != NULL && info_ptr->bit_depth < 16) { unsigned int sample_max = (1U << info_ptr->bit_depth) - 1U; - if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && - trans_color->gray <= sample_max) || - (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + if (!(info_ptr->format & PNG_FORMAT_FLAG_COLOR) ? + trans_color->gray <= sample_max : trans_color->red <= sample_max && trans_color->green <= sample_max && - trans_color->blue <= sample_max)) + trans_color->blue <= sample_max) { info_ptr->trans_color = *trans_color; info_ptr->valid |= PNG_INFO_tRNS; @@ -1135,7 +1135,7 @@ check_location(png_const_structrp png_ptr, int location) * change; previously the app had to use the * png_set_unknown_chunk_location API below for each chunk. */ - if (location == 0 && (png_ptr->mode & PNG_IS_READ_STRUCT) == 0) + if (location == 0 && !png_ptr->read_struct) { /* Write struct, so unknown chunks come from the app */ png_app_warning(png_ptr, @@ -1179,18 +1179,16 @@ png_set_unknown_chunks(png_structrp png_ptr, * code) but may be meaningless if the read or write handling of unknown * chunks is not compiled in. */ -# if !defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) && \ - defined(PNG_READ_SUPPORTED) - if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) +# ifndef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + if (png_ptr->read_struct) { png_app_error(png_ptr, "no unknown chunk support on read"); return; } # endif -# if !defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) && \ - defined(PNG_WRITE_SUPPORTED) - if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0) +# ifndef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + if (!png_ptr->read_struct) { png_app_error(png_ptr, "no unknown chunk support on write"); @@ -1209,9 +1207,7 @@ png_set_unknown_chunks(png_structrp png_ptr, if (np == NULL) { - png_chunk_report(png_ptr, "too many unknown chunks", - PNG_CHUNK_WRITE_ERROR); - + png_chunk_report(png_ptr, "too many unknown chunks", PNG_CHUNK_ERROR); return; } @@ -1244,7 +1240,7 @@ png_set_unknown_chunks(png_structrp png_ptr, if (np->data == NULL) { png_chunk_report(png_ptr, "unknown chunk: out of memory", - PNG_CHUNK_WRITE_ERROR); + PNG_CHUNK_ERROR); /* But just skip storing the unknown chunk */ continue; } @@ -1293,24 +1289,9 @@ png_set_unknown_chunk_location(png_const_structrp png_ptr, png_inforp info_ptr, } #endif /* STORE_UNKNOWN_CHUNKS */ -#ifdef PNG_MNG_FEATURES_SUPPORTED -png_uint_32 PNGAPI -png_permit_mng_features (png_structrp png_ptr, png_uint_32 mng_features) -{ - png_debug(1, "in png_permit_mng_features"); - - if (png_ptr == NULL) - return 0; - - png_ptr->mng_features_permitted = mng_features & PNG_ALL_MNG_FEATURES; - - return png_ptr->mng_features_permitted; -} -#endif - #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED static unsigned int -add_one_chunk(png_const_structrp png_ptr, png_bytep list, unsigned int count, +add_one_chunk(png_structrp png_ptr, png_bytep list, unsigned int count, png_const_bytep add, int keep) { unsigned int i; @@ -1322,8 +1303,8 @@ add_one_chunk(png_const_structrp png_ptr, png_bytep list, unsigned int count, { if (memcmp(list, add, 4) == 0) { - list[4] = png_check_byte(png_ptr, keep); - + list[4] = keep & 0x3; + png_cache_known_unknown(png_ptr, add, keep); return count; } } @@ -1333,10 +1314,10 @@ add_one_chunk(png_const_structrp png_ptr, png_bytep list, unsigned int count, ++count; memcpy(list, add, 4); list[4] = png_check_byte(png_ptr, keep); + png_cache_known_unknown(png_ptr, add, keep); } return count; - PNG_UNUSEDRC(png_ptr) } void PNGAPI @@ -1349,16 +1330,27 @@ png_set_keep_unknown_chunks(png_structrp png_ptr, int keep, if (png_ptr == NULL) return; + /* To actually use IF_SAFE or ALWAYS on read it is necessary to have the + * read SAVE code enabled. + */ if (keep < 0 || keep >= PNG_HANDLE_CHUNK_LAST) { png_app_error(png_ptr, "png_set_keep_unknown_chunks: invalid keep"); - return; } +# ifndef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + /* This is only a warning; if the application handles the chunk with a + * read callback it may work fine. + */ + if (png_ptr->read_struct && keep >= PNG_HANDLE_CHUNK_IF_SAFE) + png_app_warning(png_ptr, + "png_set_keep_unknown_chunks: unsupported keep"); +# endif + if (num_chunks_in <= 0) { - png_ptr->unknown_default = keep; + png_ptr->unknown_default = (unsigned)keep & 0x3; /* '0' means just set the flags, so stop here */ if (num_chunks_in == 0) @@ -1402,7 +1394,6 @@ png_set_keep_unknown_chunks(png_structrp png_ptr, int keep, * which can be switched off. */ png_app_error(png_ptr, "png_set_keep_unknown_chunks: no chunk list"); - return; } @@ -1418,7 +1409,6 @@ png_set_keep_unknown_chunks(png_structrp png_ptr, int keep, if (num_chunks + old_num_chunks > UINT_MAX/5) { png_app_error(png_ptr, "png_set_keep_unknown_chunks: too many chunks"); - return; } @@ -1511,7 +1501,7 @@ png_set_read_user_chunk_fn(png_structrp png_ptr, png_voidp user_chunk_ptr, } #endif -#ifdef PNG_INFO_IMAGE_SUPPORTED +#ifdef PNG_WRITE_PNG_SUPPORTED void PNGAPI png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr, png_bytepp row_pointers) @@ -1542,7 +1532,7 @@ png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size) png_error(png_ptr, "invalid compression buffer size"); # ifdef PNG_SEQUENTIAL_READ_SUPPORTED - if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) + if (png_ptr->read_struct) { png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */ return; @@ -1550,7 +1540,7 @@ png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size) # endif # ifdef PNG_WRITE_SUPPORTED - if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0) + if (!png_ptr->read_struct) { if (png_ptr->zowner != 0) { @@ -1596,7 +1586,7 @@ void PNGAPI png_set_invalid(png_const_structrp png_ptr, png_inforp info_ptr, int mask) { if (png_ptr != NULL && info_ptr != NULL) - info_ptr->valid &= ~mask; + info_ptr->valid &= PNG_BIC_MASK(mask); } @@ -1653,30 +1643,8 @@ png_set_benign_errors(png_structrp png_ptr, int allowed) PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN; else - png_ptr->flags &= ~(PNG_FLAG_BENIGN_ERRORS_WARN | + png_ptr->flags &= PNG_BIC_MASK(PNG_FLAG_BENIGN_ERRORS_WARN | PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN); } #endif /* BENIGN_ERRORS */ - -#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED - /* Whether to report invalid palette index; added at libng-1.5.10. - * It is possible for an indexed (color-type==3) PNG file to contain - * pixels with invalid (out-of-range) indexes if the PLTE chunk has - * fewer entries than the image's bit-depth would allow. We recover - * from this gracefully by filling any incomplete palette with zeros - * (opaque black). By default, when this occurs libpng will issue - * a benign error. This API can be used to override that behavior. - */ -void PNGAPI -png_set_check_for_invalid_index(png_structrp png_ptr, int allowed) -{ - png_debug(1, "in png_set_check_for_invalid_index"); - - if (allowed > 0) - png_ptr->num_palette_max = 0; - - else - png_ptr->num_palette_max = -1; -} -#endif #endif /* READ || WRITE */ diff --git a/pngstruct.h b/pngstruct.h index 38256ebf5..fac39a2a5 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -130,19 +130,228 @@ typedef struct png_colorspace typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp; /* General flags for the 'flags' field */ -#define PNG_COLORSPACE_HAVE_GAMMA 0x0001 -#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002 -#define PNG_COLORSPACE_HAVE_INTENT 0x0004 -#define PNG_COLORSPACE_FROM_gAMA 0x0008 -#define PNG_COLORSPACE_FROM_cHRM 0x0010 -#define PNG_COLORSPACE_FROM_sRGB 0x0020 -#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040 -#define PNG_COLORSPACE_MATCHES_sRGB 0x0080 /* exact match on profile */ -#define PNG_COLORSPACE_RGB_TO_GRAY_SET 0x0100 /* user specified coeffs */ -#define PNG_COLORSPACE_INVALID 0x8000 -#define PNG_COLORSPACE_CANCEL(flags) (0xffff ^ (flags)) +#define PNG_COLORSPACE_HAVE_GAMMA 0x0001U +#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002U +#define PNG_COLORSPACE_HAVE_INTENT 0x0004U +#define PNG_COLORSPACE_FROM_gAMA 0x0008U +#define PNG_COLORSPACE_FROM_cHRM 0x0010U +#define PNG_COLORSPACE_FROM_sRGB 0x0020U +#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040U +#define PNG_COLORSPACE_MATCHES_sRGB 0x0080U /* exact match on profile */ +#define PNG_COLORSPACE_RGB_TO_GRAY_SET 0x0100U /* user specified coeffs */ +#define PNG_COLORSPACE_INVALID 0x8000U +#define PNG_COLORSPACE_CANCEL(flags) (0xffffU - (flags)) #endif /* COLORSPACE || GAMMA */ +#ifdef PNG_TRANSFORM_MECH_SUPPORTED +/***************************** READ and WRITE TRANSFORMS *********************** + * These structures are used in pngrtran.c, pngwtran.c and pngtrans.c to hold + * information about transforms in progress. This mechanism was introduced in + * libpng 1.7.0 to ensure reliable transform code and to fix multiple bugs in + * the pre-1.7 transform handling. + * + * Prior to 1.7.0 the internal transform routines 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. + ***************************** READ and WRITE TRANSFORMS **********************/ +typedef struct +{ + png_structp png_ptr; /* png_struct for error handling and some + * transform parameters. May be aliased. + */ + png_const_voidp sp; /* Source; the input row. */ + png_voidp dp; /* Output buffer for the transformed row, + * this may be the same as sp. + */ + /* If the row is changed the tranform routine must write the result to + * dp[] and set sp to dp, otherwise it must not write to dp and must leave + * sp unchanged. dp[] and sp[] are both 'malloc' aligned; i.e. they have + * the system alignment, so the data can be read as any valid ANSI-C + * type. + */ + png_uint_32 width; /* width of row */ +# ifdef PNG_READ_GAMMA_SUPPORTED + png_fixed_point gamma; /* Current gamma of the row data */ + /* When a row is being transformed this contains the current gamma of the + * data if known. During initialization the value is used to accumulate + * information for png_struct::row_gamma in the first step, + * PNG_TC_INIT_FORMAT, then used to insert the correct gamma transforms + * during PNG_TC_INIT_FINAL. The field is only used on read; write + * transforms do not modify the gamma of the data. + */ +# endif + unsigned int format; /* As pngstruct::row_format below */ + unsigned int range; /* Count of range transforms */ +# define PNG_TC_CHANNELS(tc) PNG_FORMAT_CHANNELS((tc).format) + unsigned int bit_depth; /* bit depth of row */ + png_byte sBIT_R; + png_byte sBIT_G; + png_byte sBIT_B; + png_byte sBIT_A; /* Signnificant bits in the row channels. */ + /* The above four values are initially set to the number of bits significant + * in the input PNG data, R/G/B are set to the same (gray) value for + * grayscale input. All values are set to the bit depth if there is no sBIT + * chunk, if there is no alpha channel sBIT_A is set to the bit depth. + * + * When any potentially spurious bits have been cleared PNG_INFO_sBIT will be + * set in invalid_info. From this point on the above values record the + * approximate number of bits of accuracy in the channels and the lower bits + * should be preserved; they potentially contain useful information. + */ +# define PNG_TC_PIXEL_DEPTH(tc) (PNG_TC_CHANNELS(tc) * (tc).bit_depth) +# define PNG_TC_ROWBYTES(tc) PNG_ROWBYTES(PNG_TC_PIXEL_DEPTH(tc), (tc).width) + unsigned int invalid_info; /* PNG_INFO_* for invalidated chunks */ + unsigned int cost; /* Cache cost */ +# define PNG_CACHE_COST_LIMIT 0x100U + /* This is a runtime structure, so size doesn't matter much, and it helps + * code reliability to use real member names here. Feel free to experiment + * with integer values rather than bitfields. + */ + unsigned int init :2; /* 0 for processing, non zero for init: */ +# define PNG_TC_INIT_FORMAT 0x01U /* Initialization step 1: just set 'format', + * 'bit_depth' and 'gamma' to the output + * values iff the transform corresponds to + * a user requested change to those values. + */ +# define PNG_TC_INIT_FINAL 0x03U /* Initialization step 2; set the 'format' + * 'bit_depth' and 'gamma' to the values the + * transform will actually produce (which + * need not be the same as the above). + */ + /* During initialization 'init' must be set and sp and dp may be NULL. If + * neither flag is set sp and dp must be non-NULL. + * + * When the transform runs it must update 'format', 'bit_depth' and 'gamma' + * to the values previously reported during PNG_TC_INIT_FINAL; not doing so + * may result in an affirm from a later transform. + */ + unsigned int caching :1; /* The color values are being used to + * generate a cache of the transforms. + */ + unsigned int palette :1; /* The values come from a PNG palette and + * the palette will not be expanded. The + * CACHE flag must be set too. A + * transform which causes the palette to + * be expanded must clear this flag. + */ +#if 0 /* NYI */ + unsigned int interchannel:1; /* Set by a transform that combines two or + * more channels together; for example + * alpha composition or RGB to gray. + */ +#endif /* NYI */ + unsigned int channel_add :1; /* A channel (alpha/filler) was added */ + unsigned int strip_alpha :1; /* Set if the alpha channel will be + * stripped on read, this also prevents + * the tRNS chunk being expanded. Only + * some transforms check this, depending + * on the handling order and checks in + * pre-1.7 versions. + */ + unsigned int expand_tRNS :1; /* Set if the tRNS chunk should be + * expanded (ignored if read_strip_alpha + * is set). If this is *not* set + * transforms which do not use alpha/tRNS + * but would invalidate it (such as + * simple gamma correction) will simply + * mark the tRNS info as invalid. + */ + unsigned int transparent_alpha :1; /* Indicates that the alpha channel + * consists entirely of opaque (1.0 alpha) + * or completely transparent (0.0 alpha) + * pixels. Set when tRNS is expanded to + * alpha. + */ + unsigned int optimized_alpha :1; /* Meaningful only when bit_depth is + * 16 and gamma is 1 or unknown (0). + * Indicates that pixels which are opaque + * (alpha 1.0) have not been expanded to + * 16-bit linear; instead these pixels + * are encoded in the final format in + * png_struct::row_bit_depth and + * png_struct::row_gamma. This will + * invariably match the file format. + */ +} png_transform_control, *png_transform_controlp; + +typedef const png_transform_control *png_const_transform_controlp; +typedef const png_row_info *png_const_row_infop; + +typedef struct png_transform *png_transformp; /* Forward declaration */ +typedef void (*png_transform_free_fn)(/* Function to free a transform */ + png_const_structrp png_ptr, + png_transformp transform); /* pointer to this transform */ + /* This function need not exist in a transform, it must free all the data + * allocated within the transform but not the transform itself. It is called + * from png_transform_free. + */ +typedef void (*png_transform_fn)(/* Function to implement a transform */ + png_transformp *transform, /* pointer to this transform */ + png_transform_controlp control); /* row information */ + /* The transform function has two modes of operation: + * + * 1) Initialization. The list of transforms is processed from the start to + * the end and each function is called with one of hte PNG_TC_INIT_ flags + * set in control->flags, control->dp and control->sp may be NULL. + * + * For read the control structure contains the input row format and bit + * depth, the transform function changes this to represent what the + * transform will produce when it runs. + * + * For write the control structure contains the *required* output format + * and bit depth. The transform function changes this to the values that + * it needs to produce the required values. + * + * In both cases the transform function may update the 'fn' function to a + * new function to perform the desired transform; this allows considerable + * optimization on multi-row images. + * + * In both cases the caller considers the pixel bit depth changes and + * records the maximum required so that it can allocate a suitably sized + * buffer. + * + * 2) Execution. + * + * In the read case the transforms are processed in the stored order and + * must transform the row data appropriately *and* update the bit depth + * and format as before. + * + * In the write case the transforms are called in the reverse order and + * the input bit depth and format should match the required values. + * + * It is valid during initialization for the transform function to push + * another transform into the list in either the read or the write case if + * the transform cannot handle (read) or produce (write) the required format. * The transform pushes another transform into the list ahead of itself (at + * *transform) and runs that initialization; when control is returned to the + * caller the caller will re-run the transform initialization. + * + * It is also valid (during initialization) to push new transforms onto the + * list, just so long as the order of the transform is greater than the + * current transform (so that the caller will still call the new transform + * initialization.) + * + * In the write case the user transform callback might still end up producing + * an unexpected format, but at present this is unavoidable; the libpng API + * is extremely inconsistent in how a user transform reports the changes it + * made. + * + * TODO: fix this, probably with an API change in 1.7.0 + */ +typedef struct png_transform /* Linked list of transform functions */ +{ + png_transformp next; /* Next transform in the list */ + png_transform_fn fn; /* Function to implement the transform */ + png_transform_free_fn free; /* Free allocated data, normally NULL */ + unsigned int order; /* Order of the transform in the list. */ + unsigned int size; /* Size of this structure (max 65535) */ + png_uint_32 args; /* Optional transform arguments. */ +} png_transform; +#endif /* TRANSFORM_MECH */ + struct png_struct_def { /* Rearranged in libpng 1.7 to attempt to lessen padding; in general @@ -165,50 +374,223 @@ struct png_struct_def * and faster. */ png_colorp palette; /* palette from the input file */ +#ifdef PNG_READ_tRNS_SUPPORTED 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 */ - png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */ - png_uint_32 crc; /* current chunk CRC value */ - png_uint_32 mode; /* tells us where we are in the PNG file */ - png_uint_32 flags; /* flags indicating various things to libpng */ - png_uint_32 transformations;/* which transformations to perform */ - png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */ - png_uint_32 free_me; /* items libpng is responsible for freeing */ - - 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 - png_uint_16 num_palette; /* number of color entries in palette */ - png_uint_16 num_trans; /* number of transparency values */ + png_uint_32 width; /* width of image in pixels */ + png_uint_32 height; /* height of image in pixels */ + png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */ + png_uint_32 chunk_length; /* Length (possibly remaining) in said chunk. */ + png_uint_32 crc; /* current chunk CRC value */ + png_uint_32 free_me; /* items libpng is responsible for freeing */ + + unsigned int flags; /* flags (should be bit fields) */ + unsigned int mode :6; /* where we are in the PNG file */ + unsigned int read_struct :1; /* this is a read (not write) struct */ + unsigned int num_palette :9; /* number of color entries in palette */ +#ifdef PNG_READ_tRNS_SUPPORTED + unsigned int num_trans :9; /* number of transparency values */ + unsigned int transparent_palette :1; /* if they are all 0 or 255 */ +#endif /* READ_tRNS */ +#ifdef PNG_GET_PALETTE_MAX_SUPPORTED + unsigned int palette_index_max :9; /* maximum palette index found in IDAT */ +#endif /* GET_PALETTE_MAX */ +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED + unsigned int palette_index_check_disabled :1; /* defaults to 0, 'enabled' */ + unsigned int palette_index_check_issued :1; /* error message output */ +#endif /* CHECK_FOR_INVALID_INDEX */ +#ifdef PNG_READ_tRNS_SUPPORTED + png_color_16 trans_color; /* transparent color for non-paletted files */ +#endif /* READ_tRNS */ +#ifdef PNG_READ_sBIT_SUPPORTED + png_color_8 sig_bit; /* significant bits in each channel */ +#endif /* READ_sBIT */ /* Single byte values, typically used either to save space or to hold 1-byte * values from the PNG chunk specifications. */ png_byte compression_type; /* file compression type (always 0) */ - png_byte filter; /* file filter type (always 0) */ + png_byte filter_method; /* file filter type (only non-0 with MNG) */ png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ - png_byte pass; /* current interlace pass (0 - 6) */ - png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ png_byte color_type; /* color type of file */ png_byte bit_depth; /* bit depth of file */ - png_byte pixel_depth; /* number of bits per pixel */ - png_byte channels; /* number of channels in file */ - png_byte sig_bytes; /* magic bytes read/written from start of file */ - png_byte transformed_pixel_depth; - /* pixel depth after read/write transforms */ + png_byte sig_bytes; /* magic bytes read/written at start of file */ + + /* Options */ +#ifdef PNG_SET_OPTION_SUPPORTED + png_byte options; /* On/off state (up to 4 options) */ +#endif + +#ifdef PNG_READ_SUPPORTED +#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) + /* The png_struct colorspace structure is only required on read - on write it + * is in (just) the info_struct. + */ + png_colorspace colorspace; +#endif +#endif /* READ */ + + /* Transform handling */ +#ifdef PNG_TRANSFORM_MECH_SUPPORTED + png_transformp transform_list; /* List of transformation to perform. */ +#endif + + /* ROW BUFFERS and CONTROL + * + * Members used for image row compression (write) or decompression (read). + * filter byte (which is in next_filter.) All fields are only used during + * IDAT processing and start of 0. + */ + png_bytep row_buffer; /* primary row buffer */ +#if defined(PNG_WRITE_FILTER_SUPPORTED) || defined(PNG_READ_SUPPORTED) + png_bytep alt_buffer; /* if two row buffers needed */ +#endif +#ifdef PNG_WRITE_FILTER_SUPPORTED + png_bytep write_row[2]; /* Two rows to test filers */ +#endif + + png_alloc_size_t row_allocated_bytes; /* Total amount allocated */ + + png_uint_32 row_number; /* current row in pass */ +#ifdef PNG_READ_GAMMA_SUPPORTED + png_fixed_point row_gamma; /* Gamma of final output */ +#if 0 /* NYI */ + unsigned int gamma_accuracy; + /* LINEAR gamma cache table size (in bits) times 100; for non-linear + * tables the value used is gamma_accuracy/gamma where 'gamma' is the + * encoding value of the data (typically less than 1). + * + * default: PNG_DEFAULT_GAMMA_ACCURACY (665) + */ +#endif /* NYI */ + png_uint_16 gamma_threshold; + /* Gamma threshold value as a fixed-point value in the range 0..1; the + * threshold at or below which gamma correction is skipped. '0' forces + * gamma correction even when there is none because the input and output + * gammas are equal. + * + * default: PNG_GAMMA_THRESHOLD_FIXED (153) + */ +#endif /* READ_GAMMA */ +#ifdef PNG_READ_TRANSFORMS_SUPPORTED + unsigned int invalid_info; /* PNG_INFO_* for invalidated chunks */ +#endif /* READ_TRANSFORMS */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED + unsigned int read_started :1; /* at least one call to png_read_row */ +#endif + unsigned int do_interlace :1; /* libpng handles the interlace */ + unsigned int pass :3; /* current (interlace) pass (0 - 6) */ + + /* The next two fields are just used by the IDAT process functions to store + * the state of IDAT processing; they should not be altered or used by other + * functions. + */ + unsigned int prev_in_alt :1; /* previous row is stored in alt_buffer */ + unsigned int row_state :2; /* state of row parsing (internal) */ + +# define PNG_RF_BITS 9 /* Number of bits required for the row format (below) */ +#ifdef PNG_TRANSFORM_MECH_SUPPORTED + /* The following fields describe the format of the user row; the output on + * read or the input on write, and give the maximum pixel depth, which + * controls the row buffer allocation size (row_allocated_bytes) above. + */ + unsigned int row_range :3; /* range error count */ + unsigned int row_bit_depth :6; /* bits per channel (up to 32) */ + unsigned int row_format:PNG_RF_BITS;/* format of output(R)/input(W) row: */ + /* PNG_FORMAT_FLAG_ALPHA 0x01U format with an alpha channel + * PNG_FORMAT_FLAG_COLOR 0x02U color format: otherwise grayscale + * PNG_FORMAT_FLAG_LINEAR 0x04U NOT used (informational) + * PNG_FORMAT_FLAG_COLORMAP 0x08U image data is color-mapped + * PNG_FORMAT_FLAG_BGR 0x10U BGR colors, else order is RGB + * PNG_FORMAT_FLAG_AFIRST 0x20U alpha channel comes first * + * PNG_FORAMT_FLAG_AFILLER 0x40U The 'alpha' channel is a filler: + * PNG_FORMAT_FLAG_ALPHA is set however the value in the alpha channel + * is not an alpha value and (therefore) cannot be used for alpha + * computations, it is just a filler value. PNG_COLOR_TYPE_FROM_FORMAT + * will return a color type *without* PNG_COLOR_MASK_ALPHA, however + * PNG_FORMAT_CHANNELS will return the correct number, including the + * filler channel. + * PNG_FORMAT_FLAG_SWAPPED 0x80U bytes or bits swapped: + * When the bit depth is 16 this means that the bytes within the + * components have been swapped, when the bit depth is less than 8 + * it means the pixels within the bytes have been swapped. It should + * not be set for 8-bit compononents (it is meaningless). + * PNG_FORMAT_FLAG_RANGE 0x100U component range not 0..bit-depth: + * Low-bit-depth grayscale components have been unpacked into bytes + * without scaling, or RGB[A] pixels have been shifted back to the + * significant-bit range from the sBIT chunk or channels (currently + * alpha or gray) have been inverted. + * PNG_FORMAT_FLAG_INVALID NOT STORED HERE + */ + unsigned int row_max_pixel :8; /* maximum pixel depth used */ +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + unsigned int info_format:PNG_RF_BITS; + /* This field is used to validate the png_info used to write the + * IHDR. This is a new check in 1.7.0; previously it was possible to pass + * a png_info from a png_read with the read tranform information in the + * format having manually removed the required transforms from the rows + * passed to png_write_row. + */ +#endif /* WRITE_TRANSFORMS */ +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + unsigned int write_invert_alpha :1; + /* This indicates the png_set_invert_alpha was called, it is used by the + * write code to implement the transform without needing to run the whole + * transform mechanism on the PNG palette data. + */ +#endif /* WRITE_INVERT_ALPHA */ +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + unsigned int rgb_to_gray_status :1; + /* If set an RGB pixel was encountered by the RGB to gray transform + * wherein !(r==g==b). + */ +#endif /* RGB_TO_GRAY */ +#endif /* TRANFORM_MECH */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_FILTER_SUPPORTED) + png_byte next_filter; /* Filter byte for upcoming row (read or + * filters+masks to try (write, if WRITE_FILTER is + * supported). + */ +#endif + +#ifdef PNG_READ_SUPPORTED + /* These, and IDAT_read_size below, control how much input and output (at + * most) is available to zlib. + */ + png_alloc_size_t read_buffer_size; /* current size of the buffer */ +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED + uInt IDAT_read_size; /* limit on read buffer size for IDAT */ +#endif + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED + int zlib_text_level; /* holds zlib compression level */ + int zlib_text_method; /* holds zlib compression method */ + int zlib_text_window_bits; /* holds zlib compression window bits */ + int zlib_text_mem_level; /* holds zlib compression memory level */ + int zlib_text_strategy; /* holds zlib compression strategy */ +#endif + +#ifdef PNG_WRITE_SUPPORTED + int zlib_level; /* holds zlib compression level */ + int zlib_method; /* holds zlib compression method */ + int zlib_window_bits; /* holds zlib compression window bits */ + int zlib_mem_level; /* holds zlib compression memory level */ + int zlib_strategy; /* holds zlib compression strategy */ + + int zlib_set_level; /* Actual values set into the zstream on write */ + int zlib_set_method; + int zlib_set_window_bits; + int zlib_set_mem_level; + int zlib_set_strategy; + + png_compression_bufferp zbuffer_list; /* Created on demand during write */ + uInt zbuffer_size; /* size of the actual zlib buffer */ +#endif /* ERROR HANDLING */ #ifdef PNG_SETJMP_SUPPORTED @@ -233,26 +615,30 @@ struct png_struct_def /* IO and BASIC READ/WRITE SUPPORT */ png_voidp io_ptr; /* user supplied data for IO callbacks */ + png_rw_ptr rw_data_fn; /* read/write some bytes (must succeed) */ #ifdef PNG_READ_SUPPORTED - png_rw_ptr read_data_fn; /* read some bytes (must succeed) */ png_read_status_ptr read_row_fn; /* called after each row is decoded */ png_bytep read_buffer; /* buffer for reading chunk data */ /* During read the following array is set up to point to the appropriate * un-filter function, this allows per-image and per-processor optimization. */ - void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info, - png_bytep row, png_const_bytep prev_row); - -#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) - /* The png_struct colorspace structure is only required on read - on write it - * is in (just) the info_struct. - */ - png_colorspace colorspace; -#endif + void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_alloc_size_t row_bytes, + unsigned int bpp, png_bytep row, png_const_bytep prev_row); #endif /* READ */ +#ifdef PNG_WRITE_SUPPORTED + png_write_status_ptr write_row_fn; /* called after each row is encoded */ + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_flush_ptr output_flush_fn; /* Function for flushing output */ + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +#endif + +#endif /* WRITE */ + #ifdef PNG_SET_USER_LIMITS_SUPPORTED png_uint_32 user_width_max; /* Maximum width on read */ png_uint_32 user_height_max; /* Maximum height on read */ @@ -282,72 +668,25 @@ struct png_struct_def png_bytep save_buffer_ptr; /* current location in save_buffer */ png_bytep save_buffer; /* buffer for previously read data */ png_bytep current_buffer_ptr; /* current location in current_buffer */ - png_bytep current_buffer; /* buffer for recently used data */ size_t save_buffer_size; /* amount of data now in save_buffer */ size_t save_buffer_max; /* total size of save_buffer */ - size_t buffer_size; /* total amount of available input data */ size_t current_buffer_size; /* amount of data now in current_buffer */ + size_t buffer_size; /* total amount of available input data */ - png_uint_32 push_length; /* size of current input chunk */ - png_uint_32 skip_length; /* bytes to skip in input data */ - - int process_mode; /* what push library is currently doing */ - int cur_palette; /* current push library palette index */ -#endif - -#ifdef PNG_WRITE_SUPPORTED - png_rw_ptr write_data_fn;/* write some bytes (must succeed) */ - png_write_status_ptr write_row_fn; /* called after each row is encoded */ -#endif - -#ifdef PNG_WRITE_FLUSH_SUPPORTED - png_flush_ptr output_flush_fn; /* Function for flushing output */ - png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ - png_uint_32 flush_rows; /* number of rows written since last flush */ -#endif - -#ifdef PNG_WRITE_SUPPORTED - png_byte usr_bit_depth; /* bit depth of users row */ - png_byte usr_channels; /* channels at start of write */ -#endif + unsigned int process_mode :8; + /* This is one or two four bit codes describing the current state of the + * 'push' reader. Normally the low four bits are a state code, however in + * some cases this may be pushed to the top four bits and replaced by a + * different temporary state code. The value is, in effect, a two entry + * stack. + */ +#endif /* PROGRESSIVE_READ */ #ifdef PNG_IO_STATE_SUPPORTED png_uint_32 io_state; /* tells the app read/write progress */ #endif - /* ROW BUFFERS - * - * Members that hold pointers to the decompressed image rows. - */ - png_bytep row_buf; /* buffer to save current (unfiltered) row. - * While reading, this is a pointer into - * big_row_buf; while writing it is separately - * allocated. - */ -#if defined(PNG_WRITE_FILTER_SUPPORTED) || defined(PNG_READ_SUPPORTED) - png_bytep prev_row; /* buffer to save previous (unfiltered) row. - * While reading this is a pointer into - * big_prev_row; while writing it is separately - * allocated if needed. - */ -#endif - -#ifdef PNG_READ_SUPPORTED - /* The row_buf and prev_row pointers are misaligned so that the start of the - * row - after the filter byte - is aligned, the 'big_' pointers record the - * original allocated pointer. These are only used while reading. - */ - png_bytep big_row_buf; - png_bytep big_prev_row; - size_t big_row_buf_size; /* Actual size of both */ -#endif - -#ifdef PNG_WRITE_FILTER_SUPPORTED - png_bytep try_row; /* buffer to save trial row when filtering */ - png_bytep tst_row; /* buffer to save best trial row when filtering */ -#endif - /* UNKNOWN CHUNK HANDLING */ /* TODO: this is excessively complicated, there are multiple ways of doing * the same thing. It should be cleaned up, possibly by finding out which @@ -366,183 +705,45 @@ struct png_struct_def /* This is called back from the unknown chunk handling */ png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ #endif -#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED - /* Temporary storage for unknown chunk that the library doesn't recognize, - * used while reading the chunk. - */ - png_unknown_chunk unknown_chunk; -#endif + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + png_uint_32 known_unknown; /* Bit mask of known chunks to be treated as + * unknown in the read code. + */ +#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + png_uint_32 save_unknown; /* Whether to save or skip these chunks: + * 'save' is 'known & save', 'skip' is + * 'known & ~save'. + */ +# define png_IDATs_skipped(pp) (((pp)->known_unknown & ~(pp)->save_unknown)&1U) +#else +# define png_IDATs_skipped(pp) ((pp)->known_unknown & 1U) +#endif /* !SAVE_UNKNOWN_CHUNKS */ +#endif /* HANDLE_AS_UNKNOWN */ #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED png_bytep chunk_list; /* List of png_byte[5]; the textual chunk name * followed by a PNG_HANDLE_* byte */ - int unknown_default; /* As PNG_HANDLE_* */ + unsigned int unknown_default :2; /* As PNG_HANDLE_* */ unsigned int num_chunk_list; /* Number of entries in the list */ #endif - /* USER TRANSFORM SUPPORT */ -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - png_user_transform_ptr read_user_transform_fn; /* user read transform */ -#endif -#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED - png_user_transform_ptr write_user_transform_fn; /* user write transform */ -#endif -#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED - png_voidp user_transform_ptr; /* user supplied data for the above */ - png_byte user_transform_depth; /* bit depth of user transformed pixels */ - png_byte user_transform_channels; /* channels in user transformed pixels */ -#endif - - /* READ TRANSFORM SUPPORT - * - * Quite a lot of things can be done to the original image data on read, and - * most of these are configurable. The data required by the configurable - * read transforms should be stored here. The png_color_16 and png_color_8 - * structures have low alignment requirements and odd sizes, so may cause - * misalignment when present. Member alignment is as follows: - * - * png_color_16 png_uint_16 - * png_color_8 png_byte - */ - /* GAMMA/BACKGROUND/ALPHA-MODE/RGB-TO-GRAY/tRNS/sBIT - * - * These things are all interrelated because they need some or all of the - * gamma tables. Some attempt has been made below to order these members by - * size, so that as little padding as possible is required. - */ -#ifdef PNG_READ_GAMMA_SUPPORTED - png_bytep gamma_table; /* gamma table for 8-bit depth files */ - png_uint_16p gamma_16_table; /* gamma table for 16-bit depth files */ - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) ||\ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - png_bytep gamma_from_1; /* converts from 1.0 to screen */ - png_uint_16p gamma_to_1; /* converts from file to 1.0 */ - png_uint_16p gamma_16_from_1; /* converts from 1.0 to screen */ - png_uint_16p gamma_16_to_1; /* converts from file to 1.0 */ -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ -#endif /* READ_GAMMA */ - - /* Integer values */ -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) - png_fixed_point background_gamma; -#endif -#ifdef PNG_READ_GAMMA_SUPPORTED - png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */ - int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */ -#endif - - /* png_color_16 */ -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) - png_color_16 background; /* background color in screen gamma space */ - png_color_16 background_1; /* background normalized to gamma 1.0 */ -#endif -#if defined(PNG_READ_tRNS_SUPPORTED) || \ - defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_EXPAND_SUPPORTED) - png_color_16 trans_color; /* transparent color for non-paletted files */ -#endif - - /* png_uint_16 */ -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - png_uint_16 rgb_to_gray_red_coeff; - png_uint_16 rgb_to_gray_green_coeff; - /* The blue coefficient is calculated from the above */ -#endif - - /* png_color_8 */ -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_sBIT_SUPPORTED) - png_color_8 sig_bit; /* significant bits in each available channel */ -#endif - - /* png_byte */ -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) - png_byte background_gamma_type; -#endif -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - png_byte rgb_to_gray_status; -#endif - - /* SHIFT - both READ_SHIFT and WRITE_SHIFT */ -#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) - png_color_8 shift; /* shift for significant bit tranformation */ -#endif - - /* FILLER SUPPORT (pixel expansion or read, contraction on write) */ -#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) - png_uint_16 filler; /* filler bytes for pixel expansion */ -#endif - - /* QUANTIZE (convert to color-mapped) */ -#ifdef PNG_READ_QUANTIZE_SUPPORTED - png_bytep palette_lookup; /* lookup table for quantizing */ - png_bytep quantize_index; /* index translation for palette files */ - png_bytep quantize_sort; /* working sort array */ - png_bytep index_to_palette; /* where the original index currently is in the - * palette - */ - png_bytep palette_to_index; /* which original index points to this palette - * color - */ -#endif - - /* MNG SUPPORT */ -#ifdef PNG_MNG_FEATURES_SUPPORTED - png_uint_32 mng_features_permitted; - png_byte filter_type; -#endif - - /* Options */ -#ifdef PNG_SET_OPTION_SUPPORTED - png_byte options; /* On/off state (up to 4 options) */ -#endif - /* COMPRESSION AND DECOMPRESSION SUPPORT. * * zlib expects a 'zstream' as the fundamental control structure, it allows * all the parameters to be passed as one pointer. */ - z_stream zstream; /* decompression structure */ + png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */ + z_stream zstream; /* decompression structure */ + unsigned int zstream_ended:1; /* no more zlib output available */ + unsigned int zstream_error:1; /* zlib error message has been output */ + unsigned int zstream_eod :1; /* all the required uncompressed data has been + * received; set by the zstream using code for + * its own purposes. */ -#ifdef PNG_READ_SUPPORTED - /* These, and IDAT_read_size below, control how much input and output (at - * most) is available to zlib. - */ - png_uint_32 idat_size; /* current IDAT size for read */ - png_alloc_size_t read_buffer_size; /* current size of the buffer */ -#endif - -#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED - int zlib_text_level; /* holds zlib compression level */ - int zlib_text_method; /* holds zlib compression method */ - int zlib_text_window_bits; /* holds zlib compression window bits */ - int zlib_text_mem_level; /* holds zlib compression memory level */ - int zlib_text_strategy; /* holds zlib compression strategy */ -#endif - -#ifdef PNG_WRITE_SUPPORTED - int zlib_level; /* holds zlib compression level */ - int zlib_method; /* holds zlib compression method */ - int zlib_window_bits; /* holds zlib compression window bits */ - int zlib_mem_level; /* holds zlib compression memory level */ - int zlib_strategy; /* holds zlib compression strategy */ - - int zlib_set_level; /* Actual values set into the zstream on write */ - int zlib_set_method; - int zlib_set_window_bits; - int zlib_set_mem_level; - int zlib_set_strategy; - - png_compression_bufferp zbuffer_list; /* Created on demand during write */ - uInt zbuffer_size; /* size of the actual zlib buffer */ -#endif - -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED - uInt IDAT_read_size; /* limit on read buffer size for IDAT */ + /* MNG SUPPORT */ +#ifdef PNG_MNG_FEATURES_SUPPORTED + unsigned int mng_features_permitted :3; #endif }; #endif /* PNGSTRUCT_H */ diff --git a/pngtest.c b/pngtest.c index 195e7f329..bb90a23a8 100644 --- a/pngtest.c +++ b/pngtest.c @@ -101,6 +101,10 @@ typedef FILE * png_FILE_p; # define SINGLE_ROWBUF_ALLOC /* Makes buffer overruns easier to nail */ #endif +#ifndef PNG_UNUSED +# define PNG_UNUSED(param) (void)param; +#endif + /* Turn on CPU timing #define PNGTEST_TIMING */ @@ -185,16 +189,14 @@ write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED -/* Example of using user transform callback (we don't transform anything, - * but merely examine the row filters. We set this to 256 rather than - * 5 in case illegal filter values are present.) +/* Example of using a user transform callback (doesn't do anything at present). */ -static png_uint_32 filters_used[256]; static void PNGCBAPI -count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data) +read_user_callback(png_structp png_ptr, png_row_infop row_info, png_bytep data) { - if (png_ptr != NULL && row_info != NULL) - ++filters_used[*(data - 1)]; + PNG_UNUSED(png_ptr) + PNG_UNUSED(row_info) + PNG_UNUSED(data) } #endif @@ -805,6 +807,8 @@ pngtest_check_text_support(png_const_structp png_ptr, png_textp text_ptr, case PNG_TEXT_COMPRESSION_zTXt: # ifndef PNG_WRITE_zTXt_SUPPORTED ++unsupported_chunks; + /* In libpng 1.7 this now does an app-error, so stop it: */ + text_ptr[num_text].compression = PNG_TEXT_COMPRESSION_NONE; # endif break; @@ -812,6 +816,7 @@ pngtest_check_text_support(png_const_structp png_ptr, png_textp text_ptr, case PNG_ITXT_COMPRESSION_zTXt: # ifndef PNG_WRITE_iTXt_SUPPORTED ++unsupported_chunks; + text_ptr[num_text].compression = PNG_TEXT_COMPRESSION_NONE; # endif break; @@ -1000,14 +1005,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) } #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - { - int i; - - for (i = 0; i<256; i++) - filters_used[i] = 0; - - png_set_read_user_transform_fn(read_ptr, count_filters); - } + png_set_read_user_transform_fn(read_ptr, read_user_callback); #endif #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED zero_samples = 0; @@ -1054,7 +1052,6 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) { png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth, color_type, interlace_type, compression_type, filter_type); -#ifndef PNG_READ_INTERLACING_SUPPORTED /* num_pass will not be set below, set it here if the image is * interlaced: what happens is that write interlacing is *not* turned * on an the partial interlaced rows are written directly. @@ -1073,7 +1070,6 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) png_error(read_ptr, "invalid interlace type"); /*NOT REACHED*/ } -#endif } } #ifdef PNG_FIXED_POINT_SUPPORTED @@ -1366,7 +1362,8 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) #endif /* SINGLE_ROWBUF_ALLOC */ pngtest_debug("Writing row data"); -#ifdef PNG_READ_INTERLACING_SUPPORTED +#if defined(PNG_READ_DEINTERLACE_SUPPORTED) &&\ + defined(PNG_WRITE_INTERLACING_SUPPORTED) num_pass = png_set_interlace_handling(read_ptr); if (png_set_interlace_handling(write_ptr) != num_pass) png_error(write_ptr, "png_set_interlace_handling: inconsistent num_pass"); @@ -1570,7 +1567,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) } # ifdef PNG_WRITE_SUPPORTED - /* If there we no write support nothing was written! */ + /* If there is no write support nothing was written! */ else if (unsupported_chunks > 0) { fprintf(STDERR, "\n %s: unsupported chunks (%d)%s", @@ -1817,21 +1814,12 @@ main(int argc, char *argv[]) kerror = test_one_file(argv[i], outname); if (kerror == 0) { -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - int k; -#endif #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED fprintf(STDERR, "\n PASS (%lu zero samples)\n", (unsigned long)zero_samples); #else fprintf(STDERR, " PASS\n"); #endif -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - for (k = 0; k<256; k++) - if (filters_used[k] != 0) - fprintf(STDERR, " Filter %d was used %lu times\n", - k, (unsigned long)filters_used[k]); -#endif #ifdef PNG_TIME_RFC1123_SUPPORTED if (tIME_chunk_present != 0) fprintf(STDERR, " tIME = %s\n", tIME_string); @@ -1908,21 +1896,12 @@ main(int argc, char *argv[]) { if (verbose == 1 || i == 2) { -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - int k; -#endif #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED fprintf(STDERR, "\n PASS (%lu zero samples)\n", (unsigned long)zero_samples); #else fprintf(STDERR, " PASS\n"); #endif -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - for (k = 0; k<256; k++) - if (filters_used[k] != 0) - fprintf(STDERR, " Filter %d was used %lu times\n", - k, (unsigned long)filters_used[k]); -#endif #ifdef PNG_TIME_RFC1123_SUPPORTED if (tIME_chunk_present != 0) fprintf(STDERR, " tIME = %s\n", tIME_string); diff --git a/pngtrans.c b/pngtrans.c index 414a66dfd..7d89b52d9 100644 --- a/pngtrans.c +++ b/pngtrans.c @@ -10,931 +10,3423 @@ * For conditions of distribution and use, see the disclaimer * and license in png.h */ - #include "pngpriv.h" #define PNG_SRC_FILE PNG_SRC_FILE_pngtrans -#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef _XOPEN_SOURCE +# include +#endif /* for swab */ -#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) -/* Turn on BGR-to-RGB mapping */ -void PNGAPI -png_set_bgr(png_structrp png_ptr) +/* Memory format enquiries */ +#ifdef PNG_GAMMA_SUPPORTED +static png_fixed_point +memory_gamma(png_const_structrp png_ptr) { - png_debug(1, "in png_set_bgr"); +# ifdef PNG_READ_GAMMA_SUPPORTED +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + if (png_ptr->read_struct) + return png_ptr->row_gamma; +# endif /* TRANSFORM_MECH */ +# endif /* READ_GAMMA */ - if (png_ptr == NULL) - return; + /* Else either no READ_GAMMA support or this is a write struct; in both + * cases there are no gamma transforms. In the write case the set of the + * gamma in the info may not have been copied to the png_struct. + */ +# if defined(PNG_GAMMA_SUPPORTED) && defined(PNG_READ_SUPPORTED) + if ((png_ptr->colorspace.flags & + (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) == + PNG_COLORSPACE_HAVE_GAMMA) + return png_ptr->colorspace.gamma; +# else /* !(GAMMA && READ) */ + PNG_UNUSED(png_ptr) +# endif /* !(GAMMA && READ) */ - png_ptr->transformations |= PNG_BGR; + /* '0' means the value is not know: */ + return 0; } -#endif +#endif /* GAMMA */ -#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) -/* Turn on 16 bit byte swapping */ -void PNGAPI -png_set_swap(png_structrp png_ptr) +unsigned int PNGAPI +png_memory_format(png_structrp png_ptr) { - png_debug(1, "in png_set_swap"); - - if (png_ptr == NULL) - return; - - if (png_ptr->bit_depth == 16) - png_ptr->transformations |= PNG_SWAP_BYTES; -} -#endif - -#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) -/* Turn on pixel packing */ -void PNGAPI -png_set_packing(png_structrp png_ptr) -{ - png_debug(1, "in png_set_packing"); - - if (png_ptr == NULL) - return; - - if (png_ptr->bit_depth < 8) + /* The in-memory format as a bitmask of PNG_FORMAT_FLAG_ values. All the + * flags listed below are used. If PNG_FORMAT_FLAG_INVALID is set the + * following caveats apply to the interpretation of PNG_FORMAT_FLAG_LINEAR: + * + * The gamma may differ from the sRGB (!LINEAR) or 1.0 (LINEAR). Call + * png_memory_gamma to find the correct value. + * + * The channel depth may differ from 8 (!LINEAR) or 16 (LINEAR). Call + * png_memory_channel_depth to find the correct value. + * + * It is only valid to call these APIS *after* either png_read_update_info + * or png_start_read_image on read or after the first row of an image has + * been written on write. + */ + if (png_ptr != NULL) { - png_ptr->transformations |= PNG_PACK; -# ifdef PNG_WRITE_SUPPORTED - png_ptr->usr_bit_depth = 8; +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + unsigned int format = png_ptr->row_format; +# else /* !TRANSFORM_MECH */ + unsigned int format = PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type); +# endif /* !TRANSFORM_MECH */ + + if (png_ptr->read_struct) /* else no way to find the gamma! */ + { +# ifdef PNG_GAMMA_SUPPORTED +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + unsigned int bit_depth = png_ptr->row_bit_depth; +# else /* !TRANSFORM_MECH */ + unsigned int bit_depth = png_ptr->bit_depth; +# endif /* !TRANSFORM_MECH */ + + /* Now work out whether this is a valid simplified API format. */ + switch (bit_depth) + { + case 8U: + { + png_fixed_point gamma = memory_gamma(png_ptr); + + if (!PNG_GAMMA_IS_sRGB(gamma)) + format |= PNG_FORMAT_FLAG_INVALID; + } + break; + + case 16: + if (memory_gamma(png_ptr) == PNG_GAMMA_LINEAR) + { + static const union + { + png_uint_16 u16; + png_byte u8[2]; + } sex = { 1U }; + + format |= PNG_FORMAT_FLAG_LINEAR; + + /* But the memory layout of the 16-bit quantities must also + * match; we need swapped data on LSB platforms. + */ + if (sex.u8[0] == ((format & PNG_FORMAT_FLAG_SWAPPED) != 0)) + break; /* ok */ + } + + /* FALL THROUGH*/ + default: /* bit depth not supported for simplified API */ + format |= PNG_FORMAT_FLAG_INVALID; + break; + } +# else /* !GAMMA */ + /* We have no way of knowing if the gamma value matches that + * expected by the simplified API so mark the format as invalid: + */ + format |= PNG_FORMAT_FLAG_INVALID; +# endif + } /* read_struct */ + + return format; + } + + return 0; +} + +unsigned int PNGAPI png_memory_channel_depth(png_structrp png_ptr) +{ + /* The actual depth of each channel in the image. */ + if (png_ptr != NULL) + { +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + return png_ptr->row_bit_depth; +# else + return png_ptr->bit_depth; # endif } -} -#endif -#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) -/* Turn on packed pixel swapping */ -void PNGAPI -png_set_packswap(png_structrp png_ptr) + return 0; +} + +#ifdef PNG_GAMMA_SUPPORTED +png_fixed_point PNGAPI +png_memory_gamma(png_structrp png_ptr) { - png_debug(1, "in png_set_packswap"); - - if (png_ptr == NULL) - return; - - if (png_ptr->bit_depth < 8) - png_ptr->transformations |= PNG_PACKSWAP; + /* The actual gamma of the image data, scaled by 100,000. This is the + * encoding gamma, e.g. 1/2.2 for sRGB. If the gamma is unknown this will + * return 0. + * + * On write this invariably returns 0; libpng does not change the gamma of + * the data on write. + * + * Note that this is not always the exact inverse of the 'screen gamma' + * passed to png_set_gamma; internal optimizations remove attempts to make + * small changes to the gamma value. This function returns the actual + * output value. + */ + return (png_ptr != NULL) ? memory_gamma(png_ptr) : 0; } -#endif +#endif /* GAMMA */ -#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) -void PNGAPI -png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits) +/* These are general purpose APIs that deal with the row buffer format in both + * the read and write case. The png_struct::row_* members describe the + * in-memory format of the image data based on the transformations requested by + * the application. + */ +#ifdef PNG_TRANSFORM_MECH_SUPPORTED +png_voidp /* PRIVATE */ +png_transform_cast_check(png_const_structp png_ptr, unsigned int src_line, + png_transformp tr, size_t size) { - png_debug(1, "in png_set_shift"); + /* Given a pointer to a transform, 'tr' validate that the underlying derived + * class has size 'size' using the tr->size field and return the same + * pointer. If there is a size mismatch the function does an affirm using + * the given line number. + */ + if (tr->size != size) + png_affirm(png_ptr, param_deb("transform upcast") src_line); - if (png_ptr == NULL) - return; - - png_ptr->transformations |= PNG_SHIFT; - png_ptr->shift = *true_bits; + return tr; } -#endif -#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ +void /* PRIAVE */ +png_transform_free(png_const_structrp png_ptr, png_transformp *list) +{ + if (*list != NULL) + { + png_transform_free(png_ptr, &(*list)-> next); + if ((*list)->free != NULL) + (*list)->free(png_ptr, *list); + png_free(png_ptr, *list); + *list = NULL; + } +} + +/* Utility to initialize a png_transform_control for read or write. */ +void /* PRIVATE */ +png_init_transform_control(png_transform_controlp tc, png_structp png_ptr) +{ + png_byte bd; /* bit depth of the row */ + png_byte cd; /* bit depth of color information */ + + memset(tc, 0, sizeof *tc); + tc->png_ptr = png_ptr; /* ALIAS */ + tc->sp = tc->dp = NULL; + tc->width = 0; + +# ifdef PNG_READ_GAMMA_SUPPORTED + /* The file gamma is set by png_set_gamma, as well as being read from the + * input PNG gAMA chunk, if present, we don't have a png_info so can't get + * the set_gAMA value but this doesn't matter because on read the gamma + * value is in png_struct::colorspace and on write it isn't used. + */ + if ((png_ptr->colorspace.flags & + (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) == + PNG_COLORSPACE_HAVE_GAMMA) + { + tc->gamma = png_ptr->colorspace.gamma; + debug(tc->gamma > 0); + } + + else + { + /* There is no input gamma, so there should be no overall gamma + * correction going on. This test works because the various things + * that set an output gamma also default the input gamma. + */ + debug(png_ptr->row_gamma == 0); + } +# endif + + /* Validate bit depth and color type here */ + cd = bd = png_ptr->bit_depth; + + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + affirm(bd == 1U || bd == 2U || bd == 4U || bd == 8U || bd == 16U); + tc->format = 0U; + break; + + case PNG_COLOR_TYPE_PALETTE: + affirm(bd == 1U || bd == 2U || bd == 4U || bd == 8U); + tc->format = PNG_FORMAT_FLAG_COLORMAP | PNG_FORMAT_FLAG_COLOR; + cd = 8U; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + affirm(bd == 8U || bd == 16U); + tc->format = PNG_FORMAT_FLAG_ALPHA; + break; + + case PNG_COLOR_TYPE_RGB: + affirm(bd == 8U || bd == 16U); + tc->format = PNG_FORMAT_FLAG_COLOR; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + affirm(bd == 8U || bd == 16U); + tc->format = PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_ALPHA; + break; + + default: + impossible("PNG color type"); + } + + tc->bit_depth = bd; + tc->range = 0; + + /* Preset the sBIT data to full precision/handled. */ + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = cd; +# ifdef PNG_READ_sBIT_SUPPORTED + { + int handled = 1; + + if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) + { + png_byte c = png_ptr->sig_bit.red; + if (c > 0 && c < cd) + { + tc->sBIT_R = c; + handled = 0; + } + + c = png_ptr->sig_bit.green; + if (c > 0 && c < cd) + { + tc->sBIT_G = c; + handled = 0; + } + + c = png_ptr->sig_bit.blue; + if (c > 0 && c < cd) + { + tc->sBIT_B = c; + handled = 0; + } + } + + else /* grayscale */ + { + png_byte c = png_ptr->sig_bit.gray; + if (c > 0 && c < cd) + { + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = c; + handled = 0; + } + } + + /* The palette-mapped format doesn't store alpha information, an + * omission in the spec that is difficult to fix. Notice that + * 'handled' is not cleared below, this is because the alpha channel is + * always linear, so the sBIT_A value can always be treated as a + * precision value. + */ + if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0) + { + png_byte c = png_ptr->sig_bit.alpha; + if (c > 0 && c < cd) + tc->sBIT_A = c; + } + + /* If 'handled' did not get cleared there is no sBIT information. */ + if (handled) + tc->invalid_info = PNG_INFO_sBIT; + } +# else /* !READ_sBIT */ + /* No sBIT information */ + tc->invalid_info = PNG_INFO_sBIT; +# endif /* !READ_sBIT */ +} + +png_transformp /*PRIVATE*/ +png_add_transform(png_structrp png_ptr, size_t size, png_transform_fn fn, + unsigned int order) +{ + /* Add a transform. This is a minimal implementation; the order is just + * controlled by 'order', the result is a point to the new transform, or + * to an existing one if one was already in the list. + */ + png_transformp *p = &png_ptr->transform_list; + + while (*p != NULL && (*p)->order < order) + p = &(*p)->next; + + if (size == 0) + size = sizeof (png_transform); + + else + affirm(size >= sizeof (png_transform)); + + if (*p == NULL || (*p)->order > order) + { + png_transformp t; + + t = png_voidcast(png_transformp, png_malloc(png_ptr, size)); + memset(t, 0, size); /* zeros out the extra data too */ + /* *p comes after the new entry, t: */ + t->next = *p; + t->fn = fn; + t->free = NULL; + t->order = order; + t->size = 0xFFFFU & size; + *p = t; + return t; + } + + else /* (*p)->order matches order, return *p */ + { + affirm((*p)->fn == fn && (*p)->order == order && (*p)->size == size); + return *p; + } +} + +png_transformp /* PRIVATE */ +png_push_transform(png_structrp png_ptr, size_t size, png_transform_fn fn, + png_transformp *transform, png_transform_controlp tc) +{ + png_transformp tr = *transform; + unsigned int order = tr->order; + + /* Basic loop detection: */ + affirm(fn != NULL && tr->fn != fn); + + /* Move the following transforms up: */ + { + unsigned int old_order = order; + + do + { + tr->order = ++old_order; + tr = tr->next; + } + while (tr != NULL && tr->order == old_order); + + affirm(tr->order > old_order); + } + + *transform = png_add_transform(png_ptr, size, fn, order); + + if (tc != NULL) + fn(transform, tc); + + return *transform; +} + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +static png_transformp +png_find_transform(png_const_structrp png_ptr, unsigned int order) + /* Find a transform with the given order, or return NULL. Currently only + * used here. + */ +{ + png_transformp p = png_ptr->transform_list; + + for (;;) + { + if (p == NULL || p->order > order) + return NULL; + + if (p->order == order) + return p; + + p = p->next; + } +} +#endif /* USER_TRANSFORM_PTR */ + +static void +remove_transform(png_const_structp png_ptr, png_transformp *transform) + /* Remove a transform on a running list */ +{ + png_transformp tp = *transform; + png_transformp next = tp->next; + + *transform = next; + tp->next = NULL; + png_transform_free(png_ptr, &tp); +} + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +void /* PRIVATE */ +png_remove_transform(png_const_structp png_ptr, png_transformp *transform) +{ + remove_transform(png_ptr, transform); +} +#endif /* READ_TRANSFORMS */ + +static unsigned int +run_transform_list_forwards(png_transform_controlp tc, png_transformp *start, + png_transformp end/*NULL for whole list*/) + /* Called from the init code and below, the caller must initialize 'tc' */ +{ + png_const_structp png_ptr = tc->png_ptr; + unsigned int max_depth = 0; + + /* Caller guarantees that *start is non-NULL */ + debug(*start != NULL); + + do + { + if ((*start)->fn != NULL) + (*start)->fn(start, tc); + + if ((*start)->fn == NULL) /* delete this transform */ + remove_transform(png_ptr, start); + + else + { + /* Handle the initialization of the maximum pixel depth. */ + unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(*tc); + + if (tc_depth > max_depth) + max_depth = tc_depth; + + /* Advance to the next transform. */ + start = &(*start)->next; + } + } + while (*start != NULL && *start != end); + + /* This only goes wrong if 'end' was non-NULL and not in the list: */ + debug(*start == end); + + return max_depth; +} + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +unsigned int /* PRIVATE */ +png_run_this_transform_list_forwards(png_transform_controlp tc, + png_transformp *start, png_transformp *end) +{ + return run_transform_list_forwards(tc, start, *end); +} +#endif /* READ_TRANSFORMS */ + +#ifdef PNG_READ_SUPPORTED +unsigned int /* PRIVATE */ +png_run_transform_list_forwards(png_structp png_ptr, png_transform_controlp tc) +{ + unsigned int max_depth = PNG_PIXEL_DEPTH(*png_ptr); + + if (png_ptr->transform_list != NULL) + { + unsigned int depth = + run_transform_list_forwards(tc, &png_ptr->transform_list, NULL); + + if (depth > max_depth) + max_depth = depth; + } + + return max_depth; +} +#endif /* READ */ + +#ifdef PNG_WRITE_SUPPORTED /* only used from pngwrite.c */ +static unsigned int +run_transform_list_backwards(png_transform_controlp tc, png_transformp *list) +{ + png_const_structp png_ptr = tc->png_ptr; + unsigned int max_depth = 0; + + if ((*list)->next != NULL) + max_depth = run_transform_list_backwards(tc, &(*list)->next); + + /* Note that the above might change (*list)->next, but it can't change + * *list itself. + */ + if ((*list)->fn != NULL) + (*list)->fn(list, tc); + + /* If that set 'fn' to NULL this transform must be removed; this is how + * (*list)->next gets changed in our caller: + */ + if ((*list)->fn == NULL) + remove_transform(png_ptr, list); + + else + { + unsigned int depth = PNG_TC_PIXEL_DEPTH(*tc); + + if (depth > max_depth) + max_depth = depth; + } + + return max_depth; +} + +void /* PRIVATE */ +png_run_transform_list_backwards(png_structp png_ptr, png_transform_controlp tc) +{ + if (png_ptr->transform_list != NULL) + { + /* This doesn't take account of the base PNG depth, but that shouldn't + * matter, it's just a check: + */ + unsigned int max_depth = + run_transform_list_backwards(tc, &png_ptr->transform_list); + + /* Better late than never (if this fires a memory overwrite has happened): + */ + affirm(max_depth <= png_ptr->row_max_pixel); + } +} +#endif /* WRITE */ + +static unsigned int +init_transform_mech(png_structrp png_ptr, png_transform_control *tc, int start) + /* Called each time to run the transform list once during initialization. */ +{ + png_init_transform_control(tc, png_ptr); + tc->init = start ? PNG_TC_INIT_FORMAT : PNG_TC_INIT_FINAL; +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + if (png_ptr->read_struct) + return png_read_init_transform_mech(png_ptr, tc); + else +# endif + return run_transform_list_forwards(tc, &png_ptr->transform_list, NULL); +} +#endif /* TRANSFORM_MECH */ + +#ifdef PNG_PALETTE_MAX_SUPPORTED +static int +set_palette_max(png_structrp png_ptr, png_transformp tr, unsigned int max) + /* Called whenever a new maximum pixel value is found */ +{ + /* One of these must be true: */ +# ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED + if (max >= png_ptr->num_palette && !png_ptr->palette_index_check_issued) + { +# ifdef PNG_READ_SUPPORTED +# ifdef PNG_WRITE_SUPPORTED + (png_ptr->read_struct ? png_chunk_benign_error : png_error) +# else /* !WRITE */ + png_chunk_benign_error +# endif /* !WRITE */ +# else /* !READ */ + png_error +# endif /* !READ */ + (png_ptr, "palette index too large"); + png_ptr->palette_index_check_issued = 1; + } +# endif +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED + png_ptr->palette_index_max = png_check_bits(png_ptr, max, 9); +# endif + + if (max == (1U << png_ptr->bit_depth)-1U) + { + tr->fn = NULL; /* no point continuing once the max has been seen */ + return 1; /* stop */ + } + + return 0; /* keep going */ +} + +static void +palette_max_1bpp(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_uint_32 width = tc->width; + + while (width >= 8) + { + if (*sp++) break; + width -= 8; + } + + if (width < 8) + { + if (width == 0 || + (*sp & (((1U<png_ptr, *tr, 1); +} + +static void +palette_max_2bpp(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_uint_32 width = tc->width; + unsigned int max = (*tr)->args; /* saved maximum */ + + while (width > 0) + { + png_uint_32 input = 0U, test; + unsigned int new_max; + + /* This just skips 0 bytes: */ + while (width > 0) + { + unsigned int next = *sp++; + + /* There may be partial pixels at the end, just remove the absent + * pixels with a right shift: + */ + if (width >= 4) + width -= 4; + else + next >>= (4U-width) * 2U, width = 0; + + if (next) + { + input = (input << 8) | next; + if ((input & 0xFF000000U) != 0) + break; + } + } + + test = input & 0xAAAAAAAAU; + + if (test != 0) + { + if ((input & (test >> 1)) != 0) + new_max = 3U; /* both bits set in at least one pixel */ + + else if (max < 2U) + new_max = 2U; + + else + continue; /* no change to max */ + } + + else /* test is 0 */ if (input != 0 && max == 0) + new_max = 1U; + + else /* input is 0, or max is at least 1 */ + continue; + + /* new_max is greater than max: */ + if (set_palette_max(tc->png_ptr, *tr, new_max)) + return; + + /* Record new_max: */ + max = new_max; + } + + /* End of input, check the next line. */ + (*tr)->args = max; +} + +static void +palette_max_4bpp(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_uint_32 width = tc->width; + unsigned int max = (*tr)->args; /* saved maximum */ + + while (width > 0) + { + unsigned int input = *sp++; + + if (width >= 2) + width -= 2; + else + input >>= 1, width = 0; + + if ((input & 0xFU) > max) + max = input & 0xFU; + + if (((input >> 4) & 0xFU) > max) + max = (input >> 4) & 0xFU; + } + + if (max > (*tr)->args) + { + if (set_palette_max(tc->png_ptr, *tr, max)) + return; + + (*tr)->args = max; + } +} + +static void +palette_max_8bpp(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_uint_32 width = tc->width; + unsigned int max = (*tr)->args; /* saved maximum */ + + while (width > 0) + { + unsigned int input = *sp++; + + if (input > max) + max = input; + + --width; + } + + if (max > (*tr)->args) + { + if (set_palette_max(tc->png_ptr, *tr, max)) + return; + + (*tr)->args = max; + } +} + +static void +palette_max_init(png_transformp *tr, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0) + { + if (tc->init == PNG_TC_INIT_FINAL) switch (tc->bit_depth) + { + case 1: (*tr)->fn = palette_max_1bpp; break; + case 2: (*tr)->fn = palette_max_2bpp; break; + case 4: (*tr)->fn = palette_max_4bpp; break; + case 8: (*tr)->fn = palette_max_8bpp; break; + default:impossible("palette bit depth"); + } + } + + else + (*tr)->fn = NULL; /* not applicable */ +# undef png_ptr +} +#endif /* PALETTE_MAX */ + +#ifdef PNG_GET_PALETTE_MAX_SUPPORTED +int PNGAPI +png_get_palette_max(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL +# ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED + && !png_ptr->palette_index_check_disabled +# endif + ) + return png_ptr->palette_index_max; + + /* This indicates to the caller that the information is not available: */ + return -1; + PNG_UNUSED(info_ptr) +} +#endif /* GET_PALETTE_MAX */ + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Whether to report invalid palette index; added at libng-1.5.10. + * It is possible for an indexed (color-type==3) PNG file to contain + * pixels with invalid (out-of-range) indexes if the PLTE chunk has + * fewer entries than the image's bit-depth would allow. We recover + * from this gracefully by filling any incomplete palette with zeros + * (opaque black). By default, when this occurs libpng will issue + * a benign error. This API can be used to override that behavior. + */ +void PNGAPI +png_set_check_for_invalid_index(png_structrp png_ptr, int enabled) +{ + /* This defaults to 0, therefore *on*: */ + if (png_ptr != NULL) + { + if (png_ptr->read_struct) +# ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED + png_ptr->palette_index_check_disabled = enabled <= 0; +# else /* !READ_CHECK_FOR_INVALID_INDEX */ + png_app_error(png_ptr, "no read palette check support"); +# endif /* !READ_CHECK_FOR_INVALID_INDEX */ + else /* write struct */ +# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + png_ptr->palette_index_check_disabled = enabled <= 0; +# else /* !WRITE_CHECK_FOR_INVALID_INDEX */ + png_app_error(png_ptr, "no write palette check support"); +# endif /* !WRITE_CHECK_FOR_INVALID_INDEX */ + } +} +#endif /* CHECK_FOR_INVALID_INDEX */ + +void /* PRIVATE */ +png_init_row_info(png_structrp png_ptr) +{ + unsigned int max_depth = PNG_PIXEL_DEPTH(*png_ptr); + +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + /* The palette index check stuff is *on* automatically. To handle this + * add it here, if it is supported. + */ +# ifdef PNG_PALETTE_MAX_SUPPORTED + /* The logic here is a little complex because of the plethora of + * #defines controlling this stuff. + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE/* fast escape */ && ( +# if defined (PNG_READ_GET_PALETTE_MAX_SUPPORTED) ||\ + defined (PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) + (png_ptr->read_struct +# ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED + && !png_ptr->palette_index_check_disabled) +# endif /* READ_CHECK_FOR_INVALID_INDEX */ +# else /* no READ support */ + 0 +# endif /* READ checks */ + || +# if defined (PNG_WRITE_GET_PALETTE_MAX_SUPPORTED) ||\ + defined (PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) + (!png_ptr->read_struct +# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + && !png_ptr->palette_index_check_disabled) +# endif /* WRITE_CHECK_FOR_INVALID_INDEX */ +# else /* no WRITE support */ + 0 +# endif /* WRITE checks */ + )) + png_add_transform(png_ptr, 0/*size*/, palette_max_init, + PNG_TR_CHECK_PALETTE); +# endif + + /* Application transforms may change the format of the data or, when + * producing interlaced images, the number of pixels in a line. This code + * determines the maximum pixel depth required and allows transformations + * a chance to initialize themselves. + */ + if (png_ptr->transform_list != NULL) + { + png_transform_control tc; + + (void)init_transform_mech(png_ptr, &tc, 1/*start*/); + + png_ptr->row_format = png_check_bits(png_ptr, tc.format, PNG_RF_BITS); + affirm(tc.bit_depth <= 32); + png_ptr->row_bit_depth = png_check_bits(png_ptr, tc.bit_depth, 6); + png_ptr->row_range = png_check_bits(png_ptr, tc.range, 3); +# ifdef PNG_READ_GAMMA_SUPPORTED + png_ptr->row_gamma = tc.gamma; +# endif /* READ_GAMMA */ + + /* The above may have cancelled all the transforms in the list. */ + if (png_ptr->transform_list != NULL) + { + /* Run the transform list again, also forward, and accumulate the + * maximum pixel depth. At this point the transforms can swap + * out their initialization code. + */ + unsigned int depth = init_transform_mech(png_ptr, &tc, 0/*final*/); + + if (depth > max_depth) + max_depth = depth; + +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + /* Set this now because it only gets resolved finally at this + * point. + */ + png_ptr->invalid_info = tc.invalid_info; +# endif /* READ_TRANSFORMS */ + + /* And check the transform fields: */ + affirm(png_ptr->row_format == tc.format && + png_ptr->row_range == tc.range && + png_ptr->row_bit_depth == tc.bit_depth); +# ifdef PNG_READ_GAMMA_SUPPORTED + affirm(png_ptr->row_gamma == tc.gamma); +# endif /* READ_GAMMA */ + } + } + + else /* png_ptr->transform_list == NULL */ + { + png_ptr->row_format = png_check_bits(png_ptr, + PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type), PNG_RF_BITS); + png_ptr->row_bit_depth = png_check_bits(png_ptr, png_ptr->bit_depth, + 6); + png_ptr->row_range = 0; +# ifdef PNG_READ_GAMMA_SUPPORTED + if ((png_ptr->colorspace.flags & + (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) == + PNG_COLORSPACE_HAVE_GAMMA) + png_ptr->row_gamma = png_ptr->colorspace.gamma; +# endif /* READ_GAMMA */ +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + png_ptr->invalid_info = 0U; +# endif /* READ_TRANSFORMS */ + } + + /* 'max_depth' is now the maximum size of a pixel, including intermediate + * results during the transforms. The current limit is 4 32-bit channels: + */ + affirm(max_depth <= 128); + png_ptr->row_max_pixel = png_check_bits(png_ptr, max_depth, 8); +# endif /* TRANSFORM_MECH */ + + /* png_calc_rowbytes does a png_error on overflow. This is how the libpng + * code validates that there won't be overflows on future PNG_ROWBYTES + * calls. + * + * The largest integer we can guarantee with ANSI-C is a 32-bit one (unsigned + * long). To allow the row to be accessed as png_uint_32[] this code sets + * the allocation to a multiple of 4: + */ + { + png_alloc_size_t rowbytes = png_calc_rowbytes(png_ptr, max_depth, + png_ptr->width); + png_alloc_size_t alloc = (rowbytes + 3U) & ~3U; + + if (alloc < rowbytes) + png_error(png_ptr, "PNG row exceeds system limits"); + + png_ptr->row_allocated_bytes = alloc; + } +} + +#if defined(PNG_READ_DEINTERLACE_SUPPORTED) || \ defined(PNG_WRITE_INTERLACING_SUPPORTED) int PNGAPI png_set_interlace_handling(png_structrp png_ptr) { png_debug(1, "in png_set_interlace handling"); - if (png_ptr != 0 && png_ptr->interlaced != 0) + if (png_ptr != 0) { - png_ptr->transformations |= PNG_INTERLACE; - return (7); + if (png_ptr->read_struct) + { +# ifdef PNG_READ_DEINTERLACE_SUPPORTED + if (png_ptr->interlaced) + { + png_ptr->do_interlace = 1; + return PNG_INTERLACE_ADAM7_PASSES; + } + + return 1; +# else /* !READ_DEINTERLACE */ + png_app_error(png_ptr, "no de-interlace support"); + /* return 0 below */ +# endif /* !READ_DEINTERLACE */ + } + + else /* write */ + { +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + png_ptr->do_interlace = 1; + png_set_write_interlace(png_ptr); + return PNG_INTERLACE_ADAM7_PASSES; + } + + return 1; +# else /* !WRITE_INTERLACING */ + png_app_error(png_ptr, "no interlace support"); + /* return 0 below */ +# endif /* !WRITE_INTERLACING */ + } } - return (1); + /* API CHANGE: 1.7.0: returns 0 if called with a NULL png_ptr */ + return 0; } -#endif +#endif /* READ_DEINTERLACE || WRITE_INTERLACING */ + +#ifdef PNG_MNG_READ_FEATURES_SUPPORTED +/* Undoes intrapixel differencing, this is called immediately after the PNG + * filter has been undone. + */ +static void +png_do_read_intrapixel_RGB8(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + /* TAKE CARE: dp and sp may be the same, in which case the assignments to *dp + * are overwriting sp[] + */ + do + { + *dp++ = PNG_BYTE(sp[0] + sp[1]); /* red+green */ + *dp++ = *++sp; /* green */ + *dp++ = PNG_BYTE(sp[0] + sp[1]); /* green+blue */ + sp += 2; + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_do_read_intrapixel_RGBA8(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + do + { + *dp++ = PNG_BYTE(sp[0] + sp[1]); /* red+green */ + *dp++ = *++sp; /* green */ + *dp++ = PNG_BYTE(sp[0] + sp[1]); /* green+blue */ + sp += 2; + *dp++ = *sp++; /* alpha */ + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_do_read_intrapixel_RGB16(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + /* The input consists of 16-bit values and, by examination of the code + * (please, someone, check; I didn't read the spec) the differencing is done + * against the 16-bit green value. + */ + do + { + unsigned int red = png_get_uint_16(sp + 0); + unsigned int green = png_get_uint_16(sp + 2); + unsigned int blue = png_get_uint_16(sp + 4); + sp += 6; + + red += green; + blue += green; + + *dp++ = PNG_BYTE(red >> 8); + *dp++ = PNG_BYTE(red); + *dp++ = PNG_BYTE(green >> 8); + *dp++ = PNG_BYTE(green); + *dp++ = PNG_BYTE(blue >> 8); + *dp++ = PNG_BYTE(blue); + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_do_read_intrapixel_RGBA16(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + /* As above but copy the alpha over too. */ + do + { + unsigned int red = png_get_uint_16(sp + 0); + unsigned int green = png_get_uint_16(sp + 2); + unsigned int blue = png_get_uint_16(sp + 4); + sp += 6; + + red += green; + blue += green; + + *dp++ = PNG_BYTE(red >> 8); + *dp++ = PNG_BYTE(red); + *dp++ = PNG_BYTE(green >> 8); + *dp++ = PNG_BYTE(green); + *dp++ = PNG_BYTE(blue >> 8); + *dp++ = PNG_BYTE(blue); + *dp++ = *sp++; + *dp++ = *sp++; /* alpha */ + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_init_read_intrapixel(png_transformp *tr, png_transform_controlp tc) +{ + /* Double check the permitted MNG features in case the app turned the feature + * on then off again. Also make sure the color type is acceptable; it must + * be RGB or RGBA. + */ + png_const_structp png_ptr = tc->png_ptr; + + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && + (png_ptr->filter_method == PNG_INTRAPIXEL_DIFFERENCING) && + (tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) == + PNG_FORMAT_FLAG_COLOR) + { + if (tc->init == PNG_TC_INIT_FINAL) switch (PNG_TC_PIXEL_DEPTH(*tc)) + { + case 24: (*tr)->fn = png_do_read_intrapixel_RGB8; break; + case 32: (*tr)->fn = png_do_read_intrapixel_RGBA8; break; + case 48: (*tr)->fn = png_do_read_intrapixel_RGB16; break; + case 64: (*tr)->fn = png_do_read_intrapixel_RGBA16; break; + default: impossible("bit depth"); + } + } + + else + (*tr)->fn = NULL; +} +#endif /* MNG_READ_FEATURES_SUPPORTED */ + +#ifdef PNG_MNG_WRITE_FEATURES_SUPPORTED +/* This is just the forward direction of the above: + * + * red := red - green + * blue:= blue- green + * + * Alpha is not changed. + */ +static void +png_do_write_intrapixel_RGB8(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + /* TAKE CARE: dp and sp may be the same, in which case the assignments to *dp + * are overwriting sp[] + */ + do + { + *dp++ = PNG_BYTE(sp[0] - sp[1]); /* red-green */ + *dp++ = *++sp; /* green */ + *dp++ = PNG_BYTE(sp[0] - sp[1]); /* green-blue */ + sp += 2; + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_do_write_intrapixel_RGBA8(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + do + { + *dp++ = PNG_BYTE(sp[0] - sp[1]); /* red-green */ + *dp++ = *++sp; /* green */ + *dp++ = PNG_BYTE(sp[0] - sp[1]); /* green-blue */ + sp += 2; + *dp++ = *sp++; /* alpha */ + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_do_write_intrapixel_RGB16(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + do + { + unsigned int red = png_get_uint_16(sp + 0); + unsigned int green = png_get_uint_16(sp + 2); + unsigned int blue = png_get_uint_16(sp + 4); + sp += 6; + + red -= green; + blue -= green; + + *dp++ = PNG_BYTE(red >> 8); + *dp++ = PNG_BYTE(red); + *dp++ = PNG_BYTE(green >> 8); + *dp++ = PNG_BYTE(green); + *dp++ = PNG_BYTE(blue >> 8); + *dp++ = PNG_BYTE(blue); + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_do_write_intrapixel_RGBA16(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + /* As above but copy the alpha over too. */ + do + { + unsigned int red = png_get_uint_16(sp + 0); + unsigned int green = png_get_uint_16(sp + 2); + unsigned int blue = png_get_uint_16(sp + 4); + sp += 6; + + red -= green; + blue -= green; + + *dp++ = PNG_BYTE(red >> 8); + *dp++ = PNG_BYTE(red); + *dp++ = PNG_BYTE(green >> 8); + *dp++ = PNG_BYTE(green); + *dp++ = PNG_BYTE(blue >> 8); + *dp++ = PNG_BYTE(blue); + *dp++ = *sp++; + *dp++ = *sp++; /* alpha */ + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_init_write_intrapixel(png_transformp *tr, png_transform_controlp tc) +{ + /* Write filter_method 64 (intrapixel differencing) only if: + * + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED, and; + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams), + * and; + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64, and; + * 4. The filter_method is 64, and; + * 5. The color_type is RGB or RGBA + */ + png_const_structp png_ptr = tc->png_ptr; + + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && + (png_ptr->filter_method == PNG_INTRAPIXEL_DIFFERENCING) && + (tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) == + PNG_FORMAT_FLAG_COLOR) + { + if (tc->init == PNG_TC_INIT_FINAL) switch (PNG_TC_PIXEL_DEPTH(*tc)) + { + case 24: (*tr)->fn = png_do_write_intrapixel_RGB8; break; + case 32: (*tr)->fn = png_do_write_intrapixel_RGBA8; break; + case 48: (*tr)->fn = png_do_write_intrapixel_RGB16; break; + case 64: (*tr)->fn = png_do_write_intrapixel_RGBA16; break; + default: impossible("bit depth"); + } + } + + else + (*tr)->fn = NULL; +} +#endif /* MNG_WRITE_FEATURES */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +png_uint_32 PNGAPI +png_permit_mng_features(png_structrp png_ptr, png_uint_32 mng_features) +{ + if (png_ptr != NULL) + { +# ifdef PNG_MNG_READ_FEATURES_SUPPORTED + if ((mng_features & PNG_FLAG_MNG_FILTER_64) != 0) + png_add_transform(png_ptr, 0/*size*/, png_init_read_intrapixel, + PNG_TR_MNG_INTRAPIXEL); +# else /* !MNG_READ_FEATURES */ + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "MNG not supported on read"); + return; + } +# endif /* !MNG_READ_FEATURES */ + +# ifdef PNG_MNG_WRITE_FEATURES_SUPPORTED + if ((mng_features & PNG_FLAG_MNG_FILTER_64) != 0) + png_add_transform(png_ptr, 0/*size*/, png_init_write_intrapixel, + PNG_TR_MNG_INTRAPIXEL); +# else /* !MNG_WRITE_FEATURES */ + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "MNG not supported on write"); + return; + } +# endif /* !MNG_WRITE_FEATURES */ + + return png_ptr->mng_features_permitted = + mng_features & PNG_ALL_MNG_FEATURES; + } + + return 0; +} +#endif /* MNG_FEATURES */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) ||\ + defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) ||\ + defined(PNG_READ_SWAP_ALPHA_SUPPORTED) ||\ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) ||\ + defined(PNG_READ_FILLER_SUPPORTED) ||\ + defined(PNG_WRITE_FILLER_SUPPORTED) ||\ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) ||\ + defined(PNG_READ_STRIP_16_TO_8_SUPPORTED) ||\ + defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) ||\ + defined(PNG_READ_EXPAND_16_SUPPORTED) ||\ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* This is a generic transform which manipulates the bytes in an input row. The + * manipulations supported are: + * + * Channel addition (alpha or filler) + * Channel removal (alpha or filler) + * Channel swaps - RGB to BGR, alpha/filler from last to first and vice versa + * + * The output is described in blocks of output pixel size 4-bit codes encoded + * as follows: + * + * 0 Advance the source pointer by the source pixel size, start the + * code list again. This code doesn't actually exist; it is simply + * the result of emptying the code list. + * 1..3 An error (ignored; treated like 0) + * 4..7 Put filler[code-4] into the output + * 8..15 Put source byte[code-8] in the output + * + * The codes are held in a png_uint_32 parameter. transform->args is used by + * the init routine to work out the required codes. The format change is a mask + * which is XORed with the tc format. Note that the init routine works out + * whether to work from the beginning or end of the row and the codes are always + * stored LSB first in the order needed. + */ +typedef struct +{ + png_transform tr; + png_uint_32 codes; /* As above */ + unsigned int format; /* format after transform */ + unsigned int bit_depth; /* bit depth after transform */ + png_byte filler[4]; /* Filler or alpha bytes, LSB first (see below) */ +} png_transform_byte_op; + +static void +png_do_byte_ops_up(png_transformp *transform, png_transform_controlp tc) + /* Row width is unchanged or decreasing */ +{ +# define png_ptr (tc->png_ptr) + png_transform_byte_op *tr = + png_transform_cast(png_transform_byte_op, *transform); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const unsigned int sp_advance = PNG_TC_PIXEL_DEPTH(*tc) >> 3; + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + + debug(tc->bit_depth == 8 || tc->bit_depth == 16); + debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0); + + tc->sp = tc->dp; + tc->format = tr->format; + tc->bit_depth = tr->bit_depth; + + /* 'output' is a 32-byte buffer that is used to delay writes for 16 bytes, + * avoiding overwrite when source and destination buffers are the same. + * 'hwm' is either 32 or 16, initially '32', when the byte counter 'i' + * reaches 'hwm' the last-but-one 16 bytes are written; the bytes + * [hwm..hwm+15] modulo 32. hwm is then swapped to hwm+16 mod 32 and i + * continues to advance. i is always below hwm. + * + * At the end the whole remaining buffer from hwm to i is written. + */ + { + const png_uint_32 codes = tr->codes; + png_uint_32 code = codes; + unsigned int i, hwm; /* buffer index and high-water-mark */ + png_byte output[32]; + + hwm = 32; + + for (i=0;;) + { + unsigned int next_code = code & 0xf; + + if (next_code >= 8) + output[i++] = sp[next_code-8]; + + else if (next_code >= 4) + output[i++] = tr->filler[next_code - 4]; + + else /* end code */ + { + sp += sp_advance; + + if (sp >= ep) + break; /* i may be == hwm at this point. */ + + code = codes; + continue; /* no ouput produced, skip the check */ + } + + code >>= 4; /* find the next code */ + + if (i == hwm) + { + hwm &= 0x10U; /* 0 or 16 */ + memcpy(dp, output + hwm, 16); + dp += 16; + i = hwm; /* reset i if hwm was 32 */ + /* hwm is only ever 16 or 32: */ + hwm += 16; + } + } + + /* Write from hwm to (i-1), the delay means there is always something to + * write. + */ + hwm &= 0x10U; + if (hwm == 16) + { + debug(i <= 16); + memcpy(dp, output + hwm, 16); + dp += 16; + } + + if (i > 0) + memcpy(dp, output, i); + +# ifndef PNG_RELEASE_BUILD + dp += i; + /* The macro expansion exceeded the limit on ANSI strings, so split it: + */ + dp -= PNG_TC_ROWBYTES(*tc); + debug(dp == tc->dp); +# endif + } + + debug(sp == ep); +# undef png_ptr +} + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +static void +png_do_byte_ops_down(png_transformp *transform, png_transform_controlp tc) + /* Row width is increasing */ +{ +# define png_ptr (tc->png_ptr) + png_transform_byte_op *tr = + png_transform_cast(png_transform_byte_op, *transform); + const png_const_bytep ep = png_voidcast(png_const_bytep, tc->sp); + const unsigned int sp_advance = PNG_TC_PIXEL_DEPTH(*tc) >> 3; + png_const_bytep sp = ep + PNG_TC_ROWBYTES(*tc); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_alloc_size_t dest_rowbytes; + + debug(tc->bit_depth == 8 || tc->bit_depth == 16); + debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0); + + tc->sp = tc->dp; + tc->format = tr->format; + tc->bit_depth = tr->bit_depth; + dest_rowbytes = PNG_TC_ROWBYTES(*tc); + dp += dest_rowbytes; + + /* In this case the 32-byte buffer is written downwards with a writes delayed + * by 16 bytes as before. 'hwm' is lower than i; 0 or 16. + */ + { + const png_uint_32 codes = tr->codes; + png_uint_32 code = codes; + unsigned int size, hwm, i; + png_byte output[32]; + + /* Align the writes to a 16-byte multiple from the start of the + * destination buffer: + */ + size = dest_rowbytes & 0xFU; + if (size == 0) size = 16; + i = size+16; + sp -= sp_advance; /* Move 1 pixel back */ + hwm = 0; + + for (;;) + { + unsigned int next_code = code & 0xf; + + if (next_code >= 8) + output[--i] = sp[next_code-8]; + + else if (next_code >= 4) + output[--i] = tr->filler[next_code - 4]; + + else /* end code */ + { + sp -= sp_advance; + + if (sp < ep) + break; + + code = codes; + continue; /* no ouput produced, skip the check */ + } + + code >>= 4; /* find the next code */ + + if (i == hwm) + { + /* A partial copy comes at the beginning to align the copies to a + * 16-byte boundary. The bytes to be written are the bytes + * i+16..(hwm-1) except that the partial buffer may reduce this. + */ + dp -= size; + hwm ^= 0x10U; /* == i+16 mod 32 */ + memcpy(dp, output + hwm, size); + size = 16; + if (i == 0) i = 32; + } + } + + /* The loop above only exits with an exit code, so 'i' has been checked + * against 'hwm' before and, because of the alignment, i will always be + * either 16 or 32: + */ + debug((i == 16 || i == 32) & (((i & 0x10U)^0x10U) == hwm)); + debug(sp+sp_advance == ep); + + /* At the end the bytes i..(hwm-1) need to be written, with the proviso + * that 'size' will be less than 16 for short rows. If 'size' is still a + * short value then the range to be written is output[i..16+(size-1)], + * otherwise (size == 16) either this is the first write and a full 32 + * bytes will be written (hwm == 0, i == 32) or 16 bytes need to be + * written. + */ + if (size < 16) + { + debug(i == 16); + dp -= size; + memcpy(dp, output + i, size); + } + + else /* size == 16 */ + { + /* Write i..(hwm-1); 16 or 32 bytes, however if 32 bytes are written + * they are contiguous and i==0. + * + * hwm is 0 or 16, i is 16 or 32, swap 0 and 32: + */ + if (hwm == 0) hwm = 32; + if (i == 32) i = 0; + affirm(i < hwm); + debug(hwm == i+16 || (i == 0 && hwm == 32)); + + hwm -= i; + dp -= hwm; + memcpy(dp, output+i, hwm); + } + } + + debug(dp == png_upcast(png_bytep, tc->dp)); + +# undef png_ptr +} +#endif /* READ_TRANSFORMS */ + +/* 16 bit byte swapping */ +static void +png_do_bswap(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_byte_op *tr = + png_transform_cast(png_transform_byte_op, *transform); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + const png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc); + + tc->format = tr->format; + tc->bit_depth = tr->bit_depth; + tc->sp = dp; + +# ifdef _XOPEN_SOURCE + /* byte swapping often has incredibly fast implementations because of the + * importance in handling ethernet traffic. X/Open defines swab() for + * this purpose and it is widely supported and normally incredibly fast: + */ + debug((rowbytes & 1) == 0); + swab(sp, dp, rowbytes); +# else /* !_XOPEN_SOURCE */ + { + const png_const_bytep ep = sp + rowbytes - 1; + + while (sp < ep) + { + png_byte b0 = *sp++; + *dp++ = *sp++; + *dp++ = b0; + } + + debug(sp == ep+1); /* even number of bytes */ + } +# endif + + PNG_UNUSED(transform) +# undef png_ptr +} + +/* The following flags, store in tr->args, are set by the relevant PNGAPI + * png_set calls then resolved below. + */ +#define PNG_BO_STRIP_ALPHA 0x0001U /* Remove an alpha channel (read only) */ +#define PNG_BO_CHOP_16_TO_8 0x0002U /* Chop 16-bit to 8-bit channels */ +#define PNG_BO_GRAY_TO_RGB 0x0004U /* G <-> RGB; replicate channels */ +/* QUANTIZE happens here */ +#define PNG_BO_EXPAND_16 0x0008U /* Expand 8-bit channels to 16-bit */ +#define PNG_BO_BGR 0x0010U /* RGB <-> BGR */ +#define PNG_BO_FILLER 0x0020U /* Add a filler/alpha */ +#define PNG_BO_SWAP_ALPHA 0x0040U /* xA <-> Ax; alpha swap */ +#define PNG_BO_SWAP_16 0x0080U /* 16-bit channel byte swapping */ + +/* The following are additional flags to qualify the transforms: */ +#define PNG_BO_FILLER_ALPHA 0x4000U /* The filler is an alpha value */ +#define PNG_BO_FILLER_FIRST 0x8000U /* The filler comes first */ + +static void +png_init_byte_ops(png_transformp *transform, png_transform_controlp tc) +{ + /* In the absence of png_set_quantize none of the above operations apply to a + * palette row except indirectly; they may apply if the palette was expanded, + * but this happens earlier in the pipeline. + * + * In the presence of png_set_quantize the rules are considerably more + * complex. In libpng 1.6.0 the following operations occur before + * png_do_quantize: + * + * PNG_BO_GRAY_TO_RGB (png_do_gray_to_rgb, but only sometimes) + * PNG_BO_STRIP_ALPHA (png_do_strip_channel; removes alpha) + * encode_alpha + * scale_16_to_8 + * PNG_BO_CHOP_16_TO_8 (png_do_chop) + * + * The following occur afterward: + * + * PNG_BO_EXPAND_16 (png_do_expand_16) + * PNG_BO_GRAY_TO_RGB (png_do_gray_to_rgb, normally) + * PNG_BO_BGR (png_do_bgr) + * PNG_BO_FILLER (png_do_read_filler) + * PNG_BO_SWAP_ALPHA (png_do_read_swap_alpha) + * PNG_BO_SWAP_16 (png_do_swap; 16-bit byte swap) + * + * The gray to RGB operation needs to occur early for GA or gray+tRNS images + * where the pixels are being composed on a non-gray value. For the moment + * we assume that if this is necessary the following 'init' code will see RGB + * at this point. + * + * The quantize operation operates only if: + * + * 1) tc->bit_depth is 8 + * 2) The color type exactly matches that required by the parameters to + * png_set_quantize; it can be RGB, RGBA or palette, but + * png_set_quantize (not the init routine) determines this. + * + * To avoid needing to know this here the two stage initialization is used + * with two transforms, one pre-quantization the other post. In the first + * stage the correct row format and depth is set up. In the second stage the + * pre-quantization transform looks for a post-quantization transform + * immediately following and, if it exists, transfers its flags to that. + */ + png_structp png_ptr = tc->png_ptr; + png_transform_byte_op *tr = + png_transform_cast(png_transform_byte_op, *transform); + png_uint_32 args = tr->tr.args; + const unsigned int png_format = tc->format; + unsigned int format = png_format; /* memory format */ + const unsigned int png_bit_depth = tc->bit_depth; + unsigned int bit_depth = png_bit_depth; /* memory bit depth */ + + debug(tc->init); + + /* Channel swaps do not occur on COLORMAP format data at present because the + * COLORMAP is limited to 1 byte per pixel (so there is nothing to + * manipulate). Likewise for low bit depth gray, however the code below may + * widen 8-bit gray to RGB. + */ + if ((png_format & PNG_FORMAT_FLAG_COLORMAP) != 0U || png_bit_depth < 8U) + { + tr->tr.fn = NULL; + return; + } + + /* This will normally happen in TC_INIT_FORMAT, but if there is a + * png_do_quantize operation which doesn't apply (this is unlikely) it will + * happen in TC_INIT_FINAL. + */ + if (tr->tr.next != NULL && tr->tr.next->order == PNG_TR_CHANNEL_POSTQ) + { + debug(tr->tr.order == PNG_TR_CHANNEL_PREQ); + + /* So we can merge this transform into the next one, note that because the + * PNG_BO_FILLER operation is POSTQ we don't need to copy anything other + * than the flags. + */ + debug((args & tr->tr.next->args) == 0U); + tr->tr.next->args |= args; + tr->tr.fn = NULL; + return; + } + + /* Else compact the flags for this transform - this is done in both + * TC_INIT_FORMAT and TC_INIT_FINAL because it is safer that way; the copy + * above shouldn't actually affect the results but might result in TO8 and + * TO16 cancelling each other because they are in separate transforms before + * the merge above. + * + * QUIET API CHANGE: + * For compatiblity with earlier versions of libpng these tests need to + * occur in the same order as the earlier transforms; 'TO8' combined with + * 'TO16' did actually do something to 16-bit data, however now it just + * preserves the original bit depth. + */ + if ((args & PNG_BO_STRIP_ALPHA) != 0U) + { + if ((format & PNG_FORMAT_FLAG_ALPHA) != 0U) + format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + + else + args &= PNG_BIC_MASK(PNG_BO_STRIP_ALPHA); + } + + if ((args & PNG_BO_CHOP_16_TO_8) != 0U) + { + /* This is the quiet API CHANGE; in fact it isn't necessary, but it + * seems likely that requesting both operations is a mistake: + */ + if ((args & PNG_BO_EXPAND_16) != 0U) + args &= PNG_BIC_MASK(PNG_BO_CHOP_16_TO_8|PNG_BO_EXPAND_16); + + else if (bit_depth == 16U) + { + bit_depth = 8U; + + /* This also makes the tRNS chunk unusable: */ + tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL; + /* These need further processing: PNG_INFO_sBIT, PNG_INFO_bKGD */ + } + + else + args &= PNG_BIC_MASK(PNG_BO_CHOP_16_TO_8); + } + + /* QUANTIZE happens here */ + + if ((args & PNG_BO_EXPAND_16) != 0U) + { + /* This only does the 8 to 16-bit part of the expansion by multiply by + * 65535/255 (257) using byte replication. The cases of low bit depth + * gray being expanded to 16-bit have to be handled separately. + */ + if (bit_depth == 8U) + bit_depth = 16U; + + else + args &= PNG_BIC_MASK(PNG_BO_EXPAND_16); + } + + if ((args & PNG_BO_GRAY_TO_RGB) != 0U) + { + if ((format & PNG_FORMAT_FLAG_COLOR) == 0U) + format |= PNG_FORMAT_FLAG_COLOR; + + else + args &= PNG_BIC_MASK(PNG_BO_GRAY_TO_RGB); + } + + if ((args & PNG_BO_BGR) != 0U) + { + /* This does not happen on colormaps: */ + if ((format & PNG_FORMAT_FLAG_COLOR) != 0U && !tc->palette) + format |= PNG_FORMAT_FLAG_BGR; + + else + args &= PNG_BIC_MASK(PNG_BO_BGR); + } + + if ((args & PNG_BO_FILLER) != 0U) + { + if ((format & PNG_FORMAT_FLAG_ALPHA) == 0U) + { + format |= PNG_FORMAT_FLAG_ALPHA; + tc->channel_add = 1U; + /* And SWAP_ALPHA did not occur, because prior to 1.7.0 the filler op + * did not set ALPHA in the color type, so use SWAP_ALPHA to handle the + * before/after filler location. + * + * NOTE: this occurs twice, once in TC_START and once in TC_FINAL, but + * that is ok, the operations are idempotent. + * + * For colormaps (tc->palette set) the filler will just end up setting + * all the tRNS entries and PNG_BO_SWAP_ALPHA will be cancelled below. + */ + if ((args & PNG_BO_FILLER_FIRST) != 0U) + args |= PNG_BO_SWAP_ALPHA; + + else + args &= PNG_BIC_MASK(PNG_BO_SWAP_ALPHA); + + if (!(args & PNG_BO_FILLER_ALPHA)) /* filler is not alpha */ + format |= PNG_FORMAT_FLAG_AFILLER; + } + + else + args &= PNG_BIC_MASK(PNG_BO_FILLER); + } + + if ((args & PNG_BO_SWAP_ALPHA) != 0U) + { + /* This does not happen on color maps: */ + if ((format & PNG_FORMAT_FLAG_ALPHA) != 0U && !tc->palette) + format |= PNG_FORMAT_FLAG_AFIRST; + + else + args &= PNG_BIC_MASK(PNG_BO_SWAP_ALPHA); + } + + if ((args & PNG_BO_SWAP_16) != 0U) + { + if (bit_depth == 16U) + format |= PNG_FORMAT_FLAG_SWAPPED; + + else + args &= PNG_BIC_MASK(PNG_BO_SWAP_16); + } + + if (args != 0U) + { + /* At the end (TC_INIT_FINAL) work out the mapping array using the codes + * defined above and store the format and bit depth changes (as changes, + * so they will work either forward or backward). The filler array must + * be set up by the png_set API. + */ + if (tc->init == PNG_TC_INIT_FINAL) + { + const unsigned int png_pixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3; + + tc->format = format; + tc->bit_depth = bit_depth; + + { + const unsigned int memory_pixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3; + unsigned int code_size, src_size; + int go_down; + png_byte codes[8]; + + /* The codes array maps the PNG format into the memory format + * assuming the mapping works upwards in the address space. + * Initially ignore the bit depth and just work on the first four + * bytes. + */ + codes[0] = 8U; + codes[1] = 9U; + codes[2] = 10U; + codes[3] = 11U; + codes[7] = codes[6] = codes[5] = codes[4] = 0U/*error*/; + + /* PNG_BO_STRIP_ALPHA: handled by memory_pixel_size */ + /* PNG_BO_CHOP_16_TO_8: handled below */ + /* PNG_BO_EXPAND_16: handled below */ + + if ((args & PNG_BO_GRAY_TO_RGB) != 0U) + { + codes[3] = 9U; /* alpha, if present */ + codes[2] = codes[1] = 8U; + +# ifdef PNG_READ_tRNS_SUPPORTED + /* Gray to RGB, so copy the tRNS G value into r,g,b: */ + if (png_ptr->num_trans == 1U) + png_ptr->trans_color.blue = + png_ptr->trans_color.green = + png_ptr->trans_color.red = + png_ptr->trans_color.gray; +# endif /* READ_tRNS */ + } + + /* 'BGR' and gray-to-RGB are mutually exclusive; with gray-to-RGB + * codes[0] == codes[2] == 8 + */ + else if ((args & PNG_BO_BGR) != 0U) + { + codes[0] = 10U; + codes[2] = 8U; + } + + if ((args & PNG_BO_FILLER) != 0U) + { + /* The filler alway goes after; for a 'before' filler the code + * above turns on SWAP_ALPHA too. The gray-to-RGB transform has + * happened already, so the location of the filler channel is + * given by 'format': + */ + if ((format & PNG_FORMAT_FLAG_COLOR) != 0U) + codes[3] = 4U; /* low byte of filler */ + + else + codes[1] = 4U; + } + + if ((args & PNG_BO_SWAP_ALPHA) != 0U) + { + if ((format & PNG_FORMAT_FLAG_COLOR) != 0U) + { + /* BGR may have swapped the early codes. gray-to-RGB may have + * set them all to '8': + */ + png_byte acode = codes[3]; + codes[3] = codes[2]; + codes[2] = codes[1]; + codes[1] = codes[0]; + codes[0] = acode; + } + + else /* GA format */ + codes[0] = codes[1], codes[1] = 8U; + } + + /* PNG_BO_SWAP_16: 16-bit only, handled below */ + + /* Now the 16-bit dependent stuff. */ + if ((args & PNG_BO_CHOP_16_TO_8) != 0U) + { + /* 16-bit input, 8-bit output, happens before FILLER so the + * filler must be an 8-bit value. Apart from a filler code (4 in + * this case) the code must be adjusted from byte 'x' to byte + * '2x' to select the MSB of each 16-bit channel. + * + * We must use PNG_FORMAT_CHANNELS here because the memory pixel + * size might (in the future) include a TO16 operation. + */ + unsigned int i = PNG_FORMAT_CHANNELS(format); + + while (i > 0U) + { + unsigned int code = codes[--i]; + + if (code > 8U) /* 8, 4 need not change */ + codes[i] = PNG_BYTE(8U+2U*(code-8U)); + } + } + + if ((args & PNG_BO_EXPAND_16) != 0U) + { + /* Don't expect this with CHOP, but it will work, setting the low + * 8-bits of each 16-bit value to the high bits. + */ + unsigned int i = PNG_FORMAT_CHANNELS(format); + + while (i > 0U) + { + png_byte code = codes[--i]; + + /* BSWAP is after FILLER, however the data passed in is a + * machine native png_uint_16. We don't know until this init + * routine whether the data is an 8 or 16-bit value because we + * don't know the full set of transforms the app will apply + * when the png_set_filler API is called. + * + * This means that the data in tr->filler[] needs to have the + * low bits in a known place, so the code here puts the low 8 + * bits in filler[0], code 4. Hence the following: + */ + if (code == 4U) + codes[2U*i/*MSB*/] = 5U, codes[2U*i+1U/*LSB*/] = 4U; + + else + codes[2U*i] = codes[2U*i+1U] = code; + } + +# ifdef PNG_READ_tRNS_SUPPORTED + /* We're just duplicating bytes, so the tRNS chunk can be + * maintained if present. If the tRNS is for a colormap this + * produces garbage in trans_color, but it isn't used. + */ + if (png_ptr->num_trans == 1U) + { +# define TO16(x) x = PNG_UINT_16((x & 0xFFU) * 0x101U) + TO16(png_ptr->trans_color.gray); + TO16(png_ptr->trans_color.red); + TO16(png_ptr->trans_color.green); + TO16(png_ptr->trans_color.blue); +# undef TO16 + } +# endif /* READ_tRNS */ + } + + else if (bit_depth == 16U) + { + /* 16-bit input and output. */ + unsigned int i = PNG_FORMAT_CHANNELS(format); + + while (i > 0U) + { + unsigned int code = codes[--i]; + + if (code == 4U) /* as above */ + codes[2U*i/*MSB*/] = 5U, codes[2U*i+1U/*LSB*/] = 4U; + + else + { + codes[2U*i] = PNG_BYTE(8U+2U*(code-8U)); + codes[2U*i+1U] = PNG_BYTE(8U+2U*(code-8U)+1U); + } + } + } + + if ((args & PNG_BO_SWAP_16) != 0U) + { + /* bswap the memory bytes. */ + unsigned int i; + png_byte bswap_codes[sizeof codes]; + + debug((memory_pixel_size & 1U) == 0U); + + for (i=0U; iread_struct) + { + /* There are no write transforms that add data to the PNG + * file; the 'filler' transform removes a channel, but that is + * the limit of the changes. + */ + unsigned int i = 0U; + png_byte write_codes[8U]; + + memset(write_codes, 0, sizeof write_codes); + + while (i= 8U) /* 8+index of PNG byte */ + write_codes[code-8U] = PNG_BYTE(8U+i); + /* else this is a filler byte to be removed */ + else + debug(code == 4U || code == 5U); + + ++i; + } + + code_size = png_pixel_size; + src_size = memory_pixel_size; + tr->format = png_format; + tr->bit_depth = png_bit_depth; + + /* The PNG size should always be <= to the memory size, the + * source pointer will be the memory, the destination the PNG + * format, so it should always be possible to do the upwards + * copy. + */ + go_down = png_pixel_size > memory_pixel_size; + affirm(!go_down); + memcpy(codes, write_codes, sizeof codes); + } + + else +# endif /* WRITE_TRANSFORMS */ + { + code_size = memory_pixel_size; + src_size = png_pixel_size; + tr->format = format; + tr->bit_depth = bit_depth; + go_down = png_pixel_size < memory_pixel_size; + } + + /* Record this for debugging: */ + tr->tr.args = args; + + /* For the same-pixel-size case check for a bswap; this is available + * in heavily optimized forms and is a common operation (50% of the + * time) with 16-bit PNG data, particularly given the handling in + * the simplified API. + */ + if (!go_down) + { + if (memory_pixel_size == png_pixel_size) + { + int the_same = 1; + int swapped = (memory_pixel_size & 1) == 0; /* even count */ + unsigned int i; + + for (i=0U; itr.fn = png_do_bswap; + return; + } + + else if (the_same) + impossible("not reached"); + } + + tr->tr.fn = png_do_byte_ops_up; + + /* Construct the code, forwards: */ + { + unsigned int i = code_size; + png_uint_32 code = 0U; + + while (i > 0U) + { + unsigned int next = codes[--i]; + + code <<= 4U; + + if ((next >= 8U && next < 8U+src_size) || + next == 4U || next == 5U) + code += next; + + else + impossible("invalid code (up)"); + } + + tr->codes = code; + } + } + + else /* go_down */ +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + { + tr->tr.fn = png_do_byte_ops_down; + + /* Construct the code, backwards: */ + { + unsigned int i = 0U; + png_uint_32 code = 0U; + + while (i < code_size) + { + unsigned int next = codes[i++]; + + code <<= 4; + + if ((next >= 8U && next < 8U+src_size) || + next == 4U || next == 5U) + code += next; + + else + impossible("invalid code (down)"); + } + + tr->codes = code; + } + } +# else /* !READ_TRANSFORMS */ + impossible("not reached"); /* because of the affirm above */ +# endif /* !READ_TRANSFORMS */ + } + } + + else /* TC_INIT_FORMAT: just store modified 'args' */ + { + tc->format = format; + tc->bit_depth = bit_depth; + tr->tr.args = args; + } + } + + else /* the transform is not applicable */ + tr->tr.fn = NULL; +} +#endif /* SWAP poo */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +void /* PRIVATE */ +png_add_rgb_to_gray_byte_ops(png_structrp png_ptr, png_transform_controlp tc, + unsigned int index, unsigned int order) + /* Add a byte_ops transform to convert RGB or RGBA data to 'gray' by + * selecting just the given change [index] The transform added is added at + * 'order'. + */ +{ + png_transform_byte_op *tr = png_transform_cast(png_transform_byte_op, + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_do_byte_ops_up, order)); + + affirm((tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) == + PNG_FORMAT_FLAG_COLOR && + index <= 2 && tc->init == PNG_TC_INIT_FINAL); + + tr->format = tc->format & PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR); + tr->bit_depth = tc->bit_depth; + + /* For 1 byte channel [index] plus, maybe, alpha: */ + if (tc->bit_depth == 8) + { + tr->codes = 8U + index + + ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ? (8U+3U) << 4 : 0U); + UNTESTED + } + + else + { + affirm(tc->bit_depth == 16); + + /* As above, but two bytes; [2*index] and [2*index+1] */ + index *= 2U; + tr->codes = (8U + index) + ((9U + index) << 4) + + ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ? + ((8U+6U) + ((9U+6U) << 4)) << 8 : 0U); + UNTESTED + } +} +#endif /* READ_RGB_TO_GRAY */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) &&\ + defined(PNG_READ_BACKGROUND_SUPPORTED) +void /* PRIVATE */ +png_push_gray_to_rgb_byte_ops(png_transformp *transform, + png_transform_controlp tc) + /* This is an init-time utility to add appropriate byte ops to expand a + * grayscale PNG data set to RGB. + */ +{ +# define png_ptr (tc->png_ptr) + png_transformp tr = png_push_transform(png_ptr, + sizeof (png_transform_byte_op), png_init_byte_ops, transform, NULL); + + tr->args = PNG_BO_GRAY_TO_RGB; + debug(tr == *transform); + png_init_byte_ops(transform, tc); +# undef png_ptr +} +#endif /* GRAY_TO_RGB && READ_BACKGROUND */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +void /* PRIVATE */ +png_add_strip_alpha_byte_ops(png_structrp png_ptr) +{ + png_add_transform(png_ptr, sizeof (png_transform_byte_op), png_init_byte_ops, + PNG_TR_CHANNEL_PREQ)->args |= PNG_BO_STRIP_ALPHA; +} +#endif /* READ_STRIP_ALPHA */ + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +/* Chop 16-bit depth files to 8-bit depth */ +void PNGAPI +png_set_strip_16(png_structrp png_ptr) +{ + if (png_ptr != NULL) + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_PREQ)->args |= + PNG_BO_CHOP_16_TO_8; +} +#endif /* READ_STRIP_16_TO_8 */ + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +void PNGAPI +png_set_gray_to_rgb(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { + png_set_expand_gray_1_2_4_to_8(png_ptr); + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_PREQ)->args |= + PNG_BO_GRAY_TO_RGB; + } +} +#endif /* READ_GRAY_TO_RGB */ + +/* QUANTIZE */ + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels. PNG_BO_EXPAND_16 also expands the tRNS chunk if + * it is present, but it requires low bit depth grayscale expanded first. This + * must also force palette to RGB. + */ +void PNGAPI +png_set_expand_16(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { + png_set_expand_gray_1_2_4_to_8(png_ptr); + png_set_palette_to_rgb(png_ptr); + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |= + PNG_BO_EXPAND_16; + } +} +#endif /* READ_EXPAND_16 */ + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +void PNGAPI +png_set_bgr(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { +# ifndef PNG_READ_BGR_SUPPORTED + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_bgr not supported on read"); + return; + } +# endif +# ifndef PNG_WRITE_BGR_SUPPORTED + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_bgr not supported on write"); + return; + } +# endif + + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |= + PNG_BO_BGR; + } +} +#endif /* BGR */ #if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* This includes png_set_filler and png_set_add_alpha. The only difference + * between the two is that the latter resulted in PNG_COLOR_MASK_ALPHA being + * added to the info_ptr color type, if png_read_update_info was called whereas + * the former did not. + * + * Regardless of whether the added channel resulted in the change to the + * png_info color type, the SWAP_ALPHA transform was not performed, even though + * it apparently occured after the add, because PNG_COLOR_MASK_ALPHA was never + * set in the 1.6 'row_info'. + * + * Consequently 'SWAP_ALPHA' and 'FILLER' were independent; one or the other + * would occur depending on the color type (not the number of channels) prior to + * the two transforms. + * + * Prior to 1.7.0 the app could obtain information about the memory format by + * calling png_read_update_info followed by png_get_color_type and + * png_get_channels. The first would return PNG_COLOR_TYPE..._ALPHA if + * png_set_add_alpha was performed and the base type if png_set_filler was + * performed, however in both cases png_get_channels would return the extra + * channel; 2 or 4. + * + * The app could also insert a user transform callback and view the color type + * in the old "row_info" structure, however this resulted in an inconsistent + * color type because png_set_alpha did not add COLOR_MASK_ALPHA to the color + * type. + * + * Hence API CHANGE: 1.7.0, row transform callbacks now see the same color type + * as reported by png_get_color_type after png_read_update_info. + */ /* Add a filler byte on read, or remove a filler or alpha byte on write. * The filler type has changed in v0.95 to allow future 2-byte fillers * for 48-bit input data, as well as to avoid problems with some compilers * that don't like bytes as parameters. */ +static void +set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc, int alpha) +{ + if (png_ptr != NULL) + { + if (filler_loc != PNG_FILLER_BEFORE && filler_loc != PNG_FILLER_AFTER) + { + png_app_error(png_ptr, "png_set_filler: invalid filler location"); + return; + } + +# ifndef PNG_READ_SWAP_SUPPORTED + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_filler not supported on read"); + return; + } +# endif +# ifndef PNG_WRITE_SWAP_SUPPORTED + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_filler not supported on write"); + return; + } +# endif + + { + png_transform_byte_op *tr = + png_transform_cast(png_transform_byte_op, + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)); + png_uint_32 args = PNG_BO_FILLER; + + if (filler_loc == PNG_FILLER_BEFORE) + args |= PNG_BO_FILLER_FIRST; + + if (alpha) + args |= PNG_BO_FILLER_ALPHA; + + tr->tr.args |= args; + + /* The filler must be stored LSByte first: */ + tr->filler[0] = PNG_BYTE(filler >> 0); + tr->filler[1] = PNG_BYTE(filler >> 8); + tr->filler[2] = PNG_BYTE(filler >> 16); + tr->filler[3] = PNG_BYTE(filler >> 24); + } + } +} + void PNGAPI png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc) { - png_debug(1, "in png_set_filler"); - - if (png_ptr == NULL) - return; - - /* In libpng 1.6 it is possible to determine whether this is a read or write - * operation and therefore to do more checking here for a valid call. - */ - if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) - { -# ifdef PNG_READ_FILLER_SUPPORTED - /* On read png_set_filler is always valid, regardless of the base PNG - * format, because other transformations can give a format where the - * filler code can execute (basically an 8 or 16-bit component RGB or G - * format.) - * - * NOTE: usr_channels is not used by the read code! (This has led to - * confusion in the past.) The filler is only used in the read code. - */ - png_ptr->filler = PNG_UINT_16(filler); /* Max bit depth is 16 */ -# else - png_app_error(png_ptr, "png_set_filler not supported on read"); - PNG_UNUSED(filler) /* not used in the write case */ - return; -# endif - } - - else /* write */ - { -# ifdef PNG_WRITE_FILLER_SUPPORTED - /* On write the usr_channels parameter must be set correctly at the - * start to record the number of channels in the app-supplied data. - */ - switch (png_ptr->color_type) - { - case PNG_COLOR_TYPE_RGB: - png_ptr->usr_channels = 4; - break; - - case PNG_COLOR_TYPE_GRAY: - if (png_ptr->bit_depth >= 8) - { - png_ptr->usr_channels = 2; - break; - } - - else - { - /* There simply isn't any code in libpng to strip out bits - * from bytes when the components are less than a byte in - * size! - */ - png_app_error(png_ptr, - "png_set_filler is invalid for low bit depth gray output"); - return; - } - - default: - png_app_error(png_ptr, - "png_set_filler: inappropriate color type"); - return; - } -# else - png_app_error(png_ptr, "png_set_filler not supported on write"); - return; -# endif - } - - /* Here on success - libpng supports the operation, set the transformation - * and the flag to say where the filler channel is. - */ - png_ptr->transformations |= PNG_FILLER; - - if (filler_loc == PNG_FILLER_AFTER) - png_ptr->flags |= PNG_FLAG_FILLER_AFTER; - - else - png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; + set_filler(png_ptr, filler, filler_loc, 0/*!alpha*/); } /* Added to libpng-1.2.7 */ void PNGAPI png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc) { - png_debug(1, "in png_set_add_alpha"); - - if (png_ptr == NULL) - return; - - png_set_filler(png_ptr, filler, filler_loc); - /* The above may fail to do anything. */ - if ((png_ptr->transformations & PNG_FILLER) != 0) - png_ptr->transformations |= PNG_ADD_ALPHA; + set_filler(png_ptr, filler, filler_loc, 1/*alpha*/); } - -#endif +#endif /* FILLER */ #if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) void PNGAPI png_set_swap_alpha(png_structrp png_ptr) { - png_debug(1, "in png_set_swap_alpha"); + if (png_ptr != NULL) + { +# ifndef PNG_READ_SWAP_SUPPORTED + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_swap_alpha not supported on read"); + return; + } +# endif +# ifndef PNG_WRITE_SWAP_SUPPORTED + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_swap_alpha not supported on write"); + return; + } +# endif - if (png_ptr == NULL) - return; - - png_ptr->transformations |= PNG_SWAP_ALPHA; + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |= + PNG_BO_SWAP_ALPHA; + } } -#endif +#endif /* SWAP_ALPHA */ -#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +void PNGAPI +png_set_swap(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { +# ifndef PNG_READ_SWAP_SUPPORTED + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_swap not supported on read"); + return; + } +# endif +# ifndef PNG_WRITE_SWAP_SUPPORTED + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_swap not supported on write"); + return; + } +# endif + + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |= + PNG_BO_SWAP_16; + } +} +#endif /* SWAP */ + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) ||\ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) ||\ + defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) ||\ + defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +static png_alloc_size_t +row_align(png_transform_controlp tc) + /* Utiltity to align the source row (sp) in a transform control; it does this + * by simply copying it to dp if it is not already aligned. As a convenience + * the utility returns the number of bytes in the row. + */ +{ + png_const_structp png_ptr = tc->png_ptr; + png_const_voidp sp = tc->sp; + png_voidp dp = tc->dp; + png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc); + + /* For alignment; if png_alignof is not supported by the compiler this will + * always do an initial memcpy if the source and destination are not the + * same. We can only get here for write; the read case always uses locally + * allocated buffers, only write reads from the application data directly. + */ +# ifdef png_alignof + debug(png_isaligned(dp, png_uint_32)); +# endif + if (sp != dp && !png_ptr->read_struct && !png_isaligned(sp, png_uint_32)) + { + UNTESTED + memcpy(dp, sp, rowbytes); + tc->sp = dp; + } + + return rowbytes; +} +#endif /* Stuff that needs row_align */ + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) ||\ + defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Bit-ops; invert bytes. This works for mono inverts too because even the low + * bit depths can be handled as bytes (since there can be no intervening + * channels). + */ +#define PNG_B_INVERT_MONO 1U +#define PNG_B_INVERT_RGB 2U /* not set, used below */ +#define PNG_B_INVERT_ALPHA 4U + +typedef struct +{ + png_transform tr; + unsigned int step0; /* initial advance on sp and dp */ + unsigned int step; /* advance after start */ + png_uint_32 mask; /* XOR mask */ +} png_transform_bit_op; + +static void +png_do_invert_all(png_transformp *transform, png_transform_controlp tc) +{ + const png_const_structp png_ptr = tc->png_ptr; + /* Invert the whole row, quickly */ + const png_const_voidp dp_end = png_upcast(png_bytep, tc->dp) + row_align(tc); + png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp); + png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp); + + tc->sp = dp; + + if (png_ptr->read_struct) + { + tc->format |= PNG_FORMAT_FLAG_RANGE; + tc->range++; + } + + else if (--(tc->range) == 0) + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE); + + while (png_upcast(void*,dp) < dp_end) + *dp++ = ~*sp++; + + PNG_UNUSED(transform); +} + +static void +png_do_invert_channel(png_transformp *transform, png_transform_controlp tc) +{ + const png_const_structp png_ptr = tc->png_ptr; + /* Invert just one channel in the row. */ + const png_transform_bit_op * const tr = + png_transform_cast(png_transform_bit_op, *transform); + const png_uint_32 mask = tr->mask; + const unsigned int step = tr->step; + const unsigned int step0 = tr->step0; + const png_const_voidp dp_end = png_upcast(png_bytep, tc->dp) + row_align(tc); + png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp); + png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp); + + tc->sp = dp; + + if (png_ptr->read_struct) + { + tc->format |= PNG_FORMAT_FLAG_RANGE; + tc->range++; + } + + else if (--(tc->range) == 0) + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE); + + if (sp == dp || step == 1) + { + sp += step0; + dp += step0; + + while (png_upcast(void*,dp) < dp_end) + *dp = *sp ^ mask, dp += step, sp += step; + } + + else /* step == 2, copy required */ + { + if (step0) /* must be 1 */ + *dp++ = *sp++; + + while (png_upcast(void*,dp) < dp_end) + { + *dp++ = *sp++ ^ mask; + if (!(png_upcast(void*,dp) < dp_end)) + break; + *dp++ = *sp++; + } + } + + PNG_UNUSED(transform); +} + +static void +png_init_invert(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_bit_op *tr = + png_transform_cast(png_transform_bit_op, *transform); + png_uint_32 invert = tr->tr.args; + png_uint_32 present; /* channels present */ + + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0) + present = 0; + + else /* not color mapped */ + { + if ((tc->format & PNG_FORMAT_FLAG_COLOR) != 0) + present = PNG_B_INVERT_RGB; + else + present = PNG_B_INVERT_MONO; + + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0) + present |= PNG_B_INVERT_ALPHA; + } + + /* Cannot invert things that aren't there: */ + invert &= present; + + /* If nothing can be inverted is present the transform is not applicable: */ + if (invert == 0) + (*transform)->fn = NULL; + + else + { + tc->format |= PNG_FORMAT_FLAG_RANGE; + tc->range++; + + if (tc->init == PNG_TC_INIT_FINAL) + { + /* If everything that is present is to be inverted just invert the + * whole row: + */ + if (invert == present) + (*transform)->fn = png_do_invert_all; + + else + { + /* One thing is to be inverted, G or A: */ + unsigned int channels = PNG_TC_CHANNELS(*tc); + unsigned int channel = + (tc->format & PNG_FORMAT_FLAG_AFIRST) != 0 ? 0 : channels-1; + + affirm(channels == 2 || channels == 4); + + if (invert != PNG_B_INVERT_ALPHA) + { + debug(invert == PNG_B_INVERT_MONO && channels == 2 && + present == PNG_B_INVERT_MONO+PNG_B_INVERT_ALPHA); + channel = (channels-1) - channel; + } + + affirm(tc->bit_depth == 8 || tc->bit_depth == 16); + + /* So channels[channel] is to be inverted, make a mask: */ + { + union + { + png_byte bytes[8]; + png_uint_32 words[2]; + } masks; + + memset(&masks, 0, sizeof masks); + + if (tc->bit_depth == 8) + { + /* channels is 2 or 4, channel < 4. */ + masks.bytes[channel+channels] = masks.bytes[channel] = 0xff; + tr->step = 1; + tr->mask = masks.words[0]; + tr->step0 = 0; + } + + else /* tc->bit_depth == 16 */ + { + channel <<= 1; /* in bytes */ + masks.bytes[channel+1] = masks.bytes[channel] = 0xff; + + if (channels == 2) + { + tr->step = 1; + tr->mask = masks.words[0]; + tr->step0 = 0; + } + + else /* channels == 4 */ + { + tr->step = 2; + + if (masks.words[0] == 0) + { + tr->mask = masks.words[1]; + tr->step0 = 1; + } + + else + { + tr->mask = masks.words[0]; + tr->step0 = 0; + } + } + } + } + + (*transform)->fn = png_do_invert_channel; + } + } + } +# undef png_ptr +} + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\ defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) void PNGAPI png_set_invert_alpha(png_structrp png_ptr) { - png_debug(1, "in png_set_invert_alpha"); - - if (png_ptr == NULL) - return; - - png_ptr->transformations |= PNG_INVERT_ALPHA; + if (png_ptr != NULL) + { + png_add_transform(png_ptr, sizeof (png_transform_bit_op), + png_init_invert, PNG_TR_INVERT)->args |= PNG_B_INVERT_ALPHA; +# ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* This is necessary to avoid palette processing on write; the only + * transform that applies to colormapped images is the tRNS chunk + * invert. + */ + png_ptr->write_invert_alpha = 1U; +# endif + } } -#endif +#endif /* INVERT_ALPHA */ #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) void PNGAPI png_set_invert_mono(png_structrp png_ptr) { - png_debug(1, "in png_set_invert_mono"); - - if (png_ptr == NULL) - return; - - png_ptr->transformations |= PNG_INVERT_MONO; + if (png_ptr != NULL) + png_add_transform(png_ptr, sizeof (png_transform_bit_op), + png_init_invert, PNG_TR_INVERT)->args |= PNG_B_INVERT_MONO; } +#endif /* INVERT */ +#endif /* INVERT_ALPHA || INVERT */ -/* Invert monochrome grayscale data */ -void /* PRIVATE */ -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->channels == 1) - { - if (!(row_info->flags & PNG_INDEXED)) /* GRAY */ - { - png_bytep rp = row + png_transform_rowbytes(row_info); - - /* Don't care about the bit depth: */ - while (rp > row) - *--rp ^= 0xff; - - row_info->flags |= PNG_INVERTED; - } - } - - else if (row_info->channels == 2) /* GRAY ALPHA */ - { - if (row_info->bit_depth == 8) - { - png_bytep rp; - - 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 - } -# undef png_ptr -} -#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_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_info->flags |= PNG_BYTE_SWAPPED; - rp = row + png_transform_rowbytes(row_info); - - while (rp >= row+2) - { - png_byte save = *--rp; - *rp = rp[-1], --rp; - *rp = save; - } - } -# undef png_ptr -} -#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] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, - 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, - 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, - 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, - 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, - 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, - 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, - 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, - 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, - 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, - 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, - 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, - 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, - 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, - 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, - 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, - 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, - 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, - 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, - 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, - 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, - 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, - 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, - 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, - 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, - 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, - 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF -}; - -static PNG_CONST png_byte twobppswaptable[256] = { - 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, - 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, - 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, - 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, - 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, - 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, - 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, - 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, - 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, - 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, - 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, - 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, - 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, - 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, - 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, - 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, - 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, - 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, - 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, - 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, - 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, - 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, - 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, - 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, - 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, - 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, - 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, - 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, - 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, - 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, - 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, - 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF -}; - -static PNG_CONST png_byte fourbppswaptable[256] = { - 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, - 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, - 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, - 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, - 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, - 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, - 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, - 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, - 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, - 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, - 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, - 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, - 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, - 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, - 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, - 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, - 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, - 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, - 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, - 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, - 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, - 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, - 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, - 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, - 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, - 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, - 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, - 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, - 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, - 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, - 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, - 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF -}; - -/* Swaps pixel packing order within bytes */ -void /* PRIVATE */ -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 ep; - png_const_bytep table; - - if (row_info->bit_depth == 1) - table = onebppswaptable; - - else if (row_info->bit_depth == 2) - table = twobppswaptable; - - else if (row_info->bit_depth == 4) - table = fourbppswaptable; - - else - return; - - row_info->flags |= PNG_PIXEL_SWAPPED; - ep = row + png_transform_rowbytes(row_info); - - while (row < ep) - *row = table[*row], ++row; - } -# undef png_ptr -} -#endif /* READ_PACKSWAP || WRITE_PACKSWAP */ - -#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ - defined(PNG_READ_STRIP_ALPHA_SUPPORTED) -/* Remove a channel - this used to be 'png_do_strip_filler' but it used a - * somewhat weird combination of flags to determine what to do. All the calls - * to png_do_strip_filler are changed in 1.5.2 to call this instead with the - * correct arguments. - * - * The routine isn't general - the channel must be the channel at the start or - * end (not in the middle) of each pixel. +/* + * WARNING + * WARNING + * WARNING + * WARNING + * WARNING The transforms below are temporary; they can and will be + * WARNING heavily optimized before release. + * WARNING + * WARNING + * WARNING */ -void /* PRIVATE */ -png_do_strip_channel(png_transform_controlp row_info, png_bytep row, - int at_start) +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +typedef struct { - 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 */ + png_transform tr; + png_color_8 true_bits; +} png_transform_shift; -# define png_ptr row_info->png_ptr +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to tc->bit_depth. Thus, if you had a row of + * 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 unsigned int +set_shifts(unsigned int format, unsigned int bit_depth, + png_const_color_8p true_bits, int *shift_start, int *shift_dec) +{ + unsigned int channels = 0; - /* 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 - * the loop simply copies (channels-1) channels until sp reaches ep. - * - * at_start: 0 -- convert AG, XG, ARGB, XRGB, AAGG, XXGG, etc. - * nonzero -- convert GA, GX, RGBA, RGBX, GGAA, RRGGBBXX, etc. - */ + if ((format & (PNG_FORMAT_FLAG_ALPHA+PNG_FORMAT_FLAG_AFIRST)) == + (PNG_FORMAT_FLAG_ALPHA+PNG_FORMAT_FLAG_AFIRST)) + ++channels; /* filled in below */ - /* GA, GX, XG cases */ - if (row_info->channels == 2) + if ((format & PNG_FORMAT_FLAG_COLOR) != 0) { - if (row_info->bit_depth == 8) - { - if (at_start != 0) /* Skip initial filler */ - ++sp; - else /* Skip initial channel and, for sp, the filler */ - sp += 2, ++dp; + unsigned int offset = /* 0 or 2 as appropriate for red */ + ((format & PNG_FORMAT_FLAG_BGR) != 0) << 1; - /* For a 1 pixel wide image there is nothing to do */ - while (sp < ep) - *dp++ = *sp, sp += 2; - } + shift_start[channels+offset] = bit_depth - true_bits->red; + if (shift_dec != NULL) shift_dec[channels+offset] = true_bits->red; - else if (row_info->bit_depth == 16) - { - if (at_start != 0) /* Skip initial filler */ - sp += 2; - else /* Skip initial channel and, for sp, the filler */ - sp += 4, dp += 2; + shift_start[channels+1] = bit_depth - true_bits->green; + if (shift_dec != NULL) shift_dec[channels+1] = true_bits->green; - while (sp < ep) - *dp++ = *sp++, *dp++ = *sp, sp += 3; - } + offset ^= 2; /* for blue */ + shift_start[channels+offset] = bit_depth - true_bits->blue; + if (shift_dec != NULL) shift_dec[channels+offset] = true_bits->blue; - else - return; /* bad bit depth */ - - row_info->channels = 1; - debug(dp == row + png_transform_rowbytes(row_info)); + channels += 3; } - /* RGBA, RGBX, XRGB cases */ - else if (row_info->channels == 4) + else /* no color: gray */ { - if (row_info->bit_depth == 8) - { - if (at_start != 0) /* Skip initial filler */ - ++sp; - else /* Skip initial channels and, for sp, the filler */ - sp += 4, dp += 3; - - /* 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; - } - - else if (row_info->bit_depth == 16) - { - if (at_start != 0) /* Skip initial filler */ - sp += 2; - else /* Skip initial channels and, for sp, the filler */ - sp += 8, dp += 6; - - while (sp < ep) - { - /* Copy 6 bytes, skip 2 */ - *dp++ = *sp++, *dp++ = *sp++; - *dp++ = *sp++, *dp++ = *sp++; - *dp++ = *sp++, *dp++ = *sp, sp += 3; - } - } - - else - return; /* bad bit depth */ - - row_info->channels = 3; - debug(dp == row + png_transform_rowbytes(row_info)); + shift_start[channels] = bit_depth - true_bits->gray; + if (shift_dec != NULL) shift_dec[channels] = true_bits->gray; + ++channels; } - else - return; /* The filler channel has gone already */ -# undef png_ptr + if ((format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + const unsigned int offset = + (format & PNG_FORMAT_FLAG_AFIRST) != 0 ? 0 : channels++; + + shift_start[offset] = bit_depth - true_bits->alpha; + if (shift_dec != NULL) shift_dec[offset] = true_bits->alpha; + } + + return channels; } -#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_transform_controlp row_info, png_bytep row) +#ifdef PNG_WRITE_SHIFT_SUPPORTED +static void +png_do_shift(png_transformp *transform, png_transform_controlp tc) { - unsigned int channels; +# define png_ptr (tc->png_ptr) + png_transform_shift *tr = + png_transform_cast(png_transform_shift, *transform); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep dp_end = dp + PNG_TC_ROWBYTES(*tc); - png_debug(1, "in png_do_bgr"); + png_debug(1, "in png_do_shift"); -# define png_ptr row_info->png_ptr + if (--(tc->range) == 0) + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE); - channels = row_info->channels; + tc->sp = dp; - if (channels == 3 || channels == 4) { - png_const_bytep ep = row + png_transform_rowbytes(row_info); + int shift_start[4], shift_dec[4]; + unsigned int channels = set_shifts(tc->format, tc->bit_depth, + &tr->true_bits, shift_start, shift_dec); - if (row_info->bit_depth == 8) + debug(PNG_TC_CHANNELS(*tc) == channels); + + /* With low res depths, could only be grayscale, so one channel */ + if (tc->bit_depth < 8) { - ep -= channels; /* Last pixel */ - row_info->flags ^= PNG_RGB_SWAPPED; + unsigned int mask; - while (row <= ep) + UNTESTED + affirm(channels == 1); + /* This doesn't matter but we expect to run before packswap: */ + debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED)); + + if (tr->true_bits.gray == 1 && tc->bit_depth == 2) + mask = 0x55; + + else if (tc->bit_depth == 4 && tr->true_bits.gray == 3) + mask = 0x11; + + else + mask = 0xff; + + while (dp < dp_end) { - png_byte save = row[0]; - row[0] = row[2]; - row[2] = save; - row += channels; - } + int j; + unsigned int v, out; - debug(row == ep+channels); - } + v = *sp++; + out = 0; -# ifdef PNG_16BIT_SUPPORTED - else if (row_info->bit_depth == 16) - { - channels *= 2; /* now in bytes */ - - ep -= channels; /* Last pixel */ - row_info->flags |= PNG_RGB_SWAPPED; - - while (row <= ep) + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) { - png_byte save = row[0]; - row[0] = row[4]; - row[4] = save; + if (j > 0) + out |= v << j; - save = row[1]; - row[1] = row[5]; - row[5] = save; - - row += channels; + else + out |= (v >> (-j)) & mask; } - debug(row == ep+channels); + *dp++ = png_check_byte(png_ptr, out); } -# endif + } + + else if (tc->bit_depth == 8) + { + unsigned int c = 0; + + UNTESTED + while (dp < dp_end) + { + + int j; + unsigned int v, out; + + v = *sp++; + out = 0; + + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + out |= v << j; + + else + out |= v >> (-j); + } + + *dp++ = png_check_byte(png_ptr, out); + if (++c == channels) c = 0; + } + } + + else /* tc->bit_depth == 16 */ + { + unsigned int c = 0, s0, s1; + + UNTESTED + if ((tc->format & PNG_FORMAT_FLAG_SWAPPED) != 0) + s0 = 0, s1 = 8; /* LSB */ + + else + s0 = 8, s1 = 0; /* MSB */ + + while (dp < dp_end) + { + int j; + unsigned int value, v; + + v = *sp++ << s0; + v += *sp++ << s1; + value = 0; + + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= v << j; + + else + value |= v >> (-j); + } + + *dp++ = PNG_BYTE(value >> s0); + *dp++ = PNG_BYTE(value >> s1); + } + } } # undef png_ptr } -#endif /* READ_BGR || WRITE_BGR */ +#endif /* WRITE_SHIFT */ -#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \ - 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_transform_controlp row_info) +#ifdef PNG_READ_SHIFT_SUPPORTED +/* Reverse the effects of png_do_shift. This routine merely shifts the + * pixels back to their significant bits values. Thus, if you have + * a row of bit depth 8, but only 5 are significant, this will shift + * the values back to 0 through 31. + */ +static void +png_do_unshift(png_transformp *transform, png_transform_controlp tc) { - if (png_ptr->num_palette < (1 << row_info->bit_depth) && - png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */ +# define png_ptr (tc->png_ptr) + png_transform_shift *tr = + png_transform_cast(png_transform_shift, *transform); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep dp_end = dp + PNG_TC_ROWBYTES(*tc); + + png_debug(1, "in png_do_unshift"); + + tc->range++; + tc->format |= PNG_FORMAT_FLAG_RANGE; + { - /* 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); + int shift[4]; + unsigned int channels = set_shifts(tc->format, tc->bit_depth, + &tr->true_bits, shift, NULL); - /* 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. - */ + debug(PNG_TC_CHANNELS(*tc) == channels); - switch (row_info->bit_depth) { - case 1: + unsigned int c, have_shift; + + for (c = have_shift = 0; c < channels; ++c) { - /* in this case, all bytes must be 0 so we don't need - * to unpack the pixels except for the rightmost one. + /* A shift of more than the bit depth is an error condition but it + * gets ignored here. */ - for (; rp > png_ptr->row_buf; rp--) - { - if ((*rp >> padding) != 0) - png_ptr->num_palette_max = 1; - padding = 0; - } + if (shift[c] <= 0 || (unsigned)/*SAFE*/shift[c] >= tc->bit_depth) + shift[c] = 0; - break; + else + have_shift = 1; } + if (have_shift == 0) + return; + } + + /* The code below will copy sp to dp, so: */ + tc->sp = dp; + + switch (tc->bit_depth) + { + default: + /* Must be 1bpp gray: should not be here! */ + impossible("unshift bit depth"); + /* NOTREACHED */ + break; + case 2: - { - for (; rp > png_ptr->row_buf; rp--) - { - int i = ((*rp >> padding) & 0x03); - - if (i > png_ptr->num_palette_max) - png_ptr->num_palette_max = i; - - i = (((*rp >> padding) >> 2) & 0x03); - - if (i > png_ptr->num_palette_max) - png_ptr->num_palette_max = i; - - i = (((*rp >> padding) >> 4) & 0x03); - - if (i > png_ptr->num_palette_max) - png_ptr->num_palette_max = i; - - i = (((*rp >> padding) >> 6) & 0x03); - - if (i > png_ptr->num_palette_max) - png_ptr->num_palette_max = i; - - padding = 0; - } + /* Must be 2bpp gray */ + debug(channels == 1 && shift[0] == 1); + debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED)); + while (dp < dp_end) + *dp++ = (*sp++ >> 1) & 0x55; break; - } case 4: - { - for (; rp > png_ptr->row_buf; rp--) + /* Must be 4bpp gray */ + debug(channels == 1); + debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED)); { - int i = ((*rp >> padding) & 0x0f); + unsigned int gray_shift = shift[0]; + unsigned int mask = 0xf >> gray_shift; /* <= 4 bits */ - if (i > png_ptr->num_palette_max) - png_ptr->num_palette_max = i; + mask |= mask << 4; /* <= 8 bits */ - i = (((*rp >> padding) >> 4) & 0x0f); - - if (i > png_ptr->num_palette_max) - png_ptr->num_palette_max = i; - - padding = 0; + while (dp < dp_end) + *dp++ = (png_byte)/*SAFE*/((*sp++ >> gray_shift) & mask); } - break; - } case 8: - { - for (; rp > png_ptr->row_buf; rp--) + /* Single byte components, G, GA, RGB, RGBA */ { - if (*rp > png_ptr->num_palette_max) - png_ptr->num_palette_max = (int) *rp; + unsigned int channel = 0; + + while (dp < dp_end) + { + *dp++ = (png_byte)/*SAFE*/(*sp++ >> shift[channel]); + if (++channel >= channels) + channel = 0; + } } - break; - } - default: + case 16: + /* Double byte components, G, GA, RGB, RGBA */ + { + unsigned int channel = 0; + unsigned int s0, s1; + + if ((tc->format & PNG_FORMAT_FLAG_SWAPPED) != 0) + s0 = 0, s1 = 8; /* LSB */ + + else + s0 = 8, s1 = 0; /* MSB */ + + while (dp < dp_end) + { + unsigned int value = *sp++ << s0; + + value += *sp++ << s1; /* <= 16 bits */ + + value >>= shift[channel]; + if (++channel >= channels) channel = 0; + *dp++ = PNG_BYTE(value >> s0); + *dp++ = PNG_BYTE(value >> s1); + } + } break; } } -} -#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 } +#endif /* READ_SHIFT */ -static unsigned int -transform_color_type(png_const_transform_controlp row_info) +static void +init_shift(png_transformp *transform, png_transform_controlp tc) { - const unsigned int ch = row_info->channels - 1; - const unsigned int indexed = row_info->flags & PNG_INDEXED; + png_const_structp png_ptr = tc->png_ptr; - /* That is 0, 1, 2, 3 for G/PALETTE, GA, RGB, RGBA. Check the - * numbers: + /* These shifts apply to the component value, not the pixel index, so skip + * palette data. In addition there is no *write* shift for palette entries; + * only a read one, so skip the write/palette case too. */ -# 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)) + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0 && + (png_ptr->read_struct || !tc->palette)) { - debug(png_ptr->color_type == out->color_type); - debug(png_ptr->bit_depth == out->bit_depth); - } -# undef png_ptr -} -#endif /* READ_TRANSFORMS | WRITE_TRANSFORMS */ + /* The only change to the format is to mark the data as having a non-PNG + * range. + */ + tc->range++; + tc->format |= PNG_FORMAT_FLAG_RANGE; + + if (tc->init == PNG_TC_INIT_FINAL) + { +# ifdef PNG_READ_SHIFT_SUPPORTED + if (png_ptr->read_struct) + { + (*transform)->fn = png_do_unshift; + return; + } +# endif +# ifdef PNG_WRITE_SHIFT_SUPPORTED + if (png_ptr->read_struct) + { + (*transform)->fn = png_do_shift; + return; + } +# endif + } + } + + else /* transform not applicable */ + (*transform)->fn = NULL; +} -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) -#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED void PNGAPI -png_set_user_transform_info(png_structrp png_ptr, png_voidp - user_transform_ptr, int user_transform_depth, int user_transform_channels) +png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits) { - png_debug(1, "in png_set_user_transform_info"); - - if (png_ptr == NULL) - return; - -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && - (png_ptr->flags & PNG_FLAG_ROW_INIT) != 0) + if (png_ptr != NULL && true_bits != NULL) { - png_app_error(png_ptr, - "info change after png_start_read_image or png_read_update_info"); - return; +# ifndef PNG_READ_SHIFT_SUPPORTED + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_shift not supported on read"); + return; + } +# endif +# ifndef PNG_WRITE_SHIFT_SUPPORTED + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_shift not supported on write"); + return; + } +# endif + + { + png_transform_shift *trs = png_transform_cast(png_transform_shift, + png_add_transform(png_ptr, sizeof (png_transform_shift), + init_shift, PNG_TR_SHIFT)); + trs->true_bits = *true_bits; + } } -#endif - - png_ptr->user_transform_ptr = user_transform_ptr; - png_ptr->user_transform_depth = png_check_byte(png_ptr, - user_transform_depth); - png_ptr->user_transform_channels = png_check_byte(png_ptr, - user_transform_channels); } -#endif +#endif /* SHIFT */ -/* This function returns a pointer to the user_transform_ptr associated with - * the user transform functions. The application should free any memory - * associated with this pointer before png_write_destroy and png_read_destroy - * are called. - */ -#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED -png_voidp PNGAPI -png_get_user_transform_ptr(png_const_structrp png_ptr) +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Turn on pixel packing */ +void PNGAPI +png_set_packing(png_structrp png_ptr) { - if (png_ptr == NULL) - return (NULL); + /* The transforms aren't symmetric, so even though there is one API there are + * two internal init functions, one for read, the other write: + */ + if (png_ptr != NULL) + { + if (png_ptr->read_struct) + { +# ifdef PNG_READ_PACK_SUPPORTED + png_add_transform(png_ptr, 0/*size*/, png_init_read_pack, + PNG_TR_PACK); +# else + png_app_error(png_ptr, "png_set_packing not supported on read"); +# endif + } - return png_ptr->user_transform_ptr; + else + { +# ifdef PNG_WRITE_PACK_SUPPORTED + png_add_transform(png_ptr, 0/*size*/, png_init_write_pack, + PNG_TR_PACK); +# else + png_app_error(png_ptr, "png_set_packing not supported on write"); +# endif + } + } } -#endif +#endif /* PACK */ +#if defined(PNG_READ_PACKSWAP_SUPPORTED) ||\ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Turn on pixel-swapping within a byte, this is symmetric - doing the swap + * twice produces the original value, so only one implementation is required for + * either read or write. + * + * Used to be refered to as "packswap", but pixel-swap seems more + * self-documenting. + */ +static void +png_do_swap_1bit(png_transformp *transform, png_transform_controlp tc) +{ + png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */ + png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp); + png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp); + + tc->sp = dp; + tc->format ^= PNG_FORMAT_FLAG_SWAPPED; + + for (;;) + { + png_uint_32 s = *sp++; + s = ((s >> 1) & 0x55555555) | ((s & 0x55555555) << 1); + s = ((s >> 2) & 0x33333333) | ((s & 0x33333333) << 2); + s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4); + *dp++ = s; + if (rowbytes <= 4) break; + rowbytes -= 4; + } + + PNG_UNUSED(transform) +} + +static void +png_do_swap_2bit(png_transformp *transform, png_transform_controlp tc) +{ + png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */ + png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp); + png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp); + + tc->sp = dp; + tc->format ^= PNG_FORMAT_FLAG_SWAPPED; + + for (;;) + { + png_uint_32 s = *sp++; + s = ((s >> 2) & 0x33333333) | ((s & 0x33333333) << 2); + s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4); + *dp++ = s; + if (rowbytes <= 4) break; + rowbytes -= 4; + } + + PNG_UNUSED(transform) +} + +static void +png_do_swap_4bit(png_transformp *transform, png_transform_controlp tc) +{ + png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */ + png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp); + png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp); + + tc->sp = dp; + tc->format ^= PNG_FORMAT_FLAG_SWAPPED; + + for (;;) + { + png_uint_32 s = *sp++; + s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4); + *dp++ = s; + if (rowbytes <= 4) break; + rowbytes -= 4; + } + + PNG_UNUSED(transform) +} + +static void +init_packswap(png_transformp *transform, png_transform_controlp tc) +{ + png_transform_fn fn; + +# define png_ptr tc->png_ptr + debug(tc->init); +# undef png_ptr + + switch (tc->bit_depth) + { + case 1: fn = png_do_swap_1bit; break; + case 2: fn = png_do_swap_2bit; break; + case 4: fn = png_do_swap_4bit; break; + + default: /* transform not applicable */ + (*transform)->fn = NULL; + return; + } + + tc->format ^= PNG_FORMAT_FLAG_SWAPPED; + if (tc->init == PNG_TC_INIT_FINAL) + (*transform)->fn = fn; +} + +void PNGAPI +png_set_packswap(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { +# ifndef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_packswap not supported on read"); + return; + } +# endif +# ifndef PNG_WRITE_PACKSWAP_SUPPORTED + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_packswap not supported on write"); + return; + } +# endif + + png_add_transform(png_ptr, 0/*size*/, init_packswap, PNG_TR_PIXEL_SWAP); + } +} +#endif /* PACKSWAP */ + +/* User transform handling */ #ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED png_uint_32 PNGAPI png_get_current_row_number(png_const_structrp png_ptr) @@ -943,7 +3435,17 @@ png_get_current_row_number(png_const_structrp png_ptr) * interlaced image. */ if (png_ptr != NULL) - return png_ptr->row_number; + { + /* In the read case png_struct::row_number is the row in the final image, + * not the pass, this will return the previous row number if the row isn't + * in the pass: + */ + if (png_ptr->read_struct) + return PNG_PASS_ROWS(png_ptr->row_number+1, png_ptr->pass)-1U; + + else + return png_ptr->row_number; + } return PNG_UINT_32_MAX; /* help the app not to fail silently */ } @@ -956,5 +3458,261 @@ png_get_current_pass_number(png_const_structrp png_ptr) return 8; /* invalid */ } #endif /* USER_TRANSFORM_INFO */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) ||\ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef struct +{ + png_transform tr; + png_user_transform_ptr user_fn; +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + png_voidp user_ptr; +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + unsigned int user_depth; + unsigned int user_channels; +#endif +#endif +} png_user_transform, *png_user_transformp; + +typedef const png_user_transform *png_const_user_transformp; + +static png_user_transformp +get_user_transform(png_structrp png_ptr) +{ + /* Note that in an added transform the whole transform is memset to 0, so we + * don't need to initialize anything. + */ + return png_transform_cast(png_user_transform, png_add_transform(png_ptr, + sizeof (png_user_transform), NULL/*function*/, PNG_TR_USER)); +} #endif /* READ_USER_TRANSFORM || WRITE_USER_TRANSFORM */ -#endif /* READ || WRITE */ + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +png_voidp PNGAPI +png_get_user_transform_ptr(png_const_structrp png_ptr) +{ + if (png_ptr != NULL) + { + png_transformp tr = png_find_transform(png_ptr, PNG_TR_USER); + + if (tr != NULL) + { + png_user_transformp tru = png_transform_cast(png_user_transform, tr); + return tru->user_ptr; + } + } + + return NULL; +} +#endif /* USER_TRANSFORM_PTR */ + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +void PNGAPI +png_set_user_transform_info(png_structrp png_ptr, png_voidp ptr, int depth, + int channels) +{ + if (png_ptr != NULL) + { + /* NOTE: this function only sets the user transform pointer on write, i.e. + * the depth and channels arguments are ignored. + */ + png_user_transformp tr = get_user_transform(png_ptr); + + tr->user_ptr = ptr; + +# ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + if (png_ptr->read_struct) + { + if (png_ptr->row_bit_depth == 0) + { + if (depth > 0 && depth <= 32 && channels > 0 && channels <= 4 && + (-depth & depth) == depth /* power of 2 */) + { + tr->user_depth = png_check_bits(png_ptr, depth, 6); + tr->user_channels = png_check_bits(png_ptr, channels, 3); + } + + else + png_app_error(png_ptr, "unsupported bit-depth or channels"); + } + else + png_app_error(png_ptr, + "cannot change user info after image start"); + } +# else /* !READ_USER_TRANSFORM */ + PNG_UNUSED(depth) + PNG_UNUSED(channels) +# endif /* !READ_USER_TRANSFORM */ + } +} +#endif /* USER_TRANSFORM_PTR */ + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +static void +png_do_read_user_transform(png_transformp *trIn, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_user_transformp tr = png_transform_cast(png_user_transform, *trIn); + + if (!tc->init && tr->user_fn != NULL) + { + png_row_info row_info; + + row_info.width = tc->width; + row_info.rowbytes = PNG_TC_ROWBYTES(*tc); + row_info.color_type = png_check_byte(png_ptr, + PNG_COLOR_TYPE_FROM_FORMAT(tc->format)); + row_info.bit_depth = png_check_byte(png_ptr, tc->bit_depth); + row_info.channels = png_check_byte(png_ptr, + PNG_FORMAT_CHANNELS(tc->format)); + row_info.bit_depth = png_check_byte(png_ptr, + PNG_TC_PIXEL_DEPTH(*tc)); + + /* TODO: fix this API, but for the moment we have to copy the row data to + * the working buffer. This is an unnecessary perf overhead when a user + * transform is used to read information without a transform or when it is + * used on write. + */ + if (tc->sp != tc->dp) + { + memcpy(tc->dp, tc->sp, PNG_TC_ROWBYTES(*tc)); + tc->sp = tc->dp; + } + + tr->user_fn(png_ptr, &row_info, png_voidcast(png_bytep, tc->dp)); + } + +# ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + if (tr->user_depth > 0) + { + /* The read transform can modify the bit depth and number of + * channels; the interface doesn't permit anything else to be + * changed. If the information isn't set the user callback has to + * produce pixels with the correct pixel depth (otherwise the + * de-interlace won't work) but there really is no other constraint. + */ + tc->bit_depth = tr->user_depth; + + /* The API is very restricted in functionality; the user_channels + * can be changed, but the color_type can't, so the format is simply + * fixed up to match the channels. + */ + if (tr->user_channels != PNG_FORMAT_CHANNELS(tc->format)) + switch (tr->user_channels) + { + case 1: + tc->format &= + PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA); + break; + + case 2: /* has to be GA */ + tc->format &= + PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP+PNG_FORMAT_FLAG_COLOR); + tc->format |= PNG_FORMAT_FLAG_ALPHA; + break; + + case 3: /* has to be RGB */ + tc->format &= + PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP|PNG_FORMAT_FLAG_ALPHA); + tc->format |= PNG_FORMAT_FLAG_COLOR; + break; + + case 4: /* has to be RGBA */ + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP); + tc->format |= (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA); + break; + + default: /* checked before */ + impossible("user channels"); + } + + debug(PNG_FORMAT_CHANNELS(tc->format) == tr->user_channels); + } +# endif /* USER_TRANSFORM_PTR */ +# undef png_ptr +} + +void PNGAPI +png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr + read_user_transform_fn) +{ + /* There is no 'init' function, just the callback above to handle the + * transform. + */ + if (png_ptr != NULL) + { + if (png_ptr->read_struct) + { + png_user_transformp tr = get_user_transform(png_ptr); + + tr->user_fn = read_user_transform_fn; + tr->tr.fn = png_do_read_user_transform; + } + + else + png_app_error(png_ptr, "cannot set a read transform on write"); + } +} +#endif /* READ_USER_TRANSFORM */ + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +static void +png_do_write_user_transform(png_transformp *trIn, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* The write side is pretty simple; call the call-back, it will make the row + * data right. + * + * BUG: we need to copy the input row (it would be a non-quiet API change + * otherwise) and we don't know how big it is because the information passed + * to png_set_user_transform_info never was used on write, but that's fine + * because the write code never did get this right, so presumably all the + * apps have worked round it... + */ + if (!tc->init) + { + png_user_transformp tr = png_transform_cast(png_user_transform, *trIn); + png_row_info row_info; + + if (tc->sp != tc->dp) /* no interlace */ + { + memcpy(tc->dp, tc->sp, PNG_TC_ROWBYTES(*tc)); + tc->sp = tc->dp; + } + + row_info.width = tc->width; + row_info.rowbytes = PNG_TC_ROWBYTES(*tc); + row_info.color_type = png_check_byte(png_ptr, + PNG_COLOR_TYPE_FROM_FORMAT(tc->format)); + row_info.bit_depth = png_check_byte(png_ptr, tc->bit_depth); + row_info.channels = png_check_byte(png_ptr, + PNG_FORMAT_CHANNELS(tc->format)); + row_info.bit_depth = png_check_byte(png_ptr, + PNG_TC_PIXEL_DEPTH(*tc)); + + /* The user function promises to give us this format: */ + tr->user_fn(png_ptr, &row_info, png_voidcast(png_bytep, tc->dp)); + } +# undef png_ptr +} + +void PNGAPI +png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr + write_user_transform_fn) +{ + + if (png_ptr != NULL) + { + if (!png_ptr->read_struct) + { + png_user_transformp tr = get_user_transform(png_ptr); + + tr->user_fn = write_user_transform_fn; + tr->tr.fn = png_do_write_user_transform; + } + + else + png_app_error(png_ptr, "cannot set a write transform on read"); + } +} +#endif /* WRITE_USER_TRANSFORM */ diff --git a/pngwio.c b/pngwio.c index 34bc2bad9..882355053 100644 --- a/pngwio.c +++ b/pngwio.c @@ -34,12 +34,11 @@ void /* PRIVATE */ png_write_data(png_structrp png_ptr, png_const_bytep data, png_size_t length) { /* NOTE: write_data_fn must not change the buffer! */ - if (png_ptr->write_data_fn != NULL ) - (*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data), - length); + if (png_ptr->rw_data_fn != NULL ) + png_ptr->rw_data_fn(png_ptr, png_constcast(png_bytep, data), length); else - png_error(png_ptr, "Call to NULL write function"); + png_app_error(png_ptr, "No write function"); } #ifdef PNG_STDIO_SUPPORTED @@ -56,7 +55,7 @@ png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) if (png_ptr == NULL) return; - check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); + check = fwrite(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr)); if (check != length) png_error(png_ptr, "Write Error"); @@ -72,20 +71,17 @@ void /* PRIVATE */ png_flush(png_structrp png_ptr) { if (png_ptr->output_flush_fn != NULL) - (*(png_ptr->output_flush_fn))(png_ptr); + png_ptr->output_flush_fn(png_ptr); } # ifdef PNG_STDIO_SUPPORTED void PNGCBAPI png_default_flush(png_structp png_ptr) { - png_FILE_p io_ptr; - if (png_ptr == NULL) return; - io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr)); - fflush(io_ptr); + fflush(png_voidcast(png_FILE_p, (png_ptr->io_ptr))); } # endif #endif @@ -126,44 +122,26 @@ png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr, if (png_ptr == NULL) return; - png_ptr->io_ptr = io_ptr; - -#ifdef PNG_STDIO_SUPPORTED - if (write_data_fn != NULL) - png_ptr->write_data_fn = write_data_fn; - - else - png_ptr->write_data_fn = png_default_write_data; -#else - png_ptr->write_data_fn = write_data_fn; -#endif - -#ifdef PNG_WRITE_FLUSH_SUPPORTED -# ifdef PNG_STDIO_SUPPORTED - - if (output_flush_fn != NULL) - png_ptr->output_flush_fn = output_flush_fn; - - else - png_ptr->output_flush_fn = png_default_flush; - -# else - png_ptr->output_flush_fn = output_flush_fn; -# endif -#else - PNG_UNUSED(output_flush_fn) -#endif /* WRITE_FLUSH */ - -#ifdef PNG_READ_SUPPORTED - /* It is an error to read while writing a png file */ - if (png_ptr->read_data_fn != NULL) + if (png_ptr->read_struct) { - png_ptr->read_data_fn = NULL; - - png_warning(png_ptr, - "Can't set both read_data_fn and write_data_fn in the" - " same structure"); + png_app_error(png_ptr, "cannot set a write function on a read struct"); + return; } -#endif + + if (write_data_fn == NULL) + { + png_app_error(png_ptr, + "API change: png_set_write_fn requires a function"); + return; + } + + png_ptr->io_ptr = io_ptr; + png_ptr->rw_data_fn = write_data_fn; +# ifdef PNG_WRITE_FLUSH_SUPPORTED + if (output_flush_fn != NULL) + png_ptr->output_flush_fn = output_flush_fn; +# else + PNG_UNUSED(output_flush_fn) +# endif /* WRITE_FLUSH */ } #endif /* WRITE */ diff --git a/pngwrite.c b/pngwrite.c index 7f5d94367..4a751003e 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -89,259 +89,288 @@ png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr) if (png_ptr == NULL || info_ptr == NULL) return; - if ((png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE) == 0) + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) { - /* Write PNG signature */ - png_write_sig(png_ptr); + int color_type = PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format); -#ifdef PNG_MNG_FEATURES_SUPPORTED - if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 && \ - png_ptr->mng_features_permitted != 0) - { - png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); - png_ptr->mng_features_permitted = 0; - } -#endif + /* Write PNG signature; doesn't set PNG_HAVE_PNG_SIGNATURE if it has + * already been written (or rather, if at least 3 bytes have already been + * written; undocumented wackiness, it means the 'PNG' at the start can be + * replace by, e.g. "FOO" or "BAR" or "MNG"). + */ + png_write_sig(png_ptr); - /* Write IHDR information. */ - png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, - info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, - info_ptr->filter_type, -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - info_ptr->interlace_type -#else - 0 -#endif - ); +# ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 && + png_ptr->mng_features_permitted != 0) + { + png_app_error(png_ptr, + "MNG features are not allowed in a PNG datastream"); + /* Recovery: disable MNG features: */ + png_ptr->mng_features_permitted = 0; + } +# endif /* MNG_FEATURES */ - /* The rest of these check to see if the valid field has the appropriate - * flag set, and if it does, writes the chunk. - * - * 1.6.0: COLORSPACE support controls the writing of these chunks too, and - * the chunks will be written if the WRITE routine is there and information - * is available in the COLORSPACE. (See png_colorspace_sync_info in png.c - * for where the valid flags get set.) - * - * Under certain circumstances the colorspace can be invalidated without - * syncing the info_struct 'valid' flags; this happens if libpng detects and - * error and calls png_error while the color space is being set, yet the - * application continues writing the PNG. So check the 'invalid' flag here - * too. - */ -#ifdef PNG_GAMMA_SUPPORTED -# ifdef PNG_WRITE_gAMA_SUPPORTED - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && - (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) != 0 && - (info_ptr->valid & PNG_INFO_gAMA) != 0) - png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma); -# endif -#endif + /* Write IHDR information. */ + png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, color_type, info_ptr->compression_type, + info_ptr->filter_type, info_ptr->interlace_type); -#ifdef PNG_COLORSPACE_SUPPORTED - /* Write only one of sRGB or an ICC profile. If a profile was supplied - * and it matches one of the known sRGB ones issue a warning. - */ -# ifdef PNG_WRITE_iCCP_SUPPORTED - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && - (info_ptr->valid & PNG_INFO_iCCP) != 0) - { +# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + /* This are used for checking later on: */ + png_ptr->info_format = info_ptr->format; +# endif /* WRITE_TRANSFORMS */ + + /* This sets the flag that prevents re-entry to the 'before PLTE' case: */ + affirm((png_ptr->mode & PNG_HAVE_IHDR) != 0); + + /* The rest of these check to see if the valid field has the appropriate + * flag set, and if it does, writes the chunk. + * + * 1.6.0: COLORSPACE support controls the writing of these chunks too, and + * the chunks will be written if the WRITE routine is there and + * information is available in the COLORSPACE. (See + * png_colorspace_sync_info in png.c for where the valid flags get set.) + * + * Under certain circumstances the colorspace can be invalidated without + * syncing the info_struct 'valid' flags; this happens if libpng detects + * an error and calls png_error while the color space is being set, yet + * the application continues writing the PNG. So check the 'invalid' + * flag here too. + */ +# ifdef PNG_WRITE_gAMA_SUPPORTED /* enables GAMMA */ + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && + (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) != 0 && + (info_ptr->valid & PNG_INFO_gAMA) != 0) + { + /* This is the inverse of the test in png.c: */ + affirm(info_ptr->colorspace.gamma >= 16 && + info_ptr->colorspace.gamma <= 625000000); + png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma); + } +# endif /* WRITE_gAMA */ + + /* Write only one of sRGB or an ICC profile. If a profile was supplied + * and it matches one of the known sRGB ones issue a warning. + */ +# ifdef PNG_WRITE_iCCP_SUPPORTED /* enables COLORSPACE, GAMMA */ + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && + (info_ptr->valid & PNG_INFO_iCCP) != 0) + { +# ifdef PNG_WRITE_sRGB_SUPPORTED + /* The app must have supplied an sRGB iCCP profile (and one that + * is recognized and therefore known to be correct) so we write + * that profile, even though it increases the size of the PNG + * significantly. A warning is reasonable: + */ + if ((info_ptr->valid & PNG_INFO_sRGB) != 0) + png_app_warning(png_ptr, + "profile matches sRGB but writing iCCP instead"); +# endif /* WRITE_sRGB */ + + png_write_iCCP(png_ptr, info_ptr->iccp_name, + info_ptr->iccp_profile); + } # ifdef PNG_WRITE_sRGB_SUPPORTED - if ((info_ptr->valid & PNG_INFO_sRGB) != 0) - png_app_warning(png_ptr, - "profile matches sRGB but writing iCCP instead"); -# endif + else /* iCCP not written */ +# endif /* WRITE_sRGB */ +# endif /* WRITE_iCCP */ - png_write_iCCP(png_ptr, info_ptr->iccp_name, - info_ptr->iccp_profile); - } -# ifdef PNG_WRITE_sRGB_SUPPORTED - else +# ifdef PNG_WRITE_sRGB_SUPPORTED /* enables COLORSPACE, GAMMA */ + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && + (info_ptr->valid & PNG_INFO_sRGB) != 0) + png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent); +# endif /* WRITE_sRGB */ + +# ifdef PNG_WRITE_sBIT_SUPPORTED + if ((info_ptr->valid & PNG_INFO_sBIT) != 0) + png_write_sBIT(png_ptr, &(info_ptr->sig_bit), color_type); +# endif /* WRITE_sBIT */ + +# ifdef PNG_WRITE_cHRM_SUPPORTED /* enables COLORSPACE */ + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && + (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0 && + (info_ptr->valid & PNG_INFO_cHRM) != 0) + png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy); +# endif /* WRITE_cHRM */ + +# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + /* The third arugment must encode only one bit, otherwise chunks will + * be written twice because the test in write_unknown_chunks is + * 'location & where'. + */ + write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR); # endif -# endif - -# ifdef PNG_WRITE_sRGB_SUPPORTED - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && - (info_ptr->valid & PNG_INFO_sRGB) != 0) - png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent); -# endif /* WRITE_sRGB */ -#endif /* COLORSPACE */ - -#ifdef PNG_WRITE_sBIT_SUPPORTED - if ((info_ptr->valid & PNG_INFO_sBIT) != 0) - png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); -#endif - -#ifdef PNG_COLORSPACE_SUPPORTED -# ifdef PNG_WRITE_cHRM_SUPPORTED - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && - (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0 && - (info_ptr->valid & PNG_INFO_cHRM) != 0) - png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy); -# endif -#endif - -#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED - write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR); -#endif - - png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; } + + else /* 1.7.0: flag multiple calls; previously ignored */ + png_app_error(png_ptr, + "png_write_info_before_PLTE called more than once"); } -void PNGAPI -png_write_info(png_structrp png_ptr, png_const_inforp info_ptr) -{ -#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) - int i; -#endif - - png_debug(1, "in png_write_info"); - - if (png_ptr == NULL || info_ptr == NULL) - return; - - png_write_info_before_PLTE(png_ptr, info_ptr); - - if ((info_ptr->valid & PNG_INFO_PLTE) != 0) - png_write_PLTE(png_ptr, info_ptr->palette, - (png_uint_32)info_ptr->num_palette); - - else if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) !=0) - png_error(png_ptr, "Valid palette required for paletted images"); - -#ifdef PNG_WRITE_tRNS_SUPPORTED - if ((info_ptr->valid & PNG_INFO_tRNS) !=0) - { -#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED - /* Invert the alpha channel (in tRNS) */ - if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0 && - info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - { - int j, jend; - - jend = info_ptr->num_trans; - if (jend > PNG_MAX_PALETTE_LENGTH) - jend = PNG_MAX_PALETTE_LENGTH; - - for (j = 0; jtrans_alpha[j] = - png_check_byte(png_ptr, 255 - info_ptr->trans_alpha[j]); - } -#endif - png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color), - info_ptr->num_trans, info_ptr->color_type); - } -#endif -#ifdef PNG_WRITE_bKGD_SUPPORTED - if ((info_ptr->valid & PNG_INFO_bKGD) != 0) - png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); -#endif - -#ifdef PNG_WRITE_hIST_SUPPORTED - if ((info_ptr->valid & PNG_INFO_hIST) != 0) - png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); -#endif - -#ifdef PNG_WRITE_oFFs_SUPPORTED - if ((info_ptr->valid & PNG_INFO_oFFs) != 0) - png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, - info_ptr->offset_unit_type); -#endif - -#ifdef PNG_WRITE_pCAL_SUPPORTED - if ((info_ptr->valid & PNG_INFO_pCAL) != 0) - png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, - info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, - info_ptr->pcal_units, info_ptr->pcal_params); -#endif - -#ifdef PNG_WRITE_sCAL_SUPPORTED - if ((info_ptr->valid & PNG_INFO_sCAL) != 0) - png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, - info_ptr->scal_s_width, info_ptr->scal_s_height); -#endif /* sCAL */ - -#ifdef PNG_WRITE_pHYs_SUPPORTED - if ((info_ptr->valid & PNG_INFO_pHYs) != 0) - png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, - info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); -#endif /* pHYs */ - -#ifdef PNG_WRITE_tIME_SUPPORTED - if ((info_ptr->valid & PNG_INFO_tIME) != 0) - { - png_write_tIME(png_ptr, &(info_ptr->mod_time)); - png_ptr->mode |= PNG_WROTE_tIME; - } -#endif /* tIME */ - -#ifdef PNG_WRITE_sPLT_SUPPORTED - if ((info_ptr->valid & PNG_INFO_sPLT) != 0) - for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) - png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); -#endif /* sPLT */ - #ifdef PNG_WRITE_TEXT_SUPPORTED +static void +png_write_text(png_structrp png_ptr, png_const_inforp info_ptr) + /* Text chunk helper */ +{ + int i; + /* Check to see if we need to write text chunks */ for (i = 0; i < info_ptr->num_text; i++) { - png_debug2(2, "Writing header text chunk %d, type %d", i, - info_ptr->text[i].compression); + png_debug2(2, "Writing text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* An internationalized chunk? */ if (info_ptr->text[i].compression > 0) { -#ifdef PNG_WRITE_iTXt_SUPPORTED - /* Write international chunk */ - png_write_iTXt(png_ptr, - info_ptr->text[i].compression, - info_ptr->text[i].key, - info_ptr->text[i].lang, - info_ptr->text[i].lang_key, - info_ptr->text[i].text); +# ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, info_ptr->text[i].compression, + info_ptr->text[i].key, info_ptr->text[i].lang, + info_ptr->text[i].lang_key, info_ptr->text[i].text); +# else /* !WRITE_iTXT */ + png_app_error(png_ptr, "Unable to write international text"); +# endif /* !WRITE_iTXT */ + /* Mark this chunk as written */ if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) - info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; else info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; -#else - png_warning(png_ptr, "Unable to write international text"); -#endif } /* If we want a compressed text chunk */ else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) { -#ifdef PNG_WRITE_zTXt_SUPPORTED - /* Write compressed chunk */ - png_write_zTXt(png_ptr, info_ptr->text[i].key, - info_ptr->text[i].text, info_ptr->text[i].compression); +# ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, info_ptr->text[i].compression); +# else /* !WRITE_zTXT */ + png_app_error(png_ptr, "Unable to write compressed text"); +# endif /* !WRITE_zTXT */ + /* Mark this chunk as written */ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; -#else - png_warning(png_ptr, "Unable to write compressed text"); -#endif } else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) { -#ifdef PNG_WRITE_tEXt_SUPPORTED - /* Write uncompressed chunk */ - png_write_tEXt(png_ptr, info_ptr->text[i].key, - info_ptr->text[i].text, - 0); +# ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0); +# else /* !WRITE_tEXt */ + /* Can't get here TODO: why not? */ + png_app_error(png_ptr, "Unable to write uncompressed text"); +# endif /* !WRITE_tEXt */ + /* Mark this chunk as written */ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; -#else - /* Can't get here */ - png_warning(png_ptr, "Unable to write uncompressed text"); -#endif } } -#endif /* tEXt */ +} +#endif /* WRITE_TEXT */ -#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED - write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE); -#endif +void PNGAPI +png_write_info(png_structrp png_ptr, png_const_inforp info_ptr) +{ + png_debug(1, "in png_write_info"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if ((png_ptr->mode & (PNG_HAVE_PLTE+PNG_HAVE_IDAT)) != 0) + { + png_app_error(png_ptr, "late call to png_write_info"); + return; + } + + /* The app may do this for us, and in 1.7.0 multiple calls are flagged as an + * application error, so this code must check: + */ + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_write_info_before_PLTE(png_ptr, info_ptr); + + if ((info_ptr->valid & PNG_INFO_PLTE) != 0) + png_write_PLTE(png_ptr, info_ptr->palette, info_ptr->num_palette); + + /* Validate the consistency of the PNG being produced; a palette must have + * been written if a palette mapped PNG is to be valid: + */ + if ((png_ptr->mode & PNG_HAVE_PLTE) == 0 && + png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Valid palette required for paletted images"); + +# ifdef PNG_WRITE_tRNS_SUPPORTED + if ((info_ptr->valid & PNG_INFO_tRNS) !=0) + { + png_write_tRNS(png_ptr, info_ptr->trans_alpha, + &(info_ptr->trans_color), info_ptr->num_trans, + PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format)); + } +# endif /* WRITE_tRNS */ + +# ifdef PNG_WRITE_bKGD_SUPPORTED + if ((info_ptr->valid & PNG_INFO_bKGD) != 0) + png_write_bKGD(png_ptr, &(info_ptr->background), + PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format)); +# endif /* WRITE_bKGD */ + +# ifdef PNG_WRITE_hIST_SUPPORTED + if ((info_ptr->valid & PNG_INFO_hIST) != 0) + png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); +# endif /* WRITE_hIST */ + +# ifdef PNG_WRITE_oFFs_SUPPORTED + if ((info_ptr->valid & PNG_INFO_oFFs) != 0) + png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, + info_ptr->offset_unit_type); +# endif /* WRITE_oFFs */ + +# ifdef PNG_WRITE_pCAL_SUPPORTED + if ((info_ptr->valid & PNG_INFO_pCAL) != 0) + png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); +# endif /* WRITE_pCAL */ + +# ifdef PNG_WRITE_sCAL_SUPPORTED + if ((info_ptr->valid & PNG_INFO_sCAL) != 0) + png_write_sCAL_s(png_ptr, info_ptr->scal_unit, info_ptr->scal_s_width, + info_ptr->scal_s_height); +# endif /* WRITE_sCAL */ + +# ifdef PNG_WRITE_pHYs_SUPPORTED + if ((info_ptr->valid & PNG_INFO_pHYs) != 0) + png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +# endif /* WRITE_pHYs */ + +# ifdef PNG_WRITE_tIME_SUPPORTED + if ((info_ptr->valid & PNG_INFO_tIME) != 0) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +# endif /* WRITE_tIME */ + +# ifdef PNG_WRITE_sPLT_SUPPORTED + if ((info_ptr->valid & PNG_INFO_sPLT) != 0) + { + int i; + + for (i = 0; i < info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); + } +# endif /* WRITE_sPLT */ + +# ifdef PNG_WRITE_TEXT_SUPPORTED + if (info_ptr->num_text > 0) + png_write_text(png_ptr, info_ptr); +# endif /* WRITE_TEXT */ + +# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE); +# endif /* WRITE_UNKNOWN_CHUNKS */ } /* Writes the end of the PNG file. If you don't want to write comments or @@ -357,90 +386,73 @@ png_write_end(png_structrp png_ptr, png_inforp info_ptr) if (png_ptr == NULL) return; - if ((png_ptr->mode & PNG_HAVE_IDAT) == 0) - png_error(png_ptr, "No IDATs written into file"); + if ((png_ptr->mode & + (PNG_HAVE_IHDR+PNG_HAVE_IDAT+PNG_AFTER_IDAT+PNG_HAVE_IEND)) != + (PNG_HAVE_IHDR+PNG_HAVE_IDAT+PNG_AFTER_IDAT)) + { + /* Out of place png_write_end: */ + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_error(png_ptr, "Missing call to png_write_info"); -#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED - if (png_ptr->num_palette_max > png_ptr->num_palette) - png_benign_error(png_ptr, "Wrote palette index exceeding num_palette"); -#endif + else if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 && png_ptr->zowner == 0) + { + /* TODO: write unknown IDAT here, for the moment allow the app to write + * IDAT then call write_end: + */ + png_app_error(png_ptr, "No IDATs written into file"); + png_ptr->mode |= PNG_HAVE_IDAT+PNG_AFTER_IDAT; + } + + else if ((png_ptr->mode & PNG_AFTER_IDAT) == 0) + { + affirm(png_ptr->zowner == png_IDAT); + png_error(png_ptr, "incomplete PNG image"); /* unrecoverable */ + } + + else if ((png_ptr->mode & PNG_HAVE_IEND) != 0) + { + png_app_error(png_ptr, "multiple calls to png_write_end"); + return; + } + + else + impossible("not reached"); + } + + /* And double check that the image rows were all written; this is actually + * a harmless error on an interlaced image because the image rows with + * data were all passed in or the above check would not work. + * + * Don't do this if the IDAT came from unknowns (TBD) or the app, above. + * + * The check depends on the precise logic in png_write_finish_row. + */ + else if (png_ptr->interlaced ? png_ptr->pass != PNG_INTERLACE_ADAM7_PASSES : + png_ptr->row_number != png_ptr->height) + png_app_error(png_ptr, "png_write_row not called to last row"); /* See if user wants us to write information chunks */ if (info_ptr != NULL) { -#ifdef PNG_WRITE_TEXT_SUPPORTED - int i; /* local index variable */ -#endif -#ifdef PNG_WRITE_tIME_SUPPORTED - /* Check to see if user has supplied a time chunk */ - if ((info_ptr->valid & PNG_INFO_tIME) != 0 && - (png_ptr->mode & PNG_WROTE_tIME) == 0) - png_write_tIME(png_ptr, &(info_ptr->mod_time)); +# ifdef PNG_WRITE_tIME_SUPPORTED + /* Check to see if user has supplied a time chunk */ + if ((info_ptr->valid & PNG_INFO_tIME) != 0) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +# endif -#endif -#ifdef PNG_WRITE_TEXT_SUPPORTED - /* Loop through comment chunks */ - for (i = 0; i < info_ptr->num_text; i++) - { - png_debug2(2, "Writing trailer text chunk %d, type %d", i, - info_ptr->text[i].compression); - /* An internationalized chunk? */ - if (info_ptr->text[i].compression > 0) - { -#ifdef PNG_WRITE_iTXt_SUPPORTED - /* Write international chunk */ - png_write_iTXt(png_ptr, - info_ptr->text[i].compression, - info_ptr->text[i].key, - info_ptr->text[i].lang, - info_ptr->text[i].lang_key, - info_ptr->text[i].text); - /* Mark this chunk as written */ - if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) - info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; - else - info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; -#else - png_warning(png_ptr, "Unable to write international text"); -#endif - } +# ifdef PNG_WRITE_TEXT_SUPPORTED + if (info_ptr->num_text > 0) + png_write_text(png_ptr, info_ptr); +# endif /* WRITE_TEXT */ - else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) - { -#ifdef PNG_WRITE_zTXt_SUPPORTED - /* Write compressed chunk */ - png_write_zTXt(png_ptr, info_ptr->text[i].key, - info_ptr->text[i].text, info_ptr->text[i].compression); - /* Mark this chunk as written */ - info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; -#else - png_warning(png_ptr, "Unable to write compressed text"); -#endif - } - - else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) - { -#ifdef PNG_WRITE_tEXt_SUPPORTED - /* Write uncompressed chunk */ - png_write_tEXt(png_ptr, info_ptr->text[i].key, - info_ptr->text[i].text, 0); - /* Mark this chunk as written */ - info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; -#else - png_warning(png_ptr, "Unable to write uncompressed text"); -#endif - } - } -#endif -#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED - write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT); -#endif +# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT); +# endif } - png_ptr->mode |= PNG_AFTER_IDAT; - /* Write end of PNG file */ png_write_IEND(png_ptr); + /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03, * and restored again in libpng-1.2.30, may cause some applications that * do not set png_ptr->output_flush_fn to crash. If your application @@ -448,11 +460,11 @@ png_write_end(png_structrp png_ptr, png_inforp info_ptr) * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to * png-mng-implement at lists.sf.net . */ -#ifdef PNG_WRITE_FLUSH_SUPPORTED -# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED - png_flush(png_ptr); +# ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED + png_flush(png_ptr); +# endif # endif -#endif } #ifdef PNG_CONVERT_tIME_SUPPORTED @@ -547,12 +559,6 @@ png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, #if PNG_RELEASE_BUILD png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; #endif - - /* TODO: delay this, it can be done in png_init_io() (if the app doesn't - * do it itself) avoiding setting the default function if it is not - * required. - */ - png_set_write_fn(png_ptr, NULL, NULL, NULL); } return png_ptr; @@ -568,19 +574,14 @@ void PNGAPI png_write_rows(png_structrp png_ptr, png_bytepp row, png_uint_32 num_rows) { - png_uint_32 i; /* row counter */ - png_bytepp rp; /* row pointer */ - png_debug(1, "in png_write_rows"); - if (png_ptr == NULL) + if (png_ptr == NULL || row == NULL) return; /* Loop through the rows */ - for (i = 0, rp = row; i < num_rows; i++, rp++) - { - png_write_row(png_ptr, *rp); - } + while (num_rows-- > 0) + png_write_row(png_ptr, *row++); } /* Write the image. You only need to call this function once, even @@ -589,11 +590,9 @@ png_write_rows(png_structrp png_ptr, png_bytepp row, void PNGAPI png_write_image(png_structrp png_ptr, png_bytepp image) { - png_uint_32 i; /* row index */ - int pass, num_pass; /* pass variables */ - png_bytepp rp; /* points to current row */ + int num_pass; /* pass variables */ - if (png_ptr == NULL) + if (png_ptr == NULL || image == NULL) return; png_debug(1, "in png_write_image"); @@ -605,89 +604,143 @@ png_write_image(png_structrp png_ptr, png_bytepp image) num_pass = png_set_interlace_handling(png_ptr); #else num_pass = 1; + + if (png_ptr->interlaced) + { + png_app_error(png_ptr, "no interlace support"); + return; + } #endif + /* Loop through passes */ - for (pass = 0; pass < num_pass; pass++) + while (num_pass-- > 0) { + png_bytepp rp = image; /* points to current row */ + png_uint_32 num_rows = png_ptr->height; + /* Loop through image */ - for (i = 0, rp = image; i < png_ptr->height; i++, rp++) - { - png_write_row(png_ptr, *rp); - } + while (num_rows-- > 0) + png_write_row(png_ptr, *rp++); } } -#ifdef PNG_MNG_FEATURES_SUPPORTED -/* Performs intrapixel differencing */ +/* Called to advance to the next row. A row may not have been output when + * libpng handles the interlace passes because the app hands libpng every image + * row for every pass. + * + * This function also writes the current row, if the pointer to the bytes is + * non-NULL, and does so with the appropriate 'flush' argument to zlib. + */ static void -png_do_write_intrapixel(png_row_infop row_info, png_bytep row) +png_write_finish_row(png_structrp png_ptr, png_byte filter_byte, + png_const_bytep row, png_alloc_size_t row_bytes) { - png_debug(1, "in png_do_write_intrapixel"); + const png_uint_32 height = png_ptr->height; + png_uint_32 row_number = png_ptr->row_number; + int flush = Z_NO_FLUSH; - if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0) + png_debug(1, "in png_write_finish_row"); + + if (png_ptr->interlaced) { - int bytes_per_pixel; - png_uint_32 row_width = row_info->width; - if (row_info->bit_depth == 8) - { - png_bytep rp; - png_uint_32 i; + unsigned int pass = png_ptr->pass; - if (row_info->color_type == PNG_COLOR_TYPE_RGB) - bytes_per_pixel = 3; - - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - bytes_per_pixel = 4; - - else - return; - - for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (png_ptr->do_interlace) { - *(rp) = (png_byte)((*rp - *(rp + 1)) & 0xff); - *(rp + 2) = (png_byte)((*(rp + 2) - *(rp + 1)) & 0xff); - } - } + /* libpng is doing the de-interlace. */ + /* Z_FINISH must be set on the last row present in the image, not + * the actual last row. + * + * NOTE: this means that the application need not call libpng all + * the way to the end of the image, but this is double checked + * below in png_write_end where png_ptr->pass is checked. + */ + if (pass == PNG_LAST_PASS(png_ptr->width, height) && + PNG_LAST_PASS_ROW(row_number, pass, height) && + PNG_ROW_IN_INTERLACE_PASS(row_number, pass)) + flush = Z_FINISH; /* end of image */ -#ifdef PNG_WRITE_16BIT_SUPPORTED - else if (row_info->bit_depth == 16) + if (++row_number == height) + { + ++pass; + row_number = 0; + png_ptr->pass = pass & 0x7; + } + } /* libpng doing interlace */ + + else /* app is doing the interlacing */ +# endif /* WRITE_INTERLACING */ + /* The application has to hand us interlaced rows. In this case + * row_number is the row number in the pass (this is not the + * behavior in the read code, where it is always the row number in + * the image.) + * + * Note that for any image row 0 of pass 0 is always present, so the + * check after the 'if' is not required at the start. + */ { - png_bytep rp; - png_uint_32 i; + affirm(row != NULL); - if (row_info->color_type == PNG_COLOR_TYPE_RGB) - bytes_per_pixel = 6; - - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - bytes_per_pixel = 8; - - else - return; - - for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + if (++row_number == PNG_PASS_ROWS(height, pass)) { - png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); - png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); - png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); - png_uint_32 red = (s0 - s1) & 0xffff; - png_uint_32 blue = (s2 - s1) & 0xffff; - *(rp ) = png_check_byte(0/*TODO: fixme*/, red >> 8); - *(rp + 1) = PNG_BYTE(red); - *(rp + 4) = png_check_byte(0/*TODO: fixme*/, blue >> 8); - *(rp + 5) = PNG_BYTE(blue); - } - } -#endif /* WRITE_16BIT */ + const png_uint_32 width = png_ptr->width; + + /* Next pass, but it may not be present because of the width. */ + row_number = 0; + + for (;;) + { + if (++pass == PNG_INTERLACE_ADAM7_PASSES) + { + flush = Z_FINISH; + break; /* end of image */ + } + + if (PNG_PASS_IN_IMAGE(width, height, pass)) + break; + } + + png_ptr->pass = pass & 0x7; + } /* end of pass */ + } /* app doing interlace */ + } /* writing an interlaced PNG */ + + else /* PNG not interlaced */ if (++row_number == height) + flush = Z_FINISH; + + png_ptr->row_number = row_number; + + if (row != NULL) + { + png_compress_IDAT(png_ptr, &filter_byte, 1, Z_NO_FLUSH); + png_compress_IDAT(png_ptr, row, row_bytes, flush); + +# ifdef PNG_WRITE_FLUSH_SUPPORTED + if (flush == Z_NO_FLUSH && + ++png_ptr->flush_rows >= png_ptr->flush_dist && + png_ptr->flush_dist > 0) + png_write_flush(png_ptr); +# endif /* WRITE_FLUSH */ } + + /* The calculations above should ensure that Z_FINISH is set on the last row + * written, the following rows are empty pass rows, so the deflate stream + * should already have been closed: + */ + else + affirm(flush == Z_NO_FLUSH || png_ptr->zowner == 0); } -#endif /* MNG_FEATURES */ /* Called by user to write a row of image data */ void PNGAPI png_write_row(png_structrp png_ptr, png_const_bytep row) { - /* 1.5.6: moved from png_struct to be a local structure: */ - png_row_info row_info; + png_uint_32 row_number, row_width; + unsigned int pass; +# ifdef PNG_WRITE_FILTER_SUPPORTED + int last_pass_row, first_pass_row; +# endif if (png_ptr == NULL) return; @@ -695,206 +748,250 @@ png_write_row(png_structrp png_ptr, png_const_bytep row) png_debug2(1, "in png_write_row (row %u, pass %d)", png_ptr->row_number, png_ptr->pass); - /* Initialize transformations and other stuff if first time */ - if (png_ptr->row_number == 0 && png_ptr->pass == 0) + row_number = png_ptr->row_number; + row_width = png_ptr->width; + pass = png_ptr->pass; + + /* Unlike the read code initialization happens automatically: */ + if (row_number == 0 && pass == 0) { - /* Make sure we wrote the header info */ - if ((png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE) == 0) - png_error(png_ptr, - "png_write_info was never called before png_write_row"); + png_init_row_info(png_ptr); /* doesn't change row/pass/width */ - /* Check for transforms that have been set but were defined out */ -#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) - if ((png_ptr->transformations & PNG_INVERT_MONO) != 0) - png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined"); -#endif - -#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) - if ((png_ptr->transformations & PNG_FILLER) != 0) - png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined"); -#endif -#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ - defined(PNG_READ_PACKSWAP_SUPPORTED) - if ((png_ptr->transformations & PNG_PACKSWAP) != 0) - png_warning(png_ptr, - "PNG_WRITE_PACKSWAP_SUPPORTED is not defined"); -#endif - -#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) - if ((png_ptr->transformations & PNG_PACK) != 0) - png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined"); -#endif - -#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) - if ((png_ptr->transformations & PNG_SHIFT) != 0) - png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined"); -#endif - -#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) - if ((png_ptr->transformations & PNG_BGR) != 0) - png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined"); -#endif - -#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) - if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0) - png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined"); -#endif - - png_write_start_row(png_ptr); + /* If the app takes a png_info from a read operation and if the app has + * performed transforms on the data the png_info can contain IHDR + * information that cannot be represented in PNG. The code that writes + * the IHDR takes the color type from the png_info::format. The app adds + * transforms, before or after writing the IHDR, then the IHDR color_type + * stored in png_struct::color_type is used in png_init_row_info above to + * work out the actual row format. + * + * Prior to 1.7.0 this was not verified (there was no easy way to do so). + * Now we can check it here, however this is an: + * + * API CHANGE: in 1.7.0 an error may be flagged against bogus info_struct + * formats even though the app had removed them itself. It's just a + * warning at present. + */ +# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + /* The test is that either the row_format produced by the write + * transforms exactly matches that in the original info_struct::format + * or that the info_struct::format was a simple mapping of the + * color_type that ended up in the IHDR: + */ + if (png_ptr->row_format != png_ptr->info_format && + PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type) != + png_ptr->info_format) + png_app_warning(png_ptr, "info_struct format does not match IHDR"); +# endif /* WRITE_TRANSFORMS */ } -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* If interlaced and not interested in row, return */ - if (png_ptr->interlaced != 0 && - (png_ptr->transformations & PNG_INTERLACE) != 0) - { - switch (png_ptr->pass) - { - case 0: - if ((png_ptr->row_number & 0x07) != 0) - { - png_write_finish_row(png_ptr); - return; - } - break; + else if (row_number >= png_ptr->height || pass >= PNG_INTERLACE_ADAM7_PASSES) + png_error(png_ptr, "Too many calls to png_write_row"); - case 1: - if ((png_ptr->row_number & 0x07) != 0 || png_ptr->width < 5) - { - png_write_finish_row(png_ptr); - return; - } - break; - - case 2: - if ((png_ptr->row_number & 0x07) != 4) - { - png_write_finish_row(png_ptr); - return; - } - break; - - case 3: - if ((png_ptr->row_number & 0x03) != 0 || png_ptr->width < 3) - { - png_write_finish_row(png_ptr); - return; - } - break; - - case 4: - if ((png_ptr->row_number & 0x03) != 2) - { - png_write_finish_row(png_ptr); - return; - } - break; - - case 5: - if ((png_ptr->row_number & 0x01) != 0 || png_ptr->width < 2) - { - png_write_finish_row(png_ptr); - return; - } - break; - - case 6: - if ((png_ptr->row_number & 0x01) == 0) - { - png_write_finish_row(png_ptr); - return; - } - break; - - default: /* error: ignore it */ - break; - } - } -#endif - - /* Set up row info for transformations */ - row_info.color_type = png_ptr->color_type; - row_info.width = png_ptr->usr_width; - row_info.channels = png_ptr->usr_channels; - row_info.bit_depth = png_ptr->usr_bit_depth; - row_info.pixel_depth = - png_check_byte(png_ptr, row_info.bit_depth * row_info.channels); - row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); - - png_debug1(3, "row_info->color_type = %d", row_info.color_type); - png_debug1(3, "row_info->width = %u", row_info.width); - png_debug1(3, "row_info->channels = %d", row_info.channels); - png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth); - png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth); - png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes); - - /* Copy user's row into buffer, leaving room for filter byte. */ - memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes); - -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* Handle interlacing */ - if (png_ptr->interlaced && png_ptr->pass < 6 && - (png_ptr->transformations & PNG_INTERLACE) != 0) - { - png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass); - /* This should always get caught above, but still ... */ - if (row_info.width == 0) - { - png_write_finish_row(png_ptr); - return; - } - } -#endif - -#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED - /* Handle other transformations */ - if (png_ptr->transformations != 0) - png_do_write_transformations(png_ptr, &row_info); -#endif - - /* At this point the row_info pixel depth must match the 'transformed' depth, - * which is also the output depth. + /* If interlaced and not interested in row, return. Note that the + * assumption here, as in the read code, is that if the app wants to write an + * interlaced image when libpng does not support WRITE_INTERLACING the app + * will only provide the rows actually in the pass. */ - if (row_info.pixel_depth != png_ptr->pixel_depth || - row_info.pixel_depth != png_ptr->transformed_pixel_depth) - png_error(png_ptr, "internal write transform logic error"); + if (png_ptr->interlaced) + { +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (png_ptr->do_interlace) + { + /* libpng is doing the de-interlace. */ + if (!PNG_ROW_IN_INTERLACE_PASS(row_number, pass) || + PNG_PASS_COLS(row_width, pass) == 0) + { + /* Not in the pass; advance to the next row. Notice that because + * the app is expected to call us once for every image row in + * every pass it is sufficient to just add one to row_number + * here. + */ + png_write_finish_row(png_ptr, 0, NULL, 0); + return; + } -#ifdef PNG_MNG_FEATURES_SUPPORTED - /* Write filter_method 64 (intrapixel differencing) only if - * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and - * 2. Libpng did not write a PNG signature (this filter_method is only - * used in PNG datastreams that are embedded in MNG datastreams) and - * 3. The application called png_permit_mng_features with a mask that - * included PNG_FLAG_MNG_FILTER_64 and - * 4. The filter_method is 64 and - * 5. The color_type is RGB or RGBA + /* Else: this row must be output, row_number is the row in the + * image. + */ + last_pass_row = + PNG_LAST_PASS_ROW(row_number, pass, png_ptr->height); + first_pass_row = row_number == PNG_PASS_START_ROW(pass); + debug(row_number >= PNG_PASS_START_ROW(pass)); + } + + else /* app is doing the interlacing */ +# endif /* WRITE_INTERLACING */ + { + /* The interlaced rows come from the application and they have the + * correct width, row_number is the row number in the pass, not the + * number of the corresponding (expanded) image row. + */ + row_width = PNG_PASS_COLS(row_width, pass); +# ifdef PNG_WRITE_FILTER_SUPPORTED + last_pass_row = + row_number+1 >= PNG_PASS_ROWS(png_ptr->height, pass); + first_pass_row = row_number == 0; +# endif /* WRITE_INTERLACING */ + } + } /* writing an interlaced PNG */ + +# ifdef PNG_WRITE_FILTER_SUPPORTED + else /* not an interlaced PNG */ + { + last_pass_row = row_number+1 >= png_ptr->height; + first_pass_row = row_number == 0; + } +# endif /* WRITE_INTERLACING */ + + /* 1.7.0: pretty much everything except the PNG row filter happens under the + * control of the transform code. + * + * png_struct::row_format is the *input* row format. During the transforms + * the png_transform_control::{sp,dp} pointers are used in the normal fashion + * with dp initially set to png_struct::row_buffer. + * + * After the transforms if there is no filtering to be done (WRITE_FILTER is + * not supported) 'sp' is written directly; it may be png_struct::row_buffer + * or it may be the original 'row' parameter to this routine. There is no + * need to save the transformed (PNG format) row. + * + * If there is filtering to be done then either the original row or + * png_transform_control::sp is filtered against the previous row, which is + * in png_struct::alt_buffer, the result is written with the appropriate + * filter byte and then the original row or png_transform_control::sp is + * saved to png_struct::alt_buffer. + * + * Thus there are four control flow possibilities depending on the pair of + * compile time flags [WRITE_TRANSFORM_MECH,WRITE_FILTER]. The simplest + * write code, which requires no internal buffer, arises when both are + * compiled out. alt_buffer is only required if WRITE_FILTER is supported + * and row_buffer is required when either are supported. */ - if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && - (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) { - /* Intrapixel differencing */ - png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1); + png_byte filter_byte; + unsigned int output_bpp; /* bits per pixel */ + png_const_bytep output_row; + png_alloc_size_t output_bytes; + +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + if (png_ptr->transform_list != NULL) /* else no transforms */ + { + png_transform_control tc; + + /* The initial values are the memory format, this was worked out in + * png_init_row_info above. + */ + memset(&tc, 0, sizeof tc); + tc.png_ptr = png_ptr; + tc.sp = row; + tc.dp = png_ptr->row_buffer; + + if (tc.dp == NULL) + png_ptr->row_buffer = png_voidcast(png_bytep, tc.dp = + png_malloc(png_ptr, png_ptr->row_allocated_bytes)); + + tc.width = row_width; /* width of interlaced row */ + tc.format = png_ptr->row_format; + tc.range = png_ptr->row_range; + tc.bit_depth = png_ptr->row_bit_depth; + /* tc.init == 0 */ + /* tc.caching: not used */ + /* tc.palette: not used */ + + /* Run the list. */ + png_run_transform_list_backwards(png_ptr, &tc); + + /* Make sure the format that resulted is compatible with PNG: */ + affirm((tc.format & PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA + + PNG_FORMAT_FLAG_COLOR + PNG_FORMAT_FLAG_LINEAR + + PNG_FORMAT_FLAG_COLORMAP)) == 0); + + /* Now we must have the PNG format from the IHDR: */ + affirm(png_ptr->bit_depth == tc.bit_depth && + png_ptr->color_type == PNG_COLOR_TYPE_FROM_FORMAT(tc.format)); + + /* If libpng is handling the interlacing the row width must now + * match the width required for this pass. + */ + affirm(tc.width == (!png_ptr->do_interlace ? + row_width : PNG_PASS_COLS(row_width, pass))); + + row_width = tc.width; + output_row = png_voidcast(png_const_bytep, tc.sp); + } + + else /* no transforms */ +# endif /* TRANSFORM_MECH */ + output_row = row; + + output_bpp = PNG_PIXEL_DEPTH(*png_ptr); + output_bytes = PNG_ROWBYTES(output_bpp, row_width); + +# ifdef PNG_WRITE_FILTER_SUPPORTED + { + png_const_bytep unfiltered_row = output_row; + png_bytep alt_buffer = png_ptr->alt_buffer; + + /* If necessary find an appropriate filter and apply it to get the + * filtered row. The function may return the original argument, it + * fills in 'filter_byte' appropriately. + */ + if (png_ptr->next_filter != PNG_FILTER_NONE) + output_row = png_write_filter_row(png_ptr, output_row, + first_pass_row, alt_buffer, output_bytes, + (output_bpp+7)>>3 /* bytes per pixel */, &filter_byte); + + else + filter_byte = PNG_FILTER_VALUE_NONE; + + /* If the available filters require it, or ever did (as evidenced by + * the presence of 'alt_buffer', store the unfiltered row in + * alt_buffer. Note that this does not happen on the last row of a + * pass, or the image. + */ + if (!last_pass_row && (alt_buffer != NULL || (png_ptr->next_filter & + (PNG_FILTER_UP+PNG_FILTER_AVG+PNG_FILTER_PAETH)) != 0)) + { + if (unfiltered_row == row) /* Must be copied */ + { + if (alt_buffer == NULL) + png_ptr->alt_buffer = alt_buffer = png_voidcast(png_bytep, + png_malloc(png_ptr, png_ptr->row_allocated_bytes)); + + memcpy(alt_buffer, unfiltered_row, output_bytes); + } + + else /* Can be swapped */ + { + png_bytep tmp = png_ptr->row_buffer; + + affirm(unfiltered_row == tmp); + png_ptr->row_buffer = alt_buffer; /* may be NULL */ + png_ptr->alt_buffer = tmp; + } + } + } +# else /* !WRITE_FILTER: no previous row to store */ + filter_byte = PNG_FILTER_VALUE_NONE; +# endif /* !WRITE_FILTER */ + + png_write_finish_row(png_ptr, filter_byte, output_row, output_bytes); } -#endif - -/* Added at libpng-1.5.10 */ -#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED - /* Check for out-of-range palette index */ - if (row_info.color_type == PNG_COLOR_TYPE_PALETTE && - png_ptr->num_palette_max >= 0) - { - 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. */ - png_write_find_filter(png_ptr, &row_info); + /* API CHANGE: 1.7.0: this is now called after png_struct::row_number and + * png_struct::pass have been updated and, at the end of the image, after the + * deflate stream has been closed. The order of the call with respect to the + * flush operation has also changed. The callback can't discover any of this + * unless it relies on the write callbacks to find the row data, and that was + * never predictable. + * + * + * TODO: API CHANGE: pass the row bytes to this function, it would be more + * useful. + */ if (png_ptr->write_row_fn != NULL) - (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); + (*(png_ptr->write_row_fn))(png_ptr, row_number, pass); } #ifdef PNG_WRITE_FLUSH_SUPPORTED @@ -919,13 +1016,15 @@ png_write_flush(png_structrp png_ptr) if (png_ptr == NULL) return; - /* We have already written out all of the data */ - if (png_ptr->row_number >= png_ptr->num_rows) - return; - - png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH); - png_ptr->flush_rows = 0; - png_flush(png_ptr); + /* Before the start of the IDAT and after the end of the image zowner will be + * something other than png_IDAT: + */ + if (png_ptr->zowner == png_IDAT) + { + png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH); + png_ptr->flush_rows = 0; + png_flush(png_ptr); + } } #endif /* WRITE_FLUSH */ @@ -936,20 +1035,32 @@ png_write_destroy(png_structrp png_ptr) png_debug(1, "in png_write_destroy"); /* Free any memory zlib uses */ - if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0) - deflateEnd(&png_ptr->zstream); + if (png_ptr->zstream.state != NULL) + { + int ret = deflateEnd(&png_ptr->zstream); + + if (ret != Z_OK) + { + png_zstream_error(png_ptr, ret); + png_warning(png_ptr, png_ptr->zstream.msg); + } + } /* Free our memory. png_free checks NULL for us. */ png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); - png_free(png_ptr, png_ptr->row_buf); - png_ptr->row_buf = NULL; + png_free(png_ptr, png_ptr->row_buffer); + png_ptr->row_buffer = NULL; #ifdef PNG_WRITE_FILTER_SUPPORTED - png_free(png_ptr, png_ptr->prev_row); - png_free(png_ptr, png_ptr->try_row); - png_free(png_ptr, png_ptr->tst_row); - png_ptr->prev_row = NULL; - png_ptr->try_row = NULL; - png_ptr->tst_row = NULL; + png_free(png_ptr, png_ptr->alt_buffer); + png_ptr->alt_buffer = NULL; + png_free(png_ptr, png_ptr->write_row[0]); + png_ptr->write_row[0] = NULL; + png_free(png_ptr, png_ptr->write_row[1]); + png_ptr->write_row[1] = NULL; +#endif + +#ifdef PNG_TRANSFORM_MECH_SUPPORTED + png_transform_free(png_ptr, &png_ptr->transform_list); #endif #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED @@ -990,6 +1101,7 @@ png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) } } +#ifdef PNG_WRITE_FILTER_SUPPORTED /* Allow the application to select one or more row filters to use. */ void PNGAPI png_set_filter(png_structrp png_ptr, int method, int filters) @@ -999,120 +1111,57 @@ png_set_filter(png_structrp png_ptr, int method, int filters) if (png_ptr == NULL) return; -#ifdef PNG_MNG_FEATURES_SUPPORTED - if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && - (method == PNG_INTRAPIXEL_DIFFERENCING)) - method = PNG_FILTER_TYPE_BASE; -#endif + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_filter: cannot be used when reading"); + return; + } - /* The only supported method, except for the check above, is - * PNG_FILTER_TYPE_BASE. The code below does not use 'method' other than - * for the check, so just keep going if png_app_error returns. - */ - if (method != PNG_FILTER_TYPE_BASE) - png_app_error(png_ptr, "Unknown custom filter method"); + if (method != png_ptr->filter_method) + { + png_app_error(png_ptr, "png_set_filter: method does not match IHDR"); + return; + } - /* If filter writing is not supported the 'filters' value must be zero, - * otherwise the value must be a single, valid, filter value or a set of the - * mask values. The defines in png.h are such that the filter masks used in - * this API and internally are 1<<(3+value), value is in the range 0..4, so - * this fits in a byte. + /* PNG and MNG use the same base adaptive filter types: */ + if (method != PNG_FILTER_TYPE_BASE && method != PNG_INTRAPIXEL_DIFFERENCING) + { + png_app_error(png_ptr, "png_set_filter: unsupported method"); + return; + } + + /* Notice that PNG_NO_FILTERS is 0 and passes this test; this is OK + * because filters then gets set to PNG_FILTER_NONE, as is required. */ -# ifdef PNG_WRITE_FILTER_SUPPORTED - /* Notice that PNG_NO_FILTERS is 0 and passes this test; this is OK - * because filters then gets set to PNG_FILTER_NONE, as is required. + if (filters < PNG_FILTER_VALUE_LAST) + filters = 0x08 << filters; + + else if ((filters & PNG_BIC_MASK(PNG_ALL_FILTERS)) != 0) + { + png_app_error(png_ptr, "png_set_filter: invalid filters mask/value"); + + /* Prior to 1.7.0 this ignored the error and just used the bits that are + * present, now it does nothing; this seems a lot safer. */ - if (filters < PNG_FILTER_VALUE_LAST) - filters = 0x08 << filters; + return; + } - else if ((filters & ~PNG_ALL_FILTERS) != 0) - { - png_app_error(png_ptr, "png_set_filter: invalid filters mask/value"); - - /* For compatibility with the previous behavior assume a mask value was - * passed and ignore the non-mask bits. - */ - filters &= PNG_ALL_FILTERS; - - /* For a possibly foolish consistency (it shouldn't matter) set - * PNG_FILTER_NONE rather than 0. - */ - if (filters == 0) - filters = PNG_FILTER_NONE; - } -# else - /* PNG_FILTER_VALUE_NONE and PNG_NO_FILTERS are both 0. */ - if (filters != 0 && filters != PNG_FILTER_NONE) - png_app_error(png_ptr, "png_set_filter: no filters supported"); - - filters = PNG_FILTER_NONE; -# endif - -# ifdef PNG_WRITE_FILTER_SUPPORTED - /* If we have allocated the row_buf, this means we have already started - * with the image and we should have allocated all of the filter buffers - * that have been selected. If prev_row isn't already allocated, then - * it is too late to start using the filters that need it, since we - * will be missing the data in the previous row. If an application - * wants to start and stop using particular filters during compression, - * it should start out with all of the filters, and then remove them - * or add them back after the start of compression. - * - * NOTE: this is a nasty constraint on the code, because it means that the - * prev_row buffer must be maintained even if there are currently no - * 'prev_row' requiring filters active. - */ - if (png_ptr->row_buf != NULL) - { - /* Repeat the checks in png_write_start_row; 1 pixel high or wide - * images cannot benefit from certain filters. If this isn't done here - * the check below will fire on 1 pixel high images. - */ - if (png_ptr->height == 1) - filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); - - if (png_ptr->width == 1) - filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH); - - if ((filters & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) != 0 - && png_ptr->prev_row == NULL) - { - /* This is the error case, however it is benign - the previous row - * is not available so the filter can't be used. Just warn here. - */ - png_app_warning(png_ptr, - "png_set_filter: UP/AVG/PAETH cannot be added after start"); - filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); - } - - /* Allocate any required buffers that have not already been allocated. - */ - png_write_alloc_filter_row_buffers(png_ptr, filters); - } -# endif /* WRITE_FILTER */ - - /* Finally store the value. - * TODO: this field could probably be removed if neither READ nor - * WRITE_FILTER are supported. - */ - png_ptr->do_filter = png_check_byte(png_ptr, filters); + /* Finally store the value. */ + png_ptr->next_filter = PNG_BYTE(filters); } +#endif /* WRITE_FILTER */ -/* This allows us to influence the way in which libpng chooses the "best" - * filter for the current scanline. While the "minimum-sum-of-absolute- - * differences metric is relatively fast and effective, there is some - * question as to whether it can be improved upon by trying to keep the - * filtered data going to zlib more consistent, hopefully resulting in - * better compression. - */ #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */ +/* Legacy API that weighted the filter metric by the number of times it had been + * used before. + */ #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_FUNCTION(void,PNGAPI png_set_filter_heuristics,(png_structrp png_ptr, int heuristic_method, int num_weights, png_const_doublep filter_weights, png_const_doublep filter_costs),PNG_DEPRECATED) { - PNG_UNUSED(png_ptr) + png_app_warning(png_ptr, "weighted filter heuristics not implemented"); PNG_UNUSED(heuristic_method) PNG_UNUSED(num_weights) PNG_UNUSED(filter_weights) @@ -1126,7 +1175,7 @@ png_set_filter_heuristics_fixed,(png_structrp png_ptr, int heuristic_method, int num_weights, png_const_fixed_point_p filter_weights, png_const_fixed_point_p filter_costs),PNG_DEPRECATED) { - PNG_UNUSED(png_ptr) + png_app_warning(png_ptr, "weighted filter heuristics not implemented"); PNG_UNUSED(heuristic_method) PNG_UNUSED(num_weights) PNG_UNUSED(filter_weights) @@ -1304,23 +1353,7 @@ png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn) png_ptr->write_row_fn = write_row_fn; } -#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED -void PNGAPI -png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr - write_user_transform_fn) -{ - png_debug(1, "in png_set_write_user_transform_fn"); - - if (png_ptr == NULL) - return; - - png_ptr->transformations |= PNG_USER_TRANSFORM; - png_ptr->write_user_transform_fn = write_user_transform_fn; -} -#endif - - -#ifdef PNG_INFO_IMAGE_SUPPORTED +#ifdef PNG_WRITE_PNG_SUPPORTED void PNGAPI png_write_png(png_structrp png_ptr, png_inforp info_ptr, int transforms, voidp params) @@ -1441,7 +1474,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr, PNG_UNUSED(params) } -#endif +#endif /* WRITE_PNG */ #ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED @@ -2014,7 +2047,7 @@ png_image_write_main(png_voidp argument) { if (colormap == 0 && (format & PNG_FORMAT_FLAG_COLOR) != 0) png_set_bgr(png_ptr); - format &= ~PNG_FORMAT_FLAG_BGR; + format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_BGR); } # endif @@ -2023,7 +2056,7 @@ png_image_write_main(png_voidp argument) { if (colormap == 0 && (format & PNG_FORMAT_FLAG_ALPHA) != 0) png_set_swap_alpha(png_ptr); - format &= ~PNG_FORMAT_FLAG_AFIRST; + format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_AFIRST); } # endif @@ -2034,7 +2067,7 @@ png_image_write_main(png_voidp argument) png_set_packing(png_ptr); /* That should have handled all (both) the transforms. */ - if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR | + if ((format & PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR | PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0) png_error(png_ptr, "png_write_image: unsupported transformation"); @@ -2055,15 +2088,17 @@ png_image_write_main(png_voidp argument) /* Apply 'fast' options if the flag is set. */ if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0) { - png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS); +# ifdef PNG_WRITE_FILTER_SUPPORTED + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS); +# endif /* WRITE_FILTER */ /* NOTE: determined by experiment using pngstest, this reflects some * balance between the time to write the image once and the time to read * it about 50 times. The speed-up in pngstest was about 10-20% of the * total (user) time on a heavily loaded system. */ -#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED - png_set_compression_level(png_ptr, 3); -#endif +# ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED + png_set_compression_level(png_ptr, 3); +# endif /* WRITE_CUSTOMIZE_COMPRESSION */ } /* Check for the cases that currently require a pre-transform on the row @@ -2120,17 +2155,12 @@ png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, { if (file != NULL) { - if (png_image_write_init(image) != 0) + if (png_image_write_init(image) != 0 && + png_image_init_io(image, file) != 0) { png_image_write_control display; int result; - /* This is slightly evil, but png_init_io doesn't do anything other - * than this and we haven't changed the standard IO functions so - * this saves a 'safe' function. - */ - image->opaque->png_ptr->io_ptr = file; - memset(&display, 0, (sizeof display)); display.image = image; display.buffer = buffer; diff --git a/pngwtran.c b/pngwtran.c index dc4c99f07..56ff95053 100644 --- a/pngwtran.c +++ b/pngwtran.c @@ -14,523 +14,365 @@ #include "pngpriv.h" #define PNG_SRC_FILE PNG_SRC_FILE_pngwtran -#ifdef PNG_WRITE_SUPPORTED -#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED - -#ifdef PNG_WRITE_PACK_SUPPORTED -/* 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). +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Pick out the correct pixels for the interlace pass. + * The basic idea here is to go through the row with a source + * pointer and a destination pointer (sp and dp), and copy the + * correct pixels for the pass. As the row gets compacted, + * sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. */ static void -png_do_pack(png_transform_controlp row_info, png_bytep row) +png_do_write_interlace_lbd(png_transformp *transform, png_transform_controlp tc) { - png_debug(1, "in png_do_pack"); + const png_const_structrp png_ptr = tc->png_ptr; + const unsigned int pass = png_ptr->pass; + const png_uint_32 row_width = tc->width; + const png_uint_32 output_width = PNG_PASS_COLS(row_width, pass); + png_uint_32 i = PNG_PASS_START_COL(pass); -# define png_ptr row_info->png_ptr - /* The comment suggests the following must be true. - * TODO: test this. + png_debug(1, "in png_do_write_interlace"); + debug(!tc->init); + + /* The data can be used in place (tc->sp) if the width isn't changed or + * the first pixel in the output is the first in the input and there is + * only one pixel in the output; this covers the last pass (PNG pass 7, + * libpng 6) and PNG passes 1, 3 and 5 with narrow images. */ - affirm(row_info->bit_depth == 8 && row_info->channels == 1); + tc->width = output_width; + if (row_width != output_width && (output_width != 1 || i > 0)) { - switch (png_ptr->bit_depth) + /* For passes before the last the pixels must be picked from the input + * row (tc->sp) and placed into the output row (tc->dp). + */ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + const unsigned int inc = PNG_PASS_COL_OFFSET(pass); + unsigned int B = (*transform)->args & 0x3; /* 0, 1 or 2 */ + + /* The row data will be moved, so do this now before 'dp' is advanced: + */ + tc->sp = dp; + + /* For pixels less than one byte wide the correct pixels have to be + * extracted from the input bytes. Because we are reading data in + * the application memory format we cannot rely on the PNG big + * endian order. Notice that this was apparently broken before + * 1.7.0. + * + * In libpng 1.7.0 libpng uses a classic bit-pump to optimize the + * extraction. In all passes before the last (6/7) no two pixels + * are adjacent in the input, so we are always extracting 1 bit. + * At present the code uses an 8-bit buffer to avoid coding for + * different byte sexes, but this could easily be changed. + * + * 'i' is the bit-index of bit in the input (sp[]), so, + * considering the 1-bit per pixel case, sp[i>>3] is the byte + * and the bit is bit (i&7) (0 lowest) on swapped (little endian) + * data or 7-(i&7) on PNG default (big-endian) data. + * + * Define these macros, where: + * + * B: the log2 bit depth (0, 1, 2 for 1bpp, 2bpp or 4bpp) of + * the data; this should be a constant. + * sp: the source pointer (sp) (a png_const_bytep) + * i: the pixel index in the input (png_uint_32) + * j: the bit index in the output (unsigned int) + * + * Unlike 'i', 'j' is interpreted directly; for LSB bytes it counts + * up, for MSB it counts down. + * + * NOTE: this could all be expanded to eliminate the code below by + * the time honoured copy'n'paste into three separate functions. This + * might be worth doing in the future. + */ +# define PIXEL_MASK ((1U << (1<>(3-(B))]) /* byte to use */ +# define SP_OFFSET_LSB ((BIT_MASK & i) << (B)) +# define SP_OFFSET_MSB ((BIT_MASK & ~i) << (B)) +# define SP_PIXEL(sex) ((SP_BYTE >> SP_OFFSET_ ## sex) & PIXEL_MASK) { - case 1: - { - png_const_bytep ep = row + png_transform_rowbytes(row_info); - png_bytep dp = row; - unsigned int mask = 0x80, v = 0; + unsigned int j; + unsigned int d; - while (row < ep) - { - if (*row++ != 0) - v |= mask; - - mask >>= 1; - - if (mask == 0) - { - mask = 0x80; - *dp++ = (png_byte)/*SAFE*/v; - v = 0; - } +# ifdef PNG_WRITE_PACKSWAP_SUPPORTED + if (tc->format & PNG_FORMAT_FLAG_SWAPPED) + for (j = 0, d = 0; i < row_width; i += inc) + { /* little-endian */ + d |= SP_PIXEL(LSB) << j; + j += 1<bit_depth = 1; - break; + else +# endif /* WRITE_PACKSWAP */ + for (j = 8, d = 0; i < row_width; i += inc) + { /* big-endian */ + j -= 1<fn = NULL; /* remove me to caller */ +} - if (shift == 0) - { - shift = 8; - *dp++ = png_check_byte(png_ptr, v); - v = 0; - } - } +static void +png_do_write_interlace_byte(png_transformp *transform, + png_transform_controlp tc) +{ + const png_const_structrp png_ptr = tc->png_ptr; + const unsigned int pass = png_ptr->pass; + const png_uint_32 row_width = tc->width; + const png_uint_32 output_width = PNG_PASS_COLS(row_width, pass); + png_uint_32 i = PNG_PASS_START_COL(pass); - if (shift != 8) - *dp++ = png_check_byte(png_ptr, v); + png_debug(1, "in png_do_write_interlace"); + debug(!tc->init); - row_info->bit_depth = 2; + /* The data can be used in place (tc->sp) if the width isn't changed or + * the first pixel in the output is the first in the input and there is + * only one pixel in the output; this covers the last pass (PNG pass 7, + * libpng 6) and PNG passes 1, 3 and 5 with narrow images. + */ + tc->width = output_width; + + if (row_width != output_width && (output_width != 1 || i > 0)) + { + /* For passes before the last the pixels must be picked from the input + * row (tc->sp) and placed into the output row (tc->dp). + */ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + const unsigned int inc = PNG_PASS_COL_OFFSET(pass); + unsigned int cbytes = (*transform)->args; + + /* The row data will be moved, so do this now before 'dp' is advanced: + */ + tc->sp = dp; + + /* Loop through the input copying each pixel to the correct place + * in the output. Note that the loop may be executed 0 times if + * this is called on a narrow image that does not contain this + * pass. + */ + for (sp += i * cbytes; i < row_width; + i += inc, sp += inc * cbytes, dp += cbytes) + if (dp != sp) /* cannot happen in practice */ + memcpy(dp, sp, cbytes); + } + + /* The transform is removed on the last pass. It can be removed earlier + * in other cases if the row width (the image width) is only 1, however + * this does not seem worth the overhead to check; PNG pass 5(4) happens + * if there are just three rows. + */ + else /* the source can be used in place */ if (pass == 6) + (*transform)->fn = NULL; /* remove me to caller */ +} + +static void +png_init_write_interlace(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + + png_debug(1, "in png_do_write_interlace"); + debug(tc->init); + + /* Do nothing on PNG_TC_INIT_FORMAT because we don't change the format, bit + * depth or gamma of the data. + */ + if (tc->init == PNG_TC_INIT_FINAL) + { + png_transformp tf = *transform; + unsigned int pixel_depth = PNG_TC_PIXEL_DEPTH(*tc); + png_uint_16 B = 0; + + switch (pixel_depth) + { + case 4: /* B == 2 */ + ++B; + /* FALL THROUGH */ + case 2: /* B == 1 */ + ++B; + /* FALL THROUGH */ + case 1: /* B == 0 */ + /* This is the low bit depth case: */ + tf->args = B; + tf->fn = png_do_write_interlace_lbd; break; - } - - case 4: - { - png_const_bytep ep = row + png_transform_rowbytes(row_info); - png_bytep dp = row; - unsigned int shift = 8, v = 0; - - while (row < ep) - { - shift -= 4; - v |= ((*row++ & 0xf) << shift); - - if (shift == 0) - { - shift = 8; - *dp++ = png_check_byte(png_ptr, v); - v = 0; - } - } - - if (shift != 8) - *dp++ = png_check_byte(png_ptr, v); - - row_info->bit_depth = 4; - break; - } default: + affirm((pixel_depth & 7) == 0); + pixel_depth >>= 3; + affirm(pixel_depth > 0 && pixel_depth <= 8); + tf->args = pixel_depth & 0xf; + tf->fn = png_do_write_interlace_byte; break; } } # undef png_ptr } -#endif -#ifdef PNG_WRITE_SHIFT_SUPPORTED -/* Shift pixel values to take advantage of whole range. Pass the - * true number of bits in bit_depth. The row should be packed - * according to row_info->bit_depth. Thus, if you had a row of - * 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_transform_controlp row_info, png_bytep row) -{ - png_debug(1, "in png_do_shift"); - -# 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->channels == 3 || row_info->channels == 4) - { - shift_start[channels] = row_info->bit_depth - bit_depth->red; - shift_dec[channels] = bit_depth->red; - channels++; - - shift_start[channels] = row_info->bit_depth - bit_depth->green; - shift_dec[channels] = bit_depth->green; - channels++; - - shift_start[channels] = row_info->bit_depth - bit_depth->blue; - shift_dec[channels] = bit_depth->blue; - channels++; - } - - 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->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 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; - 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; - - else if (row_info->bit_depth == 4 && bit_depth->gray == 3) - mask = 0x11; - - else - mask = 0xff; - - for (i = 0; i < row_bytes; i++, bp++) - { - int j; - unsigned int v, out; - - v = *bp; - out = 0; - - for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) - { - if (j > 0) - out |= v << j; - - else - out |= (v >> (-j)) & mask; - } - - *bp = png_check_byte(png_ptr, out); - } - } - - else if (row_info->bit_depth == 8) - { - png_bytep bp = row; - png_uint_32 i; - png_uint_32 istop = channels * row_info->width; - - for (i = 0; i < istop; i++, bp++) - { - - const unsigned int c = i%channels; - int j; - unsigned int v, out; - - v = *bp; - out = 0; - - for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) - { - if (j > 0) - out |= v << j; - - else - out |= v >> (-j); - } - - *bp = png_check_byte(png_ptr, out); - } - } - - else - { - png_bytep bp; - png_uint_32 i; - png_uint_32 istop = channels * row_info->width; - - for (bp = row, i = 0; i < istop; i++) - { - const unsigned int c = i%channels; - int j; - unsigned int value, v; - - v = png_get_uint_16(bp); - value = 0; - - for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) - { - if (j > 0) - value |= v << j; - - else - value |= v >> (-j); - } - *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_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->channels == 4) - { - if (row_info->bit_depth == 8) - { - png_const_bytep ep = row + png_transform_rowbytes(row_info) - 4; - - /* This converts from ARGB to RGBA */ - while (row <= ep) - { - 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 if (row_info->bit_depth == 16) - { - /* This converts from AARRGGBB to RRGGBBAA */ - png_const_bytep ep = row + png_transform_rowbytes(row_info) - 8; - - while (row <= ep) - { - 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->channels == 2) - { - if (row_info->bit_depth == 8) - { - /* This converts from AG to GA */ - png_const_bytep ep = row + png_transform_rowbytes(row_info) - 2; - - /* This converts from ARGB to RGBA */ - while (row <= ep) - { - 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_const_bytep ep = row + png_transform_rowbytes(row_info) - 4; - - while (row <= ep) - { - 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_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->channels == 4) - { - if (row_info->bit_depth == 8) - { - /* This inverts the alpha channel in RGBA */ - png_const_bytep ep = row + png_transform_rowbytes(row_info) - 1; - - row += 3; /* alpha channel */ - while (row <= ep) - *row ^= 0xff, row += 4; - } - -#ifdef PNG_WRITE_16BIT_SUPPORTED - else if (row_info->bit_depth == 16) - { - /* This inverts the alpha channel in RRGGBBAA */ - png_const_bytep ep = row + png_transform_rowbytes(row_info) - 2; - - row += 6; - - while (row <= ep) - row[0] ^= 0xff, row[1] ^= 0xff, row += 8; - } -#endif /* WRITE_16BIT */ - } - - else if (row_info->channels == 2) - { - if (row_info->bit_depth == 8) - { - /* This inverts the alpha channel in GA */ - png_const_bytep ep = row + png_transform_rowbytes(row_info) - 1; - - ++row; - - while (row <= ep) - *row ^= 0xff, row += 2; - } - -#ifdef PNG_WRITE_16BIT_SUPPORTED - else - { - /* This inverts the alpha channel in GGAA */ - png_const_bytep ep = row + png_transform_rowbytes(row_info) - 2; - - row += 2; - - while (row <= ep) - row[0] ^= 0xff, row[1] ^= 0xff, row += 4; - } -#endif /* WRITE_16BIT */ - } - } -# undef png_ptr -} -#endif - -/* Transform the data according to the user's wishes. The order of - * transformations is significant. - */ void /* PRIVATE */ -png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info_in) +png_set_write_interlace(png_structrp png_ptr) { - png_transform_control display; - - png_debug(1, "in png_do_write_transformations"); - - if (png_ptr == NULL) - return; - -#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED - if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0) - if (png_ptr->write_user_transform_fn != NULL) - (*(png_ptr->write_user_transform_fn)) /* User write transform - function */ - (png_ptr, /* png_ptr */ - 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 */ - /* png_byte bit_depth; bit depth of samples */ - /* 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 */ -#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(&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(&display, png_ptr->row_buf + 1); -#endif + /* This is checked in the caller: */ + debug(png_ptr->interlaced == PNG_INTERLACE_ADAM7); + png_add_transform(png_ptr, 0, png_init_write_interlace, PNG_TR_INTERLACE); +} +#endif /* WRITE_INTERLACING */ #ifdef PNG_WRITE_PACK_SUPPORTED - if ((png_ptr->transformations & PNG_PACK) != 0) - png_do_pack(&display, png_ptr->row_buf + 1); -#endif +/* Pack pixels into bytes. */ +static void +png_do_write_pack(png_transformp *transform, png_transform_controlp tc) +{ + png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = png_upcast(png_const_bytep, tc->sp) + rowbytes; + png_bytep dp = png_voidcast(png_bytep, tc->dp); -#ifdef PNG_WRITE_SWAP_SUPPORTED -# ifdef PNG_16BIT_SUPPORTED - if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0) - png_do_swap(&display, png_ptr->row_buf + 1); -# endif -#endif + png_debug(1, "in png_do_pack"); -#ifdef PNG_WRITE_SHIFT_SUPPORTED - if ((png_ptr->transformations & PNG_SHIFT) != 0) - png_do_shift(&display, png_ptr->row_buf + 1); -#endif +# define png_ptr tc->png_ptr -#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED - if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0) - png_do_write_swap_alpha(&display, png_ptr->row_buf + 1); -#endif + switch ((*transform)->args) + { + case 1: + { + unsigned int mask = 0x80, v = 0; -#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED - if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0) - png_do_write_invert_alpha(&display, png_ptr->row_buf + 1); -#endif + while (sp < ep) + { + if (*sp++ != 0) + v |= mask; -#ifdef PNG_WRITE_BGR_SUPPORTED - if ((png_ptr->transformations & PNG_BGR) != 0) - png_do_bgr(&display, png_ptr->row_buf + 1); -#endif + mask >>= 1; -#ifdef PNG_WRITE_INVERT_SUPPORTED - if ((png_ptr->transformations & PNG_INVERT_MONO) != 0) - png_do_invert(&display, png_ptr->row_buf + 1); -#endif + if (mask == 0) + { + mask = 0x80; + *dp++ = PNG_BYTE(v); + v = 0; + } + } - /* 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"); + if (mask != 0x80) + *dp++ = PNG_BYTE(v); + break; + } - display.flags &= PNG_INDEXED; - png_end_transform_control(row_info_in, &display); + case 2: + { + unsigned int shift = 8, v = 0; + + while (sp < ep) + { + shift -= 2; + v |= (*sp++ & 0x3) << shift; + + if (shift == 0) + { + shift = 8; + *dp++ = PNG_BYTE(v); + v = 0; + } + } + + if (shift != 8) + *dp++ = PNG_BYTE(v); + break; + } + + case 4: + { + unsigned int shift = 8, v = 0; + + while (sp < ep) + { + shift -= 4; + v |= ((*sp++ & 0xf) << shift); + + if (shift == 0) + { + shift = 8; + *dp++ = PNG_BYTE(v); + v = 0; + } + } + + if (shift != 8) + *dp++ = PNG_BYTE(v); + break; + } + + default: + impossible("bit depth"); + } + + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0 && + --(tc->range) == 0) + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE); + + tc->bit_depth = (*transform)->args; + tc->sp = tc->dp; +# undef png_ptr } -#endif /* WRITE_TRANSFORMS */ -#endif /* WRITE */ + +void /* PRIVATE */ +png_init_write_pack(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr tc->png_ptr + debug(tc->init); +# undef png_ptr + + /* The init routine is called *forward* so the transform control we get has + * the required bit depth and the transform routine will increase it to 8 + * bits per channel. The code doesn't really care how many channels there + * are, but the only way to get a channel depth of less than 8 is to have + * just one channel. + */ + if (tc->bit_depth < 8) /* else no packing/unpacking */ + { + if (tc->init == PNG_TC_INIT_FINAL) + { + (*transform)->fn = png_do_write_pack; + /* Record this for the backwards run: */ + (*transform)->args = tc->bit_depth & 0xf; + } + + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0) + { + tc->range++; + tc->format |= PNG_FORMAT_FLAG_RANGE; /* forwards: backwards cancels */ + } + + tc->bit_depth = 8; + } + + else /* the transform is not applicable */ + (*transform)->fn = NULL; +} +#endif /* WRITE_PACK */ diff --git a/pngwutil.c b/pngwutil.c index 4692149bb..3a05c4e37 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -40,7 +40,7 @@ png_save_uint_16(png_bytep buf, unsigned int i) buf[0] = PNG_BYTE(i >> 8); buf[1] = PNG_BYTE(i); } -#endif +#endif /* WRITE_INT_FUNCTIONS */ /* Simple function to write the signature. If we have already written * the magic bytes of the signature, or more likely, the PNG stream is @@ -208,21 +208,22 @@ png_image_size(png_structrp png_ptr) /* Only return sizes up to the maximum of a png_uint_32; do this by limiting * the width and height used to 15 bits. */ - png_uint_32 h = png_ptr->height; + const png_uint_32 h = png_ptr->height; + const png_uint_32 w = png_ptr->width; + const unsigned int pd = PNG_PIXEL_DEPTH(*png_ptr); + png_alloc_size_t rowbytes = PNG_ROWBYTES(pd, w); - if (png_ptr->rowbytes < 32768 && h < 32768) + if (rowbytes < 32768 && h < 32768) { if (png_ptr->interlaced != 0) { /* Interlacing makes the image larger because of the replication of * both the filter byte and the padding to a byte boundary. */ - png_uint_32 w = png_ptr->width; - unsigned int pd = png_ptr->pixel_depth; png_alloc_size_t cb_base; int pass; - for (cb_base=0, pass=0; pass<=6; ++pass) + for (cb_base=0, pass=0; passrowbytes+1) * h; + return (rowbytes+1) * h; } else @@ -339,7 +340,7 @@ png_deflate_claim(png_structrp png_ptr, png_uint_32 owner, if ((png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY) != 0) strategy = png_ptr->zlib_strategy; - else if (png_ptr->do_filter != PNG_FILTER_NONE) + else if (png_ptr->next_filter != PNG_FILTER_NONE) strategy = PNG_Z_DEFAULT_STRATEGY; else @@ -389,17 +390,23 @@ png_deflate_claim(png_structrp png_ptr, png_uint_32 owner, } /* Check against the previous initialized values, if any. */ - if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0 && + if (png_ptr->zstream.state != NULL && (png_ptr->zlib_set_level != level || png_ptr->zlib_set_method != method || png_ptr->zlib_set_window_bits != windowBits || png_ptr->zlib_set_mem_level != memLevel || png_ptr->zlib_set_strategy != strategy)) { - if (deflateEnd(&png_ptr->zstream) != Z_OK) - png_warning(png_ptr, "deflateEnd failed (ignored)"); + /* This shadows 'ret' deliberately; we ignore failures in deflateEnd: + */ + int ret_end = deflateEnd(&png_ptr->zstream); - png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED; + if (ret_end != Z_OK || png_ptr->zstream.state != NULL) + { + png_zstream_error(png_ptr, ret_end); + png_warning(png_ptr, png_ptr->zstream.msg); + png_ptr->zstream.state = NULL; /* zlib error recovery */ + } } /* For safety clear out the input and output pointers (currently zlib @@ -413,22 +420,17 @@ png_deflate_claim(png_structrp png_ptr, png_uint_32 owner, /* Now initialize if required, setting the new parameters, otherwise just * to a simple reset to the previous parameters. */ - if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0) + if (png_ptr->zstream.state != NULL) ret = deflateReset(&png_ptr->zstream); else - { ret = deflateInit2(&png_ptr->zstream, level, method, windowBits, memLevel, strategy); - if (ret == Z_OK) - png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED; - } - /* The return code is from either deflateReset or deflateInit2; they have * pretty much the same set of error codes. */ - if (ret == Z_OK) + if (ret == Z_OK && png_ptr->zstream.state != NULL) png_ptr->zowner = owner; else @@ -757,7 +759,7 @@ png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key) */ void /* PRIVATE */ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, - int bit_depth, int color_type, int compression_type, int filter_type, + int bit_depth, int color_type, int compression_type, int filter_method, int interlace_type) { png_byte buf[13]; /* Buffer to store the IHDR info */ @@ -777,11 +779,10 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, #ifdef PNG_WRITE_16BIT_SUPPORTED case 16: #endif - png_ptr->channels = 1; break; + break; default: - png_error(png_ptr, - "Invalid bit depth for grayscale image"); + png_error(png_ptr, "Invalid bit depth for grayscale image"); } break; @@ -793,7 +794,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, #endif png_error(png_ptr, "Invalid bit depth for RGB image"); - png_ptr->channels = 3; break; case PNG_COLOR_TYPE_PALETTE: @@ -803,7 +803,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, case 2: case 4: case 8: - png_ptr->channels = 1; break; default: @@ -815,7 +814,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, if (bit_depth != 8 && bit_depth != 16) png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); - png_ptr->channels = 2; break; case PNG_COLOR_TYPE_RGB_ALPHA: @@ -826,7 +824,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, #endif png_error(png_ptr, "Invalid bit depth for RGBA image"); - png_ptr->channels = 4; break; default: @@ -835,7 +832,7 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, if (compression_type != PNG_COMPRESSION_TYPE_BASE) { - png_warning(png_ptr, "Invalid compression type specified"); + png_app_error(png_ptr, "Invalid compression type specified"); compression_type = PNG_COMPRESSION_TYPE_BASE; } @@ -849,72 +846,61 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, * 5. The color_type is RGB or RGBA */ if ( -#ifdef PNG_MNG_FEATURES_SUPPORTED - !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && - ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && - (color_type == PNG_COLOR_TYPE_RGB || - color_type == PNG_COLOR_TYPE_RGB_ALPHA) && - (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && -#endif - filter_type != PNG_FILTER_TYPE_BASE) +# ifdef PNG_MNG_FEATURES_SUPPORTED + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && + ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_method == PNG_INTRAPIXEL_DIFFERENCING)) && +# endif /* MNG_FEATURES */ + filter_method != PNG_FILTER_TYPE_BASE) { - png_warning(png_ptr, "Invalid filter type specified"); - filter_type = PNG_FILTER_TYPE_BASE; + png_app_error(png_ptr, "Invalid filter type specified"); + filter_method = PNG_FILTER_TYPE_BASE; } -#ifdef PNG_WRITE_INTERLACING_SUPPORTED if (interlace_type != PNG_INTERLACE_NONE && interlace_type != PNG_INTERLACE_ADAM7) { - png_warning(png_ptr, "Invalid interlace type specified"); + png_app_error(png_ptr, "Invalid interlace type specified"); interlace_type = PNG_INTERLACE_ADAM7; } -#else - interlace_type=PNG_INTERLACE_NONE; -#endif /* Save the relevant information */ png_ptr->bit_depth = png_check_byte(png_ptr, bit_depth); png_ptr->color_type = png_check_byte(png_ptr, color_type); png_ptr->interlaced = png_check_byte(png_ptr, interlace_type); -#ifdef PNG_MNG_FEATURES_SUPPORTED - png_ptr->filter_type = png_check_byte(png_ptr, filter_type); -#endif + png_ptr->filter_method = png_check_byte(png_ptr, filter_method); png_ptr->compression_type = png_check_byte(png_ptr, compression_type); png_ptr->width = width; png_ptr->height = height; - png_ptr->pixel_depth = png_check_byte(png_ptr, - bit_depth * png_ptr->channels); - png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); - /* Set the usr info, so any transformations can modify it */ - png_ptr->usr_width = png_ptr->width; - png_ptr->usr_bit_depth = png_ptr->bit_depth; - png_ptr->usr_channels = png_ptr->channels; - /* Pack the header information into the buffer */ png_save_uint_32(buf, width); png_save_uint_32(buf + 4, height); buf[8] = png_check_byte(png_ptr, bit_depth); buf[9] = png_check_byte(png_ptr, color_type); buf[10] = png_check_byte(png_ptr, compression_type); - buf[11] = png_check_byte(png_ptr, filter_type); + buf[11] = png_check_byte(png_ptr, filter_method); buf[12] = png_check_byte(png_ptr, interlace_type); /* Write the chunk */ png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13); - if ((png_ptr->do_filter) == PNG_NO_FILTERS) - { - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || - png_ptr->bit_depth < 8) - png_ptr->do_filter = PNG_FILTER_NONE; +# ifdef PNG_WRITE_FILTER_SUPPORTED + /* TODO: review this setting */ + if (png_ptr->next_filter == PNG_NO_FILTERS /* not yet set */) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + png_ptr->bit_depth < 8) + png_ptr->next_filter = PNG_FILTER_NONE; - else - png_ptr->do_filter = PNG_ALL_FILTERS; - } + else + png_ptr->next_filter = PNG_ALL_FILTERS; + } +# endif - png_ptr->mode = PNG_HAVE_IHDR; /* not READY_FOR_ZTXT */ + png_ptr->mode |= PNG_HAVE_IHDR; } /* Write the palette. We are careful not to trust png_color to be in the @@ -923,7 +909,7 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, */ void /* PRIVATE */ png_write_PLTE(png_structrp png_ptr, png_const_colorp palette, - png_uint_32 num_pal) + unsigned int num_pal) { png_uint_32 i; png_const_colorp pal_ptr; @@ -932,10 +918,10 @@ png_write_PLTE(png_structrp png_ptr, png_const_colorp palette, png_debug(1, "in png_write_PLTE"); if (( -#ifdef PNG_MNG_FEATURES_SUPPORTED - (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 && -#endif - num_pal == 0) || num_pal > 256) +# ifdef PNG_MNG_FEATURES_SUPPORTED + (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 && +# endif /* MNG_FEATURES */ + num_pal == 0) || num_pal > PNG_MAX_PALETTE_LENGTH) { if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { @@ -957,35 +943,19 @@ png_write_PLTE(png_structrp png_ptr, png_const_colorp palette, return; } - png_ptr->num_palette = png_check_u16(png_ptr, num_pal); + png_ptr->num_palette = png_check_bits(png_ptr, num_pal, 9); png_debug1(3, "num_palette = %d", png_ptr->num_palette); - png_write_chunk_header(png_ptr, png_PLTE, (png_uint_32)(num_pal * 3)); -#ifdef PNG_POINTER_INDEXING_SUPPORTED + png_write_chunk_header(png_ptr, png_PLTE, num_pal * 3U); for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) { buf[0] = pal_ptr->red; buf[1] = pal_ptr->green; buf[2] = pal_ptr->blue; - png_write_chunk_data(png_ptr, buf, (png_size_t)3); + png_write_chunk_data(png_ptr, buf, 3U); } -#else - /* This is a little slower but some buggy compilers need to do this - * instead - */ - pal_ptr=palette; - - for (i = 0; i < num_pal; i++) - { - buf[0] = pal_ptr[i].red; - buf[1] = pal_ptr[i].green; - buf[2] = pal_ptr[i].blue; - png_write_chunk_data(png_ptr, buf, (png_size_t)3); - } - -#endif png_write_chunk_end(png_ptr); png_ptr->mode |= PNG_HAVE_PLTE; } @@ -1156,6 +1126,35 @@ png_write_IEND(png_structrp png_ptr) png_ptr->mode |= PNG_HAVE_IEND; } +#if defined(PNG_WRITE_gAMA_SUPPORTED) || defined(PNG_WRITE_cHRM_SUPPORTED) +static int +png_save_int_31(png_structrp png_ptr, png_bytep buf, png_int_32 i) + /* Save a signed value as a PNG unsigned value; the argument is required to + * be in the range 0..0x7FFFFFFFU. If not a *warning* is produced and false + * is returned. Because this is only called from png_write_cHRM_fixed and + * png_write_gAMA_fixed below this is safe (we don't need either chunk, + * particularly if the value is bogus.) + * + * The warning is png_app_error; it may return if the app tells it to but the + * app can have it error out. JB 20150821: I believe the checking in png.c + * actually makes this error impossible, but this is safe. + */ +{ + if (i >= 0 && i <= 0x7FFFFFFF) + { + png_save_uint_32(buf, (png_uint_32)/*SAFE*/i); + return 1; + } + + else + { + png_chunk_report(png_ptr, "negative value in cHRM or gAMA", + PNG_CHUNK_WRITE_ERROR); + return 0; + } +} +#endif /* WRITE_gAMA || WRITE_cHRM */ + #ifdef PNG_WRITE_gAMA_SUPPORTED /* Write a gAMA chunk */ void /* PRIVATE */ @@ -1166,8 +1165,8 @@ png_write_gAMA_fixed(png_structrp png_ptr, png_fixed_point file_gamma) png_debug(1, "in png_write_gAMA"); /* file_gamma is saved in 1/100,000ths */ - png_save_uint_32(buf, (png_uint_32)file_gamma); - png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); + if (png_save_int_31(png_ptr, buf, file_gamma)) + png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); } #endif @@ -1259,9 +1258,6 @@ png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) png_size_t entry_size = (spalette->depth == 8 ? 6 : 10); png_size_t palette_size = entry_size * spalette->nentries; png_sPLT_entryp ep; -#ifndef PNG_POINTER_INDEXING_SUPPORTED - int i; -#endif png_debug(1, "in png_write_sPLT"); @@ -1279,7 +1275,6 @@ png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) png_write_chunk_data(png_ptr, &spalette->depth, 1); /* Loop through each palette entry, writing appropriately */ -#ifdef PNG_POINTER_INDEXING_SUPPORTED for (ep = spalette->entries; epentries + spalette->nentries; ep++) { if (spalette->depth == 8) @@ -1302,31 +1297,6 @@ png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) png_write_chunk_data(png_ptr, entrybuf, entry_size); } -#else - ep=spalette->entries; - for (i = 0; i>spalette->nentries; i++) - { - if (spalette->depth == 8) - { - entrybuf[0] = png_check_byte(png_ptr, ep[i].red); - entrybuf[1] = png_check_byte(png_ptr, ep[i].green); - entrybuf[2] = png_check_byte(png_ptr, ep[i].blue); - entrybuf[3] = png_check_byte(png_ptr, ep[i].alpha); - png_save_uint_16(entrybuf + 4, ep[i].frequency); - } - - else - { - png_save_uint_16(entrybuf + 0, ep[i].red); - png_save_uint_16(entrybuf + 2, ep[i].green); - png_save_uint_16(entrybuf + 4, ep[i].blue); - png_save_uint_16(entrybuf + 6, ep[i].alpha); - png_save_uint_16(entrybuf + 8, ep[i].frequency); - } - - png_write_chunk_data(png_ptr, entrybuf, entry_size); - } -#endif png_write_chunk_end(png_ptr); } @@ -1345,16 +1315,15 @@ png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type) /* Make sure we don't depend upon the order of PNG_COLOR_8 */ if ((color_type & PNG_COLOR_MASK_COLOR) != 0) { - png_byte maxbits; + unsigned int maxbits; - maxbits = png_check_byte(png_ptr, color_type==PNG_COLOR_TYPE_PALETTE ? 8 : - png_ptr->usr_bit_depth); + maxbits = color_type==PNG_COLOR_TYPE_PALETTE ? 8 : png_ptr->bit_depth; if (sbit->red == 0 || sbit->red > maxbits || sbit->green == 0 || sbit->green > maxbits || sbit->blue == 0 || sbit->blue > maxbits) { - png_warning(png_ptr, "Invalid sBIT depth specified"); + png_app_error(png_ptr, "Invalid sBIT depth specified"); return; } @@ -1366,9 +1335,9 @@ png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type) else { - if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) + if (sbit->gray == 0 || sbit->gray > png_ptr->bit_depth) { - png_warning(png_ptr, "Invalid sBIT depth specified"); + png_app_error(png_ptr, "Invalid sBIT depth specified"); return; } @@ -1378,9 +1347,9 @@ png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type) if ((color_type & PNG_COLOR_MASK_ALPHA) != 0) { - if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) + if (sbit->alpha == 0 || sbit->alpha > png_ptr->bit_depth) { - png_warning(png_ptr, "Invalid sBIT depth specified"); + png_app_error(png_ptr, "Invalid sBIT depth specified"); return; } @@ -1401,19 +1370,15 @@ png_write_cHRM_fixed(png_structrp png_ptr, const png_xy *xy) png_debug(1, "in png_write_cHRM"); /* Each value is saved in 1/100,000ths */ - png_save_int_32(buf, xy->whitex); - png_save_int_32(buf + 4, xy->whitey); - - png_save_int_32(buf + 8, xy->redx); - png_save_int_32(buf + 12, xy->redy); - - png_save_int_32(buf + 16, xy->greenx); - png_save_int_32(buf + 20, xy->greeny); - - png_save_int_32(buf + 24, xy->bluex); - png_save_int_32(buf + 28, xy->bluey); - - png_write_complete_chunk(png_ptr, png_cHRM, buf, 32); + if (png_save_int_31(png_ptr, buf, xy->whitex) && + png_save_int_31(png_ptr, buf + 4, xy->whitey) && + png_save_int_31(png_ptr, buf + 8, xy->redx) && + png_save_int_31(png_ptr, buf + 12, xy->redy) && + png_save_int_31(png_ptr, buf + 16, xy->greenx) && + png_save_int_31(png_ptr, buf + 20, xy->greeny) && + png_save_int_31(png_ptr, buf + 24, xy->bluex) && + png_save_int_31(png_ptr, buf + 28, xy->bluey)) + png_write_complete_chunk(png_ptr, png_cHRM, buf, 32); } #endif @@ -1429,7 +1394,8 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, if (color_type == PNG_COLOR_TYPE_PALETTE) { - if (num_trans <= 0 || num_trans > png_ptr->num_palette) + affirm(num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH); + if ((unsigned int)/*SAFE*/num_trans > png_ptr->num_palette) { /* This is an error which can only be reliably detected late. */ png_app_error(png_ptr, @@ -1437,23 +1403,38 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, return; } - /* Write the chunk out as it is */ - png_write_complete_chunk(png_ptr, png_tRNS, trans_alpha, - (png_size_t)num_trans); + { +# ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + union + { + png_uint_32 u32[1]; + png_byte b8[PNG_MAX_PALETTE_LENGTH]; + } inverted_alpha; + + /* Invert the alpha channel (in tRNS) if required */ + if (png_ptr->write_invert_alpha) + { + int i; + + memcpy(inverted_alpha.b8, trans_alpha, num_trans); + + for (i=0; 4*igray >= (1 << png_ptr->bit_depth)) - { - /* This can no longer happen because it is checked in png_set_tRNS */ - png_app_error(png_ptr, - "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); - - return; - } - + affirm(tran->gray < (1 << png_ptr->bit_depth)); png_save_uint_16(buf, tran->gray); png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)2); } @@ -1464,26 +1445,12 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, png_save_uint_16(buf, tran->red); png_save_uint_16(buf + 2, tran->green); png_save_uint_16(buf + 4, tran->blue); -#ifdef PNG_WRITE_16BIT_SUPPORTED - if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]) != 0) -#else - if ((buf[0] | buf[2] | buf[4]) != 0) -#endif - { - /* Also checked in png_set_tRNS */ - png_app_error(png_ptr, - "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); - return; - } - + affirm(png_ptr->bit_depth == 8 || (buf[0] | buf[2] | buf[4]) == 0); png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)6); } - else - { - /* Checked in png_set_tRNS */ - png_app_error(png_ptr, "Can't write tRNS with an alpha channel"); - } + else /* Already checked in png_set_tRNS */ + impossible("invalid tRNS"); } #endif @@ -1499,13 +1466,13 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) if (color_type == PNG_COLOR_TYPE_PALETTE) { if ( -#ifdef PNG_MNG_FEATURES_SUPPORTED - (png_ptr->num_palette != 0 || - (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0) && -#endif +# ifdef PNG_MNG_FEATURES_SUPPORTED + (png_ptr->num_palette != 0 || + (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0) && +# endif /* MNG_FEATURES */ back->index >= png_ptr->num_palette) { - png_warning(png_ptr, "Invalid background palette index"); + png_app_error(png_ptr, "Invalid background palette index"); return; } @@ -1524,7 +1491,7 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) if ((buf[0] | buf[2] | buf[4]) != 0) #endif { - png_warning(png_ptr, + png_app_error(png_ptr, "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); return; @@ -1537,7 +1504,7 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) { if (back->gray >= (1 << png_ptr->bit_depth)) { - png_warning(png_ptr, + png_app_error(png_ptr, "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); return; @@ -1772,7 +1739,39 @@ png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key, png_write_chunk_end(png_ptr); } -#endif +#endif /* WRITE_iTXt */ + +#if defined(PNG_WRITE_oFFs_SUPPORTED) ||\ + defined(PNG_WRITE_pCAL_SUPPORTED) +/* PNG signed integers are saved in 32-bit 2's complement format. ANSI C-90 + * defines a cast of a signed integer to an unsigned integer either to preserve + * the value, if it is positive, or to calculate: + * + * (UNSIGNED_MAX+1) + integer + * + * Where UNSIGNED_MAX is the appropriate maximum unsigned value, so when the + * negative integral value is added the result will be an unsigned value + * correspnding to the 2's complement representation. + */ +static int +save_int_32(png_structrp png_ptr, png_bytep buf, png_int_32 j) +{ + png_uint_32 i = 0xFFFFFFFFU & (png_uint_32)/*SAFE & CORRECT*/j; + + if (i != 0x80000000U/*value not permitted*/) + { + png_save_uint_32(buf, i); + return 1; + } + + else + { + png_chunk_report(png_ptr, "invalid value in oFFS or pCAL", + PNG_CHUNK_WRITE_ERROR); + return 0; + } +} +#endif /* WRITE_oFFs || WRITE_pCAL */ #ifdef PNG_WRITE_oFFs_SUPPORTED /* Write the oFFs chunk */ @@ -1787,13 +1786,18 @@ png_write_oFFs(png_structrp png_ptr, png_int_32 x_offset, png_int_32 y_offset, if (unit_type >= PNG_OFFSET_LAST) png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); - png_save_int_32(buf, x_offset); - png_save_int_32(buf + 4, y_offset); - buf[8] = png_check_byte(png_ptr, unit_type); - - png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9); + if (save_int_32(png_ptr, buf, x_offset) && + save_int_32(png_ptr, buf + 4, y_offset)) + { + /* unit type is 0 or 1, this has been checked already so the following + * is safe: + */ + buf[8] = unit_type != 0; + png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9); + } } -#endif +#endif /* WRITE_oFFs */ + #ifdef PNG_WRITE_pCAL_SUPPORTED /* Write the pCAL chunk (described in the PNG extensions document) */ void /* PRIVATE */ @@ -1802,11 +1806,9 @@ png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, png_charpp params) { png_uint_32 purpose_len; - png_size_t units_len, total_len; - png_size_tp params_len; + size_t units_len; png_byte buf[10]; png_byte new_purpose[80]; - int i; png_debug1(1, "in png_write_pCAL (%d parameters)", nparams); @@ -1823,41 +1825,44 @@ png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, png_debug1(3, "pCAL purpose length = %d", (int)purpose_len); units_len = strlen(units) + (nparams == 0 ? 0 : 1); png_debug1(3, "pCAL units length = %d", (int)units_len); - total_len = purpose_len + units_len + 10; - params_len = (png_size_tp)png_malloc(png_ptr, - (png_alloc_size_t)(nparams * (sizeof (png_size_t)))); - - /* Find the length of each parameter, making sure we don't count the - * null terminator for the last parameter. - */ - for (i = 0; i < nparams; i++) + if (save_int_32(png_ptr, buf, X0) && + save_int_32(png_ptr, buf + 4, X1)) { - params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1); - png_debug2(3, "pCAL parameter %d length = %lu", i, - (unsigned long)params_len[i]); - total_len += params_len[i]; + png_size_tp params_len = png_voidcast(png_size_tp, + png_malloc(png_ptr, nparams * sizeof (png_size_t))); + int i; + size_t total_len = purpose_len + units_len + 10; + + /* Find the length of each parameter, making sure we don't count the + * null terminator for the last parameter. + */ + for (i = 0; i < nparams; i++) + { + params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu", i, + (unsigned long)params_len[i]); + total_len += params_len[i]; + } + + png_debug1(3, "pCAL total length = %d", (int)total_len); + png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, new_purpose, purpose_len); + buf[8] = png_check_byte(png_ptr, type); + buf[9] = png_check_byte(png_ptr, nparams); + png_write_chunk_data(png_ptr, buf, (png_size_t)10); + png_write_chunk_data(png_ptr, (png_const_bytep)units, + (png_size_t)units_len); + + for (i = 0; i < nparams; i++) + png_write_chunk_data(png_ptr, (png_const_bytep)params[i], + params_len[i]); + + png_free(png_ptr, params_len); + png_write_chunk_end(png_ptr); } - - png_debug1(3, "pCAL total length = %d", (int)total_len); - png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len); - png_write_chunk_data(png_ptr, new_purpose, purpose_len); - png_save_int_32(buf, X0); - png_save_int_32(buf + 4, X1); - buf[8] = png_check_byte(png_ptr, type); - buf[9] = png_check_byte(png_ptr, nparams); - png_write_chunk_data(png_ptr, buf, (png_size_t)10); - png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len); - - for (i = 0; i < nparams; i++) - { - png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]); - } - - png_free(png_ptr, params_len); - png_write_chunk_end(png_ptr); } -#endif +#endif /* WRITE_pCAL */ #ifdef PNG_WRITE_sCAL_SUPPORTED /* Write the sCAL chunk */ @@ -1941,583 +1946,235 @@ png_write_tIME(png_structrp png_ptr, png_const_timep mod_time) } #endif -#ifdef PNG_WRITE_FILTER_SUPPORTED -void /* PRIVATE */ -png_write_alloc_filter_row_buffers(png_structrp png_ptr, int filters) - /* Allocate row buffers for any filters that need them. This is also called - * from png_set_filter if the filters are changed during write to ensure that - * the required buffers exist. png_set_filter ensures that up/avg/paeth are - * only set if png_ptr->prev_row is allocated. - */ -{ - /* The buffer size is determined just by the output row size, not any - * processing requirements. - */ - png_alloc_size_t buf_size = png_ptr->rowbytes + 1; - - if (((filters & (PNG_FILTER_SUB | PNG_FILTER_UP | PNG_FILTER_AVG | - PNG_FILTER_PAETH)) != 0) && png_ptr->try_row == NULL) - { - int num_filters = 0; - - png_ptr->try_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); - - if (filters & PNG_FILTER_SUB) - num_filters++; - - if (filters & PNG_FILTER_UP) - num_filters++; - - if (filters & PNG_FILTER_AVG) - num_filters++; - - if (filters & PNG_FILTER_PAETH) - num_filters++; - - if (num_filters > 1) - png_ptr->tst_row = png_voidcast(png_bytep, - png_malloc(png_ptr, buf_size)); - } -} -#endif /* WRITE_FILTER */ - -/* Initializes the row writing capability of libpng */ -void /* PRIVATE */ -png_write_start_row(png_structrp png_ptr) -{ -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - - /* Start of interlace block */ - static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - /* Start of interlace block in the y direction */ - static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; - - /* Offset to next interlace block in the y direction */ - static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; -#endif - -#ifdef PNG_WRITE_FILTER_SUPPORTED - int filters; -#endif - - png_alloc_size_t buf_size; - unsigned int usr_pixel_depth; - - png_debug(1, "in png_write_start_row"); - - if (png_ptr == NULL) - return; - - usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth; - buf_size = PNG_ROWBYTES(usr_pixel_depth, png_ptr->width) + 1; - - /* 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 = usr_pixel_depth; - - /* Set up row buffer */ - png_ptr->row_buf = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); - - png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; - -#ifdef PNG_WRITE_FILTER_SUPPORTED - filters = png_ptr->do_filter; - - if (png_ptr->height == 1) - filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); - - if (png_ptr->width == 1) - filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH); - - if (filters == 0) - filters = PNG_FILTER_NONE; - - /* We only need to keep the previous row if we are using one of the following - * filters. - */ - if ((filters & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) != 0) - png_ptr->prev_row = png_voidcast(png_bytep, png_calloc(png_ptr, - buf_size)); - - png_write_alloc_filter_row_buffers(png_ptr, filters); - - /* in case it was changed above */ - png_ptr->do_filter = png_check_byte(png_ptr, filters); -#else - png_ptr->do_filter = PNG_FILTER_NONE; -#endif /* WRITE_FILTER */ - -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* If interlaced, we need to set up width and height of pass */ - if (png_ptr->interlaced != 0) - { - if ((png_ptr->transformations & PNG_INTERLACE) == 0) - { - png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - - png_pass_ystart[0]) / png_pass_yinc[0]; - - png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - - png_pass_start[0]) / png_pass_inc[0]; - } - - else - { - png_ptr->num_rows = png_ptr->height; - png_ptr->usr_width = png_ptr->width; - } - } - - else -#endif - { - png_ptr->num_rows = png_ptr->height; - png_ptr->usr_width = png_ptr->width; - } -} - -/* Internal use only. Called when finished processing a row of data. */ -void /* PRIVATE */ -png_write_finish_row(png_structrp png_ptr) -{ -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - - /* Start of interlace block */ - static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - /* Start of interlace block in the y direction */ - static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; - - /* Offset to next interlace block in the y direction */ - static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; -#endif - - png_debug(1, "in png_write_finish_row"); - - /* Next row */ - png_ptr->row_number++; - - /* See if we are done */ - if (png_ptr->row_number < png_ptr->num_rows) - return; - -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* If interlaced, go to next pass */ - if (png_ptr->interlaced != 0) - { - png_ptr->row_number = 0; - if ((png_ptr->transformations & PNG_INTERLACE) != 0) - { - png_ptr->pass++; - } - - else - { - /* Loop until we find a non-zero width or height pass */ - do - { - png_ptr->pass++; - - if (png_ptr->pass >= 7) - break; - - png_ptr->usr_width = (png_ptr->width + - png_pass_inc[png_ptr->pass] - 1 - - png_pass_start[png_ptr->pass]) / - png_pass_inc[png_ptr->pass]; - - png_ptr->num_rows = (png_ptr->height + - png_pass_yinc[png_ptr->pass] - 1 - - png_pass_ystart[png_ptr->pass]) / - png_pass_yinc[png_ptr->pass]; - - if ((png_ptr->transformations & PNG_INTERLACE) != 0) - break; - - } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); - - } - - /* Reset the row above the image for the next pass */ - if (png_ptr->pass < 7) - { - if (png_ptr->prev_row != NULL) - memset(png_ptr->prev_row, 0, - (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* - png_ptr->usr_bit_depth, png_ptr->width)) + 1); - - return; - } - } -#endif - - /* If we get here, we've just written the last row, so we need - to flush the compressor */ - png_compress_IDAT(png_ptr, NULL, 0, Z_FINISH); -} - -#ifdef PNG_WRITE_INTERLACING_SUPPORTED -/* Pick out the correct pixels for the interlace pass. - * The basic idea here is to go through the row with a source - * pointer and a destination pointer (sp and dp), and copy the - * correct pixels for the pass. As the row gets compacted, - * sp will always be >= dp, so we should never overwrite anything. - * See the default: case for the easiest code to understand. - */ -void /* PRIVATE */ -png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) -{ - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - - /* Start of interlace block */ - static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - png_debug(1, "in png_do_write_interlace"); - - /* We don't have to do anything on the last pass (6) */ - if (pass < 6) - { - /* Each pixel depth is handled separately */ - switch (row_info->pixel_depth) - { - case 1: - { - png_bytep sp; - png_bytep dp; - unsigned int shift; - int d; - int value; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - dp = row; - d = 0; - shift = 7; - - for (i = png_pass_start[pass]; i < row_width; - i += png_pass_inc[pass]) - { - sp = row + (png_size_t)(i >> 3); - value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; - d |= (value << shift); - - if (shift == 0) - { - shift = 7; - *dp++ = png_check_byte(0/*TODO: fixme*/, d); - d = 0; - } - - else - shift--; - - } - if (shift != 7) - *dp = png_check_byte(0/*TODO: fixme*/, d); - - break; - } - - case 2: - { - png_bytep sp; - png_bytep dp; - unsigned int shift; - int d; - int value; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - dp = row; - shift = 6; - d = 0; - - for (i = png_pass_start[pass]; i < row_width; - i += png_pass_inc[pass]) - { - sp = row + (png_size_t)(i >> 2); - value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; - d |= (value << shift); - - if (shift == 0) - { - shift = 6; - *dp++ = png_check_byte(0/*TODO: fixme*/, d); - d = 0; - } - - else - shift -= 2; - } - if (shift != 6) - *dp = png_check_byte(0/*TODO: fixme*/, d); - - break; - } - - case 4: - { - png_bytep sp; - png_bytep dp; - unsigned int shift; - int d; - int value; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - dp = row; - shift = 4; - d = 0; - for (i = png_pass_start[pass]; i < row_width; - i += png_pass_inc[pass]) - { - sp = row + (png_size_t)(i >> 1); - value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; - d |= (value << shift); - - if (shift == 0) - { - shift = 4; - *dp++ = png_check_byte(0/*TODO: fixme*/, d); - d = 0; - } - - else - shift -= 4; - } - if (shift != 4) - *dp = png_check_byte(0/*TODO: fixme*/, d); - - break; - } - - default: - { - png_bytep sp; - png_bytep dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - png_size_t pixel_bytes; - - /* Start at the beginning */ - dp = row; - - /* Find out how many bytes each pixel takes up */ - pixel_bytes = (row_info->pixel_depth >> 3); - - /* Loop through the row, only looking at the pixels that matter */ - for (i = png_pass_start[pass]; i < row_width; - i += png_pass_inc[pass]) - { - /* Find out where the original pixel is */ - sp = row + (png_size_t)i * pixel_bytes; - - /* Move the pixel */ - if (dp != sp) - memcpy(dp, sp, pixel_bytes); - - /* Next pixel */ - dp += pixel_bytes; - } - break; - } - } - /* Set new row width */ - row_info->width = (row_info->width + - png_pass_inc[pass] - 1 - - png_pass_start[pass]) / - png_pass_inc[pass]; - - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, - row_info->width); - } -} -#endif - /* This filters the row, chooses which filter to use, if it has not already * been specified by the application, and then writes the row out with the * chosen filter. */ -static void /* PRIVATE */ -png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, - png_size_t row_bytes); - #ifdef PNG_WRITE_FILTER_SUPPORTED static png_size_t /* PRIVATE */ -png_setup_sub_row(png_structrp png_ptr, const png_uint_32 bpp, - const png_size_t row_bytes, const png_size_t lmins) +png_setup_sub_row(const int bpp/*BYTES per pixel*/, + const png_alloc_size_t row_bytes, const png_size_t lmins, png_const_bytep rp, + png_bytep dp) { - png_bytep rp, dp, lp; - png_size_t i; png_size_t sum = 0; - int v; - png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB; - - for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1; i < bpp; - i++, rp++, dp++) + /* Advance one pixel, or one byte, whichever is greater: */ { - v = *dp = *rp; - sum += (v < 128) ? v : 256 - v; + int i; + + for (i = 0; i < bpp; i++, rp++, dp++) + { + int v = *dp = *rp; + sum += (v < 128) ? v : 256 - v; + } } - for (lp = png_ptr->row_buf + 1; i < row_bytes; - i++, rp++, lp++, dp++) + /* Do the 'sub' filter on the corresponding preceding byte: */ { - v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); - sum += (v < 128) ? v : 256 - v; + png_alloc_size_t i; - if (sum > lmins) /* We are already worse, don't continue. */ - break; + for (i = bpp; i < row_bytes; i++, rp++, dp++) + { + int v = *dp = PNG_BYTE(rp[0] - rp[-bpp]); + + if (lmins) + { + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + } } - return (sum); + return sum; } static png_size_t /* PRIVATE */ -png_setup_up_row(png_structrp png_ptr, const png_size_t row_bytes, - const png_size_t lmins) +png_setup_up_row(const png_alloc_size_t row_bytes, const png_size_t lmins, + png_const_bytep rp, png_const_bytep pp, png_bytep dp) { - png_bytep rp, dp, pp; - png_size_t i; + png_alloc_size_t i; png_size_t sum = 0; - int v; - png_ptr->try_row[0] = PNG_FILTER_VALUE_UP; - - for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, - pp = png_ptr->prev_row + 1; i < row_bytes; - i++, rp++, pp++, dp++) + for (i = 0; i < row_bytes; i++, rp++, pp++, dp++) { - v = *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); - sum += (v < 128) ? v : 256 - v; + int v = *dp = PNG_BYTE(*rp - *pp); - if (sum > lmins) /* We are already worse, don't continue. */ - break; + if (lmins) + { + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } } - return (sum); + return sum; } static png_size_t /* PRIVATE */ -png_setup_avg_row(png_structrp png_ptr, const png_uint_32 bpp, - const png_size_t row_bytes, const png_size_t lmins) +png_setup_avg_row(const int bpp, const png_alloc_size_t row_bytes, + const png_size_t lmins, png_const_bytep rp, png_const_bytep pp, png_bytep dp) { - png_bytep rp, dp, pp, lp; - png_uint_32 i; png_size_t sum = 0; - int v; - png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG; - - for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, - pp = png_ptr->prev_row + 1; i < bpp; i++) { - v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + int i; - sum += (v < 128) ? v : 256 - v; + for (i = 0; i < bpp; i++, rp++, pp++, dp++) + { + unsigned int v = *dp = PNG_BYTE(*rp - *pp / 2); + sum += (v < 128) ? v : 256 - v; + } } - for (lp = png_ptr->row_buf + 1; i < row_bytes; i++) { - v = *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) - & 0xff); + png_alloc_size_t i; - sum += (v < 128) ? v : 256 - v; + for (i = bpp; i < row_bytes; i++, rp++, pp++, dp++) + { + unsigned int v = *dp = PNG_BYTE(rp[0] - (*pp + rp[-bpp]) / 2); - if (sum > lmins) /* We are already worse, don't continue. */ - break; + if (lmins) + { + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + } } - return (sum); + return sum; } static png_size_t /* PRIVATE */ -png_setup_paeth_row(png_structrp png_ptr, const png_uint_32 bpp, - const png_size_t row_bytes, const png_size_t lmins) +png_setup_paeth_row(const int bpp, const png_alloc_size_t row_bytes, + const png_size_t lmins, png_const_bytep rp, png_const_bytep pp, png_bytep dp) { - png_bytep rp, dp, pp, cp, lp; - png_size_t i; png_size_t sum = 0; - int v; - png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH; - - for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, - pp = png_ptr->prev_row + 1; i < bpp; i++) { - v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + int i; - sum += (v < 128) ? v : 256 - v; + for (i = 0; i < bpp; i++, dp++, rp++, pp++) + { + int v = *dp = PNG_BYTE(*rp - *pp); /* UP for the first pixel */ + sum += (v < 128) ? v : 256 - v; + } } - for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes; - i++) { - int a, b, c, pa, pb, pc, p; + png_alloc_size_t i; - b = *pp++; - c = *cp++; - a = *lp++; + for (i = bpp; i < row_bytes; i++, pp++, rp++, dp++) + { + int a, b, c, pa, pb, pc, p, v; - p = b - c; - pc = a - c; + b = pp[0]; + c = pp[-bpp]; + a = rp[-bpp]; + + p = b - c; + pc = a - c; #ifdef PNG_USE_ABS - pa = abs(p); - pb = abs(pc); - pc = abs(p + pc); + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); #else - pa = p < 0 ? -p : p; - pb = pc < 0 ? -pc : pc; - pc = (p + pc) < 0 ? -(p + pc) : p + pc; + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; #endif - p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + v = *dp = PNG_BYTE(*rp - p); - v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + if (lmins) + { + sum += (v < 128) ? v : 256 - v; - sum += (v < 128) ? v : 256 - v; - - if (sum > lmins) /* We are already worse, don't continue. */ - break; + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + } } - return (sum); + return sum; } -#endif /* WRITE_FILTER */ - -void /* PRIVATE */ -png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) +static png_bytep +test_buffer(png_structrp png_ptr, png_const_bytep in_use) + /* Return an output row sized buffer from the two available, but never the + * one pointed to by 'in_use'. Dynamically allocates buffers as required. + * The buffers are always big enough for a full output row because they are + * retained until the whole image has been written. + */ { -#ifndef PNG_WRITE_FILTER_SUPPORTED - png_write_filtered_row(png_ptr, png_ptr->row_buf, row_info->rowbytes+1); -#else - png_byte filter_to_do = png_ptr->do_filter; - png_bytep row_buf; - png_bytep best_row; - png_uint_32 bpp; + int n = 0; + png_bytep p = png_ptr->write_row[n]; + + debug(in_use != NULL); + + if (p == in_use) + p = png_ptr->write_row[++n]; + + if (p == NULL) + png_ptr->write_row[n] = p = png_voidcast(png_bytep, png_malloc(png_ptr, + PNG_ROWBYTES(PNG_PIXEL_DEPTH(*png_ptr), png_ptr->width))); + + return p; +} + +png_const_bytep /* PRIVATE */ +png_write_filter_row(png_structrp png_ptr, png_const_bytep row_buf, + int first_pass_row, png_const_bytep prev_row, png_alloc_size_t row_bytes, + unsigned int bpp, png_bytep filter_byte) +{ + png_const_bytep best_row; png_size_t mins; - png_size_t row_bytes = row_info->rowbytes; + unsigned int filter_to_do = png_ptr->next_filter; + png_byte best_filter; png_debug(1, "in png_write_find_filter"); - /* Find out how many bytes offset each pixel is */ - bpp = (row_info->pixel_depth + 7) >> 3; + /* API CHANGE: 1.7.0: previously it was possible to select AVG, PAETH and UP + * on the first row, but this complicates the code. In practice with the + * default settings only AVG was tried because 'UP' is the same as 'NONE' and + * 'PAETH' is the same as 'SUB' + * + * There are two cases; the row at the start of pass and the case where + * the application set the filters so that libpng did not save the + * previous row. The first case switches the filters, the second just + * clears them from the list. + */ + if (first_pass_row) + { + if (filter_to_do & PNG_FILTER_UP) filter_to_do |= PNG_FILTER_NONE; + if (filter_to_do & PNG_FILTER_PAETH) filter_to_do |= PNG_FILTER_SUB; + /* AVG not handled */ + } + + if (first_pass_row || prev_row == NULL) + filter_to_do &= + PNG_BIC_MASK(PNG_FILTER_UP+PNG_FILTER_AVG+PNG_FILTER_PAETH); + + /* A second optimization is possible for narrow images. If an interlaced + * image is 5-12 pixels wide pass 2 will have only one column. 1bpp + * grayscale images 8 pixels or less wide only have one byte per row (and the + * filter acts on the bytes for low bit depth images.) 1bpp grayscale + * interlaced images will only have 1 byte in early passes (1 and 2) when the + * image is 64 or fewer pixels wide. In these cases the 'SUB' filter reduces + * to 'NONE' + */ + if (row_bytes <= bpp && (filter_to_do & PNG_FILTER_SUB) != 0) + { + filter_to_do |= PNG_FILTER_NONE; + filter_to_do &= PNG_BIC_MASK(PNG_FILTER_SUB); + } - row_buf = png_ptr->row_buf; mins = PNG_SIZE_MAX - 256/* so we can detect potential overflow of the running sum */; @@ -2545,23 +2202,29 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) * (i.e., ~ root-mean-square approach) */ - /* We don't need to test the 'no filter' case if this is the only filter * that has been chosen, as it doesn't actually do anything to the data. */ - best_row = png_ptr->row_buf; - - - if ((filter_to_do & PNG_FILTER_NONE) != 0 && filter_to_do != PNG_FILTER_NONE) + if (filter_to_do <= PNG_FILTER_NONE) /* no filters to chose from */ { - png_bytep rp; + *filter_byte = PNG_FILTER_VALUE_NONE; + return row_buf; + } + + best_row = row_buf; + best_filter = PNG_FILTER_VALUE_NONE; + + /* TODO: make this into a loop to avoid all the code replication */ + if ((filter_to_do & PNG_FILTER_NONE) != 0) + { + png_const_bytep rp; png_size_t sum = 0; png_size_t i; int v; if (PNG_SIZE_MAX/128 <= row_bytes) { - for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) + for (i = 0, rp = row_buf; i < row_bytes; i++, rp++) { /* Check for overflow */ if (sum > PNG_SIZE_MAX/128 - 256) @@ -2573,7 +2236,7 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) } else /* Overflow is not possible */ { - for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) + for (i = 0, rp = row_buf; i < row_bytes; i++, rp++) { v = *rp; sum += (v < 128) ? v : 256 - v; @@ -2587,145 +2250,105 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) if (filter_to_do == PNG_FILTER_SUB) /* It's the only filter so no testing is needed */ { - (void) png_setup_sub_row(png_ptr, bpp, row_bytes, mins); - best_row = png_ptr->try_row; + png_bytep tst_row = test_buffer(png_ptr, best_row); + (void)png_setup_sub_row(bpp, row_bytes, 0, row_buf, tst_row); + best_row = tst_row; + best_filter = PNG_FILTER_VALUE_SUB; } else if ((filter_to_do & PNG_FILTER_SUB) != 0) { png_size_t sum; - png_size_t lmins = mins; - sum = png_setup_sub_row(png_ptr, bpp, row_bytes, lmins); + png_bytep tst_row = test_buffer(png_ptr, best_row); + + sum = png_setup_sub_row(bpp, row_bytes, mins, row_buf, tst_row); if (sum < mins) { mins = sum; - best_row = png_ptr->try_row; - if (png_ptr->tst_row != NULL) - { - png_ptr->try_row = png_ptr->tst_row; - png_ptr->tst_row = best_row; - } + best_row = tst_row; + best_filter = PNG_FILTER_VALUE_SUB; } } /* Up filter */ if (filter_to_do == PNG_FILTER_UP) { - (void) png_setup_up_row(png_ptr, row_bytes, mins); - best_row = png_ptr->try_row; + png_bytep tst_row = test_buffer(png_ptr, best_row); + (void)png_setup_up_row(row_bytes, 0, row_buf, prev_row, tst_row); + best_row = tst_row; + best_filter = PNG_FILTER_VALUE_UP; } else if ((filter_to_do & PNG_FILTER_UP) != 0) { png_size_t sum; - png_size_t lmins = mins; - sum = png_setup_up_row(png_ptr, row_bytes, lmins); + png_bytep tst_row = test_buffer(png_ptr, best_row); + + sum = png_setup_up_row(row_bytes, mins, row_buf, prev_row, tst_row); if (sum < mins) { mins = sum; - best_row = png_ptr->try_row; - if (png_ptr->tst_row != NULL) - { - png_ptr->try_row = png_ptr->tst_row; - png_ptr->tst_row = best_row; - } + best_row = tst_row; + best_filter = PNG_FILTER_VALUE_UP; } } /* Avg filter */ if (filter_to_do == PNG_FILTER_AVG) { - (void) png_setup_avg_row(png_ptr, bpp, row_bytes, mins); - best_row = png_ptr->try_row; + png_bytep tst_row = test_buffer(png_ptr, best_row); + (void)png_setup_avg_row(bpp, row_bytes, 0, row_buf, prev_row, tst_row); + best_row = tst_row; + best_filter = PNG_FILTER_VALUE_AVG; } else if ((filter_to_do & PNG_FILTER_AVG) != 0) { png_size_t sum; - png_size_t lmins = mins; + png_bytep tst_row = test_buffer(png_ptr, best_row); - sum= png_setup_avg_row(png_ptr, bpp, row_bytes, lmins); + sum = png_setup_avg_row(bpp, row_bytes, mins, row_buf, prev_row, + tst_row); if (sum < mins) { mins = sum; - best_row = png_ptr->try_row; - if (png_ptr->tst_row != NULL) - { - png_ptr->try_row = png_ptr->tst_row; - png_ptr->tst_row = best_row; - } + best_row = tst_row; + best_filter = PNG_FILTER_VALUE_AVG; } } /* Paeth filter */ - if ((filter_to_do == PNG_FILTER_PAETH) != 0) + if (filter_to_do == PNG_FILTER_PAETH) { - (void) png_setup_paeth_row(png_ptr, bpp, row_bytes, mins); - best_row = png_ptr->try_row; + png_bytep tst_row = test_buffer(png_ptr, best_row); + (void)png_setup_paeth_row(bpp, row_bytes, 0, row_buf, prev_row, tst_row); + best_row = tst_row; + best_filter = PNG_FILTER_VALUE_PAETH; } else if ((filter_to_do & PNG_FILTER_PAETH) != 0) { png_size_t sum; - png_size_t lmins = mins; + png_bytep tst_row = test_buffer(png_ptr, best_row); - sum = png_setup_paeth_row(png_ptr, bpp, row_bytes, lmins); + sum = png_setup_paeth_row(bpp, row_bytes, mins, row_buf, prev_row, + tst_row); if (sum < mins) { mins = sum; - best_row = png_ptr->try_row; - if (png_ptr->tst_row != NULL) - { - png_ptr->try_row = png_ptr->tst_row; - png_ptr->tst_row = best_row; - } + best_row = tst_row; + best_filter = PNG_FILTER_VALUE_PAETH; } } /* Do the actual writing of the filtered row data from the chosen filter. */ - png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1); -#endif /* WRITE_FILTER */ + affirm(best_filter < PNG_FILTER_VALUE_LAST); + *filter_byte = best_filter; + return best_row; } - - -/* Do the actual writing of a previously filtered row. */ -static void -png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, - png_size_t full_row_length/*includes filter byte*/) -{ - png_debug(1, "in png_write_filtered_row"); - - png_debug1(2, "filter = %d", filtered_row[0]); - - png_compress_IDAT(png_ptr, filtered_row, full_row_length, Z_NO_FLUSH); - -#ifdef PNG_WRITE_FILTER_SUPPORTED - /* Swap the current and previous rows */ - if (png_ptr->prev_row != NULL) - { - png_bytep tptr; - - tptr = png_ptr->prev_row; - png_ptr->prev_row = png_ptr->row_buf; - png_ptr->row_buf = tptr; - } #endif /* WRITE_FILTER */ - - /* Finish row - updates counters and flushes zlib if last row */ - png_write_finish_row(png_ptr); - -#ifdef PNG_WRITE_FLUSH_SUPPORTED - png_ptr->flush_rows++; - - if (png_ptr->flush_dist > 0 && - png_ptr->flush_rows >= png_ptr->flush_dist) - { - png_write_flush(png_ptr); - } -#endif /* WRITE_FLUSH */ -} #endif /* WRITE */ diff --git a/scripts/chunkdesc.h b/scripts/chunkdesc.h new file mode 100644 index 000000000..e3deab6b4 --- /dev/null +++ b/scripts/chunkdesc.h @@ -0,0 +1,39 @@ +/* PNG CHUNK DESCRIPTIONS. + * + * If this list is changed in any way scripts/chunkhash.c must be rebuilt and + * run to regenerate the lookup functions for the tables described from this + * list. + * + * IDAT MUST be first in the list; it must have index '0'. The order of the + * remaining chunks comes from section 5.6 "Chunk ordering" in the ISO spec + * plus the ordering rules in the PNG extensions documnet. + * + * Keep PNG_CHUNK_BEGIN and PNG_CHUNK_END at the beginning and end. + */ +PNG_CHUNK_BEGIN(IDAT, 73, 68, 65, 84, within_IDAT, after_start) +PNG_CHUNK( IHDR, 73, 72, 68, 82, before_start, at_start) +PNG_CHUNK( cHRM, 99, 72, 82, 77, before_PLTE, after_start) +PNG_CHUNK( gAMA, 103, 65, 77, 65, before_PLTE, after_start) +PNG_CHUNK( iCCP, 105, 67, 67, 80, before_PLTE, after_start) +PNG_CHUNK( sBIT, 115, 66, 73, 84, before_PLTE, after_start) +PNG_CHUNK( sRGB, 115, 82, 71, 66, before_PLTE, after_start) +PNG_CHUNK( PLTE, 80, 76, 84, 69, before_PLTE, after_start) +PNG_CHUNK( bKGD, 98, 75, 71, 68, before_IDAT, after_PLTE) +PNG_CHUNK( hIST, 104, 73, 83, 84, before_IDAT, after_PLTE) +PNG_CHUNK( tRNS, 116, 82, 78, 83, before_IDAT, after_PLTE) +PNG_CHUNK( oFFs, 111, 70, 70, 115, before_IDAT, after_start) +PNG_CHUNK( pCAL, 112, 67, 65, 76, before_IDAT, after_start) +PNG_CHUNK( sCAL, 115, 67, 65, 76, before_IDAT, after_start) +PNG_CHUNK( sTER, 115, 84, 69, 82, before_IDAT, after_start) +PNG_CHUNK( pHYs, 112, 72, 89, 115, before_IDAT, after_start) +PNG_CHUNK( sPLT, 115, 80, 76, 84, before_IDAT, after_start) +PNG_CHUNK( tIME, 116, 73, 77, 69, before_end, after_start) +PNG_CHUNK( iTXt, 105, 84, 88, 116, before_end, after_start) +PNG_CHUNK( tEXt, 116, 69, 88, 116, before_end, after_start) +PNG_CHUNK( zTXt, 122, 84, 88, 116, before_end, after_start) +PNG_CHUNK( fRAc, 102, 82, 65, 99, before_end, after_start) +PNG_CHUNK( gIFg, 103, 73, 70, 103, before_end, after_start) +PNG_CHUNK( gIFt, 103, 73, 70, 116, before_end, after_start) +PNG_CHUNK( gIFx, 103, 73, 70, 120, before_end, after_start) +PNG_CHUNK( dSIG, 100, 83, 73, 71, before_end, after_start) +PNG_CHUNK_END( IEND, 73, 69, 78, 68, before_end, after_IDAT) diff --git a/scripts/chunkhash.c b/scripts/chunkhash.c new file mode 100644 index 000000000..98a6153b1 --- /dev/null +++ b/scripts/chunkhash.c @@ -0,0 +1,245 @@ +#include +#include +#include +#include + +#include "pnglibconf.h.prebuilt" +#include "../pngpriv.h" + +static const struct +{ + png_uint_32 name; +} +png_known_chunks[] = +/* See scripts/chunkdesc.h for how this works: */ +#define PNG_CHUNK_END(n, c1, c2, c3, c4, before, after)\ + { png_ ##n } +#define PNG_CHUNK(n, c1, c2, c3, c4, before, after)\ + PNG_CHUNK_END(n, c1, c2, c3, c4, before, after), +#define PNG_CHUNK_BEGIN(n, c1, c2, c3, c4, before, after)\ + PNG_CHUNK_END(n, c1, c2, c3, c4, before, after), +{ +# include "chunkdesc.h" +}; + +#define C_KNOWN ((sizeof png_known_chunks)/(sizeof png_known_chunks[0])) + +static unsigned int +index_of(png_uint_32 name) +{ + unsigned int i; + + if (name == 0) + return 0; + + for (i=0; i> shift),n += n >> s1),n += n >> s2)) + +inline unsigned int hash(png_uint_32 name, unsigned int shift, unsigned int s1, + unsigned int s2, unsigned int s3, unsigned int s4, unsigned int s5) +{ + /* Running the search gives (shift,s1,s2) (2,8,16) */ + //return PNG_CHUNK_HASH(name, shift, s1, s2, s3, s4, s5); + name += name >> shift; + name += name >> s1; + name += name >> s2; + //name += name >> s3; + return 0x3f & name; + /*return 0x3f & ((name) + ( + ((name >> bitorder[s1]) & 0x01) + + ((name >> (bitorder[s2]-1)) & 0x02) + + ((name >> (bitorder[s3]-2)) & 0x04) + + ((name >> (bitorder[s4]-3)) & 0x08) + + ((name >> (bitorder[s5]-4)) & 0x10)));*/ +} + +int main(void) { + unsigned int s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0; + unsigned int shift = 0; + png_uint_32 mask; + unsigned int bitcount; + unsigned int mineq; + png_uint_32 sarray; + unsigned int shift_save; + png_uint_32 reverse_index_save[64]; + + assert(C_KNOWN <= 64); + + /* Check IDAT: */ + assert(index_of(png_IDAT) == 0); + + /* Build a mask of all the bits that differ in at least one of the known + * names. + */ + { + png_uint_32 set, unset; + int i; + + for (i=0, set=unset=0; i> j) & 1) + ++ones[j]; + + if ((mask >> (31-j)) & 1) + b[k++] = ((name >> (31-j)) & 1) ? 'o' : ' '; + } + + b[k] = 0; + + //printf("%s: %s\n", s, b); + } + + memset(bitorder, 0, sizeof bitorder); + bitcount = 0; + + for (i=0; i>1; + unsigned int hi = (C_KNOWN + i)>>1; + int j; + + for (j=0; j<32; ++j) if (ones[j] == lo || ones[j] == hi) + { + //printf(" %2d,", j); + bitorder[bitcount++] = j; + } + } + + //printf("\nbitcount=%u, C_KNOWN=%lu\n", bitcount, C_KNOWN); + } + + /* s? are masks to exclude bits from the hash, one for each byte: */ + mineq = C_KNOWN; + sarray = 0; + for (shift=0; shift<32; ++shift) + for (s1=0; s1<32; ++s1) + for (s2=s1+1; s2<32; ++s2) + //for (s3=s2+1; s3<32; ++s3) + //for (s4=s3+1; s4> %u),n += n >> %u),n += n >> %u)]\n\n", + shift, s1, s2); + printf("static png_byte\n" + "png_chunk_index(png_uint_32 name)\n" + "{\n" + " name += name >> %u;\n" + " name += name >> %u;\n" + " name += name >> %u;\n" + " return png_chunk_lut[name & 0x3f];\n" + "}\n", shift, s1, s2); + + return 0; + } + + if (eq < mineq) + { + mineq = eq; + sarray = s1 + bitcount * (s2 + bitcount * (s3 + bitcount * + (s4 + bitcount *s5))); + memcpy(reverse_index_save, reverse_index, sizeof reverse_index_save); + shift_save = shift; + } + } + + s1 = sarray % bitcount; + s2 = (sarray / bitcount) % bitcount; + s3 = (sarray / bitcount / bitcount) % bitcount; + s4 = (sarray / bitcount / bitcount / bitcount) % bitcount; + s5 = (sarray / bitcount / bitcount / bitcount / bitcount) % bitcount; + + printf("best: %u clashes with bits: %u, %u, %u, %u, %u, %u\n", + mineq, shift_save, s1, s2, s3, s4, s5); + + { + int i; + png_uint_32 reverse_index[64]; + + memset(reverse_index, 0, sizeof reverse_index); + + for (i=0; i for each option you -# don't want in that file in that file. You can also turn on options -# using PNG_