Filter code change prep.

This commit moves code round and changes the filter write interfaces that took
png_uint_32 buffer pixel counts to unsigned int.  Also moves compression code
and definitions into pngwutil.c so that the compression code is isolated from
other definitions.

Signed-off-by: John Bowler <jbowler@acm.org>
This commit is contained in:
John Bowler 2015-11-27 15:42:52 -08:00
parent 14d11b9f35
commit b3a18efebf
7 changed files with 268 additions and 243 deletions

24
png.c
View File

@ -866,58 +866,58 @@ png_access_version_number(void)
* like Z_OK or Z_STREAM_END where the error code is apparently a success code.
*/
void /* PRIVATE */
png_zstream_error(png_structrp png_ptr, int ret)
png_zstream_error(z_stream *zstream, int ret)
{
/* Translate 'ret' into an appropriate error string, priority is given to the
* one in zstream if set. This always returns a string, even in cases like
* Z_OK or Z_STREAM_END where the error code is a success code.
*/
if (png_ptr->zstream.msg == NULL) switch (ret)
if (zstream->msg == NULL) switch (ret)
{
default:
case Z_OK:
png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return code");
zstream->msg = PNGZ_MSG_CAST("unexpected zlib return code");
break;
case Z_STREAM_END:
/* Normal exit */
png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected end of LZ stream");
zstream->msg = PNGZ_MSG_CAST("unexpected end of LZ stream");
break;
case Z_NEED_DICT:
/* This means the deflate stream did not have a dictionary; this
* indicates a bogus PNG.
*/
png_ptr->zstream.msg = PNGZ_MSG_CAST("missing LZ dictionary");
zstream->msg = PNGZ_MSG_CAST("missing LZ dictionary");
break;
case Z_ERRNO:
/* gz APIs only: should not happen */
png_ptr->zstream.msg = PNGZ_MSG_CAST("zlib IO error");
zstream->msg = PNGZ_MSG_CAST("zlib IO error");
break;
case Z_STREAM_ERROR:
/* internal libpng error */
png_ptr->zstream.msg = PNGZ_MSG_CAST("bad parameters to zlib");
zstream->msg = PNGZ_MSG_CAST("bad parameters to zlib");
break;
case Z_DATA_ERROR:
png_ptr->zstream.msg = PNGZ_MSG_CAST("damaged LZ stream");
zstream->msg = PNGZ_MSG_CAST("damaged LZ stream");
break;
case Z_MEM_ERROR:
png_ptr->zstream.msg = PNGZ_MSG_CAST("insufficient memory");
zstream->msg = PNGZ_MSG_CAST("insufficient memory");
break;
case Z_BUF_ERROR:
/* End of input or output; not a problem if the caller is doing
* incremental read or write.
*/
png_ptr->zstream.msg = PNGZ_MSG_CAST("truncated");
zstream->msg = PNGZ_MSG_CAST("truncated");
break;
case Z_VERSION_ERROR:
png_ptr->zstream.msg = PNGZ_MSG_CAST("unsupported zlib version");
zstream->msg = PNGZ_MSG_CAST("unsupported zlib version");
break;
case PNG_UNEXPECTED_ZLIB_RETURN:
@ -926,7 +926,7 @@ png_zstream_error(png_structrp png_ptr, int ret)
* and change pngpriv.h. Note that this message is "... return",
* whereas the default/Z_OK one is "... return code".
*/
png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return");
zstream->msg = PNGZ_MSG_CAST("unexpected zlib return");
break;
}
}

View File

@ -953,7 +953,7 @@ PNG_INTERNAL_FUNCTION(void, png_copy_row,(png_const_structrp png_ptr,
/* Zlib support */
#define PNG_UNEXPECTED_ZLIB_RETURN (-7)
PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret),
PNG_INTERNAL_FUNCTION(void, png_zstream_error,(z_stream *zstream, int ret),
PNG_EMPTY);
/* Used by the zlib handling functions to ensure that z_stream::msg is always
* set before they return.
@ -1098,10 +1098,6 @@ PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr,
PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr,
png_const_colorp palette, unsigned int num_pal),PNG_EMPTY);
PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr,
png_const_bytep row_data, png_alloc_size_t row_data_length, int flush),
PNG_EMPTY);
PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY);
#ifdef PNG_WRITE_gAMA_SUPPORTED
@ -1215,7 +1211,7 @@ PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr,
*/
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,
png_uint_32 x, unsigned int 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);

