mirror of
https://git.code.sf.net/p/libpng/code.git
synced 2025-07-10 18:04:09 +02:00
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 <jbowler@acm.org>
This commit is contained in:
parent
ba356b4e4d
commit
4905ed4aae
6
png.h
6
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.
|
* when there is no previous row.
|
||||||
*
|
*
|
||||||
* 2) PNG_SELECT_FILTER_SUPPORTED:
|
* 2) PNG_SELECT_FILTER_SUPPORTED:
|
||||||
* libpng will buffer rows until enough data is available to perform a
|
* If the PNG rows are long enough (have enough bytes; at least 256) libpng
|
||||||
* reasonable filter selection heuristic then select filters for at least the
|
* will buffer each row and perform a filter selection heuristic to chose an
|
||||||
* first buffered row.
|
* appropriate filter.
|
||||||
*
|
*
|
||||||
* 3) !PNG_SELECT_FILTER_SUPPORTED:
|
* 3) !PNG_SELECT_FILTER_SUPPORTED:
|
||||||
* libpng selects the first filter in the list (there is no warning that this
|
* libpng selects the first filter in the list (there is no warning that this
|
||||||
|
|||||||
@ -390,7 +390,7 @@
|
|||||||
#define PNG_SRC_FILE_pngwtran (PNG_SRC_FILE_pngwrite +4096)
|
#define PNG_SRC_FILE_pngwtran (PNG_SRC_FILE_pngwrite +4096)
|
||||||
#define PNG_SRC_FILE_pngwutil (PNG_SRC_FILE_pngwtran +1024)
|
#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\
|
#define PNG_SRC_FILE_arm_filter_neon_intrinsics\
|
||||||
(PNG_SRC_FILE_arm_arm_init +1024)
|
(PNG_SRC_FILE_arm_arm_init +1024)
|
||||||
|
|
||||||
|
|||||||
758
pngwutil.c
758
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
|
* rows are required while if no filter selection is to be done only the
|
||||||
* previous row pointer is required.
|
* previous row pointer is required.
|
||||||
*/
|
*/
|
||||||
png_bytep previous_write_row;
|
png_alloc_size_t write_row_size; /* Actual size of the buffers */
|
||||||
png_alloc_size_t write_row_size; /* Actual size of the buffers */
|
png_bytep previous_write_row; /* Last row written, if any */
|
||||||
png_uint_32 save_row_count; /* Total number to be buffered */
|
# ifdef PNG_SELECT_FILTER_SUPPORTED
|
||||||
# define SAVE_ROW_COUNT_UNSET 0xFFFFFFFFU
|
png_bytep current_write_row; /* Row being written */
|
||||||
|
# endif /* SELECT_FILTER */
|
||||||
|
|
||||||
unsigned int row_buffer_max_pixels;
|
unsigned int row_buffer_max_pixels;
|
||||||
/* The maximum number of pixels that can fit in PNG_ROW_BUFFER_SIZE
|
/* 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 */
|
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 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 */
|
# endif /* WRITE_FILTER */
|
||||||
|
|
||||||
/* Compression settings: see below for how these are encoded. */
|
/* 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);
|
png_zlib_compress_init(png_ptr, &ps->s);
|
||||||
|
|
||||||
# ifdef PNG_WRITE_FILTER_SUPPORTED
|
# 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 */
|
# endif /* WRITE_FILTER */
|
||||||
# ifdef PNG_WRITE_FLUSH_SUPPORTED
|
# ifdef PNG_WRITE_FLUSH_SUPPORTED
|
||||||
/* Set this to prevent flushing by making it larger than the number
|
/* 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: */
|
/* Free any mode-specific data that is owned here: */
|
||||||
if (ps->previous_write_row != NULL)
|
if (ps->previous_write_row != NULL)
|
||||||
{
|
{
|
||||||
/* No saved rows, so the previous row buffer was allocated: */
|
|
||||||
png_bytep p = ps->previous_write_row;
|
png_bytep p = ps->previous_write_row;
|
||||||
ps->previous_write_row = NULL;
|
ps->previous_write_row = NULL;
|
||||||
png_free(png_ptr, p);
|
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 */
|
# endif /* WRITE_FILTER */
|
||||||
|
|
||||||
/* The main z_stream opaque pointer needs to remain set to png_ptr; it is
|
/* 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
|
/* 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
|
* this case the code which skipped the row must do the increment
|
||||||
* below! See 'interlace_row' in pngwrite.c and the code in
|
* 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
|
* In that case an earlier row will be the last one in the pass (if the
|
||||||
* pass is in the output), check this here:
|
* 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 */
|
#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
|
/* 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.
|
* in the relevant PNG format but has had no filtering done.
|
||||||
*/
|
*/
|
||||||
static void
|
void /* PRIVATE */
|
||||||
write_png_rows(png_structrp png_ptr, png_const_bytep *rows,
|
png_write_png_rows(png_structrp png_ptr, png_const_bytep *rows,
|
||||||
png_uint_32 num_rows)
|
png_uint_32 num_rows)
|
||||||
{
|
{
|
||||||
const png_zlib_statep ps = png_ptr->zlib_state;
|
const png_zlib_statep ps = png_ptr->zlib_state;
|
||||||
const unsigned int bpp = png_ptr->row_output_pixel_depth;
|
const unsigned int bpp = png_ptr->row_output_pixel_depth;
|
||||||
# ifdef PNG_WRITE_FILTER_SUPPORTED
|
# ifdef PNG_WRITE_FILTER_SUPPORTED
|
||||||
const png_byte filter = ps->filters;
|
|
||||||
png_const_bytep previous_row = ps->previous_write_row;
|
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 */
|
# else /* !WRITE_FILTER */
|
||||||
|
/* These are constant in the no-filer case: */
|
||||||
const png_byte filter = PNG_FILTER_VALUE_NONE;
|
const png_byte filter = PNG_FILTER_VALUE_NONE;
|
||||||
const png_uint_32 max_pixels = ps->zlib_max_pixels;
|
const png_uint_32 max_pixels = ps->zlib_max_pixels;
|
||||||
const png_uint_32 block_pixels = ps->zlib_max_aligned_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;
|
png_uint_32 pixels_in_pass = 0U;
|
||||||
unsigned int first_row_in_pass = 0U; /* For do_interlace */
|
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 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 */
|
int pass = -1; /* Invalid: force calculation first time round */
|
||||||
|
|
||||||
debug(png_ptr->row_output_pixel_depth == PNG_PIXEL_DEPTH(*png_ptr));
|
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);
|
debug(pass == 0);
|
||||||
last_row_in_pass = png_ptr->height - 1U;
|
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
|
else
|
||||||
@ -3620,22 +3929,21 @@ write_png_rows(png_structrp png_ptr, png_const_bytep *rows,
|
|||||||
const png_uint_32 height = png_ptr->height;
|
const png_uint_32 height = png_ptr->height;
|
||||||
|
|
||||||
last_row_in_pass = PNG_PASS_ROWS(height, pass);
|
last_row_in_pass = PNG_PASS_ROWS(height, pass);
|
||||||
|
debug(pass >= 0 && pass < 7);
|
||||||
|
|
||||||
# ifdef PNG_WRITE_INTERLACING_SUPPORTED
|
# ifdef PNG_WRITE_INTERLACING_SUPPORTED
|
||||||
if (png_ptr->do_interlace)
|
if (png_ptr->do_interlace)
|
||||||
{
|
{
|
||||||
/* libpng is doing the interlace handling and this is pass 6.
|
/* libpng is doing the interlace handling, the row number is
|
||||||
* The row may have to be skipped below.
|
* actually the row in the image.
|
||||||
*/
|
*
|
||||||
affirm(pass == 6); /* so pixels_in_pass is correct */
|
* This overflows when the PNG height is such that the are no
|
||||||
|
* rows in this pass. This does not matter; because there are
|
||||||
/* This overflows for 1 pixel high PNG, this does not matter;
|
* no rows the value doesn't get used.
|
||||||
* the result is 0xffffffff which is fine.
|
|
||||||
*/
|
*/
|
||||||
last_row_in_pass =
|
last_row_in_pass =
|
||||||
PNG_ROW_FROM_PASS_ROW(last_row_in_pass-1U, pass);
|
PNG_ROW_FROM_PASS_ROW(last_row_in_pass-1U, pass);
|
||||||
first_row_in_pass = 1U;
|
first_row_in_pass = PNG_PASS_START_ROW(pass);
|
||||||
row_info_flags |= png_pass_last; /* if there are any rows */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else /* Application handles the interlace */
|
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
|
/* The row does exist, so this works without checking the column
|
||||||
* count.
|
* count.
|
||||||
*/
|
*/
|
||||||
|
debug(last_row_in_pass > 0U);
|
||||||
debug(pass < 7 && last_row_in_pass > 0U);
|
|
||||||
last_row_in_pass -= 1U;
|
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. */
|
/* 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))
|
PNG_ROW_IN_INTERLACE_PASS(png_ptr->row_number, pass))
|
||||||
# endif /* WRITE_INTERLACING */
|
# 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 ==
|
(png_ptr->row_number ==
|
||||||
first_row_in_pass ? png_pass_first_row : 0) |
|
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_const_bytep row = *rows;
|
||||||
png_uint_32 pixels_to_go = pixels_in_pass;
|
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
|
# 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)
|
if (filter != PNG_FILTER_VALUE_NONE)
|
||||||
{
|
{
|
||||||
int start_of_row = 1;
|
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
|
/* 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
|
* next row is the first one in the pass (this relies on png_write_end_row
|
||||||
* setting row_number to 0 at the end!)
|
* 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 &&
|
if (png_ptr->row_number != 0U && previous_row != NULL && SAVE_ROW(ps) &&
|
||||||
previous_row != NULL)
|
ps->previous_write_row != previous_row/*all rows skipped*/)
|
||||||
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 */
|
|
||||||
{
|
{
|
||||||
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)
|
else
|
||||||
{
|
# endif /* SELECT_FILTER */
|
||||||
/* 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)
|
if (ps->previous_write_row != NULL)
|
||||||
max = 0x7FFFFFFF;
|
memcpy(ps->previous_write_row, previous_row,
|
||||||
}
|
png_calc_rowbytes(png_ptr, bpp, pixels_in_pass));
|
||||||
|
|
||||||
else /* bpp > 8U */
|
else
|
||||||
{
|
ps->previous_write_row = allocate_row(png_ptr, previous_row,
|
||||||
max /= byte_pp;
|
png_calc_rowbytes(png_ptr, bpp, pixels_in_pass));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
# endif /* WRITE_FILTER */
|
||||||
# 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
|
#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
|
/* 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
|
* been specified by the application, and then writes the row out with the
|
||||||
* chosen filter.
|
* 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 bpp = png_ptr->row_output_pixel_depth;
|
||||||
const unsigned int row_bits = width * bpp;
|
const unsigned int row_bits = width * bpp;
|
||||||
png_bytep prev_row;
|
png_bytep prev_row = ps->previous_write_row;
|
||||||
|
|
||||||
debug(bpp <= 64U && width <= 65535U &&
|
debug(bpp <= 64U && width <= 65535U &&
|
||||||
width < 65535U/bpp); /* Expensive: only matters on 16-bit */
|
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.
|
* only called once between starting a new list of rows.
|
||||||
*/
|
*/
|
||||||
if (x == 0)
|
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 is either NULL or the position in the previous row buffer */
|
||||||
prev_row = ps->previous_write_row;
|
|
||||||
|
|
||||||
if (prev_row != NULL && x > 0)
|
if (prev_row != NULL && x > 0)
|
||||||
prev_row += png_calc_rowbytes(png_ptr, bpp, x);
|
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
|
/* 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
|
* 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))
|
if (!(row_info_flags & png_pass_last_row) && SAVE_ROW(ps)) {
|
||||||
memcpy(prev_row, unfiltered_row, (row_bits + 7U) >> 3);
|
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 */
|
#else /* !WRITE_FILTER */
|
||||||
void /* PRIVATE */
|
void /* PRIVATE */
|
||||||
png_write_start_IDAT(png_structrp png_ptr)
|
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)
|
if ((row_info_flags & png_row_end) != 0)
|
||||||
png_write_end_row(png_ptr, flush);
|
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 */
|
#endif /* !WRITE_FILTER */
|
||||||
|
|
||||||
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */
|
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */
|
||||||
|
|||||||
@ -935,31 +935,8 @@ option CONVERT_tIME requires WRITE_ANCILLARY_CHUNKS
|
|||||||
# the 'first' (lowest numbered) filter will be selected an this typically
|
# the 'first' (lowest numbered) filter will be selected an this typically
|
||||||
# works out as PNG_FILTER_VALUE_NONE.
|
# 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 WRITE_FILTER requires WRITE
|
||||||
option SELECT_FILTER requires WRITE_FILTER disabled
|
option SELECT_FILTER requires WRITE_FILTER
|
||||||
setting COMPRESSION_BUFFER_MAX default 8453377
|
|
||||||
setting COMPRESSION_BUFFER_LIMIT default 8453377
|
|
||||||
|
|
||||||
# added at libpng-1.5.4
|
# added at libpng-1.5.4
|
||||||
|
|
||||||
|
|||||||
@ -100,7 +100,7 @@
|
|||||||
#define PNG_READ_tRNS_SUPPORTED
|
#define PNG_READ_tRNS_SUPPORTED
|
||||||
#define PNG_READ_zTXt_SUPPORTED
|
#define PNG_READ_zTXt_SUPPORTED
|
||||||
#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
|
#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
|
||||||
/*#undef PNG_SELECT_FILTER_SUPPORTED*/
|
#define PNG_SELECT_FILTER_SUPPORTED
|
||||||
#define PNG_SEQUENTIAL_READ_SUPPORTED
|
#define PNG_SEQUENTIAL_READ_SUPPORTED
|
||||||
#define PNG_SETJMP_SUPPORTED
|
#define PNG_SETJMP_SUPPORTED
|
||||||
#define PNG_SETTING_SUPPORTED
|
#define PNG_SETTING_SUPPORTED
|
||||||
@ -192,8 +192,6 @@
|
|||||||
/* settings */
|
/* settings */
|
||||||
#define PNG_ABORT { (abort()); }
|
#define PNG_ABORT { (abort()); }
|
||||||
#define PNG_API_RULE 0
|
#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_GAMMA_ACCURACY 665
|
||||||
#define PNG_DEFAULT_READ_MACROS 1
|
#define PNG_DEFAULT_READ_MACROS 1
|
||||||
#define PNG_GAMMA_THRESHOLD_FIXED 153
|
#define PNG_GAMMA_THRESHOLD_FIXED 153
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user