From 3184947a25b78dee14b0dda09f368ab3f6107c8f Mon Sep 17 00:00:00 2001 From: John Bowler Date: Mon, 14 Sep 2015 20:42:40 -0700 Subject: [PATCH] chunk handling and transform rewrite This implements a new chunk parse implementation that can be shared, it is currently shared by the progressive reader and the sequential one (not, yet, the writer). The patch also implements shared transform handling that is used throughout. Signed-off-by: John Bowler --- example.c | 8 +- png.c | 389 +- png.h | 511 +- pngdebug.h | 6 +- pngerror.c | 65 +- pngget.c | 52 +- pnginfo.h | 20 +- pngpread.c | 1567 ++--- pngpriv.h | 1363 ++-- pngread.c | 1281 ++-- pngrio.c | 42 +- pngrtran.c | 11324 +++++++++++++++++--------------- pngrutil.c | 4047 ++++++------ pngset.c | 188 +- pngstruct.h | 749 ++- pngtest.c | 53 +- pngtrans.c | 4302 +++++++++--- pngwio.c | 72 +- pngwrite.c | 1444 ++-- pngwtran.c | 802 +-- pngwutil.c | 1247 ++-- scripts/chunkdesc.h | 39 + scripts/chunkhash.c | 245 + scripts/pnglibconf.dfa | 429 +- scripts/pnglibconf.h.prebuilt | 21 +- scripts/symbols.def | 5 +- 26 files changed, 16933 insertions(+), 13338 deletions(-) create mode 100644 scripts/chunkdesc.h create mode 100644 scripts/chunkhash.c 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_