View File

@ -751,7 +751,7 @@ png_read_destroy(png_structrp png_ptr)
if (ret != Z_OK)
{
png_zstream_error(png_ptr, ret);
png_zstream_error(&png_ptr->zstream, ret);
png_warning(png_ptr, png_ptr->zstream.msg);
}
}

View File

@ -362,7 +362,7 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
else
{
png_zstream_error(png_ptr, ret);
png_zstream_error(&png_ptr->zstream, ret);
png_ptr->zstream_ended = 1;
}
@ -493,7 +493,7 @@ png_zlib_inflate(png_structrp png_ptr, png_uint_32 owner, int finish,
if (ret != Z_BUF_ERROR)
png_ptr->zstream_ended = 1;
png_zstream_error(png_ptr, ret);
png_zstream_error(&png_ptr->zstream, ret);
return ret;
}
@ -667,14 +667,14 @@ png_decompress_chunk(png_structrp png_ptr,
{
/* Out of memory allocating the buffer */
ret = Z_MEM_ERROR;
png_zstream_error(png_ptr, Z_MEM_ERROR);
png_zstream_error(&png_ptr->zstream, Z_MEM_ERROR);
}
}
else
{
/* inflateReset failed, store the error message */
png_zstream_error(png_ptr, ret);
png_zstream_error(&png_ptr->zstream, ret);
if (ret == Z_STREAM_END)
ret = PNG_UNEXPECTED_ZLIB_RETURN;
@ -697,7 +697,7 @@ png_decompress_chunk(png_structrp png_ptr,
else
{
/* Application/configuration limits exceeded */
png_zstream_error(png_ptr, Z_MEM_ERROR);
png_zstream_error(&png_ptr->zstream, Z_MEM_ERROR);
return Z_MEM_ERROR;
}
}
@ -758,7 +758,7 @@ png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size,
png_ptr->zstream.avail_out = 0; /* Should not be required, but is safe */
/* Ensure the error message pointer is always set: */
png_zstream_error(png_ptr, ret);
png_zstream_error(&png_ptr->zstream, ret);
return ret;
}
@ -4513,7 +4513,7 @@ png_read_finish_IDAT(png_structrp png_ptr)
/* This is just a warning; it's safe, and the zstream_error flag is
* not set.
*/
png_zstream_error(png_ptr, ret);
png_zstream_error(&png_ptr->zstream, ret);
png_chunk_warning(png_ptr, png_ptr->zstream.msg);
}
}

View File

@ -64,14 +64,7 @@
#ifdef PNG_WRITE_SUPPORTED
/* The type of a compression buffer list used by the write code. */
typedef struct png_compression_buffer
{
struct png_compression_buffer *next;
png_byte output[1]; /* actually zbuf_size */
} png_compression_buffer, *png_compression_bufferp;
#define PNG_COMPRESSION_BUFFER_SIZE(pp)\
(offsetof(png_compression_buffer, output) + (pp)->zbuffer_size)
typedef struct png_compression_buffer *png_compression_bufferp;
#endif
/* Colorspace support; structures used in png_struct, png_info and in internal
@ -755,6 +748,7 @@ struct png_struct_def
*/
png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */
z_stream zstream; /* decompression structure */
unsigned int zstream_start:1; /* before first byte of stream */
unsigned int zstream_ended:1; /* no more zlib output available */
unsigned int zstream_error:1; /* zlib error message has been output */
unsigned int zstream_eod :1; /* all the required uncompressed data has been

View File

@ -1093,40 +1093,6 @@ png_write_row(png_structrp png_ptr, png_const_bytep row)
} /* png_ptr != NULL */
}
#ifdef PNG_WRITE_FLUSH_SUPPORTED
/* Set the automatic flush interval or 0 to turn flushing off */
void PNGAPI
png_set_flush(png_structrp png_ptr, int nrows)
{
png_debug(1, "in png_set_flush");
if (png_ptr == NULL)
return;
png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
}
/* Flush the current output buffers now */
void PNGAPI
png_write_flush(png_structrp png_ptr)
{
png_debug(1, "in png_write_flush");
if (png_ptr == NULL)
return;
/* Before the start of the IDAT and after the end of the image zowner will be
* something other than png_IDAT:
*/
if (png_ptr->zowner == png_IDAT)
{
png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH);
png_ptr->flush_rows = 0;
png_flush(png_ptr);
}
}
#endif /* WRITE_FLUSH */
/* Free any memory used in png_ptr struct without freeing the struct itself. */
static void
png_write_destroy(png_structrp png_ptr)
@ -1140,7 +1106,7 @@ png_write_destroy(png_structrp png_ptr)
if (ret != Z_OK)
{
png_zstream_error(png_ptr, ret);
png_zstream_error(&png_ptr->zstream, ret);
png_warning(png_ptr, png_ptr->zstream.msg);
}
}

