From 4905ed4aae1f1b832aa6903f592d1c95dee24228 Mon Sep 17 00:00:00 2001 From: John Bowler Date: Mon, 9 May 2016 13:08:50 -0700 Subject: [PATCH] Reenable filter selection (dummy) This implements the code for row-by-row filter selection but does not provide an actual implementation; the selection function just chooses the lowest set filter bit. Signed-off-by: John Bowler --- png.h | 6 +- pngpriv.h | 2 +- pngwutil.c | 758 ++++++++++++++++++++-------------- scripts/pnglibconf.dfa | 25 +- scripts/pnglibconf.h.prebuilt | 4 +- 5 files changed, 458 insertions(+), 337 deletions(-) diff --git a/png.h b/png.h index 82414521a..8267e4fac 100644 --- a/png.h +++ b/png.h @@ -1692,9 +1692,9 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, * when there is no previous row. * * 2) PNG_SELECT_FILTER_SUPPORTED: - * libpng will buffer rows until enough data is available to perform a - * reasonable filter selection heuristic then select filters for at least the - * first buffered row. + * If the PNG rows are long enough (have enough bytes; at least 256) libpng + * will buffer each row and perform a filter selection heuristic to chose an + * appropriate filter. * * 3) !PNG_SELECT_FILTER_SUPPORTED: * libpng selects the first filter in the list (there is no warning that this diff --git a/pngpriv.h b/pngpriv.h index d6bb433fa..c93b20b65 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -390,7 +390,7 @@ #define PNG_SRC_FILE_pngwtran (PNG_SRC_FILE_pngwrite +4096) #define PNG_SRC_FILE_pngwutil (PNG_SRC_FILE_pngwtran +1024) -#define PNG_SRC_FILE_arm_arm_init (PNG_SRC_FILE_pngwutil +4096) +#define PNG_SRC_FILE_arm_arm_init (PNG_SRC_FILE_pngwutil +8192) #define PNG_SRC_FILE_arm_filter_neon_intrinsics\ (PNG_SRC_FILE_arm_arm_init +1024) diff --git a/pngwutil.c b/pngwutil.c index 1f58c8fdf..9f829918e 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -848,10 +848,11 @@ typedef struct png_zlib_state * rows are required while if no filter selection is to be done only the * previous row pointer is required. */ - png_bytep previous_write_row; - png_alloc_size_t write_row_size; /* Actual size of the buffers */ - png_uint_32 save_row_count; /* Total number to be buffered */ -# define SAVE_ROW_COUNT_UNSET 0xFFFFFFFFU + png_alloc_size_t write_row_size; /* Actual size of the buffers */ + png_bytep previous_write_row; /* Last row written, if any */ +# ifdef PNG_SELECT_FILTER_SUPPORTED + png_bytep current_write_row; /* Row being written */ +# endif /* SELECT_FILTER */ unsigned int row_buffer_max_pixels; /* The maximum number of pixels that can fit in PNG_ROW_BUFFER_SIZE @@ -863,8 +864,15 @@ typedef struct png_zlib_state */ unsigned int filter_mask :8; /* mask of filters to consider on NEXT row */ +# define PREVIOUS_ROW_FILTERS\ + (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH) unsigned int filters :8; /* Filters for current row */ - unsigned int do_select :1; /* Set if filter selection should be done */ + unsigned int save_row :2; /* As below: */ +# define SAVE_ROW_UNSET 0U +# define SAVE_ROW_OFF 1U /* Previous-row filters will be ignored */ +# define SAVE_ROW_DEFAULT 2U /* Default to save rows set by libpng */ +# define SAVE_ROW_ON 3U /* Force rows to be saved */ +# define SAVE_ROW(ps) ((ps)->save_row >= SAVE_ROW_DEFAULT) # endif /* WRITE_FILTER */ /* Compression settings: see below for how these are encoded. */ @@ -894,7 +902,10 @@ png_create_zlib_state(png_structrp png_ptr) png_zlib_compress_init(png_ptr, &ps->s); # ifdef PNG_WRITE_FILTER_SUPPORTED - ps->save_row_count = SAVE_ROW_COUNT_UNSET; + ps->previous_write_row = NULL; +# ifdef PNG_SELECT_FILTER_SUPPORTED + ps->current_write_row = NULL; +# endif /* SELECT_FILTER */ # endif /* WRITE_FILTER */ # ifdef PNG_WRITE_FLUSH_SUPPORTED /* Set this to prevent flushing by making it larger than the number @@ -915,11 +926,19 @@ png_deflate_release(png_structrp png_ptr, png_zlib_statep ps, int check) /* Free any mode-specific data that is owned here: */ if (ps->previous_write_row != NULL) { - /* No saved rows, so the previous row buffer was allocated: */ png_bytep p = ps->previous_write_row; ps->previous_write_row = NULL; png_free(png_ptr, p); } + +# ifdef PNG_SELECT_FILTER_SUPPORTED + if (ps->current_write_row != NULL) + { + png_bytep p = ps->current_write_row; + ps->current_write_row = NULL; + png_free(png_ptr, p); + } +# endif /* SELECT_FILTER */ # endif /* WRITE_FILTER */ /* The main z_stream opaque pointer needs to remain set to png_ptr; it is @@ -2867,7 +2886,7 @@ png_write_end_row(png_structrp png_ptr, int flush) /* NOTE: the last row of the original image may not be in the pass, in * this case the code which skipped the row must do the increment * below! See 'interlace_row' in pngwrite.c and the code in - * write_png_rows below. + * png_write_png_rows below. * * In that case an earlier row will be the last one in the pass (if the * pass is in the output), check this here: @@ -3567,23 +3586,313 @@ png_set_filter(png_structrp png_ptr, int method, int filtersIn) } #endif /* WRITE_FILTER */ +static png_zlib_statep +write_start_IDAT(png_structrp png_ptr) + /* Shared code which does everything except the filter support */ +{ + png_zlib_statep ps = png_ptr->zlib_state; + + /* Set up the IDAT compression state. Expect the state to have been released + * by the previous owner, but it doesn't much matter if there was an error. + * Note that the stream is not claimed yet. + */ + debug(png_ptr->zowner == 0U); + + /* Create the zlib state if ncessary: */ + if (ps == NULL) + png_create_zlib_state(png_ptr), ps = png_ptr->zlib_state; + + /* Delayed initialization of the zlib state maxima; this is not done above in + * case the zlib_state is created before the IHDR has been written, which + * would lead to the various png_struct fields used below being + * uninitialized. + */ + { + /* Initialization of the buffer size constants. */ + const unsigned int bpp = PNG_PIXEL_DEPTH(*png_ptr); + const unsigned int byte_pp = bpp >> 3; /* May be 0 */ + const unsigned int pixel_block = + /* Number of pixels required to maintain PNG_ROW_BUFFER_BYTE_ALIGN + * alignment. For multi-byte pixels use the first set bit to determine + * if the pixels have a greater alignment already. + */ + bpp < 8U ? + PNG_ROW_BUFFER_BYTE_ALIGN * (8U/bpp) : + PNG_ROW_BUFFER_BYTE_ALIGN <= (byte_pp & -byte_pp) ? + 1U : + PNG_ROW_BUFFER_BYTE_ALIGN / (byte_pp & -byte_pp); + + /* pixel_block must always be a power of two: */ + debug(bpp > 0 && pixel_block > 0 && + (pixel_block & -pixel_block) == pixel_block && + ((8U*PNG_ROW_BUFFER_BYTE_ALIGN-1U) & (pixel_block*bpp)) == 0U); + + /* Zlib maxima */ + { + png_uint_32 max = (uInt)-1; /* max bytes */ + + if (bpp <= 8U) + { + /* Maximum number of bytes PNG can generate in the lower bit depth + * cases: + */ + png_uint_32 png_max = + (0x7FFFFFFF + PNG_ADDOF(bpp)) >> PNG_SHIFTOF(bpp); + + if (png_max < max) + max = 0x7FFFFFFF; + } + + else /* bpp > 8U */ + { + max /= byte_pp; + if (max > 0x7FFFFFFF) + max = 0x7FFFFFFF; + } + + /* So this is the maximum number of pixels regardless of alignment: */ + ps->zlib_max_pixels = max; + + /* For byte alignment the value has to be a multiple of pixel_block and + * that is a power of 2, so: + */ + ps->zlib_max_aligned_pixels = max & ~(pixel_block-1U); + } + +# ifdef PNG_WRITE_FILTER_SUPPORTED + /* PNG_ROW_BUFFER maxima; this is easier because PNG_ROW_BUFFER_SIZE is + * limited so that the number of bits fits in any ANSI-C + * (unsigned int). + */ + { + const unsigned int max = (8U * PNG_ROW_BUFFER_SIZE) / bpp; + + ps->row_buffer_max_pixels = max; + ps->row_buffer_max_aligned_pixels = max & ~(pixel_block-1U); + } +# endif /* WRITE_FILTER */ + } + + { + const png_alloc_size_t image_size = png_image_size_checked(png_ptr); + const png_uint_32 settings = pz_default_settings(ps->pz_IDAT, png_IDAT, + image_size > 0 && image_size < 0xffffffffU ? image_size : 0xffffffffU); + + /* Freeze the settings now; this avoids the need to call + * pz_default_settings again when the zlib stream is initialized. Also, + * the caller relies on this. + */ + ps->pz_IDAT = settings; + } + + return ps; +} + +#ifdef PNG_WRITE_FILTER_SUPPORTED +void /* PRIVATE */ +png_write_start_IDAT(png_structrp png_ptr) +{ + png_zlib_statep ps = write_start_IDAT(png_ptr); + const png_alloc_size_t write_row_size = png_write_row_buffer_size(png_ptr); + /* NOTE: this will be 0 for very long rows on 32-bit or less systems */ + png_byte mask = ps->filter_mask; + + ps->write_row_size = write_row_size; + + /* Now default the filter mask if it hasn't been set already: */ + if (mask == 0) + { +# ifdef PNG_SELECT_FILTER_SUPPORTED + /* The result depends on the png compression level: */ + const int png_level = pz_value(png_level, ps->pz_IDAT); + + /* If the bit depth is less than 8, so pixels are not byte aligned, + * PNG filtering hardly ever helps because there is no correlation + * between the bytes on which the filter works and the actual pixel + * values. Note that GIF is a whole lot better at this because it + * uses LZW to compress a bit-stream, not a byte stream as in the + * deflate implementation of LZ77. + * + * If the row size is less than 256 bytes filter selection + * algorithms are flakey. The libpng 1.6 and earlier algorithm + * worked in 1.6 and earlier with more than 128 bytes, but it failed + * if the total data size of the PNG was less than 512 bytes, so the + * test on write_row_size below seems like a reasonable + * simplification. Tests show that the libpng 1.6 filter selection + * heuristic did give worse results than 'none' on average for PNG + * files with a row length of 256 bytes or less except for 8-bit + * gray+alpha PNG files, however even in that case the results were + * only 1% larger with 'none'. + * + * Tests also show that for 16-bit components 'none' does as well as + * the libpng 1.6 algorithm when the row size is 1024 bytes or less, + * so for the moment (until different algorithms have been tested in + * 1.7) this condition is included as well. + * + * NOTE: the libpng 1.6 (and earlier) algorithm seems to work + * because it biases the byte codes in the output towards 0 and 255. + * Zlib doesn't care what the codes are, but Huffman encoding always + * benefits from a biased distribution. + */ + if (write_row_size == 0U /* row cannot be buffered */ || + png_level < 4 || png_ptr->bit_depth < 8 || write_row_size <= 256U + || (png_ptr->bit_depth == 16 && write_row_size <= 1024)) + mask = PNG_FILTER_NONE; /* NOTE: the mask, not the value! */ + + /* ELSE: there are at least 256 bytes in every row and the pixels + * are multiples of a byte. + */ + else if (png_level < 7) + mask = PNG_FAST_FILTERS; + + else + mask = PNG_ALL_FILTERS; +# else /* !SELECT_FILTER */ + mask = PNG_FILTER_NONE; +# endif /* !SELECT_FILTER */ + + ps->filter_mask = mask; + } +} + +static png_byte +png_write_start_row(png_zlib_statep ps, int start_of_pass, int no_previous_row) + /* Called at the start of a row to set up anything required for filter + * handling in the row. Sets png_zlib_state::filters to a single filter. + */ +{ + /* No filter selection, so choose the first filter */ + unsigned int mask = ps->filter_mask; + + /* If we see a previous-row filter in mask and png_zlib_state::save_row is + * still unset set it. This means that the first time a previous-row filter + * is seen row-saving gets turned on. + */ + if (ps->save_row == SAVE_ROW_UNSET && (mask & PREVIOUS_ROW_FILTERS) != 0U) + ps->save_row = SAVE_ROW_DEFAULT; + + if ((no_previous_row /* row not stored */ && !start_of_pass) || + ps->save_row == SAVE_ROW_OFF /* disabled by app */ || + ps->write_row_size == 0U /* row too large to buffer */) + mask &= PNG_BIC_MASK(PREVIOUS_ROW_FILTERS); + + /* On the first row of a pass Paeth is equivalent to sub and up is equivalent + * to none, so try to simplify the mask in in this case. + */ + else if (start_of_pass) { +# define MATCH(flags) ((mask & (flags)) == (flags)) + if (MATCH(PNG_FILTER_NONE|PNG_FILTER_UP)) + mask &= PNG_BIC_MASK(PNG_FILTER_UP); + + if (MATCH(PNG_FILTER_SUB|PNG_FILTER_PAETH)) + mask &= PNG_BIC_MASK(PNG_FILTER_PAETH); +# undef MATCH + } + +# ifdef PNG_SELECT_FILTER_SUPPORTED + if ((mask & (mask-1U)) == 0U /* single bit set */ || + ps->write_row_size == 0U /* row cannot be buffered */) +# endif /* SELECT_FILTER */ + /* Convert the lowest set bit into the corresponding value. If no bits + * are set select NONE. After this switch statement the value of + * ps->filters is guaranteed to just be a single filter. + */ + switch (mask & -mask) + { + default: mask = PNG_FILTER_VALUE_NONE; break; + case PNG_FILTER_SUB: mask = PNG_FILTER_VALUE_SUB; break; + case PNG_FILTER_UP: mask = PNG_FILTER_VALUE_UP; break; + case PNG_FILTER_AVG: mask = PNG_FILTER_VALUE_AVG; break; + case PNG_FILTER_PAETH: mask = PNG_FILTER_VALUE_PAETH; break; + } + + return ps->filters = PNG_BYTE(mask); +} + +static png_bytep +allocate_row(png_structrp png_ptr, png_const_bytep data, png_alloc_size_t size) + /* Utility to allocate and save some row bytes. If the result is NULL the + * allocation failed and the png_zlib_struct will have been updated to + * prevent further allocation attempts. + */ +{ + const png_zlib_statep ps = png_ptr->zlib_state; + png_bytep buffer; + + debug(ps->write_row_size > 0U); + + /* OOM is handled silently, as is the case where the row is too large to + * buffer. + */ + buffer = png_voidcast(png_bytep, + png_malloc_base(png_ptr, ps->write_row_size)); + + /* Setting write_row_size to 0 switches on the code for handling a row that + * is too large to buffer. This will kick in next time round, i.e. on the + * next row. + */ + if (buffer == NULL) + ps->write_row_size = 0U; + + else + memcpy(buffer, data, size); + + return buffer; +} +#endif /* WRITE_FILTER */ + +#ifdef PNG_SELECT_FILTER_SUPPORTED +static png_byte +select_filter(png_zlib_statep ps, png_const_bytep row, + png_const_bytep prev, unsigned int bpp, png_uint_32 width, int start_of_pass) + /* Select a filter from the list provided by png_write_start_row. */ +{ + png_byte filters = png_write_start_row(ps, start_of_pass, prev == NULL); + +# define png_ptr ps_png_ptr(ps) + if (filters >= PNG_FILTER_NONE) /* multiple filters to test */ + { + debug((filters & (filters-1)) != 0U); + + switch (filters & -filters) + { + case PNG_FILTER_NONE: filters = PNG_FILTER_VALUE_NONE; break; + case PNG_FILTER_SUB: filters = PNG_FILTER_VALUE_SUB; break; + case PNG_FILTER_UP: filters = PNG_FILTER_VALUE_UP; break; + case PNG_FILTER_AVG: filters = PNG_FILTER_VALUE_AVG; break; + case PNG_FILTER_PAETH: filters = PNG_FILTER_VALUE_PAETH; break; + default: NOT_REACHED; + } + + PNG_UNUSED(row) + PNG_UNUSED(bpp) + PNG_UNUSED(width) + } + + debug(filters < PNG_FILTER_VALUE_LAST); +# undef png_ptr + + return ps->filters = filters; +} +#else /* !SELECT_FILTER */ + /* Filter selection not being done, just call png_write_start_row: */ +# define select_filter(ps, rp, pp, bpp, width, start_of_pass)\ + png_write_start_row((ps), (start_of_pass), (pp) == NULL) +#endif /* !SELECT_FILTER */ + /* This is the common function to write multiple rows of PNG data. The data is * in the relevant PNG format but has had no filtering done. */ -static void -write_png_rows(png_structrp png_ptr, png_const_bytep *rows, +void /* PRIVATE */ +png_write_png_rows(png_structrp png_ptr, png_const_bytep *rows, png_uint_32 num_rows) { const png_zlib_statep ps = png_ptr->zlib_state; const unsigned int bpp = png_ptr->row_output_pixel_depth; # ifdef PNG_WRITE_FILTER_SUPPORTED - const png_byte filter = ps->filters; png_const_bytep previous_row = ps->previous_write_row; - const png_uint_32 max_pixels = filter == PNG_FILTER_VALUE_NONE ? - ps->zlib_max_pixels : ps->row_buffer_max_pixels; - const png_uint_32 block_pixels = filter == PNG_FILTER_VALUE_NONE ? - ps->row_buffer_max_aligned_pixels : ps->zlib_max_aligned_pixels; # else /* !WRITE_FILTER */ + /* These are constant in the no-filer case: */ const png_byte filter = PNG_FILTER_VALUE_NONE; const png_uint_32 max_pixels = ps->zlib_max_pixels; const png_uint_32 block_pixels = ps->zlib_max_aligned_pixels; @@ -3595,7 +3904,7 @@ write_png_rows(png_structrp png_ptr, png_const_bytep *rows, png_uint_32 pixels_in_pass = 0U; unsigned int first_row_in_pass = 0U; /* For do_interlace */ unsigned int pixels_at_end = 0U; /* for a partial byte at the end */ - unsigned int row_info_flags = png_row_end; + unsigned int base_info_flags = png_row_end; int pass = -1; /* Invalid: force calculation first time round */ debug(png_ptr->row_output_pixel_depth == PNG_PIXEL_DEPTH(*png_ptr)); @@ -3612,7 +3921,7 @@ write_png_rows(png_structrp png_ptr, png_const_bytep *rows, { debug(pass == 0); last_row_in_pass = png_ptr->height - 1U; - row_info_flags |= png_pass_last; /* there is only one */ + base_info_flags |= png_pass_last; /* there is only one */ } else @@ -3620,22 +3929,21 @@ write_png_rows(png_structrp png_ptr, png_const_bytep *rows, const png_uint_32 height = png_ptr->height; last_row_in_pass = PNG_PASS_ROWS(height, pass); + debug(pass >= 0 && pass < 7); # ifdef PNG_WRITE_INTERLACING_SUPPORTED if (png_ptr->do_interlace) { - /* libpng is doing the interlace handling and this is pass 6. - * The row may have to be skipped below. - */ - affirm(pass == 6); /* so pixels_in_pass is correct */ - - /* This overflows for 1 pixel high PNG, this does not matter; - * the result is 0xffffffff which is fine. + /* libpng is doing the interlace handling, the row number is + * actually the row in the image. + * + * This overflows when the PNG height is such that the are no + * rows in this pass. This does not matter; because there are + * no rows the value doesn't get used. */ last_row_in_pass = PNG_ROW_FROM_PASS_ROW(last_row_in_pass-1U, pass); - first_row_in_pass = 1U; - row_info_flags |= png_pass_last; /* if there are any rows */ + first_row_in_pass = PNG_PASS_START_ROW(pass); } else /* Application handles the interlace */ @@ -3644,16 +3952,18 @@ write_png_rows(png_structrp png_ptr, png_const_bytep *rows, /* The row does exist, so this works without checking the column * count. */ - - debug(pass < 7 && last_row_in_pass > 0U); + debug(last_row_in_pass > 0U); last_row_in_pass -= 1U; - - if (pass == PNG_LAST_PASS(pixels_in_pass/*PNG width*/, height)) - row_info_flags |= png_pass_last; - - /* Finally, adjust pixels_in_pass for the interlacing: */ - pixels_in_pass = PNG_PASS_COLS(pixels_in_pass, pass); } + + if (pass == PNG_LAST_PASS(pixels_in_pass/*PNG width*/, height)) + base_info_flags |= png_pass_last; + + /* Finally, adjust pixels_in_pass for the interlacing (skip the + * final pass; it is full width). + */ + if (pass < 6) + pixels_in_pass = PNG_PASS_COLS(pixels_in_pass, pass); } /* Mask out the bits in a partial byte. */ @@ -3678,17 +3988,29 @@ write_png_rows(png_structrp png_ptr, png_const_bytep *rows, PNG_ROW_IN_INTERLACE_PASS(png_ptr->row_number, pass)) # endif /* WRITE_INTERLACING */ { - const int flush = row_flush(ps, row_info_flags | + const unsigned int row_info_flags = base_info_flags | (png_ptr->row_number == first_row_in_pass ? png_pass_first_row : 0) | - (png_ptr->row_number == last_row_in_pass ? png_pass_last_row : 0)); + (png_ptr->row_number == last_row_in_pass ? png_pass_last_row : 0); + const int flush = row_flush(ps, row_info_flags); png_const_bytep row = *rows; png_uint_32 pixels_to_go = pixels_in_pass; - - /* The row handling uses png_compress_IDAT directly if there is no - * filter to be applied, otherwise it uses filter_row. - */ # ifdef PNG_WRITE_FILTER_SUPPORTED + /* The filter can change each time round. Call png_write_start_row + * to resolve any changes. Note that when this function is used to + * do filter selection from png_write_png_data on the first row + * png_write_start_row will get called twice. + */ + const png_byte filter = select_filter(ps, row, previous_row, bpp, + pixels_in_pass, png_ptr->row_number == first_row_in_pass); + const png_uint_32 max_pixels = filter == PNG_FILTER_VALUE_NONE ? + ps->zlib_max_pixels : ps->row_buffer_max_pixels; + const png_uint_32 block_pixels = filter == PNG_FILTER_VALUE_NONE ? + ps->row_buffer_max_aligned_pixels : ps->zlib_max_aligned_pixels; + + /* The row handling uses png_compress_IDAT directly if there is no + * filter to be applied, otherwise it uses filter_row. + */ if (filter != PNG_FILTER_VALUE_NONE) { int start_of_row = 1; @@ -3802,246 +4124,34 @@ write_png_rows(png_structrp png_ptr, png_const_bytep *rows, /* previous_row must be copied back unless we don't need it because the * next row is the first one in the pass (this relies on png_write_end_row * setting row_number to 0 at the end!) - * - * png_write_start_row (below) creates the buffer if it may be needed. - * - * NOTE: when libpng handles an interlaced image the entire loop may be - * skipped above and previous_row will still be NULL. - * - * TODO: delay this. */ - if (png_ptr->row_number != 0U && ps->previous_write_row != NULL && - previous_row != NULL) - memcpy(ps->previous_write_row, previous_row, - png_calc_rowbytes(png_ptr, bpp, pixels_in_pass)); -# endif -} - -static png_zlib_statep -write_start_IDAT(png_structrp png_ptr) - /* Shared code which does everything except the filter support */ -{ - png_zlib_statep ps = png_ptr->zlib_state; - - /* Set up the IDAT compression state. Expect the state to have been released - * by the previous owner, but it doesn't much matter if there was an error. - * Note that the stream is not claimed yet. - */ - debug(png_ptr->zowner == 0U); - - /* Create the zlib state if ncessary: */ - if (ps == NULL) - png_create_zlib_state(png_ptr), ps = png_ptr->zlib_state; - - /* Delayed initialization of the zlib state maxima; this is not done above in - * case the zlib_state is created before the IHDR has been written, which - * would lead to the various png_struct fields used below being - * uninitialized. - */ - { - /* Initialization of the buffer size constants. */ - const unsigned int bpp = PNG_PIXEL_DEPTH(*png_ptr); - const unsigned int byte_pp = bpp >> 3; /* May be 0 */ - const unsigned int pixel_block = - /* Number of pixels required to maintain PNG_ROW_BUFFER_BYTE_ALIGN - * alignment. For multi-byte pixels use the first set bit to determine - * if the pixels have a greater alignment already. - */ - bpp < 8U ? - PNG_ROW_BUFFER_BYTE_ALIGN * (8U/bpp) : - PNG_ROW_BUFFER_BYTE_ALIGN <= (byte_pp & -byte_pp) ? - 1U : - PNG_ROW_BUFFER_BYTE_ALIGN / (byte_pp & -byte_pp); - - /* pixel_block must always be a power of two: */ - debug(bpp > 0 && pixel_block > 0 && - (pixel_block & -pixel_block) == pixel_block && - ((8U*PNG_ROW_BUFFER_BYTE_ALIGN-1U) & (pixel_block*bpp)) == 0U); - - /* Zlib maxima */ + if (png_ptr->row_number != 0U && previous_row != NULL && SAVE_ROW(ps) && + ps->previous_write_row != previous_row/*all rows skipped*/) { - png_uint_32 max = (uInt)-1; /* max bytes */ +# ifdef PNG_SELECT_FILTER_SUPPORTED + /* We might be able to avoid any copy. */ + if (ps->current_write_row == previous_row) + { + png_bytep old = ps->previous_write_row; + ps->previous_write_row = ps->current_write_row; + ps->current_write_row = old; /* may be NULL */ + } - if (bpp <= 8U) - { - /* Maximum number of bytes PNG can generate in the lower bit depth - * cases: - */ - png_uint_32 png_max = - (0x7FFFFFFF + PNG_ADDOF(bpp)) >> PNG_SHIFTOF(bpp); + else +# endif /* SELECT_FILTER */ - if (png_max < max) - max = 0x7FFFFFFF; - } + if (ps->previous_write_row != NULL) + memcpy(ps->previous_write_row, previous_row, + png_calc_rowbytes(png_ptr, bpp, pixels_in_pass)); - else /* bpp > 8U */ - { - max /= byte_pp; - if (max > 0x7FFFFFFF) - max = 0x7FFFFFFF; - } - - /* So this is the maximum number of pixels regardless of alignment: */ - ps->zlib_max_pixels = max; - - /* For byte alignment the value has to be a multiple of pixel_block and - * that is a power of 2, so: - */ - ps->zlib_max_aligned_pixels = max & ~(pixel_block-1U); + else + ps->previous_write_row = allocate_row(png_ptr, previous_row, + png_calc_rowbytes(png_ptr, bpp, pixels_in_pass)); } - -# ifdef PNG_WRITE_FILTER_SUPPORTED - /* PNG_ROW_BUFFER maxima; this is easier because PNG_ROW_BUFFER_SIZE is - * limited so that the number of bits fits in any ANSI-C - * (unsigned int). - */ - { - const unsigned int max = (8U * PNG_ROW_BUFFER_SIZE) / bpp; - - ps->row_buffer_max_pixels = max; - ps->row_buffer_max_aligned_pixels = max & ~(pixel_block-1U); - } -# endif /* WRITE_FILTER */ - } - - { - const png_alloc_size_t image_size = png_image_size_checked(png_ptr); - const png_uint_32 settings = pz_default_settings(ps->pz_IDAT, png_IDAT, - image_size > 0 && image_size < 0xffffffffU ? image_size : 0xffffffffU); - - /* Freeze the settings now; this avoids the need to call - * pz_default_settings again when the zlib stream is initialized. Also, - * the caller relies on this. - */ - ps->pz_IDAT = settings; - } - - return ps; +# endif /* WRITE_FILTER */ } #ifdef PNG_WRITE_FILTER_SUPPORTED -void /* PRIVATE */ -png_write_start_IDAT(png_structrp png_ptr) -{ - png_zlib_statep ps = write_start_IDAT(png_ptr); - png_byte mask; - - { - /* Now default the filter mask if it hasn't been set already: */ - mask = ps->filter_mask; - - if (mask == 0) - { -# ifdef PNG_SELECT_FILTER_SUPPORTED - /* The result depends on the png compression level: */ - const int png_level = pz_value(png_level, ps->pz_IDAT); - - if (png_level < 4) - mask = PNG_FILTER_NONE; /* NOTE: the mask, not the value! */ - - else if (png_level < 7) - mask = PNG_FAST_FILTERS; - - else - mask = PNG_ALL_FILTERS; -# else /* !SELECT_FILTER */ - mask = PNG_FILTER_NONE; -# endif /* !SELECT_FILTER */ - - ps->filter_mask = mask; - } - } - - { - const png_alloc_size_t write_row_size = - png_write_row_buffer_size(png_ptr); /* may be 0 */ - png_uint_32 src = ps->save_row_count; /* may be set by the app */; - - ps->write_row_size = write_row_size; - - /* If the row is too long to buffer on this system skip the allocation; - * the per-row code will handle the absence of the buffer. - */ - if (write_row_size == 0U) /* row too large to buffer */ - ps->save_row_count = 0U;/* no buffering; filters will be NONE or SUB */ - - /* If unset no filter selection is required (or, maybe, available), - * respect the app setting if the buffering has been set to 'off' or - * 'previous row': - */ - if (src >= 2U) - { - /* This is slightly more complicated. The previous-row filters only - * actually require a previous row after the first row in the pass, so - * only if height is 2 or more in a non-interlaced image and 3 or more - * in an interlaced image. Set save_row_count to 1 or 0 as - * appropriate: - * - * If the app set save_row_count to 2 or more then filter selection is - * compiled out, use the same logic to check height: - */ - ps->save_row_count = - (src < SAVE_ROW_COUNT_UNSET /* app setting */ || - (src == SAVE_ROW_COUNT_UNSET /* default */ && - (mask & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) != 0U)) - && png_ptr->height > 1U+(png_ptr->interlaced != PNG_INTERLACE_NONE); - } - } - - /* Don't allocate anything yet. png_write_rows_internal (pngwrite.c) may end - * up passing the whole pass or the whole image, in which case extra - * buffering is not required. - */ -} - -static void -png_write_start_row(png_zlib_statep ps) - /* Called at the start of a row to set up anything required for filter - * handling in the row. Sets png_zlib_state::filters to a single filter. - * - * NOTE: this is not called at the start of *every* row. If multiple rows - * are processed at once it is only called once. - */ -{ - /* No filter selection, so choose the first filter */ - unsigned int mask = ps->filter_mask; - - if (ps->save_row_count < 1U) /* no previous row support */ - mask &= PNG_BIC_MASK(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); - - /* Convert the lowest set bit into the corresponding value. If no bits - * are set select NONE. After this switch statement the value of - * ps->filters is guaranteed to just be a single filter. - */ - switch (mask & -mask) - { - default: ps->filters = PNG_FILTER_VALUE_NONE; break; - case PNG_FILTER_SUB: ps->filters = PNG_FILTER_VALUE_SUB; break; - case PNG_FILTER_UP: ps->filters = PNG_FILTER_VALUE_UP; break; - case PNG_FILTER_AVG: ps->filters = PNG_FILTER_VALUE_AVG; break; - case PNG_FILTER_PAETH: ps->filters = PNG_FILTER_VALUE_PAETH; break; - } - - /* If previous row filters are enabled make sure that the previous row - * buffer is allocated. - */ - if (ps->save_row_count != 0U && ps->previous_write_row == NULL) - { - /* OOM is handled silently, as is the case where the row is too large - * to buffer. - */ - ps->previous_write_row = png_voidcast(png_bytep, - png_malloc_base(ps_png_ptr(ps), ps->write_row_size)); - - if (ps->previous_write_row == NULL) - { - ps->save_row_count = 0U; /* OOM */ - if (ps->filters > PNG_FILTER_VALUE_SUB) - ps->filters = PNG_FILTER_VALUE_NONE; - } - } -} - /* 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. @@ -4093,7 +4203,7 @@ png_write_png_data(png_structrp png_ptr, png_bytep prev_pixels, { const unsigned int bpp = png_ptr->row_output_pixel_depth; const unsigned int row_bits = width * bpp; - png_bytep prev_row; + png_bytep prev_row = ps->previous_write_row; debug(bpp <= 64U && width <= 65535U && width < 65535U/bpp); /* Expensive: only matters on 16-bit */ @@ -4102,11 +4212,64 @@ png_write_png_data(png_structrp png_ptr, png_bytep prev_pixels, * only called once between starting a new list of rows. */ if (x == 0) - png_write_start_row(ps); + png_write_start_row(ps, (row_info_flags & png_pass_first_row) != 0, + prev_row == NULL); + + /* If filter selection is required the filter will have at least one mask + * bit set. + */ +# ifdef PNG_SELECT_FILTER_SUPPORTED + if (ps->filters >= PNG_FILTER_NONE/*lowest mask bit*/) + { + /* If the entire row is passed in the input process it via + * immediately, otherwise the row must be buffered for later + * analysis. + */ + png_const_bytep row; + + if (x > 0 || (row_info_flags & png_row_end) == 0) + { + /* The row must be saved for later. */ + png_bytep buffer = ps->current_write_row; + + /* png_write_start row should always check this: */ + debug(ps->write_row_size > 0U); + + if (buffer != NULL) + memcpy(buffer + png_calc_rowbytes(png_ptr, bpp, x), + unfiltered_row, (row_bits + 7U) >> 3); + + + else if (x == 0U) + ps->current_write_row = buffer = allocate_row(png_ptr, + unfiltered_row, (row_bits + 7U) >> 3); + + row = buffer; + } + + else + row = unfiltered_row; + + if (row != NULL) /* else out of memory */ + { + /* At row end, process the save buffer. */ + if ((row_info_flags & png_row_end) != 0) + png_write_png_rows(png_ptr, &row, 1U); + + /* Early return to skip the single-filter code */ + return; + } + + /* Caching the row failed, so process the row using the lowest set + * filter. The allocation error should only ever happen at the + * start of the row. If this goes wrong the output will have been + * damaged. + */ + affirm(x == 0U); + } +# endif /* SELECT_FILTER */ /* prev_row is either NULL or the position in the previous row buffer */ - prev_row = ps->previous_write_row; - if (prev_row != NULL && x > 0) prev_row += png_calc_rowbytes(png_ptr, bpp, x); @@ -4116,30 +4279,23 @@ png_write_png_data(png_structrp png_ptr, png_bytep prev_pixels, /* 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. + * write_previous_row may have garbage in a partial byte at the end as a + * result of this memcpy. */ - if (prev_row != NULL && !(row_info_flags & png_pass_last_row)) - memcpy(prev_row, unfiltered_row, (row_bits + 7U) >> 3); + if (!(row_info_flags & png_pass_last_row) && SAVE_ROW(ps)) { + if (prev_row != NULL) + memcpy(prev_row, unfiltered_row, (row_bits + 7U) >> 3); + + /* NOTE: if the application sets png_zlib_state::save_row in a callback + * it isn't possible to do the save until the next row. allocate_row + * handles OOM silently by turning off the save. + */ + else if (x == 0) /* can allocate the save buffer */ + ps->previous_write_row = + allocate_row(png_ptr, unfiltered_row, (row_bits + 7U) >> 3); + } } } - -void /*PRIVATE */ -png_write_png_rows(png_structrp png_ptr, png_const_bytep *rows, - png_uint_32 num_rows) - /* This is the fast version of the above which receives complete rows. The - * final byte may still require separate handling. - */ -{ - const png_zlib_statep ps = png_ptr->zlib_state; - - affirm(ps != NULL); - - /* Set the filter to use: */ - png_write_start_row(ps); - - /* Now write all the rows with the same filter: */ - write_png_rows(png_ptr, rows, num_rows); -} #else /* !WRITE_FILTER */ void /* PRIVATE */ png_write_start_IDAT(png_structrp png_ptr) @@ -4174,16 +4330,6 @@ png_write_png_data(png_structrp png_ptr, png_bytep prev_pixels, if ((row_info_flags & png_row_end) != 0) png_write_end_row(png_ptr, flush); } - -void /*PRIVATE */ -png_write_png_rows(png_structrp png_ptr, png_const_bytep *rows, - png_uint_32 num_rows) - /* This is the fast version of the above which receives complete rows. The - * final byte may still require separate handling. - */ -{ - write_png_rows(png_ptr, rows, num_rows); -} #endif /* !WRITE_FILTER */ #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */ diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa index 510fce3f0..439febc6c 100644 --- a/scripts/pnglibconf.dfa +++ b/scripts/pnglibconf.dfa @@ -935,31 +935,8 @@ option CONVERT_tIME requires WRITE_ANCILLARY_CHUNKS # the 'first' (lowest numbered) filter will be selected an this typically # works out as PNG_FILTER_VALUE_NONE. # -# COMPRESSION_BUFFER_MAX -# WARNING: take care if you set this. This is the maximum amount of input -# data that the implementation of deflate can consume before it outputs a -# Huffman table for that data. I.e. before it commits to an encoding of the -# data it has read. This is used solely to implement a limit on the amount -# of image data buffering that occurs inside libpng before filter selection -# is done. Normally the limit is never reached because of the next setting, -# but this is a compile time limit and it is intended to prevent a potential -# DNS service as a result of an application setting the libpng equivalent of -# volume level 11 (read the wikipedia article on "Up to eleven"). -# -# NOTE: the image of a black cat in a coal mine obviously requires this -# limit, but some more valid images can get very close; well over 8MByte. -# -# COMPRESSION_BUFFER_LIMIT -# This is the (overrideable) default for the amount of memory libpng will -# buffer before selecting a filter for a row. It is limited itself to -# COMPRESSION_BUFFER_MAX as values above that level make no change (see the -# previous paragraph.) -# -# See png.h for more description of these options. option WRITE_FILTER requires WRITE -option SELECT_FILTER requires WRITE_FILTER disabled -setting COMPRESSION_BUFFER_MAX default 8453377 -setting COMPRESSION_BUFFER_LIMIT default 8453377 +option SELECT_FILTER requires WRITE_FILTER # added at libpng-1.5.4 diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt index 64dc121de..c8da8c74b 100644 --- a/scripts/pnglibconf.h.prebuilt +++ b/scripts/pnglibconf.h.prebuilt @@ -100,7 +100,7 @@ #define PNG_READ_tRNS_SUPPORTED #define PNG_READ_zTXt_SUPPORTED #define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED -/*#undef PNG_SELECT_FILTER_SUPPORTED*/ +#define PNG_SELECT_FILTER_SUPPORTED #define PNG_SEQUENTIAL_READ_SUPPORTED #define PNG_SETJMP_SUPPORTED #define PNG_SETTING_SUPPORTED @@ -192,8 +192,6 @@ /* settings */ #define PNG_ABORT { (abort()); } #define PNG_API_RULE 0 -#define PNG_COMPRESSION_BUFFER_LIMIT 8453377 -#define PNG_COMPRESSION_BUFFER_MAX 8453377 #define PNG_DEFAULT_GAMMA_ACCURACY 665 #define PNG_DEFAULT_READ_MACROS 1 #define PNG_GAMMA_THRESHOLD_FIXED 153