mirror of
https://git.code.sf.net/p/libpng/code.git
synced 2025-07-10 18:04:09 +02:00
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:
parent
14d11b9f35
commit
b3a18efebf
24
png.c
24
png.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
14
pngrutil.c
14
pngrutil.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
10
pngstruct.h
10
pngstruct.h
@ -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
|
||||
|
||||
36
pngwrite.c
36
pngwrite.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
417
pngwutil.c
417
pngwutil.c
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user