View File

@ -203,7 +203,7 @@ png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string,
* point at which a lower LZ window size can be used.)
*/
static png_alloc_size_t
png_image_size(png_structrp png_ptr)
png_image_size(png_const_structrp png_ptr)
{
/* Only return sizes up to the maximum of a png_uint_32; do this by limiting
* the width and height used to 15 bits.
@ -242,6 +242,16 @@ png_image_size(png_structrp png_ptr)
return 0xffffffffU;
}
/* The type of the compression buffer list (opaque outside this file) */
typedef struct png_compression_buffer
{
struct png_compression_buffer *next;
png_byte output[1]; /* actually zbuf_size */
} png_compression_buffer;
#define PNG_COMPRESSION_BUFFER_SIZE(pp)\
(offsetof(png_compression_buffer, output) + (pp)->zbuffer_size)
#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
/* This is the code to hack the first two bytes of the deflate stream (the
* deflate header) to correct the windowBits value to match the actual data
@ -407,7 +417,7 @@ png_deflate_claim(png_structrp png_ptr, png_uint_32 owner,
if (ret_end != Z_OK || png_ptr->zstream.state != NULL)
{
png_zstream_error(png_ptr, ret_end);
png_zstream_error(&png_ptr->zstream, ret_end);
png_warning(png_ptr, png_ptr->zstream.msg);
png_ptr->zstream.state = NULL; /* zlib error recovery */
}
@ -438,7 +448,7 @@ png_deflate_claim(png_structrp png_ptr, png_uint_32 owner,
png_ptr->zowner = owner;
else
png_zstream_error(png_ptr, ret);
png_zstream_error(&png_ptr->zstream, ret);
return ret;
}
@ -616,7 +626,7 @@ png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name,
}
else
png_zstream_error(png_ptr, ret);
png_zstream_error(&png_ptr->zstream, ret);
/* Reset zlib for another zTXt/iTXt or image data */
png_ptr->zowner = 0;
@ -967,162 +977,6 @@ png_write_PLTE(png_structrp png_ptr, png_const_colorp palette,
png_ptr->mode |= PNG_HAVE_PLTE;
}
/* This is similar to png_text_compress, above, except that it does not require
* all of the data at once and, instead of buffering the compressed result,
* writes it as IDAT chunks. Unlike png_text_compress it *can* png_error out
* because it calls the write interface. As a result it does its own error
* reporting and does not return an error code. In the event of error it will
* just call png_error. The input data length may exceed 32-bits. The 'flush'
* parameter is exactly the same as that to deflate, with the following
* meanings:
*
* Z_NO_FLUSH: normal incremental output of compressed data
* Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush
* Z_FINISH: this is the end of the input, do a Z_FINISH and clean up
*
* The routine manages the acquire and release of the png_ptr->zstream by
* checking and (at the end) clearing png_ptr->zowner; it does some sanity
* checks on the 'mode' flags while doing this.
*/
void /* PRIVATE */
png_compress_IDAT(png_structrp png_ptr, png_const_bytep input,
png_alloc_size_t input_len, int flush)
{
if (png_ptr->zowner != png_IDAT)
{
/* First time. Ensure we have a temporary buffer for compression and
* trim the buffer list if it has more than one entry to free memory.
* If 'WRITE_COMPRESSED_TEXT' is not set the list will never have been
* created at this point, but the check here is quick and safe.
*/
if (png_ptr->zbuffer_list == NULL)
{
png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp,
png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
png_ptr->zbuffer_list->next = NULL;
}
else
png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next);
/* It is a terminal error if we can't claim the zstream. */
if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK)
png_error(png_ptr, png_ptr->zstream.msg);
/* The output state is maintained in png_ptr->zstream, so it must be
* initialized here after the claim.
*/
png_ptr->zstream.next_out = png_ptr->zbuffer_list->output;
png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
}
/* Now loop reading and writing until all the input is consumed or an error
* terminates the operation. The _out values are maintained across calls to
* this function, but the input must be reset each time.
*/
png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input);
png_ptr->zstream.avail_in = 0; /* set below */
for (;;)
{
int ret;
/* INPUT: from the row data */
uInt avail = ZLIB_IO_MAX;
if (avail > input_len)
avail = (uInt)input_len; /* safe because of the check */
png_ptr->zstream.avail_in = avail;
input_len -= avail;
ret = deflate(&png_ptr->zstream, input_len > 0 ? Z_NO_FLUSH : flush);
/* Include as-yet unconsumed input */
input_len += png_ptr->zstream.avail_in;
png_ptr->zstream.avail_in = 0;
/* OUTPUT: write complete IDAT chunks when avail_out drops to zero. Note
* that these two zstream fields are preserved across the calls, therefore
* there is no need to set these up on entry to the loop.
*/
if (png_ptr->zstream.avail_out == 0)
{
png_bytep data = png_ptr->zbuffer_list->output;
uInt size = png_ptr->zbuffer_size;
/* Write an IDAT containing the data then reset the buffer. The
* first IDAT may need deflate header optimization.
*/
#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 &&
png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
optimize_cmf(data, png_image_size(png_ptr));
#endif
png_write_complete_chunk(png_ptr, png_IDAT, data, size);
png_ptr->mode |= PNG_HAVE_IDAT;
png_ptr->zstream.next_out = data;
png_ptr->zstream.avail_out = size;
/* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with
* the same flush parameter until it has finished output, for NO_FLUSH
* it doesn't matter.
*/
if (ret == Z_OK && flush != Z_NO_FLUSH)
continue;
}
/* The order of these checks doesn't matter much; it just affects which
* possible error might be detected if multiple things go wrong at once.
*/
if (ret == Z_OK) /* most likely return code! */
{
/* If all the input has been consumed then just return. If Z_FINISH
* was used as the flush parameter something has gone wrong if we get
* here.
*/
if (input_len == 0)
{
if (flush == Z_FINISH)
png_error(png_ptr, "Z_OK on Z_FINISH with output space");
return;
}
}
else if (ret == Z_STREAM_END && flush == Z_FINISH)
{
/* This is the end of the IDAT data; any pending output must be
* flushed. For small PNG files we may still be at the beginning.
*/
png_bytep data = png_ptr->zbuffer_list->output;
uInt size = png_ptr->zbuffer_size - png_ptr->zstream.avail_out;
#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 &&
png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
optimize_cmf(data, png_image_size(png_ptr));
#endif
png_write_complete_chunk(png_ptr, png_IDAT, data, size);
png_ptr->zstream.avail_out = 0;
png_ptr->zstream.next_out = NULL;
png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT;
png_ptr->zowner = 0; /* Release the stream */
return;
}
else
{
/* This is an error condition. */
png_zstream_error(png_ptr, ret);
png_error(png_ptr, png_ptr->zstream.msg);
}
}
}
/* Write an IEND chunk */
void /* PRIVATE */
png_write_IEND(png_structrp png_ptr)
@ -1956,9 +1810,195 @@ png_write_tIME(png_structrp png_ptr, png_const_timep mod_time)
}
#endif
/* This is similar to png_text_compress, above, except that it does not require
* all of the data at once and, instead of buffering the compressed result,
* writes it as IDAT chunks. Unlike png_text_compress it *can* png_error out
* because it calls the write interface. As a result it does its own error
* reporting and does not return an error code. In the event of error it will
* just call png_error. The input data length may exceed 32-bits. The 'flush'
* parameter is exactly the same as that to deflate, with the following
* meanings:
*
* Z_NO_FLUSH: normal incremental output of compressed data
* Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush
* Z_FINISH: this is the end of the input, do a Z_FINISH and clean up
*
* The routine manages the acquire and release of the png_ptr->zstream by
* checking and (at the end) clearing png_ptr->zowner; it does some sanity
* checks on the 'mode' flags while doing this.
*/
static void
png_start_IDAT(png_structrp png_ptr)
{
/* First time. Ensure we have a temporary buffer for compression and
* trim the buffer list if it has more than one entry to free memory.
* If 'WRITE_COMPRESSED_TEXT' is not set the list will never have been
* created at this point, but the check here is quick and safe.
*/
if (png_ptr->zbuffer_list == NULL)
{
png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp,
png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
png_ptr->zbuffer_list->next = NULL;
}
else
png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next);
/* It is a terminal error if we can't claim the zstream. */
if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK)
png_error(png_ptr, png_ptr->zstream.msg);
/* The output state is maintained in png_ptr->zstream, so it must be
* initialized here after the claim.
*/
png_ptr->zstream.next_out = png_ptr->zbuffer_list->output;
png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
}
static void
png_compress_IDAT(png_structrp png_ptr, z_stream *zstream,
png_const_bytep input, uInt input_len, int flush)
{
/* Now loop reading and writing until all the input is consumed or an error
* terminates the operation. The _out values are maintained across calls to
* this function, but the input must be reset each time.
*/
zstream->next_in = PNGZ_INPUT_CAST(input);
zstream->avail_in = 0; /* set below */
for (;;)
{
int ret;
/* INPUT: from the row data */
zstream->avail_in = input_len;
input_len = 0;
ret = deflate(zstream, input_len > 0 ? Z_NO_FLUSH : flush);
/* Include as-yet unconsumed input */
input_len += zstream->avail_in;
zstream->avail_in = 0;
/* OUTPUT: write complete IDAT chunks when avail_out drops to zero. Note
* that these two zstream fields are preserved across the calls, therefore
* there is no need to set these up on entry to the loop.
*/
if (zstream->avail_out == 0)
{
png_bytep data = png_ptr->zbuffer_list->output;
uInt size = png_ptr->zbuffer_size;
/* Write an IDAT containing the data then reset the buffer. The
* first IDAT may need deflate header optimization.
*/
#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 &&
png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
optimize_cmf(data, png_image_size(png_ptr));
#endif
png_write_complete_chunk(png_ptr, png_IDAT, data, size);
png_ptr->mode |= PNG_HAVE_IDAT;
zstream->next_out = data;
zstream->avail_out = size;
/* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with
* the same flush parameter until it has finished output, for NO_FLUSH
* it doesn't matter.
*/
if (ret == Z_OK && flush != Z_NO_FLUSH)
continue;
}
/* The order of these checks doesn't matter much; it just affects which
* possible error might be detected if multiple things go wrong at once.
*/
if (ret == Z_OK) /* most likely return code! */
{
/* If all the input has been consumed then just return. If Z_FINISH
* was used as the flush parameter something has gone wrong if we get
* here.
*/
if (input_len == 0)
{
if (flush == Z_FINISH)
png_error(png_ptr, "Z_OK on Z_FINISH with output space");
return;
}
}
else if (ret == Z_STREAM_END && flush == Z_FINISH)
{
/* This is the end of the IDAT data; any pending output must be
* flushed. For small PNG files we may still be at the beginning.
*/
png_bytep data = png_ptr->zbuffer_list->output;
uInt size = png_ptr->zbuffer_size - zstream->avail_out;
#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 &&
png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
optimize_cmf(data, png_image_size(png_ptr));
#endif
png_write_complete_chunk(png_ptr, png_IDAT, data, size);
zstream->avail_out = 0;
zstream->next_out = NULL;
png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT;
png_ptr->zowner = 0; /* Release the stream */
return;
}
else
{
/* This is an error condition. */
png_zstream_error(zstream, ret);
png_error(png_ptr, zstream->msg);
}
}
}
#ifdef PNG_WRITE_FLUSH_SUPPORTED
/* Set the automatic flush interval or 0 to turn flushing off */
void PNGAPI
png_set_flush(png_structrp png_ptr, int nrows)
{
png_debug(1, "in png_set_flush");
if (png_ptr == NULL)
return;
png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
}
/* Flush the current output buffers now */
void PNGAPI
png_write_flush(png_structrp png_ptr)
{
png_debug(1, "in png_write_flush");
if (png_ptr == NULL)
return;
/* Before the start of the IDAT and after the end of the image zowner will be
* something other than png_IDAT:
*/
if (png_ptr->zowner == png_IDAT)
{
png_compress_IDAT(png_ptr, &png_ptr->zstream, NULL, 0, Z_SYNC_FLUSH);
png_ptr->flush_rows = 0;
png_flush(png_ptr);
}
}
#endif /* WRITE_FLUSH */
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*/,
unsigned int 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
@ -1967,22 +2007,24 @@ write_filtered_row(png_structrp png_ptr, png_const_bytep filtered_row,
* only has one significant bit!
*/
debug(row_bytes > 0);
affirm(row_bytes <= ZLIB_IO_MAX); /* I.e. it fits in a uInt */
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, &png_ptr->zstream, buffer, 1U/*len*/,
Z_NO_FLUSH);
}
png_compress_IDAT(png_ptr, filtered_row, row_bytes,
png_compress_IDAT(png_ptr, &png_ptr->zstream, 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*/,
unsigned int 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
@ -2012,7 +2054,7 @@ write_unfiltered_rowbits(png_structrp png_ptr, png_const_bytep filtered_row,
#ifdef PNG_WRITE_FILTER_SUPPORTED
static void
filter_block_singlebyte(png_alloc_size_t row_bytes, png_bytep sub_row,
filter_block_singlebyte(unsigned int 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)
{
@ -2059,7 +2101,7 @@ filter_block_singlebyte(png_alloc_size_t row_bytes, png_bytep sub_row,
}
static void
filter_block_multibyte(png_alloc_size_t row_bytes,
filter_block_multibyte(unsigned int 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)
@ -2112,12 +2154,12 @@ filter_block_multibyte(png_alloc_size_t row_bytes,
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,
unsigned int 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 */
unsigned int 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];
@ -2188,7 +2230,7 @@ filter_row(png_structrp png_ptr, png_const_bytep prev_row,
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,
unsigned int 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. */
@ -2211,15 +2253,20 @@ find_filter(png_structrp png_ptr, png_const_bytep prev_row,
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 width/*pixels*/, int first_row_in_pass, int last_pass_row,
unsigned int filters_to_try, int end_of_image)
{
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;
const unsigned int row_bits = width * bpp;
/* These invariants are expected from the caller: */
affirm(width < 65536U && bpp <= 64U && row_bits <= 8U*PNG_ROW_BUFFER_SIZE);
affirm(width < 65536U && bpp <= 64U && width < 65536U/bpp &&
row_bits <= 8U*PNG_ROW_BUFFER_SIZE);
/* Set up the IDAT zlib compression if not set up yet: */
if (png_ptr->zowner != png_IDAT)
png_start_IDAT(png_ptr);
if (x == 0U) /* start of row */
{
@ -2264,6 +2311,23 @@ png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels,
}
}
}
if (first_row_in_pass)
{
/* On the first row UP and NONE are the same, PAETH and SUB are the
* same, so if both members of a pair occur together eliminate the one
* that depends on the previous row. This will avoid the filter
* selection code while allowing the app to ensure all the filters can
* be used (prev_row is allocated) on the first row.
*/
# define match(mask) (filters_to_try & (mask)) == mask
if (match(PNG_FILTER_NONE+PNG_FILTER_UP))
filters_to_try &= PNG_BIC_MASK(PNG_FILTER_UP);
if (match(PNG_FILTER_SUB+PNG_FILTER_PAETH))
filters_to_try &= PNG_BIC_MASK(PNG_FILTER_UP);
# undef match
}
} /* start of row */
else if (prev_row != NULL)
@ -2362,7 +2426,7 @@ png_set_filter(png_structrp png_ptr, int method, int filtersIn)
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 width/*pixels*/, int first_row_in_pass, int last_pass_row,
unsigned int filters_to_try/*from previous call*/, int end_of_image)
{
const unsigned int bpp = png_ptr->row_output_pixel_depth;
@ -2371,7 +2435,12 @@ png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels,
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);
affirm(width < 65536U && bpp <= 64U && width < 65536U/bpp &&
row_bits <= 8U*PNG_ROW_BUFFER_SIZE);
/* Set up the IDAT zlib compression if not set up yet: */
if (png_ptr->zowner != png_IDAT)
png_start_IDAT(png_ptr);
write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits,
x == 0 ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST, end_of_image);