diff --git a/pngstruct.h b/pngstruct.h index cae87cf10..43b39fae5 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -566,7 +566,7 @@ struct png_struct_def * wherein !(r==g==b). */ #endif /* RGB_TO_GRAY */ -#endif /* TRANFORM_MECH */ +#endif /* TRANSFORM_MECH */ #ifdef PNG_READ_SUPPORTED /* These, and IDAT_size below, control how much input and output (at most) is @@ -599,12 +599,7 @@ struct png_struct_def int zlib_set_window_bits; int zlib_set_mem_level; int zlib_set_strategy; - - unsigned int zbuffer_start; /* Bytes written from start */ - png_uint_32 zbuffer_len; /* Length of data in list */ - png_compression_bufferp zbuffer_list; /* Created on demand during write */ - png_compression_bufferp *zbuffer_end; /* 'next' field of current buffer */ -#endif +#endif /* WRITE */ /* ERROR HANDLING */ #ifdef PNG_SETJMP_SUPPORTED @@ -748,14 +743,23 @@ struct png_struct_def * zlib expects a 'zstream' as the fundamental control structure, it allows * all the parameters to be passed as one pointer. */ - 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 */ + png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */ +# ifdef PNG_WRITE_SUPPORTED + png_compression_bufferp zbuffer_list; /* Created on demand during write */ + png_compression_bufferp *zbuffer_end; /* 'next' field of current buffer */ + png_uint_32 zbuffer_len; /* Length of data in list */ + unsigned int zbuffer_start; /* Bytes written from start */ +# endif /* WRITE */ +# ifdef PNG_READ_SUPPORTED + unsigned int zstream_ended:1; /* no more zlib output available [read] */ + unsigned int zstream_error:1; /* zlib error message has been output [read] */ +# endif /* READ */ +# ifdef PNG_PROGRESSIVE_READ_SUPPORTED unsigned int zstream_eod :1; /* all the required uncompressed data has been * received; set by the zstream using code for - * its own purposes. */ + * its own purposes. [progressive read] */ +# endif /* PROGRESSIVE_READ */ /* MNG SUPPORT */ #ifdef PNG_MNG_FEATURES_SUPPORTED diff --git a/pngwutil.c b/pngwutil.c index 468859763..6d01aa1c8 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -1907,11 +1907,148 @@ png_start_IDAT(png_structrp png_ptr) } static void -png_compress_IDAT(png_structrp png_ptr, png_const_voidp input, uInt input_len, - int flush) +png_write_IDAT(png_structrp png_ptr, int end_of_image) { + png_compression_bufferp *listp = &png_ptr->zbuffer_list; + png_compression_bufferp list; + png_uint_32 output_len = png_ptr->zbuffer_len; + png_uint_32 start = png_ptr->zbuffer_start; + png_uint_32 size = png_ptr->IDAT_size; + const png_uint_32 min_size = (end_of_image ? 1U : size); + + affirm(output_len >= min_size); + + /* png_struct::zbuffer_end points to the pointer to the next (unused) + * compression buffer. We don't need those blocks to produce output so + * free them now to save space. This also ensures that *zbuffer_end is + * NULL so can be used to store the list head when wrapping the list. + */ + png_free_buffer_list(png_ptr, png_ptr->zbuffer_end); + + /* Write at least one chunk, of size 'size', write chunks until + * 'output_len' is less than 'min_size'. + */ + list = *listp; + + /* First, if this is the very first IDAT (PNG_HAVE_IDAT not set) + * optimize the CINFO field: + */ +# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED + if ((png_ptr->mode & PNG_HAVE_IDAT) == 0U) + { + affirm(start == 0U); + optimize_cmf(list->output, png_image_size(png_ptr)); + } +# endif /* WRITE_OPTIMIZE_CMF */ + + do /* write chunks */ + { + /* 'size' is the size of the chunk to write, limited to IDAT_size: + */ + if (size > output_len) /* Z_FINISH */ + size = output_len; + + debug(size >= min_size); + png_write_chunk_header(png_ptr, png_IDAT, size); + + do /* write the data of one chunk */ + { + /* The chunk data may be split across multiple compression + * buffers. This loop moves 'list' down the available + * compression buffers. + */ + png_uint_32 avail = PNG_ROW_BUFFER_SIZE - start; /* in *list */ + + if (avail > output_len) /* valid bytes */ + avail = output_len; + + if (avail > size) /* bytes needed for chunk */ + avail = size; + + affirm(list != NULL && avail > 0U && + start+avail <= PNG_ROW_BUFFER_SIZE); + png_write_chunk_data(png_ptr, list->output+start, avail); + output_len -= avail; + size -= avail; + start += avail; + + if (start == PNG_ROW_BUFFER_SIZE) + { + /* End of the buffer. If all the compressed data has been + * consumed (output_len == 0) this will set list to NULL + * because of the png_free_buffer_list call above. At this + * point 'size' should be 0 too and the loop will terminate. + */ + start = 0U; + listp = &list->next; + list = *listp; /* May be NULL at the end */ + } + } while (size > 0); + + png_write_chunk_end(png_ptr); + size = png_ptr->IDAT_size; /* For the next chunk */ + } while (output_len >= min_size); + + png_ptr->mode |= PNG_HAVE_IDAT; + png_ptr->zbuffer_len = output_len; + + if (output_len > 0U) /* Still got stuff to write */ + { + affirm(!end_of_image && list != NULL); + + /* If any compression buffers have been completely written move them + * to the end of the list so that they can be re-used and move + * 'list' to the head: + */ + if (listp != &png_ptr->zbuffer_list) /* list not at start */ + { + debug(list != png_ptr->zbuffer_list /* obviously */ && + listp != png_ptr->zbuffer_end /* because *end == NULL */); + *png_ptr->zbuffer_end = png_ptr->zbuffer_list; + *listp = NULL; + png_ptr->zbuffer_list = list; + } + + /* 'list' is now at the start, so 'start' can be stored. */ + png_ptr->zbuffer_start = start; + png_ptr->zbuffer_len = output_len; + } + + else /* output_len == 0U; all compressed data has been written */ + { + if (end_of_image) + { + png_ptr->zowner = 0U; /* release z_stream */ + png_ptr->mode |= PNG_AFTER_IDAT; + } + + /* Else: this is unlikely but possible; the compression code managed + * to exactly fill an IDAT chunk with the data for this block of row + * bytes so nothing is left in the buffer list. Simply reset the + * output pointers to the start of the list. This code is executed + * on Z_FINISH as well just to make the state safe. + */ + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0U; + png_ptr->zbuffer_start = 0U; + png_ptr->zbuffer_len = 0U; + png_ptr->zbuffer_end = &png_ptr->zbuffer_list; + } /* output_len == 0 */ +} + +typedef struct +{ + png_compression_bufferp *zbuffer_end; /* 'next' field of current buffer */ + png_uint_32 zbuffer_len; /* Length of data in list */ +} png_IDAT_compression_state; + +static int +png_compress_IDAT_test(png_structrp png_ptr, png_IDAT_compression_state *state, + z_stream *zstream, png_const_voidp input, uInt input_len, int flush) +{ + png_uint_32 output_len = 0U; int ret; - + /* The stream must have been claimed: */ affirm(png_ptr->zowner == png_IDAT); @@ -1919,22 +2056,34 @@ png_compress_IDAT(png_structrp png_ptr, png_const_voidp input, uInt input_len, * buffer list. next_in must be set here, avail_in comes from the input_len * parameter: */ - { - png_uint_32 output_len = 0U; + zstream->next_in = PNGZ_INPUT_CAST(png_voidcast(const Bytef*, input)); + ret = png_compress(png_ptr, zstream, &state->zbuffer_end, input_len, + &output_len, flush); + implies(ret == Z_OK || ret == Z_FINISH, zstream->avail_in == 0U); + zstream->next_in = NULL; + zstream->avail_in = 0U; /* safety */ - png_ptr->zstream.next_in = - PNGZ_INPUT_CAST(png_voidcast(const Bytef*,input)); - ret = png_compress(png_ptr, &png_ptr->zstream, &png_ptr->zbuffer_end, - input_len, &output_len, flush); - implies(ret == Z_OK || ret == Z_FINISH, png_ptr->zstream.avail_in == 0U); - png_ptr->zstream.next_in = NULL; - png_ptr->zstream.avail_in = 0U; /* safety */ + /* If IDAT_size is set to PNG_UINT_31_MAX the length will be larger, but + * not enough to overflow a png_uint_32. + */ + state->zbuffer_len += output_len; + return ret; - /* If IDAT_size is set to PNG_UINT_31_MAX the length will be larger, but - * not enough to overflow a png_uint_32. - */ - png_ptr->zbuffer_len += output_len; - } +} + +static void +png_compress_IDAT(png_structp png_ptr, png_const_voidp input, uInt input_len, + int flush) +{ + png_IDAT_compression_state state; + int ret; + + state.zbuffer_end = png_ptr->zbuffer_end; + state.zbuffer_len = png_ptr->zbuffer_len; + ret = png_compress_IDAT_test(png_ptr, &state, &png_ptr->zstream, input, + input_len, flush); + png_ptr->zbuffer_end = state.zbuffer_end; + png_ptr->zbuffer_len = state.zbuffer_len; /* Check the return code. */ if (ret == Z_OK || ret == Z_STREAM_END) @@ -1956,133 +2105,7 @@ png_compress_IDAT(png_structrp png_ptr, png_const_voidp input, uInt input_len, * written this function call is complete. */ if (flush == Z_FINISH || png_ptr->zbuffer_len >= png_ptr->IDAT_size) - { - png_compression_bufferp *listp = &png_ptr->zbuffer_list; - png_compression_bufferp list; - png_uint_32 output_len = png_ptr->zbuffer_len; - png_uint_32 start = png_ptr->zbuffer_start; - png_uint_32 size = png_ptr->IDAT_size; - const png_uint_32 min_size = (flush == Z_FINISH ? 1U : size); - - affirm(output_len >= min_size); - - /* png_struct::zbuffer_end points to the pointer to the next (unused) - * compression buffer. We don't need those blocks to produce output so - * free them now to save space. This also ensures that *zbuffer_end is - * NULL so can be used to store the list head when wrapping the list. - */ - png_free_buffer_list(png_ptr, png_ptr->zbuffer_end); - - /* Write at least one chunk, of size 'size', write chunks until - * 'output_len' is less than 'min_size'. - */ - list = *listp; - - /* First, if this is the very first IDAT (PNG_HAVE_IDAT not set) - * optimize the CINFO field: - */ -# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED - if ((png_ptr->mode & PNG_HAVE_IDAT) == 0U) - { - affirm(start == 0U); - optimize_cmf(list->output, png_image_size(png_ptr)); - } -# endif /* WRITE_OPTIMIZE_CMF */ - - do /* write chunks */ - { - /* 'size' is the size of the chunk to write, limited to IDAT_size: - */ - if (size > output_len) /* Z_FINISH */ - size = output_len; - - debug(size >= min_size); - png_write_chunk_header(png_ptr, png_IDAT, size); - - do /* write the data of one chunk */ - { - /* The chunk data may be split across multiple compression - * buffers. This loop moves 'list' down the available - * compression buffers. - */ - png_uint_32 avail = PNG_ROW_BUFFER_SIZE - start; /* in *list */ - - if (avail > output_len) /* valid bytes */ - avail = output_len; - - if (avail > size) /* bytes needed for chunk */ - avail = size; - - affirm(list != NULL && avail > 0U && - start+avail <= PNG_ROW_BUFFER_SIZE); - png_write_chunk_data(png_ptr, list->output+start, avail); - output_len -= avail; - size -= avail; - start += avail; - - if (start == PNG_ROW_BUFFER_SIZE) - { - /* End of the buffer. If all the compressed data has been - * consumed (output_len == 0) this will set list to NULL - * because of the png_free_buffer_list call above. At this - * point 'size' should be 0 too and the loop will terminate. - */ - start = 0U; - listp = &list->next; - list = *listp; /* May be NULL at the end */ - } - } while (size > 0); - - png_write_chunk_end(png_ptr); - size = png_ptr->IDAT_size; /* For the next chunk */ - } while (output_len >= min_size); - - png_ptr->mode |= PNG_HAVE_IDAT; - png_ptr->zbuffer_len = output_len; - - if (output_len > 0U) /* Still got stuff to write */ - { - affirm(flush != Z_FINISH && list != NULL); - - /* If any compression buffers have been completely written move them - * to the end of the list so that they can be re-used and move - * 'list' to the head: - */ - if (listp != &png_ptr->zbuffer_list) /* list not at start */ - { - debug(list != png_ptr->zbuffer_list /* obviously */ && - listp != png_ptr->zbuffer_end /* because *end == NULL */); - *png_ptr->zbuffer_end = png_ptr->zbuffer_list; - *listp = NULL; - png_ptr->zbuffer_list = list; - } - - /* 'list' is now at the start, so 'start' can be stored. */ - png_ptr->zbuffer_start = start; - png_ptr->zbuffer_len = output_len; - } - - else /* output_len == 0U; all compressed data has been written */ - { - if (flush == Z_FINISH) /* end of data */ - { - png_ptr->zowner = 0U; /* release z_stream */ - png_ptr->mode |= PNG_AFTER_IDAT; - } - - /* Else: this is unlikely but possible; the compression code managed - * to exactly fill an IDAT chunk with the data for this block of row - * bytes so nothing is left in the buffer list. Simply reset the - * output pointers to the start of the list. This code is executed - * on Z_FINISH as well just to make the state safe. - */ - png_ptr->zstream.next_out = NULL; - png_ptr->zstream.avail_out = 0U; - png_ptr->zbuffer_start = 0U; - png_ptr->zbuffer_len = 0U; - png_ptr->zbuffer_end = &png_ptr->zbuffer_list; - } /* output_len == 0 */ - } /* flush == FINISH || png_ptr->zbuffer_len >= png_ptr->IDAT_size */ + png_write_IDAT(png_ptr, flush == Z_FINISH); } else /* ret != Z_OK && ret != Z_STREAM_END */ diff --git a/scripts/options.awk b/scripts/options.awk index 81b82ff09..79d24bbdc 100755 --- a/scripts/options.awk +++ b/scripts/options.awk @@ -243,7 +243,7 @@ $1 == "file" && NF >= 2{ # option NAME ( (requires|enables|if) NAME* | on | off | disabled | # sets SETTING VALUE+ )* -# +# # Declares an option 'NAME' and describes its default setting (disabled) # and its relationship to other options. The option is disabled # unless *all* the options listed after 'requires' are set and at diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa index 845a7da83..f01535457 100644 --- a/scripts/pnglibconf.dfa +++ b/scripts/pnglibconf.dfa @@ -125,7 +125,7 @@ file pnglibconf.h scripts/pnglibconf.dfa PNGLCONF_H # # To avoid confusion use -DPNG_foo_SUPPORTED= on the command line, which does # the same thing as the #define. -# +# # SUMMARY: # These lines/macro settings are equivalent: # @@ -460,7 +460,7 @@ option SET_USER_LIMITS requires USER_LIMITS # See the comments above about how to change options and settings. # READ/WRITE tranform support -# +# # The internal TRANSFORM_MECH options are used to turn on (or off) the required # support code for the read and write transforms. They are off by default, # switching them on is not a good idea. Switching them off will cause the build @@ -496,7 +496,7 @@ option READ_16BIT requires READ enables 16BIT option READ_TRANSFORMS requires READ = NO_READ_TRANSFORMS READ_TRANSFORMS_NOT_SUPPORTED -option READ_QUANTIZE requires READ_TRANSFORMS enables TRANFORM_MECH +option READ_QUANTIZE requires READ_TRANSFORMS enables TRANSFORM_MECH # Read gamma handling. Gamma processing is a core part of libpng and many of # the capabilities are dependent on libpng performing gamma correction. diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt index 91e62e5a8..7abce4811 100644 --- a/scripts/pnglibconf.h.prebuilt +++ b/scripts/pnglibconf.h.prebuilt @@ -116,7 +116,6 @@ #define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED #define PNG_TEXT_SUPPORTED #define PNG_TIME_RFC1123_SUPPORTED -#define PNG_TRANFORM_MECH_SUPPORTED #define PNG_TRANSFORM_MECH_SUPPORTED #define PNG_UNKNOWN_CHUNKS_SUPPORTED #define PNG_USER_CHUNKS_SUPPORTED @@ -194,7 +193,7 @@ #define PNG_DEFAULT_GAMMA_ACCURACY 665 #define PNG_DEFAULT_READ_MACROS 1 #define PNG_GAMMA_THRESHOLD_FIXED 153 -#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_IDAT_READ_SIZE 4096 #define PNG_INFLATE_BUF_SIZE 1024 #define PNG_MAX_GAMMA_8 11 #define PNG_QUANTIZE_BLUE_BITS 5 @@ -206,7 +205,7 @@ #define PNG_USER_CHUNK_MALLOC_MAX 8000000 #define PNG_USER_HEIGHT_MAX 1000000 #define PNG_USER_WIDTH_MAX 1000000 -#define PNG_ZBUF_SIZE 8192 +#define PNG_ZBUF_SIZE 4096 #define PNG_ZLIB_HEADER #define PNG_ZLIB_VERNUM 0 #define PNG_Z_DEFAULT_COMPRESSION (-1)