diff --git a/Makefile.am b/Makefile.am index 13b8cc002..02546d0b9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,7 +53,7 @@ TESTS =\ tests/pngvalid-gamma-threshold tests/pngvalid-gamma-transform\ tests/pngvalid-progressive-size\ tests/pngvalid-progressive-interlace-standard\ - tests/pngvalid-transform\ + tests/pngvalid-transform tests/pngvalid-interlace-transform\ tests/pngvalid-progressive-standard tests/pngvalid-standard\ tests/pngstest-1.8 tests/pngstest-1.8-alpha tests/pngstest-linear\ tests/pngstest-linear-alpha tests/pngstest-none tests/pngstest-none-alpha\ diff --git a/contrib/libtests/makepng.c b/contrib/libtests/makepng.c index 1d3a0addd..164e0ad7d 100644 --- a/contrib/libtests/makepng.c +++ b/contrib/libtests/makepng.c @@ -148,6 +148,13 @@ #include +#if PNG_LIBPNG_VER < 10700 + /* WRITE_INTERLACING was used instead of WRITE_INTERLACE prior to 1.7 */ +# ifdef PNG_WRITE_INTERLACING_SUPPORTED +# define PNG_WRITE_INTERLACE_SUPPORTED +# endif +#endif /* WRITE_INTERLACE check */ + /* Work round for GCC complaints about casting a (double) function result to * an unsigned: */ @@ -944,11 +951,11 @@ write_png(const char **name, FILE *fp, int color_type, int bit_depth, png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, filters); { -# ifdef PNG_WRITE_INTERLACING_SUPPORTED +# ifdef PNG_WRITE_INTERLACE_SUPPORTED int passes = png_set_interlace_handling(png_ptr); -# else /* !WRITE_INTERLACING */ +# else /* !WRITE_INTERLACE */ int passes = 1; -# endif /* !WRITE_INTERLACING */ +# endif /* !WRITE_INTERLACE */ int pass; png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); diff --git a/contrib/libtests/pngvalid.c b/contrib/libtests/pngvalid.c index 47a24065c..26550086a 100644 --- a/contrib/libtests/pngvalid.c +++ b/contrib/libtests/pngvalid.c @@ -120,6 +120,10 @@ typedef png_byte *png_const_bytep; /* READ_INTERLACING was used instead of READ_DEINTERLACE. */ # ifdef PNG_READ_INTERLACING_SUPPORTED # define PNG_READ_DEINTERLACE_SUPPORTED +# endif + /* WRITE_INTERLACING was used instead of WRITE_INTERLACE. */ +# ifdef PNG_WRITE_INTERLACING_SUPPORTED +# define PNG_WRITE_INTERLACE_SUPPORTED # endif #endif @@ -3545,17 +3549,18 @@ transform_row(png_const_structp pp, png_byte buffer[TRANSFORM_ROWMAX], * interlacing support. If there is no write interlacing we can't generate test * cases with interlace: */ -#ifdef PNG_WRITE_INTERLACING_SUPPORTED +#ifdef PNG_WRITE_INTERLACE_SUPPORTED # define INTERLACE_LAST PNG_INTERLACE_LAST # define check_interlace_type(type) ((void)(type)) # define set_write_interlace_handling(pp,type) png_set_interlace_handling(pp) +# define do_own_interlace 0 #elif PNG_LIBPNG_VER < 10700 # define set_write_interlace_handling(pp,type) (1) static void check_interlace_type(int const interlace_type) { /* Prior to 1.7.0 libpng does not support the write of an interlaced image - * unless PNG_WRITE_INTERLACING_SUPPORTED, even with do_interlace so the + * unless PNG_WRITE_INTERLACE_SUPPORTED, even with do_interlace so the * code here does the pixel interlace itself, so: */ if (interlace_type != PNG_INTERLACE_NONE) @@ -3568,15 +3573,66 @@ check_interlace_type(int const interlace_type) } } # define INTERLACE_LAST (PNG_INTERLACE_NONE+1) +# define do_own_interlace 0 #else /* libpng 1.7+ */ # define set_write_interlace_handling(pp,type)\ npasses_from_interlace_type(pp,type) # define check_interlace_type(type) ((void)(type)) -# define INTERLACE_LAST (PNG_INTERLACE_NONE+1) -#endif /* WRITE_INTERLACING tests */ +# define INTERLACE_LAST PNG_INTERLACE_LAST +# define do_own_interlace 1 +#endif /* WRITE_INTERLACE tests */ #define CAN_WRITE_INTERLACE\ - PNG_LIBPNG_VER >= 10700 || defined PNG_WRITE_INTERLACING_SUPPORTED + PNG_LIBPNG_VER >= 10700 || defined PNG_WRITE_INTERLACE_SUPPORTED + +/* The following two routines use the PNG interlace support macros from + * png.h to interlace or deinterlace rows. + */ +static void +interlace_row(png_bytep buffer, png_const_bytep imageRow, + unsigned int pixel_size, png_uint_32 w, int pass) +{ + png_uint_32 xin, xout, xstep; + + /* Note that this can, trivially, be optimized to a memcpy on pass 7, the + * code is presented this way to make it easier to understand. In practice + * consult the code in the libpng source to see other ways of doing this. + * + * It is OK for buffer and imageRow to be identical, because 'xin' moves + * faster than 'xout' and we copy up. + */ + xin = PNG_PASS_START_COL(pass); + xstep = 1U< 0) + interlace_row(buffer, buffer, + bit_size(pp, colour_type, bit_depth), w, pass); + else + continue; + } +# endif /* do_own_interlace */ + png_write_row(pp, buffer); } } @@ -3745,59 +3826,13 @@ make_transform_images(png_modifier *pm) char name[FILE_NAME_SIZE]; standard_name(name, sizeof name, 0, colour_type, bit_depth, - palette_number, interlace_type, 0, 0, 0); + palette_number, interlace_type, 0, 0, do_own_interlace); make_transform_image(&pm->this, colour_type, bit_depth, palette_number, interlace_type, name); } } } -/* The following two routines use the PNG interlace support macros from - * png.h to interlace or deinterlace rows. - */ -static void -interlace_row(png_bytep buffer, png_const_bytep imageRow, - unsigned int pixel_size, png_uint_32 w, int pass) -{ - png_uint_32 xin, xout, xstep; - - /* Note that this can, trivially, be optimized to a memcpy on pass 7, the - * code is presented this way to make it easier to understand. In practice - * consult the code in the libpng source to see other ways of doing this. - */ - xin = PNG_PASS_START_COL(pass); - xstep = 1U< 0) + interlace_row(buffer, buffer, + bit_size(pp, colour_type, bit_depth), w, pass); + else + continue; + } +# endif /* do_own_interlace */ + png_write_row(pp, buffer); } } @@ -4254,7 +4314,7 @@ make_errors(png_modifier* const pm, png_byte const colour_type, char name[FILE_NAME_SIZE]; standard_name(name, sizeof name, 0, colour_type, 1<bit_width)) != 0) { char msg[64]; - sprintf(msg, "display row[%lu][%d] changed from %.2x to %.2x", + sprintf(msg, "display row[%lu][%d] changed from %.2x to %.2x", (unsigned long)y, where-1, std[where-1], store_image_row(dp->ps, pp, iDisplay, y)[where-1]); png_error(pp, msg); @@ -5396,7 +5456,7 @@ test_size(png_modifier* const pm, png_byte const colour_type, # endif # endif /* READ_DEINTERLACE */ -# ifdef PNG_WRITE_INTERLACING_SUPPORTED +# ifdef PNG_WRITE_INTERLACE_SUPPORTED /* Test the libpng write side against the pngvalid read side: */ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/, PNG_INTERLACE_ADAM7, w, h, 0), 1/*do_interlace*/, @@ -5407,7 +5467,7 @@ test_size(png_modifier* const pm, png_byte const colour_type, # endif # ifdef PNG_READ_DEINTERLACE_SUPPORTED -# ifdef PNG_WRITE_INTERLACING_SUPPORTED +# ifdef PNG_WRITE_INTERLACE_SUPPORTED /* Test both together: */ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/, PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/, diff --git a/png.c b/png.c index 5fc1758b0..715488e53 100644 --- a/png.c +++ b/png.c @@ -237,6 +237,19 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, jmp_buf create_jmp_buf; # endif + /* This is a compile-type only test to ensure that the build satisifies the + * contraints for the row buffer stack allocations. A 'duplicate case + * statements' style of error means that one of the tests below failed: + */ + switch (0) + { + case 0: + case PNG_ROW_BUFFER_SIZE >= PNG_MIN_ROW_BUFFER_SIZE: /*1*/ + case 2*(PNG_ROW_BUFFER_SIZE <= PNG_MAX_ROW_BUFFER_SIZE): /*2*/ + default: + break; + } + /* This temporary stack-allocated structure is used to provide a place to * build enough context to allow the user provided memory allocator (if any) * to be called. @@ -2362,6 +2375,99 @@ png_calc_rowbytes(png_const_structrp png_ptr, unsigned int pixel_depth, return rowbytes; } +unsigned int /*PRIVATE*/ +png_max_pixel_block(png_const_structrp png_ptr) +{ + /* Need the *smallest* pixel size that must occur in alignment units. On + * read this is the PNG pixel depth because the read transforms cannot reduce + * the pixel size below the input size or 8-bits, whichever is smaller. + * + * On write the 'pack' transform can pack 8-bit pixels back to a lower bit + * depth, but the lowest bit depth is still given by the PNG pixel size. + */ + const unsigned int pixel_depth = PNG_PIXEL_DEPTH(*png_ptr); + const unsigned int pixel_block = /* count of pixels in a block */ + pixel_depth < 8U ? + PNG_ROW_BUFFER_BYTE_ALIGN * (8U/pixel_depth) : + PNG_ROW_BUFFER_BYTE_ALIGN; /* pixels may be any whole byte size */ + /* The maximum block size in bits is MAX_PIXEL_DEPTH*pixel_block so work out + * the minimum number of pixel blocks that can fit in PNG_ROW_BUFFER_SIZE + * bytes and use this to calculate the maximum number of pixels: + */ + return pixel_block * + ((8U*PNG_ROW_BUFFER_SIZE) / (png_ptr->row_max_pixel_depth*pixel_block)); +} + +void /* PRIVATE */ +png_copy_row(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp, + png_uint_32 x/*in INPUT*/, png_uint_32 width/*of INPUT*/, + unsigned int pixel_depth, int clear/*clear the final byte*/) + /* Copy the row in row_buffer; this is the non-interlaced copy used in both + * the read and write code. + */ +{ + png_alloc_size_t cb; + unsigned int remaining; /* remaining bits in a partial byte */ + + /* Copy 'cb' pixels, but take care with the last byte because it may + * be partially written. 'x' must correspond to the start of a byte, check + * that too: + */ + switch (pixel_depth) + { + case 1U: remaining = width & 7U; + debug((x & 7U) == 0U); + cb = width >> 3; + dp += x >> 3; + break; + case 2U: remaining = (width << 1) & 6U; + debug((x & 3U) == 0U); + cb = width >> 2; + dp += x >> 2; + break; + case 4U: remaining = (width << 2) & 4U; + debug((x & 1U) == 0U); + cb = width >> 1; + dp += x >> 1; + break; + case 8U: remaining = 0U; + cb = width; + dp += x; + break; + default: remaining = 0U; + cb = png_calc_rowbytes(png_ptr, pixel_depth, width); + dp += png_calc_rowbytes(png_ptr, pixel_depth, x); + break; + } + + memcpy(dp, sp, cb); + + if (remaining > 0U) + { + /* 'remaining' is the number of bits still to be copied. Format may be + * little endian; bits to copy in the bottom of 's'. Make 'remaining' + * into a mask of the bits to *preserve* in dp. + */ + if ((png_ptr->row_format & PNG_FORMAT_FLAG_SWAPPED) == 0U) + remaining = 0xffU >> remaining; + + else + remaining = 0xffU << remaining; + + /* remaining is now the bits to *keep* from the destination byte, if + * 'clear' is true the source bytes aren't copied - this is for security + * reasons to avoid copying undefined bits at the end of a row. If + * 'clear' is set the destination bits are not preserved, they are just + * set to 0. + */ + if (clear) + dp[cb] = PNG_BYTE(sp[cb] & ~remaining); + + else + dp[cb] = PNG_BYTE((sp[cb] & ~remaining) | (dp[cb] & remaining)); + } +} + void /* PRIVATE */ png_check_IHDR(png_const_structrp png_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, diff --git a/png.h b/png.h index 7d166f5b3..fc25e3e34 100644 --- a/png.h +++ b/png.h @@ -1463,7 +1463,7 @@ PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p #endif #if defined(PNG_READ_DEINTERLACE_SUPPORTED) || \ - defined(PNG_WRITE_INTERLACING_SUPPORTED) + defined(PNG_WRITE_INTERLACE_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, * otherwise it will not have the desired effect. Note that it is still @@ -1676,11 +1676,12 @@ PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, * * The resultant argument fits in a single byte. */ -#define PNG_FILTER_NONE (0x08 << PNG_FILTER_VALUE_NONE) -#define PNG_FILTER_SUB (0x08 << PNG_FILTER_VALUE_SUB) -#define PNG_FILTER_UP (0x08 << PNG_FILTER_VALUE_UP) -#define PNG_FILTER_AVG (0x08 << PNG_FILTER_VALUE_AVG) -#define PNG_FILTER_PAETH (0x08 << PNG_FILTER_VALUE_PAETH) +#define PNG_FILTER_MASK(value) (0x08 << (value)) +#define PNG_FILTER_NONE PNG_FILTER_MASK(PNG_FILTER_VALUE_NONE) +#define PNG_FILTER_SUB PNG_FILTER_MASK(PNG_FILTER_VALUE_SUB) +#define PNG_FILTER_UP PNG_FILTER_MASK(PNG_FILTER_VALUE_UP) +#define PNG_FILTER_AVG PNG_FILTER_MASK(PNG_FILTER_VALUE_AVG) +#define PNG_FILTER_PAETH PNG_FILTER_MASK(PNG_FILTER_VALUE_PAETH) /* Then two convenience values. PNG_NO_FILTERS is the same as * PNG_FILTER_VALUE_NONE, but this is harmless because they mean the same thing. @@ -2710,9 +2711,9 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, /* 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. + * is present 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)\ diff --git a/pngpread.c b/pngpread.c index 154f78fab..8343968b1 100644 --- a/pngpread.c +++ b/pngpread.c @@ -335,13 +335,11 @@ png_push_read_unknown(png_structrp png_ptr, png_inforp info_ptr) /* 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); + png_handle_unknown(png_ptr, info_ptr, buffer); # else /* !READ_UNKNOWN_CHUNKS */ - PNG_UNUSED(info_ptr) + PNG_UNUSED(info_ptr) # endif /* !READ_UNKNOWN_CHUNKS */ - png_ptr->process_mode = png_read_chunk_header; } @@ -362,7 +360,7 @@ png_push_have_row(png_structrp png_ptr, png_bytep row) * one: */ # ifdef PNG_READ_DEINTERLACE_SUPPORTED - if (!png_ptr->do_interlace) + if (!png_ptr->do_interlace) # endif { affirm(PNG_ROW_IN_INTERLACE_PASS(row_number, pass) && row != NULL); @@ -482,7 +480,8 @@ png_push_read_process_IDAT(png_structp png_ptr, png_bytep *bufferp, if (!png_ptr->zstream_eod) { png_bytep row_buffer = NULL; - png_row_op row_op = png_read_process_IDAT(png_ptr); + png_row_op row_op = + png_read_process_IDAT(png_ptr, NULL, NULL, 1/*save row*/); if (row_op != png_row_incomplete) { @@ -517,28 +516,34 @@ png_push_read_process_IDAT(png_structp png_ptr, png_bytep *bufferp, */ 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. + /* png_struct::transformed_row contains a complete, transformed, + * row; this is processed in both 'sparkle' and 'block' mode. */ - row_buffer = png_ptr->row_buffer; +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + row_buffer = png_ptr->transformed_row; + if (row_buffer == NULL) +# endif /* TRANSFORM_MECH */ + 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. + * png_struct::transformed_row 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; - } + if (png_ptr->do_interlace) + { +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + row_buffer = png_ptr->transformed_row; + if (row_buffer == NULL) +# endif + row_buffer = png_ptr->row_buffer; + break; + } # endif - continue; case png_row_skip: @@ -833,23 +838,6 @@ png_process_data(png_structrp png_ptr, png_inforp info_ptr, 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) -{ - if (png_ptr == NULL) - 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 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_DEINTERLACE */ - void PNGAPI png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr, png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, diff --git a/pngpriv.h b/pngpriv.h index 91fad2a8a..f7adfa6fe 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -556,6 +556,47 @@ # define png_isaligned(ptr, type) 0 #endif +/* Buffer alignment control. These #defines control how the buffers used during + * read are aligned and how big they are. + */ +#ifndef PNG_ROW_BUFFER_ALIGN_TYPE + /* The absolute minimum alignment for a row buffer is that required for + * png_uint_32 direct access. The #define is of a legal C type that can be + * used as the type in the definition of the first member of a C union; give + * a typedef name if in doubt. + */ +# define PNG_ROW_BUFFER_ALIGN_TYPE png_uint_32 +#endif /* !ROW_BUFFER_ALIGN_TYPE */ +#ifndef PNG_ROW_BUFFER_BYTE_ALIGN + /* This is the minimum size in bytes of the buffer used while processing + * parts of row. Except at the end of the row pixels will always be + * processed in blocks such that the block size is a multiple of this number + */ +# define PNG_ROW_BUFFER_BYTE_ALIGN\ + ((unsigned int)/*SAFE*/(sizeof (PNG_ROW_BUFFER_ALIGN_TYPE))) +#endif /* !ROW_BUFFER_BYTE_ALIGN */ +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +# define PNG_MAX_PIXEL_BYTES 16U /* 4x32-bit channels */ +#else /* !READ_USER_TRANSFORM */ +# define PNG_MAX_PIXEL_BYTES 8U /* 4x16-bit channels */ +#endif /* !READ_USER_TRANSFORM */ +/* PNG_ROW_BUFFER_SIZE is a compile time constant for the size of the row + * buffer. The minimum size of 2048 bytes is intended to allow the buffer to + * hold a complete 256 entry color map of 64-bit (8-byte) pixels. This is a + * requirement at some points of the colormap handling code. + * + * The maximum size is intended to allow (unsigned int) indexing of the buffer, + * it only affects systems with a 16-bit unsigned value where it limits the + * maximum to 4096 bytes. + */ +#define PNG_MIN_ROW_BUFFER_SIZE\ + (PNG_MAX_PIXEL_BYTES * PNG_ROW_BUFFER_BYTE_ALIGN * 8U) +#define PNG_MAX_ROW_BUFFER_SIZE ((UINT_MAX / 16U) + 1U) +#ifndef PNG_ROW_BUFFER_SIZE +# define PNG_ROW_BUFFER_SIZE\ + (PNG_MIN_ROW_BUFFER_SIZE < 2048U ? 2048U : PNG_MIN_ROW_BUFFER_SIZE) +#endif /* ROW_BUFFER_SIZE */ + /* End of memory model/platform independent support */ /* End of 1.5.0beta36 move from pngconf.h */ @@ -892,9 +933,23 @@ PNG_INTERNAL_FUNCTION(png_uint_16, png_u16_affirm,(png_const_structrp png_ptr, /* 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); +PNG_INTERNAL_FUNCTION(png_alloc_size_t,png_calc_rowbytes, + (png_const_structrp png_ptr, unsigned int pixel_depth, + png_uint_32 row_width),PNG_EMPTY); + +/* Common code to calculate the maximum number of pixels to transform or filter + * at one time; controlled by PNG_ROW_BUFFER_SIZE above: + */ +PNG_INTERNAL_FUNCTION(unsigned int,png_max_pixel_block, + (png_const_structrp png_ptr),PNG_EMPTY); + +/* Copy the row in row_buffer; this is the non-interlaced copy used in both + * the read and write code. + */ +PNG_INTERNAL_FUNCTION(void, png_copy_row,(png_const_structrp png_ptr, + png_bytep dp, png_const_bytep sp, png_uint_32 x/*in INPUT*/, + png_uint_32 width/*of INPUT*/, unsigned int pixel_depth, + int clear/*clear the final byte*/),PNG_EMPTY); /* Zlib support */ #define PNG_UNEXPECTED_ZLIB_RETURN (-7) @@ -1149,33 +1204,21 @@ 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 -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. - */ - -#ifdef PNG_READ_DEINTERLACE_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr, - png_bytep row, int display),PNG_EMPTY); -#endif /* READ_DEINTERLACE */ - -/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ - -#ifdef PNG_WRITE_INTERLACING_SUPPORTED -/* Turn on write interlacing */ -PNG_INTERNAL_FUNCTION(void,png_set_write_interlace,(png_structrp),PNG_EMPTY); -#endif - -#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. +/* Choose the best filter to use and filter the row data then write it out. If + * WRITE_FILTERING is not supported this just writes the data out with a zero + * (NONE) filter byte. + * + * This may be called multiple times per row, but calls must be in 'x' order; + * first a call with x 0 to mark the start of the row and, at the end, one with + * 'end_of_row' set (this can be done in the same function call if the whole row + * is passed.) */ -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(unsigned int, png_write_filter_row, + (png_structrp png_ptr, png_bytep prev_pixels, png_const_bytep unfiltered_row, + png_uint_32 x, png_uint_32 width/*pixels*/, int first_row_in_pass, + int last_pass_row, unsigned int filters_to_try/*from previous call*/, + int end_of_image), + PNG_EMPTY); #ifdef PNG_TRANSFORM_MECH_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_transform_free,(png_const_structrp png_ptr, @@ -1595,17 +1638,52 @@ typedef enum png_row_incomplete, /* more IDAT data needed for row */ png_row_process, - /* png_struct::row_buffer contains a complete, transformed, row */ + /* png_struct::row_buffer contains a complete 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); +PNG_INTERNAL_FUNCTION(png_row_op,png_read_process_IDAT,(png_structrp png_ptr, + png_bytep transformed_row, png_bytep display_row, int save_row), + 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. + * it will return png_row_incomplete if it needs input. + * + * transformed_row: The transformed pixels of the input are written here. + * For interlaced images only the pixels in the pass will + * be written, the other pixels will not be touched. + * + * display_row: The transformed pixels but replicated to that the entire + * buffer will have been initialized. For passes after the + * first the pixels written are determined by the 'block' + * algorithm; only those *following* pixels which are + * written by *later* passes are written (with a copy of the + * pixel from the pass.) + * + * save_row: A boolean which indicates that the row (unexpanded) + * should be saved in png_struct::transformed_row. This can + * be used in a later call to png_combine_row. + * + * During reading the row is built up until png_row_process is returned. At + * this point png_struct::row_buffer contains the original PNG row from the + * file and, if save_row was set, png_struct::transformed_row contains the + * row after the selected row transforms have been performed. For interlaced + * images both are the width of the interlace pass. + * + * When png_row_repeat is returned the same is true, except that the buffers + * still contain the contents of the preceding row (the one where this + * funciton returned png_row_pricess). + * + * The row buffers should not be accessed if png_row_skip is returned; this + * row is not modified in the current pass. + */ + +PNG_INTERNAL_FUNCTION(void,png_read_free_row_buffers,(png_structrp png_ptr), + PNG_EMPTY); + /* Free allocated row buffers; done as soon as possible to avoid carrying + * around all the memory for longer than necessary. */ PNG_INTERNAL_FUNCTION(int,png_read_finish_IDAT,(png_structrp png_ptr), @@ -1671,7 +1749,6 @@ PNG_INTERNAL_FUNCTION(void,png_handle_chunk,(png_structrp png_ptr, * png_handle_chunk (via the libpng read callback.) */ -/* Handle the transformations for reading and writing */ 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. diff --git a/pngread.c b/pngread.c index 86d51f412..4e39be95b 100644 --- a/pngread.c +++ b/pngread.c @@ -353,11 +353,6 @@ png_read_IDAT(png_structrp png_ptr) png_ptr->zstream.avail_in = IDAT_size; } -#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 @@ -406,60 +401,42 @@ png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) * 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)) + switch (png_read_process_IDAT(png_ptr, row, dsp_row, 0/*no save*/)) { case png_row_incomplete: /* more IDAT data needed for row */ debug(png_ptr->zstream.avail_in == 0); continue; - 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'*/); - - goto display_row; /* Skip the 'DEINT' check */ - 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. + /* row not in this pass, but the existing row in row_buffer or (if + * transforms are happening) png_struct::transformed_row is + * available from a previous row. */ -#ifdef PNG_READ_DEINTERLACE_SUPPORTED - if (!png_ptr->do_interlace) - continue; -#else - continue; -#endif - - display_row: - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 1/*'rectangle/block'*/); - - goto row_fn; /* Skip the 'DEINT' check */ - + /* FALL THROUGH */ 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: + * more need be done, except the read_row_fn and then only if libpng + * is doing the interlace handling (this is the historical + * behavior!) */ -#ifdef PNG_READ_DEINTERLACE_SUPPORTED - if (!png_ptr->do_interlace) +# ifdef PNG_READ_DEINTERLACE_SUPPORTED + if (!png_ptr->do_interlace) continue; +# else /* !do_interlace */ continue; -#else - continue; -#endif - - row_fn: +# endif /* !do_interlace */ + /* FALL THROUGH */ + case png_row_process: + /* png_read_process_IDAT has done everything we need, the only extra + * required is to call the application row callback. + */ if (png_ptr->read_row_fn != NULL) png_ptr->read_row_fn(png_ptr, png_ptr->row_number, png_ptr->pass); - + /* And return now because the next row has been processed; so there + * is exactly one read_row_fn callback for each call to + * png_read_process_IDAT. + */ return; default: @@ -732,10 +709,8 @@ png_read_destroy(png_structrp png_ptr) { png_debug(1, "in png_read_destroy"); - 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_read_free_row_buffers(png_ptr); + png_free(png_ptr, png_ptr->read_buffer); png_ptr->read_buffer = NULL; diff --git a/pngrutil.c b/pngrutil.c index 4ecff2659..85b6d73df 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -3102,62 +3102,26 @@ png_handle_chunk(png_structrp png_ptr, png_inforp info_ptr) 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 +static void +copy_row(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp, + png_uint_32 x/*in INPUT*/, png_uint_32 width/*of INPUT*/, int clear) + /* Copy the row in row_buffer; this is the 'simple' case of 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)); - } + png_copy_row(png_ptr, dp, sp, x, width, +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + png_ptr->row_bit_depth * PNG_FORMAT_CHANNELS(png_ptr->row_format), +# else + PNG_PIXEL_DEPTH(*png_ptr), +# endif + clear/*clear partial byte at end of row*/); } #ifdef PNG_READ_DEINTERLACE_SUPPORTED -void /* PRIVATE */ -png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) +static void +combine_row(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp, + png_uint_32 x/*in INPUT*/, png_uint_32 width/*of INPUT*/, 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 @@ -3172,16 +3136,16 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) * 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: + * In 1.7.0 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: + * The input data comes from png_struct and sp: * - * png_struct::row_buffer; the row data + * sp[width(pixels)]; the row data from input[x(pixels)...] * png_struct::pass; the pass * png_struct::row_number; the row number in the *image* * png_struct::row_bit_depth, @@ -3190,7 +3154,9 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) * 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: + * passes are arguments to the API. The destination is the pointer to the + * entire row buffer, not just the part from output[x] on. 'display' is + * interpreted as: * * 0: only overwrite destination pixels that will correspond to the source * pixel in the final image. 'sparkle' mode. @@ -3199,6 +3165,8 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) * from *later* passes. 'block' mode. */ { + const unsigned int pass = png_ptr->pass; + png_debug(1, "in png_combine_row"); /* Factor out the copy case first, the 'display' argument is irrelevant in @@ -3206,37 +3174,47 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) */ if (!png_ptr->do_interlace || png_ptr->pass == 6) { - png_copy_row(png_ptr, dp); + copy_row(png_ptr, dp, sp, x, width, 0/*do not clear*/); return; } else /* not a simple copy */ { - unsigned int pixel_depth = + const 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 + /* The first source pixel is written to PNG_COL_FROM_PASS of the * destination: */ - unsigned int dstart = PNG_PASS_START_COL(pass); /* in pixels */ + png_uint_32 dx = PNG_COL_FROM_PASS_COL(x, pass); + /* The corresponding offset within the 8x8 block: */ + const unsigned int dstart = dx & 0x7U; + /* Find the first pixel written in any 8x8 block IN THIS PASS: */ + const unsigned int pass_start = PNG_PASS_START_COL(pass); /* 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: + const unsigned int doffset = PNG_PASS_COL_OFFSET(pass); + /* In 'block' mode when PNG_PASS_START_COL(pass) is 0 (PNG passes 1,3,5,7) + * the same pixel is replicated doffset times, when PNG_PASS_START_COL is + * non-zero (PNG passes 2,4,6) it is replicated PNG_PASS_START_COL times. + * For 'sparkle' mode only one copy of the pixel is written: */ - unsigned int drep = display ? (dstart ? dstart : doffset) : 1; + unsigned int drep = display ? (pass_start ? pass_start : doffset) : 1; + + /* Standard check for byte alignment */ + debug(((x * pixel_depth/*OVERFLOW OK*/) & 0x7U) == 0U); /* The caller should have excluded the narrow cases: */ - affirm(row_width > dstart); - row_width -= dstart; + affirm(row_width > dx); + row_width -= dx; + /* Advance dp to the start of the 8x8 block containing the first pixel to + * write, adjust dx to be an offset within the block: + */ + dp += png_calc_rowbytes(png_ptr, pixel_depth, dx & ~0x7U); + dx &= 0x7U; /* So each source pixel sp[i] is written to: * @@ -3250,12 +3228,12 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) * * Cherry pick the easy cases: */ - if (pixel_depth > 8) + if (pixel_depth > 8U) { - affirm((pixel_depth & 7) == 0); /* Convert to bytes: */ - pixel_depth >>= 3; - dp += dstart * pixel_depth; + const unsigned int pixel_bytes = pixel_depth >> 3; + + dp += dstart * pixel_bytes; for (;;) { @@ -3264,19 +3242,19 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) if (drep > row_width) drep = row_width; - for (c=0; c= row_width) break; row_width -= doffset; - dp += (doffset-drep) * pixel_depth; - sp += pixel_depth; + dp += (doffset-drep) * pixel_bytes; + sp += pixel_bytes; } } - else if (pixel_depth == 8) + else if (pixel_depth == 8U) { /* Optimize the common 1-byte per pixel case (typical case for palette * mapped images): @@ -3303,13 +3281,13 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) /* Pixels are 1, 2 or 4 bits in size. */ unsigned int spixel = *sp++; unsigned int dbrep = pixel_depth * drep; - unsigned int spos = 0; + unsigned int spos = 0U; # ifdef PNG_READ_PACKSWAP_SUPPORTED const int lsb = (png_ptr->row_format & PNG_FORMAT_FLAG_SWAPPED) != 0; # endif /* READ_PACKSWAP */ - if (dbrep >= 8) + if (dbrep >= 8U) { /* brep must be greater than 1, the destination does not require * sub-byte addressing except, maybe, at the end. @@ -3317,9 +3295,9 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) * db is the count of bytes required to replicate the source pixel * drep times. */ - affirm((dbrep & 7) == 0); + debug((dbrep & 7U) == 0U); dbrep >>= 3; - affirm((dstart * pixel_depth & 7) == 0); + debug((dstart * pixel_depth & 7U) == 0U); dp += (dstart * pixel_depth) >> 3; for (;;) @@ -3332,18 +3310,17 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) spixel_rep >>= spos; else # endif /* READ_PACKSWAP */ - spixel_rep >>= (8-pixel_depth)-spos; + spixel_rep >>= (8U-pixel_depth)-spos; switch (pixel_depth) { - case 1: spixel_rep &= 1; spixel_rep |= spixel_rep << 1; - /*FALL THROUGH*/ - case 2: spixel_rep &= 3; spixel_rep |= spixel_rep << 2; - /*FALL THROUGH*/ - case 4: spixel_rep &= 15; spixel_rep |= spixel_rep << 4; - /*FALL THROUGH*/ - default: - break; + case 1U: spixel_rep &= 1U; spixel_rep |= spixel_rep << 1; + /*FALL THROUGH*/ + case 2U: spixel_rep &= 3U; spixel_rep |= spixel_rep << 2; + /*FALL THROUGH*/ + case 4U: spixel_rep &= 15U; spixel_rep |= spixel_rep << 4; + /*FALL THROUGH*/ + default: break; } /* This may leave some pixels unwritten when there is a partial @@ -3364,7 +3341,7 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) { unsigned int mask; - affirm(drep < 8); + debug(drep < 8U); dp += dbrep; /* Set 'mask' to have 0's where *dp must be overwritten @@ -3377,8 +3354,7 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) # endif /* READ_PACKSWAP */ mask = 0xff >> drep; - *dp = png_check_byte(png_ptr, - (*dp & mask) | (spixel_rep & ~mask)); + *dp = PNG_BYTE((*dp & mask) | (spixel_rep & ~mask)); } break; @@ -3387,8 +3363,8 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) row_width -= doffset; dp += (doffset * pixel_depth) >> 3; spos += pixel_depth; - if (spos == 8) - spixel = *sp++, spos = 0; + if (spos == 8U) + spixel = *sp++, spos = 0U; } /* for (;;) */ } /* pixel_depth * drep >= 8 */ @@ -3397,15 +3373,15 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) /* brep may be 1, pixel_depth may be 1, 2 or 4, dbrep is the number * of bits to set. */ + unsigned int bstart = dstart * pixel_depth; /* in bits */ unsigned int dpixel; - dstart *= pixel_depth; - dp += dstart >> 3; - dstart &= 7; + dp += bstart >> 3; + bstart &= 7U; dpixel = *dp; /* dpixel: current *dp, being modified - * dstart: bit offset within dpixel + * bstart: bit offset within dpixel * drep: pixel size to write (used as a check against row_width) * doffset: pixel step to next written destination * @@ -3415,7 +3391,7 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) * * Set dbrep to a mask for the bits to set: */ - dbrep = (1<>= spos; else # endif /* READ_PACKSWAP */ - spixel_rep >>= (8-pixel_depth)-spos; + spixel_rep >>= (8U-pixel_depth)-spos; switch (pixel_depth) { - case 1: spixel_rep &= 1; spixel_rep |= spixel_rep << 1; - /*FALL THROUGH*/ - case 2: spixel_rep &= 3; spixel_rep |= spixel_rep << 2; - /*FALL THROUGH*/ - case 4: spixel_rep &= 15; spixel_rep |= spixel_rep << 4; - /*FALL THROUGH*/ - default: - break; + case 1U: spixel_rep &= 1U; spixel_rep |= spixel_rep << 1; + /*FALL THROUGH*/ + case 2U: spixel_rep &= 3U; spixel_rep |= spixel_rep << 2; + /*FALL THROUGH*/ + case 4U: spixel_rep &= 15U; spixel_rep |= spixel_rep << 4; + /*FALL THROUGH*/ + default: break; } /* 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; + drep = row_width, dbrep = (1U<<(pixel_depth*drep))-1U; { unsigned int mask; - /* Mask dbrep bits at dstart: */ + /* Mask dbrep bits at bstart: */ # ifdef PNG_READ_PACKSWAP_SUPPORTED if (lsb) - mask = dstart; + mask = bstart; else # endif /* READ_PACKSWAP */ - mask = (8-pixel_depth)-dstart; + mask = (8U-pixel_depth)-bstart; mask = dbrep << mask; dpixel &= ~mask; @@ -3464,105 +3439,140 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) if (doffset >= row_width) { - *dp = png_check_byte(png_ptr, dpixel); + *dp = PNG_BYTE(dpixel); break; } row_width -= doffset; - dstart += doffset * pixel_depth; + bstart += doffset * pixel_depth; - if (dstart >= 8) + if (bstart >= 8U) { - *dp = png_check_byte(png_ptr, dpixel); - dp += dstart >> 3; - dstart &= 7; + *dp = PNG_BYTE(dpixel); + dp += bstart >> 3; + bstart &= 7U; dpixel = *dp; } spos += pixel_depth; - if (spos == 8) - spixel = *sp++, spos = 0; + if (spos == 8U) + spixel = *sp++, spos = 0U; } /* for (;;) */ } /* pixel_depth * drep < 8 */ } /* pixel_depth < 8 */ } /* not a simple copy */ } -#endif /* READ_DEINTERLACE */ + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +void PNGAPI +png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row, + png_const_bytep new_row) +{ + /* 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 above), otherwise + * it must be png_struct::transformed_row + */ + if (png_ptr != NULL && new_row != NULL) + { + if (new_row != png_ptr->row_buffer +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + && new_row != png_ptr->transformed_row +# endif /* TRANSFORM_MECH */ + ) + png_app_error(png_ptr, "invalid call to png_progressive_combine_row"); + else + { + png_uint_32 width = png_ptr->width; + + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + const unsigned int pass = png_ptr->pass; + width = PNG_PASS_COLS(width, pass); + } + + combine_row(png_ptr, old_row, new_row, 0U, width, 1/*blocky display*/); + } + } +} +#endif /* PROGRESSIVE_READ */ +#else /* !READ_DEINTERLACE */ + /* No read deinterlace support, so 'combine' always reduces to 'copy', there + * is no 'display' argument: + */ +# define combine_row(pp, dp, sp, x, w, display)\ + copy_row(pp, dp, sp, x, w, 0/*!clear*/) +#endif /* !READ_DEINTERLACE */ static void -png_read_filter_row_sub(png_alloc_size_t istop, unsigned int bpp, - png_bytep row, png_const_bytep prev_row) +png_read_filter_row_sub(png_alloc_size_t row_bytes, unsigned int bpp, + png_bytep row, png_const_bytep prev_row, png_const_bytep prev_pixels) { - png_alloc_size_t i; - png_bytep rp = row + bpp; + while (row_bytes >= bpp) + { + unsigned int i; + + for (i=0; i 0) { - *rp = PNG_BYTE(*rp + *pp++); - rp++; + *row = PNG_BYTE(*row + *prev_row); + ++row; + ++prev_row; + --row_bytes; } PNG_UNUSED(bpp) + PNG_UNUSED(prev_pixels) } static void -png_read_filter_row_avg(png_alloc_size_t istop, unsigned int bpp, - png_bytep row, png_const_bytep prev_row) +png_read_filter_row_avg(png_alloc_size_t row_bytes, unsigned int bpp, + png_bytep row, png_const_bytep prev_row, png_const_bytep prev_pixels) { - png_alloc_size_t i; - png_bytep rp = row; - png_const_bytep pp = prev_row; - - istop -= bpp; - for (i = 0; i < bpp; i++) + while (row_bytes >= bpp) { - *rp = PNG_BYTE(*rp + (*pp++ / 2)); - rp++; - } + unsigned int i; - for (i = 0; i < istop; i++) - { - *rp = PNG_BYTE(*rp + (*pp++ + *(rp-bpp)) / 2); + for (i=0; irow_buffer = png_voidcast(png_bytep, - png_malloc(png_ptr, png_ptr->row_allocated_bytes)); + png_ptr->row_buffer = png_voidcast(png_bytep, png_malloc(png_ptr, + png_calc_rowbytes(png_ptr, PNG_PIXEL_DEPTH(*png_ptr), png_ptr->width))); if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK) png_error(png_ptr, png_ptr->zstream.msg); @@ -3864,28 +3898,78 @@ png_read_start_IDAT(png_structrp png_ptr) * preserved and preset at the next call to the function. * * The function may also call png_error if an unrecoverable error occurs. + * + * The caller passes in a callback function and parameter to be called when row + * data is available. The callback is called repeatedly for each row to handle + * all the transformed row data. */ png_row_op /*PRIVATE*/ -png_read_process_IDAT(png_structrp png_ptr) +png_read_process_IDAT(png_structrp png_ptr, png_bytep transformed_row, + png_bytep display_row, int save_row) { - png_uint_32 width = png_ptr->width; + /* Common sub-expressions. These are all constant across the whole PNG, but + * are recalculated here each time because this is fast and it only happens + * once per row + once per block of input data. + */ + const unsigned int max_pixels = png_max_pixel_block(png_ptr); + const unsigned int pixel_depth = png_ptr->row_input_pixel_depth; + /* The number of input bytes read each time (cannot overflow because it is + * limited by PNG_ROW_BUFFER_SIZE): + */ + const unsigned int input_byte_count = (max_pixels * pixel_depth) / 8U; + const unsigned int bpp = (pixel_depth+0x7U)>>3; + const png_uint_32 width = png_ptr->width; + const unsigned int interlaced = png_ptr->interlaced != PNG_INTERLACE_NONE; + 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 */ + start_of_row = 0U, /* at the start of the row; read a filter byte */ 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) */ + processing_row = 3U /* control returned to caller to process the row */ } 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); + /* Basic sanity checks: */ + affirm(pixel_depth > 0U && pixel_depth <= 64U && + input_byte_count <= PNG_ROW_BUFFER_SIZE && + pixel_depth <= 8U*PNG_MAX_PIXEL_BYTES); + for (;;) switch (state) { + png_alloc_size_t row_bytes_processed; + png_alloc_size_t bytes_read; /* bytes in pixel_buffer */ + png_uint_32 pass_width; + png_byte row_filter; + union + { + PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment; + png_byte buffer[16U]; + } previous_pixels; + union + { + PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment; + png_byte buffer[PNG_ROW_BUFFER_SIZE]; + } pixel_buffer; + + case need_row_bytes: + /* The above variables need to be restored: */ + row_bytes_processed = png_ptr->row_bytes_read; + bytes_read = row_bytes_processed % input_byte_count; + row_bytes_processed -= bytes_read; + + pass_width = width; + if (interlaced) + pass_width = PNG_PASS_COLS(pass_width, pass); + + memcpy(pixel_buffer.buffer, png_ptr->scratch, bytes_read); + memcpy(previous_pixels.buffer, png_ptr->scratch+bytes_read, 2*bpp); + row_filter = png_ptr->scratch[bytes_read+2*bpp]; + + goto pixel_loop; + 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. @@ -3898,8 +3982,8 @@ png_read_process_IDAT(png_structrp png_ptr) * is always necessary to read the filter byte of the next row. */ png_ptr->pass = ++pass & 0x7; - row_number = 0; - } + row_number = 0U; + } /* end of pass */ png_ptr->row_number = row_number; @@ -3909,15 +3993,13 @@ png_read_process_IDAT(png_structrp png_ptr) */ 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); + pass_width = PNG_PASS_COLS(width, pass); /* On average most rows are skipped, so do this first: */ if (pass_width == 0 || @@ -3978,369 +4060,337 @@ png_read_process_IDAT(png_structrp png_ptr) (~((row) >> (3U-((pass) >> 1))) & ~(pass) & 0x1U) /* Hence: */ - png_ptr->row_state = processing_row; + debug(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; - } + /* processed; fall through to start_of_row */ } /* 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) + case start_of_row: { - /* 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; [not used below] */ - /* FALL THROUGH */ - - 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; /* as opposed to 'start_of_pass' */ - /* 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: + /* Read the filter byte for the next row, previous_pixels is just + * used as a temporary buffer; it is reset below. */ png_alloc_size_t cb = png_inflate_IDAT(png_ptr, 0/*finish*/, - &png_ptr->next_filter, 1); + previous_pixels.buffer, 1U); /* This can be temporary; it verifies the invariants on how * png_inflate_IDAT updates the {next,avail}_out fields: */ #ifndef __COVERITY__ /* Suppress bogus Coverity complaint */ debug(png_ptr->zstream.avail_out == 1-cb && - png_ptr->zstream.next_out == cb + &png_ptr->next_filter); + png_ptr->zstream.next_out == cb + previous_pixels.buffer); #endif - /* next_out points into png_struct, for security do this: */ + /* next_out points to previous_pixels, for security do this: */ png_ptr->zstream.next_out = NULL; - png_ptr->zstream.avail_out = 0; + png_ptr->zstream.avail_out = 0U; /* One byte, so we either got it or have to get more input data: */ - if (cb != 1) + if (cb != 1U) { - affirm(cb == 0 && png_ptr->zstream.avail_in == 0); - png_ptr->row_state = state & 3U; + affirm(cb == 0U && png_ptr->zstream.avail_in == 0U); + png_ptr->row_state = start_of_row; return png_row_incomplete; } + } - /* Check the filter byte. */ - row_filter = png_ptr->next_filter; + /* Check the filter byte. */ + row_filter = previous_pixels.buffer[0]; + if (row_filter >= PNG_FILTER_VALUE_LAST) + png_chunk_error(png_ptr, "invalid PNG filter"); - if (row_filter >= PNG_FILTER_VALUE_LAST) - png_chunk_error(png_ptr, "invalid PNG filter"); + /* These are needed for the filter check below: */ + pass_width = width; + if (interlaced) + pass_width = PNG_PASS_COLS(pass_width, pass); - 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? + /* 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. */ -# ifdef PNG_TRANSFORM_MECH_SUPPORTED - if (png_ptr->transform_list != NULL) + if (row_number == 0) switch (row_filter) + { + case PNG_FILTER_VALUE_UP: + /* x-0 == x, so do this optimization: */ + row_filter = PNG_FILTER_VALUE_NONE; + break; + + case PNG_FILTER_VALUE_PAETH: + /* The Paeth predictor is always the preceding (leftwards) + * value, so this is the same as sub: + */ + row_filter = PNG_FILTER_VALUE_SUB; + break; + + case 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. Zero out the + * row buffer to make AVG work. + * + * This is only required if 'pass' is >0, because on the first + * pass the code that allocated the row buffer zeroed it (for + * security reasons). + */ + if (pass > 0) + memset(png_ptr->row_buffer, 0U, + PNG_ROWBYTES(pixel_depth, pass_width)); + break; + + default: + break; + } /* switch row_filter */ + + /* Always zero the 'previous pixel' out at the start of a row; this + * allows the filter code to ignore the row start. + */ + memset(previous_pixels.buffer, 0U, sizeof previous_pixels.buffer); + + row_bytes_processed = 0U; + bytes_read = 0U; + + pixel_loop: + /* At this point the following must be set correctly: + * + * row_bytes_processed: bytes processed so far + * pass_width: width of a row in this pass in pixels + * pixel_depth: depth in bits of a pixel + * bytes_read: count of filtered bytes in pixel_buffer + * row_filter: filter byte for this row + * previous_pixels[0]: pixel 'a' for the filter + * previous_pixels[1]: pixel 'c' for the filter + * pixel_buffer[]: bytes_read filtered bytes + * + * The code in the loop decompresses PNG_ROW_BUFFER_SIZE filterd pixels + * from the input, unfilters and transforms them, then saves them to + * png_struct::row_buffer[row_bytes_processed...]. + */ + { /* pixel loop */ + const png_alloc_size_t row_bytes = + PNG_ROWBYTES(pixel_depth, pass_width); + png_bytep row_buffer = png_ptr->row_buffer + row_bytes_processed; + unsigned int pixels; + png_uint_32 x; + + /* Sanity check for potential buffer overwrite: */ + affirm(row_bytes > row_bytes_processed); + + /* Work out the current pixel index of the pixel at the start of the + * row buffer: + */ + switch (pixel_depth) { - 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 */ - } + case 1U: x = (png_uint_32)/*SAFE*/(row_bytes_processed << 3); + break; + case 2U: x = (png_uint_32)/*SAFE*/(row_bytes_processed << 2); + break; + case 4U: x = (png_uint_32)/*SAFE*/(row_bytes_processed << 1); + break; + case 8U: x = (png_uint_32)/*SAFE*/row_bytes_processed; + break; + default: x = (png_uint_32)/*SAFE*/(row_bytes_processed / bpp); + debug(row_bytes_processed % bpp == 0U); + break; } -# endif + + for (pixels = max_pixels; x < pass_width; x += pixels) + { + if (pixels > pass_width - x) + pixels = (unsigned int)/*SAFE*/(pass_width - x); + + /* At the end of the image pass Z_FINISH to zlib to optimize the + * final read (very slightly, is this worth doing?) To do this + * work out if we are at the end. + */ + { + const png_uint_32 height = png_ptr->height; + + /* last_pass_row indicates that this is the last row in this + * pass (the test is optimized for the non-interlaced case): + */ + const int 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: + */ + const int finish = last_pass_row && (!interlaced || + pass >= PNG_LAST_PASS(width, height)); + + const png_alloc_size_t bytes_to_read = + PNG_ROWBYTES(pixel_depth, pixels); + + png_alloc_size_t cb; + + affirm(bytes_to_read > bytes_read); + cb = png_inflate_IDAT(png_ptr, finish, + pixel_buffer.buffer + bytes_read, + bytes_to_read - bytes_read); + bytes_read += cb; + + if (bytes_read < bytes_to_read) + { + /* Fewer bytes were read than needed: we need to stash all + * the information required at pixel_loop in png_struct so + * that the need_row_bytes case can restore it when more + * input is available. + */ + debug(png_ptr->zstream.avail_in == 0U); + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0U; + + png_ptr->row_bytes_read = row_bytes_processed + bytes_read; + memcpy(png_ptr->scratch, pixel_buffer.buffer, bytes_read); + memcpy(png_ptr->scratch+bytes_read, previous_pixels.buffer, + 2*bpp); + png_ptr->scratch[bytes_read+2*bpp] = row_filter; + png_ptr->row_state = need_row_bytes; + return png_row_incomplete; + } + + debug(bytes_read == bytes_to_read); + } /* fill pixel_buffer */ + + /* The buffer is full or the row is complete but the calculation + * was done using the pixel count, so double check against the + * byte count here: + */ + implies(bytes_read != input_byte_count, + bytes_read == row_bytes - row_bytes_processed); + + /* At this point all the required information to process the next + * block of pixels in the row has been read from the input stream + * and the original, filtered, row data is held in pixel_buffer. + * + * Because the buffer will be transformed after the unfilter + * operation we require whole pixels: + */ + debug(bytes_read >= bpp && bytes_read % bpp == 0); + + if (row_filter > PNG_FILTER_VALUE_NONE) + { + /* This is checked in the read code above: */ + debug(row_filter < PNG_FILTER_VALUE_LAST); + + /* Lazy init of the read functions, which allows hand crafted + * optimizations for 'bpp' (which does not change.) + */ + if (png_ptr->read_filter[0] == NULL) + png_init_filter_functions(png_ptr, bpp); + + /* Pixels 'a' then 'c' are in previous_pixels, pixel 'b' is in + * row_buffer and pixel 'x' (filtered) is in pixel_buffer. + */ + png_ptr->read_filter[row_filter-1](bytes_read, bpp, + pixel_buffer.buffer, row_buffer, previous_pixels.buffer); + } /* do the filter */ + + /* Now pixel_buffer.buffer contains the *un*filtered bytes of the + * current row and row_buffer needs updating with these. First + * preserve pixels 'a' and 'c' for the next time round the loop + * (if necessary). + */ + if (bytes_read < row_bytes - row_bytes_processed) + { + debug(bytes_read == input_byte_count); + memcpy(previous_pixels.buffer/* pixel 'a' */, + pixel_buffer.buffer + bytes_read - bpp, bpp); + memcpy(previous_pixels.buffer+bpp/* pixel 'c' */, + row_buffer + bytes_read - bpp, bpp); + } + + /* Now overwrite the previous row pixels in row_buffer with the + * current row pixels: + */ + memcpy(row_buffer, pixel_buffer.buffer, bytes_read); + row_buffer += bytes_read; + row_bytes_processed += bytes_read; + bytes_read = 0U; /* for next buffer */ + + /* Any transforms can now be performed along with any output + * handling (copy or interlace handling). + */ +# 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); + + tc.width = pixels; + tc.sp = tc.dp = pixel_buffer.buffer; + + /* Run the list. It is ok if it doesn't end up doing + * anything; this can happen with a lazy init. + * + * TODO: I don't think lazy inits happen any more, hence + * the 'debug' below. + */ + max_depth = png_run_transform_list_forwards(png_ptr, &tc); + debug(png_ptr->transform_list != NULL); + + /* This is too late, a stack overwrite has already + * happened, but it may still prevent exploits: + */ + affirm(max_depth <= png_ptr->row_max_pixel_depth); + + /* It is very important that the transform produces the + * same pixel format as the TC_INIT steps: + */ + 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 + /* This sometimes fails at present when gamma + * transforms are eliminated in PNG_TC_INIT_FINAL: + */ + debug(png_ptr->row_gamma == tc.gamma); +# endif /* READ_GAMMA */ + + /* If the caller needs the row saved (for the progressive + * read API) or if this PNG is interlaced and this row may + * be required in a subsequent pass (any pass before the + * last one) then it is stored in + * png_struct::transformed_row, and that may need to be + * allocated here. + */ +# if defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\ + defined(PNG_READ_DEINTERLACE_SUPPORTED) + if (png_ptr->transform_list != NULL && + (save_row || (png_ptr->do_interlace && pass < 6U))) + { + if (png_ptr->transformed_row == NULL) + png_ptr->transformed_row = png_voidcast(png_bytep, + png_malloc(png_ptr, png_calc_rowbytes(png_ptr, + png_ptr->row_bit_depth * + PNG_FORMAT_CHANNELS(png_ptr->row_format), + save_row ? width : (width+1U)>>1))); + + copy_row(png_ptr, png_ptr->transformed_row, + pixel_buffer.buffer, x, pixels, 1/*clear*/); + } +# endif /* PROGRESSIVE_READ || READ_DEINTERLACE */ + } /* transform_list != NULL */ +# endif /* TRANSFORM_MECH */ + + /* There are now 'pixels' possibly transformed pixels starting at + * row pixel x, where 'x' is an index in the interlaced row if + * interlacing is happening. Handle this row. + */ + if (transformed_row != NULL) + combine_row(png_ptr, transformed_row, pixel_buffer.buffer, + x, pixels, 0/*!display*/); + + if (display_row != NULL) + combine_row(png_ptr, display_row, pixel_buffer.buffer, x, + pixels, 1/*display*/); + } /* for x < pass_width */ + } /* pixel loop */ png_ptr->row_state = processing_row; return png_row_process; @@ -4348,6 +4398,29 @@ png_read_process_IDAT(png_structrp png_ptr) default: impossible("bad row state"); } /* forever switch */ + + PNG_UNUSED(save_row); /* May not be used above */ +} + +void /* PRIVATE */ +png_read_free_row_buffers(png_structrp png_ptr) +{ + /* The transformed row only gets saved if needed: */ +# if (defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\ + defined(PNG_READ_DEINTERLACE_SUPPORTED)) &&\ + defined(PNG_TRANSFORM_MECH_SUPPORTED) + if (png_ptr->transformed_row != NULL) + { + png_free(png_ptr, png_ptr->transformed_row); + png_ptr->transformed_row = NULL; + } +# endif /* PROGRESSIVE_READ || READ_DEINTERLACE */ + + if (png_ptr->row_buffer != NULL) + { + png_free(png_ptr, png_ptr->row_buffer); + png_ptr->row_buffer = NULL; + } } /* Complete reading of the IDAT chunks. This returns 0 if more data is to @@ -4367,21 +4440,10 @@ png_read_finish_IDAT(png_structrp png_ptr) IDAT_truncated } error = no_error; - /* Release row_buffer and alt_buffer first; they can use considerable - * amounts of memory. + /* Release the rowd buffers 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; - } + png_read_free_row_buffers(png_ptr); affirm(png_ptr->zowner == png_IDAT); /* else this should not be called */ diff --git a/pngstruct.h b/pngstruct.h index d85ad2c3c..6ca50c91f 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -446,15 +446,20 @@ struct png_struct_def * 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_bytep row_buffer; /* primary row buffer */ +#endif /* WRITE_FILTER || READ */ +#if (defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\ + defined(PNG_READ_DEINTERLACE_SUPPORTED)) &&\ + defined(PNG_TRANSFORM_MECH_SUPPORTED) + png_bytep transformed_row; /* pointer to the transformed row, if + * required. May point to row_buffer. + */ +#endif /* (PROGRESSIVE_READ || READ_DEINTERLACE) && TRANSFORM_MECH */ - png_alloc_size_t row_allocated_bytes; /* Total amount allocated */ +#ifdef PNG_READ_SUPPORTED + png_alloc_size_t row_bytes_read; /* Total read in row */ +#endif /* READ */ png_uint_32 row_number; /* current row in pass */ #ifdef PNG_READ_GAMMA_SUPPORTED @@ -484,16 +489,34 @@ struct png_struct_def #ifdef PNG_SEQUENTIAL_READ_SUPPORTED unsigned int read_started :1; /* at least one call to png_read_row */ #endif +#if defined (PNG_READ_DEINTERLACE_SUPPORTED) ||\ + defined (PNG_WRITE_INTERLACE_SUPPORTED) unsigned int do_interlace :1; /* libpng handles the interlace */ +# endif /* READ_DEINTERLACE, WRITE_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) */ + /* The following fields are set by png_row_init to the pixel depths of the + * pixels at various states. If transforms are not supported they will + * always be the same value: + * + * READ WRITE + * input: PNG From application + * output: To application PNG + * max: Largest in transform + */ + unsigned int row_input_pixel_depth :8; + unsigned int row_output_pixel_depth :8; + unsigned int row_max_pixel_depth :8; +#ifdef PNG_WRITE_FILTER_SUPPORTED + unsigned int filter_mask :8; /* mask of filters to consider on write */ +#endif /* WRITE_FILTER */ + # 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 @@ -528,9 +551,8 @@ struct png_struct_def * 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; + 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 @@ -539,27 +561,20 @@ struct png_struct_def */ #endif /* WRITE_TRANSFORMS */ #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED - unsigned int write_invert_alpha :1; + 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; + 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. @@ -629,7 +644,8 @@ struct png_struct_def * un-filter function, this allows per-image and per-processor optimization. */ 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); + unsigned int bpp, png_bytep row, png_const_bytep prev_row, + png_const_bytep prev_pixels); #endif /* READ */ #ifdef PNG_WRITE_SUPPORTED @@ -749,5 +765,10 @@ struct png_struct_def #ifdef PNG_MNG_FEATURES_SUPPORTED unsigned int mng_features_permitted :3; #endif + + /* SCRATCH buffers, used when control returns to the application or a read + * loop. + */ + png_byte scratch[PNG_ROW_BUFFER_SIZE+16U]; }; #endif /* PNGSTRUCT_H */ diff --git a/pngtest.c b/pngtest.c index 126b847ff..59b13ac31 100644 --- a/pngtest.c +++ b/pngtest.c @@ -65,7 +65,7 @@ defined PNG_READ_tEXt_SUPPORTED &&\ defined PNG_READ_tIME_SUPPORTED &&\ defined PNG_READ_zTXt_SUPPORTED &&\ - defined PNG_WRITE_INTERLACING_SUPPORTED + defined PNG_WRITE_INTERLACE_SUPPORTED #ifdef PNG_ZLIB_HEADER # include PNG_ZLIB_HEADER /* defined by pnglibconf.h from 1.7 */ @@ -844,12 +844,14 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) png_structp write_ptr; png_infop write_info_ptr; png_infop write_end_info_ptr; +#ifdef PNG_WRITE_FILTER_SUPPORTED int interlace_preserved = 1; -#else +#endif /* WRITE_FILTER */ +#else /* !WRITE */ png_structp write_ptr = NULL; png_infop write_info_ptr = NULL; png_infop write_end_info_ptr = NULL; -#endif +#endif /* !WRITE */ png_bytep row_buf; png_uint_32 y; png_uint_32 width, height; @@ -1380,7 +1382,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) pngtest_debug("Writing row data"); #if defined(PNG_READ_DEINTERLACE_SUPPORTED) &&\ - defined(PNG_WRITE_INTERLACING_SUPPORTED) + defined(PNG_WRITE_INTERLACE_SUPPORTED) /* Both must be defined for libpng to be able to handle the interlace, * otherwise it gets handled below by simply reading and writing the passes * directly. @@ -1622,7 +1624,8 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) return (1); } -#ifdef PNG_WRITE_SUPPORTED /* else nothing was written */ +#if defined (PNG_WRITE_SUPPORTED) /* else nothing was written */ &&\ + defined (PNG_WRITE_FILTER_SUPPORTED) if (interlace_preserved != 0) /* else the files will be changed */ { for (;;) @@ -1699,7 +1702,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) } } } -#endif /* WRITE */ +#endif /* WRITE && WRITE_FILTER */ FCLOSE(fpin); FCLOSE(fpout); diff --git a/pngtest.png b/pngtest.png index cb3fef4cf..2f0da8310 100644 Binary files a/pngtest.png and b/pngtest.png differ diff --git a/pngtrans.c b/pngtrans.c index 5cb50aef5..e9e3f12db 100644 --- a/pngtrans.c +++ b/pngtrans.c @@ -558,7 +558,7 @@ png_run_transform_list_backwards(png_structp png_ptr, png_transform_controlp tc) /* Better late than never (if this fires a memory overwrite has happened): */ - affirm(max_depth <= png_ptr->row_max_pixel); + affirm(max_depth <= png_ptr->row_max_pixel_depth); } } #endif /* WRITE */ @@ -832,52 +832,42 @@ png_set_check_for_invalid_index(png_structrp png_ptr, int enabled) void /* PRIVATE */ png_init_row_info(png_structrp png_ptr) { - unsigned int max_depth = PNG_PIXEL_DEPTH(*png_ptr); + /* PNG pixels never exceed 64 bits in depth: */ + const png_byte png_depth = + png_check_bits(png_ptr, PNG_PIXEL_DEPTH(*png_ptr), 7U); -#ifdef PNG_TRANSFORM_MECH_SUPPORTED -# ifdef PNG_PALETTE_MAX_SUPPORTED - /* The palette index check stuff is *on* automatically. To handle this - * add it here, if it is supported. - * - * The logic here is a little complex because of the plethora of - * #defines controlling this stuff. - */ -# undef PNG_READ_CHECK_PALETTE -# undef PNG_WRITE_CHECK_PALETTE - -# if defined(PNG_READ_GET_PALETTE_MAX_SUPPORTED) ||\ - defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) -# ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED -# define PNG_READ_CHECK_PALETTE \ - (png_ptr->read_struct && !png_ptr->palette_index_check_disabled) -# else -# define PNG_READ_CHECK_PALETTE (png_ptr->read_struct) +# 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 -# else /* no READ support */ -# define PNG_READ_CHECK_PALETTE 0 -# endif - -# if defined(PNG_WRITE_GET_PALETTE_MAX_SUPPORTED) ||\ - defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) -# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED -# define PNG_WRITE_CHECK_PALETTE \ - (!png_ptr->read_struct && !png_ptr->palette_index_check_disabled) -# else -# define PNG_WRITE_CHECK_PALETTE (!png_ptr->read_struct) -# endif -# else /* no WRITE support */ -# define PNG_WRITE_CHECK_PALETTE 0 -# endif - - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE/* fast escape */ && - (PNG_READ_CHECK_PALETTE || PNG_WRITE_CHECK_PALETTE)) - { - png_add_transform(png_ptr, 0/*size*/, palette_max_init, - PNG_TR_CHECK_PALETTE); - } -# undef PNG_READ_CHECK_PALETTE -# undef PNG_WRITE_CHECK_PALETTE -# endif /* PALETTE_MAX */ /* Application transforms may change the format of the data or, when * producing interlaced images, the number of pixels in a line. This code @@ -894,9 +884,9 @@ png_init_row_info(png_structrp png_ptr) 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 */ +# 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) @@ -905,25 +895,60 @@ png_init_row_info(png_structrp png_ptr) * 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*/); + unsigned int max_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. + /* init_transform_mech is expected to take the input depth into + * account: */ - png_ptr->invalid_info = tc.invalid_info; -# endif /* READ_TRANSFORMS */ + debug(max_depth >= png_depth); + if (max_depth < png_depth) + max_depth = png_depth; + affirm(max_depth <= (png_ptr->read_struct ? 128U : 64U)); + +# 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 */ +# ifdef PNG_READ_GAMMA_SUPPORTED + affirm(png_ptr->row_gamma == tc.gamma); +# endif /* READ_GAMMA */ + + png_ptr->row_max_pixel_depth = + png_check_bits(png_ptr, max_depth, 8U); + + /* On 'read' input_depth is the PNG pixel depth and output_depth is + * the depth of the pixels passed to the application, but on 'write' + * the transform list is reversed so output_depth is the PNG depth + * and input_depth the application depth. + */ + { + const png_byte app_depth = + png_check_bits(png_ptr, PNG_TC_PIXEL_DEPTH(tc), 8U); + + affirm(app_depth <= max_depth); + + if (png_ptr->read_struct) + { + png_ptr->row_input_pixel_depth = png_depth; + png_ptr->row_output_pixel_depth = app_depth; + } + + else + { + png_ptr->row_input_pixel_depth = app_depth; + png_ptr->row_output_pixel_depth = png_depth; + } + + return; /* to skip the default settings below */ + } } } @@ -934,46 +959,27 @@ png_init_row_info(png_structrp png_ptr) 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 */ +# 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 */ } +# endif /* TRANSFORM_MECH */ - /* '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: + /* We get here if there are no transforms therefore no change to the pixel + * bit depths. */ - { - 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; - } + png_ptr->row_output_pixel_depth = png_ptr->row_max_pixel_depth = + png_ptr->row_input_pixel_depth = png_depth; } #if defined(PNG_READ_DEINTERLACE_SUPPORTED) || \ - defined(PNG_WRITE_INTERLACING_SUPPORTED) + defined(PNG_WRITE_INTERLACE_SUPPORTED) int PNGAPI png_set_interlace_handling(png_structrp png_ptr) { @@ -999,26 +1005,25 @@ png_set_interlace_handling(png_structrp png_ptr) else /* write */ { -# ifdef PNG_WRITE_INTERLACING_SUPPORTED +# ifdef PNG_WRITE_INTERLACE_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 */ +# else /* !WRITE_INTERLACE */ png_app_error(png_ptr, "no interlace support"); /* return 0 below */ -# endif /* !WRITE_INTERLACING */ +# endif /* !WRITE_INTERLACE */ } } /* API CHANGE: 1.7.0: returns 0 if called with a NULL png_ptr */ return 0; } -#endif /* READ_DEINTERLACE || WRITE_INTERLACING */ +#endif /* READ_DEINTERLACE || WRITE_INTERLACE */ #ifdef PNG_MNG_READ_FEATURES_SUPPORTED /* Undoes intrapixel differencing, this is called immediately after the PNG diff --git a/pngwrite.c b/pngwrite.c index 4a751003e..5e3781b1f 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -425,12 +425,14 @@ png_write_end(png_structrp png_ptr, png_inforp info_ptr) * * 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. + * The check depends on the precise logic in png_write_row. */ - else if (png_ptr->interlaced ? png_ptr->pass != PNG_INTERLACE_ADAM7_PASSES : - png_ptr->row_number != png_ptr->height) + else if (png_ptr->pass != 7U) png_app_error(png_ptr, "png_write_row not called to last row"); + else + debug(png_ptr->row_number == 0U); + /* See if user wants us to write information chunks */ if (info_ptr != NULL) { @@ -597,7 +599,7 @@ png_write_image(png_structrp png_ptr, png_bytepp image) png_debug(1, "in png_write_image"); -#ifdef PNG_WRITE_INTERLACING_SUPPORTED +#ifdef PNG_WRITE_INTERLACE_SUPPORTED /* Initialize interlace handling. If image is not interlaced, * this will set pass to 1 */ @@ -624,280 +626,71 @@ png_write_image(png_structrp png_ptr, png_bytepp image) } } -/* 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. - */ +#if defined(PNG_WRITE_INTERLACE_SUPPORTED) ||\ + defined(PNG_WRITE_TRANSFORMS_SUPPORTED) static void -png_write_finish_row(png_structrp png_ptr, png_byte filter_byte, - png_const_bytep row, png_alloc_size_t row_bytes) +write_row_buffered(png_structrp png_ptr, png_const_bytep row, + int first_row_in_pass, int last_pass_row, int end_of_image, + void (*copy_fn)(png_const_structrp png_ptr, png_bytep row_buffer, + png_const_bytep row, png_uint_32 x, unsigned int count, unsigned int p), + unsigned int copy_parameter) { - const png_uint_32 height = png_ptr->height; - png_uint_32 row_number = png_ptr->row_number; - int flush = Z_NO_FLUSH; + unsigned int max_pixels = png_max_pixel_block(png_ptr); + const unsigned int pass = png_ptr->pass; + const png_uint_32 width = png_ptr->interlaced == PNG_INTERLACE_NONE ? + png_ptr->width : PNG_PASS_COLS(png_ptr->width, pass); + png_uint_32 x; + unsigned int filters; + png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4 2-byte channels each */ - png_debug(1, "in png_write_finish_row"); + memset(prev_pixels, 0U, sizeof prev_pixels); - if (png_ptr->interlaced) + for (x = 0U, filters = 0U; x < width; x += max_pixels) { - unsigned int pass = png_ptr->pass; + int finish = 0; -# ifdef PNG_WRITE_INTERLACING_SUPPORTED - if (png_ptr->do_interlace) - { - /* 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 */ - - 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. - */ + union { - affirm(row != NULL); + PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment; + png_byte buffer[PNG_ROW_BUFFER_SIZE]; + } pixel_buffer; - if (++row_number == PNG_PASS_ROWS(height, pass)) - { - const png_uint_32 width = png_ptr->width; + if (max_pixels > width - x) + max_pixels = (unsigned int)/*SAFE*/(width - x); - /* Next pass, but it may not be present because of the width. */ - row_number = 0; + if (end_of_image && x + max_pixels >= width) + finish = 1; - for (;;) - { - if (++pass == PNG_INTERLACE_ADAM7_PASSES) - { - flush = Z_FINISH; - break; /* end of image */ - } + /* Copy a block of input pixels into the buffer, effecting the interlace + * on the way if required. The argument is the number of pixels in the + * buffer, not the number handled from the input which will be larger in + * the interlaced case. + */ + copy_fn(png_ptr, pixel_buffer.buffer, row, x, max_pixels, copy_parameter); - 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); -} - -/* Called by user to write a row of image data */ -void PNGAPI -png_write_row(png_structrp png_ptr, png_const_bytep row) -{ - 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; - - png_debug2(1, "in png_write_row (row %u, pass %d)", - png_ptr->row_number, png_ptr->pass); - - 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) - { - png_init_row_info(png_ptr); /* doesn't change row/pass/width */ - - /* 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. + /* Now pixel_buffer[0..max_pixels-1] contains max_pixels pixels which may + * need to be transformed (the interlace has already been handled). */ # 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 */ - } - - else if (row_number >= png_ptr->height || pass >= PNG_INTERLACE_ADAM7_PASSES) - png_error(png_ptr, "Too many calls to png_write_row"); - - /* 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 (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; - } - - /* 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. - */ - { - 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 */ + if (png_ptr->transform_list != NULL) { png_transform_control tc; /* The initial values are the memory format, this was worked out in - * png_init_row_info above. + * png_init_row_info below. */ memset(&tc, 0, sizeof tc); tc.png_ptr = png_ptr; - tc.sp = row; - tc.dp = png_ptr->row_buffer; + tc.sp = tc.dp = pixel_buffer.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.width = max_pixels; /* width of block that we have */ 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 */ + debug(PNG_TC_PIXEL_DEPTH(tc) == png_ptr->row_input_pixel_depth); /* Run the list. */ png_run_transform_list_backwards(png_ptr, &tc); @@ -910,88 +703,394 @@ png_write_row(png_structrp png_ptr, png_const_bytep row) /* 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); } +# endif /* WRITE_TRANSFORMS */ - else /* no transforms */ -# endif /* TRANSFORM_MECH */ - output_row = row; + /* Call png_write_filter_row to write this block of data, the test on + * maxpixels says if this is the final block in the row, 'filters' is + * initialized when 0 is 0 then preserved here for later blocks: + */ + filters = png_write_filter_row(png_ptr, prev_pixels, pixel_buffer.buffer, + x, max_pixels, first_row_in_pass, last_pass_row, filters, finish); + } +} +#endif /* WRITE { INTERLACING || TRANSFORMS } */ - output_bpp = PNG_PIXEL_DEPTH(*png_ptr); - output_bytes = PNG_ROWBYTES(output_bpp, row_width); +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +static void +copy_row(png_const_structrp png_ptr, png_bytep row_buffer, + png_const_bytep row, png_uint_32 x, unsigned int count, + unsigned int pixel_depth) +{ + /* Copy row[x..x+count] pixels to row_buffer. */ + png_copy_row(png_ptr, row_buffer, row, x, count, pixel_depth, 1/*clear*/); +} +#endif /* WRITE_TRANSFORMS */ -# ifdef PNG_WRITE_FILTER_SUPPORTED +#ifdef PNG_WRITE_INTERLACE_SUPPORTED +static void +interlace_row_lbd(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp, + png_uint_32 x, unsigned int count, const unsigned int B) +{ + /* 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. + */ + const unsigned int pass = png_ptr->pass; + png_uint_32 i = PNG_COL_FROM_PASS_COL(x, pass); + const unsigned int inc = PNG_PASS_COL_OFFSET(pass); + + /* 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) + { + unsigned int j; + unsigned int d; + + /* The data is always in the PNG, big-endian, format: */ + for (j = 8U, d = 0U; count > 0U; --count, i += inc) + { /* big-endian */ + j -= 1U<pass; + const unsigned int inc = PNG_PASS_COL_OFFSET(pass); + + /* 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 += PNG_COL_FROM_PASS_COL(x, pass) * cbytes; count > 0; + --count, sp += inc * cbytes, dp += cbytes) + memcpy(dp, sp, cbytes); +} +#endif /* WRITE_INTERLACE */ + +static void +write_row_unbuffered(png_structrp png_ptr, png_const_bytep row, + int first_row_in_pass, int last_pass_row, int end_of_image) +{ + /* Split the row into blocks of the appropriate size: */ + const unsigned int input_depth = png_ptr->row_input_pixel_depth; + unsigned int max_pixels = png_max_pixel_block(png_ptr); + png_uint_32 max_bytes = max_pixels; + const unsigned int pass = png_ptr->pass; + const png_uint_32 width = png_ptr->interlaced == PNG_INTERLACE_NONE ? + png_ptr->width : PNG_PASS_COLS(png_ptr->width, pass); + png_uint_32 x; + unsigned int filters; + png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4 2-byte channels each */ + + /* max_pixels is at most 16 bits, input_depth is at most 64, so the product + * always fits in 32 bits: + */ + max_bytes *= input_depth; /* bits */ + debug(input_depth <= 64 && (max_bytes & 7U) == 0U); + max_bytes >>= 3; + + memset(prev_pixels, 0U, sizeof prev_pixels); + + for (x = 0U, filters = 0U; x < width; x += max_pixels, row += max_bytes) + { + int finish = 0; + + if (max_pixels > width - x) + { + max_bytes = width - x; + max_pixels = (unsigned int)/*SAFE*/max_bytes; + max_bytes = (max_bytes * input_depth + 7U) >> 3; + } + + if (end_of_image && x + max_pixels >= width) + finish = 1; + + filters = png_write_filter_row(png_ptr, prev_pixels, row, x, max_pixels, + first_row_in_pass, last_pass_row, filters, finish); + } +} + +static void +write_row_core(png_structrp png_ptr, png_const_bytep row, + int first_row_in_pass, int last_pass_row, int end_of_image) +{ +# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + if (png_ptr->transform_list != NULL) + write_row_buffered(png_ptr, row, first_row_in_pass, last_pass_row, + end_of_image, copy_row, png_ptr->row_input_pixel_depth); + + else +# endif /* WRITE_TRANSFORMS */ + + /* If control reaches this point the intermediate buffer is not required and + * the input data can be used unmodified. + */ + write_row_unbuffered(png_ptr, row, first_row_in_pass, last_pass_row, + end_of_image); +} + +/* Write a single non-interlaced row. */ +static void +write_row_non_interlaced(png_structrp png_ptr, png_const_bytep row) +{ + const png_uint_32 row_number = png_ptr->row_number+1U; + const int last_pass_row = row_number == png_ptr->height; + + write_row_core(png_ptr, row, row_number == 1U, last_pass_row, + last_pass_row); + + if (!last_pass_row) + png_ptr->row_number = row_number; + + else + { + png_ptr->row_number = 0U; + png_ptr->pass = 7U; + } +} + +/* Write a single interlaced row. */ +static void +write_row_interlaced(png_structrp png_ptr, png_const_bytep row) +{ + /* row_number is the row in the pass. The app must only call png_write_row + * the correct number of times. 'pass' is set to 7U at the end. + */ + const png_uint_32 row_number = png_ptr->row_number+1U; + unsigned int pass = png_ptr->pass; + const int last_pass_row = row_number == PNG_PASS_ROWS(png_ptr->height, pass); + + write_row_core(png_ptr, row, row_number == 1U, last_pass_row, + last_pass_row && pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height)); + + if (!last_pass_row) + png_ptr->row_number = row_number; + + else + { + png_ptr->row_number = 0U; + + do + ++pass; + while (pass < 7U && + !PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, pass)); + + png_ptr->pass = 0x7U & pass; + } +} + +#ifdef PNG_WRITE_INTERLACE_SUPPORTED +/* Interlace a row then write it out. */ +static int +interlace_row(png_structrp png_ptr, png_const_bytep row) +{ + /* row_number is in the image, it may not be in the pass and, likewise, the + * pass may not exist. + */ + const png_uint_32 row_number = png_ptr->row_number; /* in image */ + const unsigned int pass = png_ptr->pass; + const int write_row = png_ptr->width > PNG_PASS_START_COL(pass) && + PNG_ROW_IN_INTERLACE_PASS(row_number, pass); + + if (write_row) + { + const int last_pass_row = + PNG_LAST_PASS_ROW(row_number, pass, png_ptr->height); + + if (pass < 6) + { + /* Libpng is doing the interlacing and pixels need to be selected + * from the input row for this pass. + */ + /* row interlacing uses either the log bit depth for low bit + * depth input or the byte count for 8bpp or bigger pixels. + */ + const unsigned int input_depth = png_ptr->row_input_pixel_depth; + unsigned int B = 0; /* log2(input_depth) */ + const int end_of_image = last_pass_row && + pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height); + + switch (input_depth) { - png_const_bytep unfiltered_row = output_row; - png_bytep alt_buffer = png_ptr->alt_buffer; + case 4U: /* B will be 2 */ + ++B; + /*FALL THROUGH*/ + case 2U: /* B will be 1 */ + ++B; + /*FALL THROUGH*/ + case 1U: /* B will be 0 */ + write_row_buffered(png_ptr, row, + row_number == PNG_PASS_START_ROW(pass), last_pass_row, + end_of_image, interlace_row_lbd, B); + break; - /* 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; - } - } + default: /* Parameter is the pixel size in bytes */ + write_row_buffered(png_ptr, row, + row_number == PNG_PASS_START_ROW(pass), last_pass_row, + end_of_image, interlace_row_byte, input_depth >> 3); + break; } -# 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); + else /* pass 6; no interlacing required */ + write_row_core(png_ptr, row, row_number == 1U, last_pass_row, + last_pass_row); } - /* 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, row_number, pass); + if (row_number+1U < png_ptr->height) + png_ptr->row_number = row_number+1U; + + else + { + png_ptr->row_number = 0U; + png_ptr->pass = 0x7U & (pass+1U); + } + + return write_row; +} +#endif /* WRITE_INTERLACE */ + +/* Called by user to write a row of image data */ +void PNGAPI +png_write_row(png_structrp png_ptr, png_const_bytep row) +{ + if (png_ptr != NULL) + { + const unsigned int pass = png_ptr->pass; + const png_uint_32 row_number = png_ptr->row_number; + + /* Unlike the read code initialization happens automatically: */ + if (row_number == 0 && pass == 0) + { + png_init_row_info(png_ptr); /* doesn't change row/pass/width */ + +# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + /* 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. + * + * 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 */ + } + + else if (pass == 7U) /* too many calls; write already ended */ + { + debug(png_ptr->row_number == 0U); + png_app_error(png_ptr, "Too many calls to png_write_row"); + return; + } + + /* Make sure there is a row to write: */ + if (row == NULL) + { + png_app_error(png_ptr, "NULL row pointer to png_write_row"); + return; + } + + if (png_ptr->interlaced == PNG_INTERLACE_NONE) + write_row_non_interlaced(png_ptr, row); + +# ifdef PNG_WRITE_INTERLACE_SUPPORTED + /* Optional: libpng does the interlacing, app passes every row of the + * image the required number of times. + */ + else if (png_ptr->do_interlace != 0U) + { + if (!interlace_row(png_ptr, row)) + return; /* row skipped; skip the write callback */ + } +# endif + + else /* app does the interlacing */ + write_row_interlaced(png_ptr, row); + + /* 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. + */ + if (png_ptr->write_row_fn != NULL) + (*(png_ptr->write_row_fn))(png_ptr, row_number, pass); + } /* png_ptr != NULL */ } #ifdef PNG_WRITE_FLUSH_SUPPORTED @@ -1050,15 +1149,6 @@ png_write_destroy(png_structrp png_ptr) png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); png_free(png_ptr, png_ptr->row_buffer); png_ptr->row_buffer = NULL; -#ifdef PNG_WRITE_FILTER_SUPPORTED - 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 @@ -1101,249 +1191,6 @@ 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) -{ - png_debug(1, "in png_set_filter"); - - if (png_ptr == NULL) - return; - - if (png_ptr->read_struct) - { - png_app_error(png_ptr, "png_set_filter: cannot be used when reading"); - return; - } - - if (method != png_ptr->filter_method) - { - png_app_error(png_ptr, "png_set_filter: method does not match IHDR"); - return; - } - - /* 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. - */ - 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. - */ - return; - } - - /* Finally store the value. */ - png_ptr->next_filter = PNG_BYTE(filters); -} -#endif /* WRITE_FILTER */ - -#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_app_warning(png_ptr, "weighted filter heuristics not implemented"); - PNG_UNUSED(heuristic_method) - PNG_UNUSED(num_weights) - PNG_UNUSED(filter_weights) - PNG_UNUSED(filter_costs) -} -#endif /* FLOATING_POINT */ - -#ifdef PNG_FIXED_POINT_SUPPORTED -PNG_FUNCTION(void,PNGAPI -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_app_warning(png_ptr, "weighted filter heuristics not implemented"); - PNG_UNUSED(heuristic_method) - PNG_UNUSED(num_weights) - PNG_UNUSED(filter_weights) - PNG_UNUSED(filter_costs) -} -#endif /* FIXED_POINT */ -#endif /* WRITE_WEIGHTED_FILTER */ - -#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED -void PNGAPI -png_set_compression_level(png_structrp png_ptr, int level) -{ - png_debug(1, "in png_set_compression_level"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_level = level; -} - -void PNGAPI -png_set_compression_mem_level(png_structrp png_ptr, int mem_level) -{ - png_debug(1, "in png_set_compression_mem_level"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_mem_level = mem_level; -} - -void PNGAPI -png_set_compression_strategy(png_structrp png_ptr, int strategy) -{ - png_debug(1, "in png_set_compression_strategy"); - - if (png_ptr == NULL) - return; - - /* The flag setting here prevents the libpng dynamic selection of strategy. - */ - png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; - png_ptr->zlib_strategy = strategy; -} - -/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a - * smaller value of window_bits if it can do so safely. - */ -void PNGAPI -png_set_compression_window_bits(png_structrp png_ptr, int window_bits) -{ - if (png_ptr == NULL) - return; - - /* Prior to 1.6.0 this would warn but then set the window_bits value. This - * meant that negative window bits values could be selected that would cause - * libpng to write a non-standard PNG file with raw deflate or gzip - * compressed IDAT or ancillary chunks. Such files can be read and there is - * no warning on read, so this seems like a very bad idea. - */ - if (window_bits > 15) - { - png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); - window_bits = 15; - } - - else if (window_bits < 8) - { - png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); - window_bits = 8; - } - - png_ptr->zlib_window_bits = window_bits; -} - -void PNGAPI -png_set_compression_method(png_structrp png_ptr, int method) -{ - png_debug(1, "in png_set_compression_method"); - - if (png_ptr == NULL) - return; - - /* This would produce an invalid PNG file if it worked, but it doesn't and - * deflate will fault it, so it is harmless to just warn here. - */ - if (method != 8) - png_warning(png_ptr, "Only compression method 8 is supported by PNG"); - - png_ptr->zlib_method = method; -} -#endif /* WRITE_CUSTOMIZE_COMPRESSION */ - -/* The following were added to libpng-1.5.4 */ -#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED -void PNGAPI -png_set_text_compression_level(png_structrp png_ptr, int level) -{ - png_debug(1, "in png_set_text_compression_level"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_text_level = level; -} - -void PNGAPI -png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level) -{ - png_debug(1, "in png_set_text_compression_mem_level"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_text_mem_level = mem_level; -} - -void PNGAPI -png_set_text_compression_strategy(png_structrp png_ptr, int strategy) -{ - png_debug(1, "in png_set_text_compression_strategy"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_text_strategy = strategy; -} - -/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a - * smaller value of window_bits if it can do so safely. - */ -void PNGAPI -png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits) -{ - if (png_ptr == NULL) - return; - - if (window_bits > 15) - { - png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); - window_bits = 15; - } - - else if (window_bits < 8) - { - png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); - window_bits = 8; - } - - png_ptr->zlib_text_window_bits = window_bits; -} - -void PNGAPI -png_set_text_compression_method(png_structrp png_ptr, int method) -{ - png_debug(1, "in png_set_text_compression_method"); - - if (png_ptr == NULL) - return; - - if (method != 8) - png_warning(png_ptr, "Only compression method 8 is supported by PNG"); - - png_ptr->zlib_text_method = method; -} -#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ -/* end of API added to libpng-1.5.4 */ - void PNGAPI png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn) { diff --git a/pngwtran.c b/pngwtran.c index 56ff95053..4c9db2f1a 100644 --- a/pngwtran.c +++ b/pngwtran.c @@ -14,236 +14,6 @@ #include "pngpriv.h" #define PNG_SRC_FILE PNG_SRC_FILE_pngwtran -#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_write_interlace_lbd(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); - - - 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. - */ - 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 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) - { - unsigned int j; - unsigned int d; - -# 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<fn = NULL; /* remove me to caller */ -} - -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); - - 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. - */ - 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; - - 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 -} - -void /* PRIVATE */ -png_set_write_interlace(png_structrp png_ptr) -{ - /* 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 /* Pack pixels into bytes. */ static void diff --git a/pngwutil.c b/pngwutil.c index f9bfe28ba..c16826e84 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -339,13 +339,17 @@ png_deflate_claim(png_structrp png_ptr, png_uint_32 owner, #ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED if ((png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY) != 0) strategy = png_ptr->zlib_strategy; - - else if (png_ptr->next_filter != PNG_FILTER_NONE) - strategy = PNG_Z_DEFAULT_STRATEGY; - else -#endif - strategy = PNG_Z_DEFAULT_NOFILTER_STRATEGY; +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_FILTER_SUPPORTED + if (png_ptr->filter_mask != PNG_FILTER_NONE) + strategy = PNG_Z_DEFAULT_STRATEGY; + else +#endif /* WRITE_FILTER */ + + /* The default with no filters: */ + strategy = PNG_Z_DEFAULT_NOFILTER_STRATEGY; } else @@ -889,14 +893,14 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, # ifdef PNG_WRITE_FILTER_SUPPORTED /* TODO: review this setting */ - if (png_ptr->next_filter == PNG_NO_FILTERS /* not yet set */) + if (png_ptr->filter_mask == 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; + png_ptr->filter_mask = PNG_FILTER_NONE; else - png_ptr->next_filter = PNG_ALL_FILTERS; + png_ptr->filter_mask = PNG_ALL_FILTERS; } # endif @@ -1952,409 +1956,726 @@ png_write_tIME(png_structrp png_ptr, png_const_timep mod_time) } #endif +static void +write_filtered_row(png_structrp png_ptr, png_const_bytep filtered_row, + png_uint_32 row_bytes, png_byte filter /*if at start of row*/, + int end_of_image) +{ + /* This handles writing a row that has been filtered, or did not need to be + * filtered. If the data row has a partial pixel it must have been handled + * correctly in the caller; filters generate a full 8 bits even if the pixel + * only has one significant bit! + */ + debug(row_bytes > 0); + + if (filter < PNG_FILTER_VALUE_LAST) /* start of row */ + { + png_byte buffer[1]; + + buffer[0] = filter; + png_compress_IDAT(png_ptr, buffer, 1U/*len*/, Z_NO_FLUSH); + } + + png_compress_IDAT(png_ptr, filtered_row, row_bytes, + end_of_image ? Z_FINISH : Z_NO_FLUSH); +} + +static void +write_unfiltered_rowbits(png_structrp png_ptr, png_const_bytep filtered_row, + png_uint_32 row_bits, png_byte filter /*if at start of row*/, + int end_of_image) +{ + /* Same as above, but it correctly clears the unused bits in a partial + * byte. + */ + const png_uint_32 row_bytes = row_bits >> 3; + + debug(filter == PNG_FILTER_VALUE_NONE || filter == PNG_FILTER_VALUE_LAST); + + if (row_bytes > 0U) + { + row_bits -= row_bytes << 3; + write_filtered_row(png_ptr, filtered_row, row_bytes, filter, + end_of_image && row_bits == 0U); + filter = PNG_FILTER_VALUE_LAST; /* written */ + } + + /* Handle a partial byte. */ + if (row_bits > 0U) + { + png_byte buffer[1]; + + buffer[0] = PNG_BYTE(filtered_row[row_bytes] & ~(0xFFU >> row_bits)); + write_filtered_row(png_ptr, buffer, 1U, filter, end_of_image); + } +} + +#ifdef PNG_WRITE_FILTER_SUPPORTED +static void +filter_block_singlebyte(png_alloc_size_t row_bytes, png_bytep sub_row, + png_bytep up_row, png_bytep avg_row, png_bytep paeth_row, + png_const_bytep row, png_const_bytep prev_row, png_bytep prev_pixels) +{ + /* Calculate rows for all four filters where the input has one byte per pixel + * (more accurately per filter-unit). + */ + png_byte a = prev_pixels[0]; + png_byte c = prev_pixels[1]; + + while (row_bytes-- > 0U) + { + const png_byte x = *row++; + const png_byte b = prev_row == NULL ? 0U : *prev_row++; + + /* Calculate each filtered byte in turn: */ + if (sub_row != NULL) *sub_row++ = 0xFFU & (x - a); + if (up_row != NULL) *up_row++ = 0xFFU & (x - b); + if (avg_row != NULL) *avg_row++ = 0xFFU & (x - (a+b)/2U); + + /* Paeth is a little more difficult: */ + if (paeth_row != NULL) + { + int pa = b-c; /* a+b-c - a */ + int pb = a-c; /* a+b-c - b */ + int pc = pa+pb; /* a+b-c - c = b-c + a-c */ + png_byte p = a; + + pa = abs(pa); + pb = abs(pb); + if (pa > pb) pa = pb, p = b; + if (pa > abs(pc)) p = c; + + *paeth_row++ = 0xFFU & (x - p); + } + + /* And set a and c for the next pixel: */ + a = x; + c = b; + } + + /* Store a and c for the next block: */ + prev_pixels[0] = a; + prev_pixels[1] = c; +} + +static void +filter_block_multibyte(png_alloc_size_t row_bytes, + const unsigned int bpp, png_bytep sub_row, png_bytep up_row, + png_bytep avg_row, png_bytep paeth_row, png_const_bytep row, + png_const_bytep prev_row, png_bytep prev_pixels) +{ + /* Calculate rows for all four filters, the input is a block of bytes such + * that row_bytes is a multiple of bpp. bpp can be 2, 3, 4, 6 or 8. + * prev_pixels will be updated to the last pixels processed. + */ + while (row_bytes >= bpp) + { + unsigned int i; + + for (i=0; i pb) pa = pb, p = b; + if (pa > abs(pc)) p = c; + + *paeth_row++ = 0xFFU & (x - p); + } + } + + row_bytes -= i; + } +} + +static void +filter_row(png_structrp png_ptr, png_const_bytep prev_row, + png_bytep prev_pixels, png_const_bytep unfiltered_row, + const png_uint_32 row_bits, unsigned int bpp, unsigned int filters_to_try, + int start_of_row, int end_of_image) +{ + /* filters_to_try identifies a single filter and it is not PNG_FILTER_NONE. + */ + png_uint_32 row_bytes = row_bits >> 3; /* complete bytes */ + png_byte filter = PNG_FILTER_VALUE_LAST /* not at start */; + png_byte filtered_row[PNG_ROW_BUFFER_SIZE]; + + debug((row_bits % bpp) == 0U); + + if (start_of_row) switch (filters_to_try) + { + case PNG_FILTER_SUB: filter = PNG_FILTER_VALUE_SUB; break; + case PNG_FILTER_UP: filter = PNG_FILTER_VALUE_UP; break; + case PNG_FILTER_AVG: filter = PNG_FILTER_VALUE_AVG; break; + case PNG_FILTER_PAETH: filter = PNG_FILTER_VALUE_PAETH; break; + default: + impossible("filter list"); + } + + if (bpp <= 8U) + { + /* There may be a partial byte at the end. */ + if (row_bytes > 0) + filter_block_singlebyte(row_bytes, + filters_to_try & PNG_FILTER_SUB ? filtered_row : NULL, + filters_to_try & PNG_FILTER_UP ? filtered_row : NULL, + filters_to_try & PNG_FILTER_AVG ? filtered_row : NULL, + filters_to_try & PNG_FILTER_PAETH ? filtered_row : NULL, + unfiltered_row, prev_row, prev_pixels); + + /* The partial byte must be handled correctly here; both the previous row + * value and the current value need to have non-present bits cleared. + */ + if ((row_bits & 7U) != 0) + { + const png_byte mask = PNG_BYTE(~(0xFFU >> (row_bits & 7U))); + png_byte buffer[2]; + + buffer[0] = unfiltered_row[row_bytes] & mask; + + if (prev_row != NULL) + buffer[1U] = prev_row[row_bytes] & mask; + + else + buffer[1U] = 0U; + + filter_block_singlebyte(1U, + filters_to_try & PNG_FILTER_SUB ? filtered_row+row_bytes : NULL, + filters_to_try & PNG_FILTER_UP ? filtered_row+row_bytes : NULL, + filters_to_try & PNG_FILTER_AVG ? filtered_row+row_bytes : NULL, + filters_to_try & PNG_FILTER_PAETH ? filtered_row+row_bytes : NULL, + buffer, buffer+1U, prev_pixels); + + ++row_bytes; /* for write_filtered_row below */ + } + } + + else + { + debug((bpp & 7U) == 0U && row_bits == (row_bytes << 3)); + filter_block_multibyte(row_bytes, bpp >> 3, + filters_to_try & PNG_FILTER_SUB ? filtered_row : NULL, + filters_to_try & PNG_FILTER_UP ? filtered_row : NULL, + filters_to_try & PNG_FILTER_AVG ? filtered_row : NULL, + filters_to_try & PNG_FILTER_PAETH ? filtered_row : NULL, + unfiltered_row, prev_row, prev_pixels); + } + + write_filtered_row(png_ptr, filtered_row, row_bytes, filter, end_of_image); +} + +static void +find_filter(png_structrp png_ptr, png_const_bytep prev_row, + png_bytep prev_pixels, png_const_bytep unfiltered_row, + png_uint_32 row_bits, unsigned int bpp, unsigned int filters_to_try, + int start_of_row, int end_of_image) +{ + /* filters_to_try identifies multiple filters, up to all five. */ + /* TODO: reimplement this, currently this just selects the first filter */ + filters_to_try &= -filters_to_try; + if (filters_to_try == PNG_FILTER_NONE) + write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits, + start_of_row ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST, + end_of_image); + + else + filter_row(png_ptr, prev_row, prev_pixels, unfiltered_row, row_bits, bpp, + filters_to_try & -filters_to_try, start_of_row, end_of_image); +} + /* 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. */ -#ifdef PNG_WRITE_FILTER_SUPPORTED -static png_size_t /* PRIVATE */ -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) +unsigned int /* PRIVATE */ +png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels, + png_const_bytep unfiltered_row, png_uint_32 x, + png_uint_32 width/*pixels*/, int first_row_in_pass, int last_pass_row, + unsigned int filters_to_try, int end_of_image) { - png_size_t sum = 0; + png_bytep prev_row = png_ptr->row_buffer; + const unsigned int bpp = png_ptr->row_output_pixel_depth; + const png_uint_32 row_bits = width * bpp; - /* Advance one pixel, or one byte, whichever is greater: */ + /* These invariants are expected from the caller: */ + affirm(width < 65536U && bpp <= 64U && row_bits <= 8U*PNG_ROW_BUFFER_SIZE); + + if (x == 0U) /* start of row */ { - int i; + /* Delaying initialization of the filter stuff. */ + if (png_ptr->filter_mask == 0U) + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_ALL_FILTERS); - for (i = 0; i < bpp; i++, rp++, dp++) + /* Now work out the filters to try for this row: */ + filters_to_try = png_ptr->filter_mask; /* else caller must preserve */ + + /* If this has a previous row filter in the set to try ensure the row + * buffer exists and ensure it is empty when first allocated and at + * the start of the pass. + */ + if ((filters_to_try & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) + != 0U) { - int v = *dp = *rp; - sum += (v < 128) ? v : 256 - v; - } - } - - /* Do the 'sub' filter on the corresponding preceding byte: */ - { - png_alloc_size_t i; - - for (i = bpp; i < row_bytes; i++, rp++, dp++) - { - int v = *dp = PNG_BYTE(rp[0] - rp[-bpp]); - - if (lmins) + if (prev_row == NULL) { - sum += (v < 128) ? v : 256 - v; + /* Just allocate for the total output row bytes; a three-row + * interlaced image requires less, but this is safe. + */ + prev_row = png_voidcast(png_bytep, png_malloc(png_ptr, + png_calc_rowbytes(png_ptr, bpp, png_ptr->width))); + png_ptr->row_buffer = prev_row; - if (sum > lmins) /* We are already worse, don't continue. */ - break; + /* If that buffer would have been required for this row issue an + * app warning and disable the filters that would have required + * the data. + */ + if (!first_row_in_pass) + { + png_app_warning(png_ptr, "Previous row filters ignored"); + /* And always turn off the filters, to prevent using + * uninitialized data. + */ + filters_to_try &= PNG_BIC_MASK( + PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); + + if (filters_to_try == 0U) + filters_to_try = PNG_FILTER_NONE; + } } } - } - return sum; -} - -static png_size_t /* PRIVATE */ -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_alloc_size_t i; - png_size_t sum = 0; - - for (i = 0; i < row_bytes; i++, rp++, pp++, dp++) - { - int v = *dp = PNG_BYTE(*rp - *pp); - - if (lmins) + /* The filters are pre-calculated in png_set_filter, however if the + * image is interlaced some passes may still be too narrow or short to + * allow certain filters. In any case the first row of the pass + * doesn't need to consider PAETH or UP (AVG is still different). + */ + if (first_row_in_pass) { - sum += (v < 128) ? v : 256 - v; - - if (sum > lmins) /* We are already worse, don't continue. */ - break; - } - } - - return sum; -} - -static png_size_t /* PRIVATE */ -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_size_t sum = 0; - - { - int i; - - for (i = 0; i < bpp; i++, rp++, pp++, dp++) - { - unsigned int v = *dp = PNG_BYTE(*rp - *pp / 2); - sum += (v < 128) ? v : 256 - v; - } - } - - { - png_alloc_size_t i; - - for (i = bpp; i < row_bytes; i++, rp++, pp++, dp++) - { - unsigned int v = *dp = PNG_BYTE(rp[0] - (*pp + rp[-bpp]) / 2); - - if (lmins) + if ((filters_to_try & PNG_FILTER_UP) != 0U) { - sum += (v < 128) ? v : 256 - v; - - if (sum > lmins) /* We are already worse, don't continue. */ - break; + filters_to_try &= PNG_BIC_MASK(PNG_FILTER_UP); + filters_to_try |= PNG_FILTER_NONE; } - } - } - return sum; -} + if ((filters_to_try & PNG_FILTER_PAETH) != 0U) + { + filters_to_try &= PNG_BIC_MASK(PNG_FILTER_PAETH); + filters_to_try |= PNG_FILTER_SUB/*equialent to PAETH here*/; + } -static png_size_t /* PRIVATE */ -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_size_t sum = 0; - - { - int i; - - 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; - } - } - - { - png_alloc_size_t i; - - for (i = bpp; i < row_bytes; i++, pp++, rp++, dp++) - { - int a, b, c, pa, pb, pc, p, v; - - 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); -#else - pa = p < 0 ? -p : p; - pb = pc < 0 ? -pc : pc; - pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#if 0 + /* If this leaves the AVG filter it will be used on the first row + * this is handled in the filter implementation by setting prev_row + * to NULL below. + */ +#else /* DOESN'T WORK (problems in the read code?) */ + if ((filters_to_try & PNG_FILTER_AVG) != 0U) + { + filters_to_try &= PNG_BIC_MASK(PNG_FILTER_AVG); + filters_to_try |= PNG_FILTER_NONE; + } #endif + } - p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; - v = *dp = PNG_BYTE(*rp - p); - - if (lmins) + /* Check for a narrow image; the blocking will never return just one + * pixel at the start unless the pass is only one pixel wide, this test + * needs to happen after the one above on PAETH: + */ + if (width == 1U) + { + if ((filters_to_try & PNG_FILTER_SUB) != 0U) { - sum += (v < 128) ? v : 256 - v; - - if (sum > lmins) /* We are already worse, don't continue. */ - break; + filters_to_try &= PNG_BIC_MASK(PNG_FILTER_SUB); + filters_to_try |= PNG_FILTER_NONE; } } + } /* start of row */ + + else if (prev_row != NULL) + { + /* Advance prev_row to the corresponding pixel above row[x], must use + * png_calc_rowbytes here otherwise the calculation using x might + * overflow. + */ + debug(((x * bpp) & 7U) == 0U); + prev_row += png_calc_rowbytes(png_ptr, bpp, x); } - return sum; + /* Now choose the correct filter implementation according to the number of + * filters in the filters_to_try list. The prev_row parameter is made NULL + * on the first row because it is uninitialized at that point. + */ + if (filters_to_try == PNG_FILTER_NONE) + write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits, + x == 0 ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST, + end_of_image); + + else if ((filters_to_try & -filters_to_try) == filters_to_try) /* 1 filter */ + filter_row(png_ptr, first_row_in_pass ? NULL : prev_row, + prev_pixels, unfiltered_row, row_bits, bpp, filters_to_try, x == 0, + end_of_image); + + else + find_filter(png_ptr, first_row_in_pass ? NULL : prev_row, + prev_pixels, unfiltered_row, row_bits, bpp, filters_to_try, x == 0, + end_of_image); + + /* Copy the current row into the previous row buffer, if available, unless + * this is the last row in the pass, when there is no point. Note that + * prev_row may have garbage in a partial byte at the end. + */ + if (prev_row != NULL && !last_pass_row) + memcpy(prev_row, unfiltered_row, (row_bits + 7U) >> 3); + + return filters_to_try; } -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. - */ +/* Allow the application to select one or more row filters to use. */ +void PNGAPI +png_set_filter(png_structrp png_ptr, int method, int filtersIn) { - int n = 0; - png_bytep p = png_ptr->write_row[n]; + unsigned int filters; - debug(in_use != NULL); + png_debug(1, "in png_set_filter"); - if (p == in_use) - p = png_ptr->write_row[++n]; + if (png_ptr == NULL) + return; - 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))); + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_filter: cannot be used when reading"); + return; + } - return p; + if (method != png_ptr->filter_method) + { + png_app_error(png_ptr, "png_set_filter: method does not match IHDR"); + return; + } + + /* 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. + */ + if (filtersIn >= 0 && filtersIn < PNG_FILTER_VALUE_LAST) + filters = 8U << filtersIn; + + else if ((filtersIn & PNG_BIC_MASK(PNG_ALL_FILTERS)) == 0) + filters = filtersIn & PNG_ALL_FILTERS; + + else + { + 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. + */ + return; + } + + /* New in 1.7.0: adjust the mask according to the image characteristics. + * This used to happen on every row, doing it here means that these checks + * happen only once every png_set_filter call, or once per image. + */ + if (filters != PNG_FILTER_NONE) + { + /* Test to see if there are enough rows to allow previous-row filters to + * work. Note that the AVG filter is still significant because it uses + * half the value of the previous pixel as the predictor, but it is + * ignored in this case. + */ + if (png_ptr->height <= (png_ptr->interlaced == PNG_INTERLACE_NONE ? 1U : + (png_ptr->width == 1U ? 3U : 2U))) + { + /* Replace 'up' by the equivalent 'none': */ + if ((filters & (PNG_FILTER_UP)) != 0) + { + filters &= PNG_BIC_MASK(PNG_FILTER_UP); + filters |= PNG_FILTER_NONE; + } + + /* Replace 'paeth' by the equivalent 'sub': */ + if ((filters & PNG_FILTER_PAETH) != 0) + { + filters &= PNG_BIC_MASK(PNG_FILTER_PAETH); + filters |= PNG_FILTER_SUB; + } + + /* Remove 'avg' unless it is the only filter in which case 'none' is + * used. (This chooses compression speed of very short images over a + * probably pointless compression option for a one line image; short + * images are common, the sub-case which benefits from AVG is not. + */ + if ((filters & PNG_FILTER_AVG) != 0) + { + filters &= PNG_BIC_MASK(PNG_FILTER_AVG); + if (filters == 0U) + filters |= PNG_FILTER_NONE; + } + } + + /* Also check for SUB on narrow images; it's equivalent to NONE on the + * first pixel. + */ + if (png_ptr->width <= (png_ptr->interlaced == PNG_INTERLACE_NONE ? 1U : + (png_ptr->height == 1U ? 3U : 1U))) + { + if ((filters & PNG_FILTER_SUB) != 0) + { + filters &= PNG_BIC_MASK(PNG_FILTER_SUB); + filters |= PNG_FILTER_NONE; + } + } + } + + debug(filters != 0U && (filters & PNG_BIC_MASK(PNG_ALL_FILTERS)) == 0U); + + png_ptr->filter_mask = png_check_bits(png_ptr, filters, 8); } - -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) +#else /* !WRITE_FILTER */ +unsigned int /* PRIVATE */ +png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels, + png_const_bytep unfiltered_row, png_uint_32 x, + png_uint_32 width/*pixels*/, int first_row_in_pass, int last_pass_row, + unsigned int filters_to_try/*from previous call*/, int end_of_image) { - png_const_bytep best_row; - png_size_t mins; - unsigned int filter_to_do = png_ptr->next_filter; - png_byte best_filter; + const unsigned int bpp = png_ptr->row_output_pixel_depth; + png_uint_32 row_bits; - png_debug(1, "in png_write_find_filter"); + row_bits = width; + row_bits *= bpp; + /* These invariants are expected from the caller: */ + affirm(width < 65536U && bpp <= 64U && row_bits <= 8U*PNG_ROW_BUFFER_SIZE); - /* 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 */ - } + write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits, + x == 0 ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST, end_of_image); - if (first_pass_row || prev_row == NULL) - filter_to_do &= - PNG_BIC_MASK(PNG_FILTER_UP+PNG_FILTER_AVG+PNG_FILTER_PAETH); + return filters_to_try; - /* 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); - } - - mins = PNG_SIZE_MAX - 256/* so we can detect potential overflow of the - running sum */; - - /* The prediction method we use is to find which method provides the - * smallest value when summing the absolute values of the distances - * from zero, using anything >= 128 as negative numbers. This is known - * as the "minimum sum of absolute differences" heuristic. Other - * heuristics are the "weighted minimum sum of absolute differences" - * (experimental and can in theory improve compression), and the "zlib - * predictive" method (not implemented yet), which does test compressions - * of lines using different filter methods, and then chooses the - * (series of) filter(s) that give minimum compressed data size (VERY - * computationally expensive). - * - * GRR 980525: consider also - * - * (1) minimum sum of absolute differences from running average (i.e., - * keep running sum of non-absolute differences & count of bytes) - * [track dispersion, too? restart average if dispersion too large?] - * - * (1b) minimum sum of absolute differences from sliding average, probably - * with window size <= deflate window (usually 32K) - * - * (2) minimum sum of squared differences from zero or running average - * (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. - */ - if (filter_to_do <= PNG_FILTER_NONE) /* no filters to chose from */ - { - *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; i < row_bytes; i++, rp++) - { - /* Check for overflow */ - if (sum > PNG_SIZE_MAX/128 - 256) - break; - - v = *rp; - sum += (v < 128) ? v : 256 - v; - } - } - else /* Overflow is not possible */ - { - for (i = 0, rp = row_buf; i < row_bytes; i++, rp++) - { - v = *rp; - sum += (v < 128) ? v : 256 - v; - } - } - - mins = sum; - } - - /* Sub filter */ - if (filter_to_do == PNG_FILTER_SUB) - /* It's the only filter so no testing is needed */ - { - 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_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 = tst_row; - best_filter = PNG_FILTER_VALUE_SUB; - } - } - - /* Up filter */ - if (filter_to_do == PNG_FILTER_UP) - { - 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_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 = tst_row; - best_filter = PNG_FILTER_VALUE_UP; - } - } - - /* Avg filter */ - if (filter_to_do == PNG_FILTER_AVG) - { - 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_bytep tst_row = test_buffer(png_ptr, best_row); - - sum = png_setup_avg_row(bpp, row_bytes, mins, row_buf, prev_row, - tst_row); - - if (sum < mins) - { - mins = sum; - best_row = tst_row; - best_filter = PNG_FILTER_VALUE_AVG; - } - } - - /* Paeth filter */ - if (filter_to_do == PNG_FILTER_PAETH) - { - 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_bytep tst_row = test_buffer(png_ptr, best_row); - - sum = png_setup_paeth_row(bpp, row_bytes, mins, row_buf, prev_row, - tst_row); - - if (sum < mins) - { - mins = sum; - best_row = tst_row; - best_filter = PNG_FILTER_VALUE_PAETH; - } - } - - /* Do the actual writing of the filtered row data from the chosen filter. */ - affirm(best_filter < PNG_FILTER_VALUE_LAST); - *filter_byte = best_filter; - return best_row; + PNG_UNUSED(first_row_in_pass); + PNG_UNUSED(prev_pixels); + PNG_UNUSED(last_pass_row); } -#endif /* WRITE_FILTER */ +#endif /* !WRITE_FILTER */ + +#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_app_warning(png_ptr, "weighted filter heuristics not implemented"); + PNG_UNUSED(heuristic_method) + PNG_UNUSED(num_weights) + PNG_UNUSED(filter_weights) + PNG_UNUSED(filter_costs) +} +#endif /* FLOATING_POINT */ + +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_FUNCTION(void,PNGAPI +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_app_warning(png_ptr, "weighted filter heuristics not implemented"); + PNG_UNUSED(heuristic_method) + PNG_UNUSED(num_weights) + PNG_UNUSED(filter_weights) + PNG_UNUSED(filter_costs) +} +#endif /* FIXED_POINT */ +#endif /* WRITE_WEIGHTED_FILTER */ + +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +void PNGAPI +png_set_compression_level(png_structrp png_ptr, int level) +{ + png_debug(1, "in png_set_compression_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_level = level; +} + +void PNGAPI +png_set_compression_mem_level(png_structrp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_compression_mem_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_mem_level = mem_level; +} + +void PNGAPI +png_set_compression_strategy(png_structrp png_ptr, int strategy) +{ + png_debug(1, "in png_set_compression_strategy"); + + if (png_ptr == NULL) + return; + + /* The flag setting here prevents the libpng dynamic selection of strategy. + */ + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; +} + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +void PNGAPI +png_set_compression_window_bits(png_structrp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + + /* Prior to 1.6.0 this would warn but then set the window_bits value. This + * meant that negative window bits values could be selected that would cause + * libpng to write a non-standard PNG file with raw deflate or gzip + * compressed IDAT or ancillary chunks. Such files can be read and there is + * no warning on read, so this seems like a very bad idea. + */ + if (window_bits > 15) + { + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + window_bits = 15; + } + + else if (window_bits < 8) + { + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); + window_bits = 8; + } + + png_ptr->zlib_window_bits = window_bits; +} + +void PNGAPI +png_set_compression_method(png_structrp png_ptr, int method) +{ + png_debug(1, "in png_set_compression_method"); + + if (png_ptr == NULL) + return; + + /* This would produce an invalid PNG file if it worked, but it doesn't and + * deflate will fault it, so it is harmless to just warn here. + */ + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + + png_ptr->zlib_method = method; +} +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +/* The following were added to libpng-1.5.4 */ +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +void PNGAPI +png_set_text_compression_level(png_structrp png_ptr, int level) +{ + png_debug(1, "in png_set_text_compression_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_level = level; +} + +void PNGAPI +png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_text_compression_mem_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_mem_level = mem_level; +} + +void PNGAPI +png_set_text_compression_strategy(png_structrp png_ptr, int strategy) +{ + png_debug(1, "in png_set_text_compression_strategy"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_strategy = strategy; +} + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +void PNGAPI +png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + + if (window_bits > 15) + { + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + window_bits = 15; + } + + else if (window_bits < 8) + { + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); + window_bits = 8; + } + + png_ptr->zlib_text_window_bits = window_bits; +} + +void PNGAPI +png_set_text_compression_method(png_structrp png_ptr, int method) +{ + png_debug(1, "in png_set_text_compression_method"); + + if (png_ptr == NULL) + return; + + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + + png_ptr->zlib_text_method = method; +} +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +/* end of API added to libpng-1.5.4 */ + #endif /* WRITE */ diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa index 9331e0267..e13680e98 100644 --- a/scripts/pnglibconf.dfa +++ b/scripts/pnglibconf.dfa @@ -593,9 +593,10 @@ option WRITE_SWAP_ALPHA requires WRITE_TRANSFORMS enables TRANSFORM_MECH option WRITE_INVERT_ALPHA requires WRITE_TRANSFORMS option WRITE_USER_TRANSFORM requires WRITE_TRANSFORMS enables TRANSFORM_MECH -# This is not required for PNG-compliant encoders, but can cause -# trouble if left undefined -option WRITE_INTERLACING requires WRITE enables TRANSFORM_MECH +# This just disables the code within libpng to implement image interlacing on +# write; the app can still write interlaced images by doing it itself. +option WRITE_INTERLACING disabled +option WRITE_INTERLACE requires WRITE enables WRITE_INTERLACING # The following is no longer implemented: option WRITE_WEIGHTED_FILTER requires WRITE diff --git a/tests/pngvalid-interlace-transform b/tests/pngvalid-interlace-transform new file mode 100755 index 000000000..c0cef1ce6 --- /dev/null +++ b/tests/pngvalid-interlace-transform @@ -0,0 +1,2 @@ +#!/bin/sh +exec ./pngvalid --strict --transform --interlace