Write code update

Implemented better defaulting of zlib settings based on image properties.
Implemented pass-through of png_write_rows when the rows can be used directly (a
common case) optimizing the handling of previous-row buffering.

Removed the METHODICAL filter selection method and disabled the HEURISTIC one;
the first was ridiculously slow (though useful for experiments) the second
doesn't work.  Filter selection is temporarily disabled (it defaults to the
lowest numbered filter in the list; typically 'none').

New handling of compression settings (incomplete), new PNG compression level
(not yet visible in an API).

Back ported 'PNG_FAST_FILTERS' from 1.6 (in png.h).

There are minimal API changes beyond removal of the selection options.  Work is
still to be done to investigate a filter selection mechanism that is at least as
good as the previous one.

Signed-off-by: John Bowler <jbowler@acm.org>
This commit is contained in:
John Bowler 2016-05-04 17:02:20 -07:00
parent 1fd42d849d
commit cde9b583a8
10 changed files with 1834 additions and 1330 deletions

View File

@ -1609,10 +1609,10 @@ read_png(struct display *dp, const char *filename)
png_alloc_size_t rb = png_get_rowbytes(dp->read_pp, dp->ip);
/* The size calc can overflow. */
if (MAX_SIZE/rb < dp->h)
if ((MAX_SIZE-dp->h)/rb < dp->h)
png_error(dp->read_pp, "image too large");
dp->size = rb * dp->h;
dp->size = rb * dp->h + dp->h/*filter byte*/;
}
display_clean_read(dp);
@ -1826,18 +1826,21 @@ write_png(struct display *dp, const char *destname)
static void
set_windowBits_hi(struct display *dp)
{
/* windowBits is in the range 8..15, but it is said that setting '8'
* prevents adequate search even if the image size is 256 bytes or less.
/* windowBits is in the range 8..15 but zlib maps '8' to '9' so it is only
* worth using if the data size is 256 byte or less.
*/
int wb = MAX_WBITS; /* for large images */
int i = VLSIZE(windowBits_IDAT);
while (wb > 9 && dp->size <= 1U<<(wb-1)) --wb;
while (wb > 8 && dp->size <= 1U<<(wb-1)) --wb;
while (--i >= 0) if (VLNAME(windowBits_IDAT)[i].name == range_hi) break;
assert(i > 0); /* vl_windowBits_IDAT always has a RANGE() */
assert(i > 1); /* vl_windowBits_IDAT always has a RANGE() */
VLNAME(windowBits_IDAT)[i].value = wb;
assert(VLNAME(windowBits_IDAT)[--i].name == range_lo);
VLNAME(windowBits_IDAT)[i].value = wb > 8 ? 9 : 8;
}
static int

43
png.h
View File

@ -1690,25 +1690,14 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action,
* ignored if SUB is set; this is because these filter pairs are equivalent
* when there is no previous row.
*
* 2) PNG_SELECT_FILTER_METHODICALLY_SUPPORTED:
* If SELECT_FILTER_METHODICALLY is 'on' libpng tries all the filters in the
* list and selects the one which gives the shortest compressed row, favoring
* earlier filters.
* 2) PNG_SELECT_FILTER_SUPPORTED:
* libpng will buffer rows until enough data is available to perform a
* reasonable filter selection heuristic then select filters for at least the
* first buffered row.
*
* 3) PNG_SELECT_FILTER_HEURISTICALLY_SUPPORTED:
* If SELECT_FILTER_HEURISTICALLY is 'on' libpng tests the start of each row
* (a few thousand bytes at most) to see which filter is likely to produce
* best compression.
*
* 4) If neither (2) nor (3) libpng selects the first filter in the list (there
* is no warning that this will happen - check the #defines if you need to
* know.)
*
* The seletion options are turned 'on' using png_set_option(method,
* PNG_OPTION_ON) and turned off with PNG_OPTION_OFF. If a selection method is
* turned off it will never be used, if neither option is turned on or off (i.e.
* if png_set_option is not called) the first supported option (2) or (3) will
* be used.
* 3) !PNG_SELECT_FILTER_SUPPORTED:
* libpng selects the first filter in the list (there is no warning that this
* will happen - check the #defines if you need to know.)
*
* If you intend to use 'previous row' filters in an image set either the UP or
* PAETH filter before the first call to png_write_row, depending on whether you
@ -1717,12 +1706,6 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action,
* You can also select AVG on the first row; it uses half the value of the
* preceding byte as a predictor and is not likely to have very good
* performance.
*
* The SELECT_FILTER_METHODICALLY option is slow and memory intensive, but it is
* almost certain to produce the smallest PNG file. Depending on the image data
* the HEURISTIC option may improve results significantly over the NONE filter
* and it has little overall effect on compression speed, however it can
* sometimes produce larger files than not using any filtering.
*/
PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method,
int filters));
@ -1752,12 +1735,12 @@ PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method,
#define PNG_FILTER_AVG PNG_FILTER_MASK(PNG_FILTER_VALUE_AVG)
#define PNG_FILTER_PAETH PNG_FILTER_MASK(PNG_FILTER_VALUE_PAETH)
/* Then two convenience values. PNG_NO_FILTERS is the same as
/* Then three convenience values. PNG_NO_FILTERS is the same as
* PNG_FILTER_VALUE_NONE, but this is harmless because they mean the same thing.
*/
#define PNG_NO_FILTERS 0x00
#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \
PNG_FILTER_AVG | PNG_FILTER_PAETH)
#define PNG_NO_FILTERS 0x00
#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP)
#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH)
#ifdef PNG_WRITE_SUPPORTED
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */
@ -3629,9 +3612,7 @@ PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory,
#define PNG_EXTENSIONS 0 /* BOTH: enable or disable extensions */
#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */
#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */
#define PNG_SELECT_FILTER_HEURISTICALLY 6 /* SOFTWARE: see png_set_filter */
#define PNG_SELECT_FILTER_METHODICALLY 8 /* SOFTWARE: see png_set_filter */
#define PNG_OPTION_NEXT 10 /* Next option - numbers must be even */
#define PNG_OPTION_NEXT 6 /* Next option - numbers must be even */
/* Return values: NOTE: there are four values and 'off' is *not* zero */
#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */

View File

@ -1080,10 +1080,6 @@ PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr,
PNG_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr,
png_const_voidp ptr, png_size_t length),PNG_EMPTY);
#ifdef PNG_WRITE_FLUSH_SUPPORTED
PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY);
#endif
/* Write various chunks */
/* Write the IHDR chunk, and update the png_struct with the necessary
@ -1199,9 +1195,9 @@ PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr,
#endif
#ifdef PNG_WRITE_SUPPORTED
/* Initialize the row compression mechanism. */
PNG_INTERNAL_FUNCTION(void, png_write_start_IDAT, (png_structp png_ptr),
PNG_EMPTY);
PNG_INTERNAL_FUNCTION(void,png_write_start_IDAT,(png_structrp png_ptr),
PNG_EMPTY);
/* Do any required initialization before IDAT or row processing starts. */
/* Choose the best filter to use and filter the row data then write it out. If
* WRITE_FILTERING is not supported this just writes the data out with a zero
@ -1226,11 +1222,21 @@ enum
# define PNG_IDAT_END(f) (((f) & ~png_pass_first_row) == \
(png_row_end+png_pass_last_row+png_pass_last))
};
PNG_INTERNAL_FUNCTION(void, png_write_filter_row, (png_structrp png_ptr,
PNG_INTERNAL_FUNCTION(void,png_write_png_data,(png_structrp png_ptr,
png_bytep prev_pixels, png_const_bytep unfiltered_row, png_uint_32 x,
unsigned int width/*pixels*/, unsigned int row_info_flags),
PNG_EMPTY);
PNG_INTERNAL_FUNCTION(void,png_write_png_rows,(png_structrp png_ptr,
png_const_bytep *rows, png_uint_32 num_rows), PNG_EMPTY);
/* As above but rows[num_rows] of correctly (PNG) formated but unfiltered
* data are passed in. For an interlaced image the rows will be interlaced
* rows and therefore may be narrower than the image width.
*
* This function advances png_structp::pass and png_structp::row_number as
* required.
*/
/* Release memory used by the deflate mechanism */
PNG_INTERNAL_FUNCTION(void, png_deflate_destroy, (png_structp png_ptr),
PNG_EMPTY);

View File

@ -26,14 +26,14 @@
#ifndef ZLIB_CONST
/* We must ensure that zlib uses 'const' in declarations. */
# define ZLIB_CONST
#endif
#endif /* !ZLIB_CONST */
#include PNG_ZLIB_HEADER
#ifdef const
/* zlib.h sometimes #defines const to nothing, undo this. */
# undef const
#endif
#endif /* const */
/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility
* with older builds.
@ -41,10 +41,10 @@
#if ZLIB_VERNUM < 0x1260
# define PNGZ_MSG_CAST(s) png_constcast(char*,s)
# define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b)
#else
#else /* ZLIB_VERNUM >= 0x1260 */
# define PNGZ_MSG_CAST(s) (s)
# define PNGZ_INPUT_CAST(b) (b)
#endif
#endif /* ZLIB_VERNUM >= 0x1260 */
/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib
* can handle at once. This type need be no larger than 16 bits (so maximum of
@ -57,17 +57,17 @@
#ifndef ZLIB_IO_MAX
# ifdef __COVERITY__
# define ZLIB_IO_MAX ((uInt)255U) /* else COVERITY whines */
# else
# else /* !COVERITY */
# define ZLIB_IO_MAX ((uInt)-1)
# endif /* COVERITY */
#endif
# endif /* !COVERITY */
#endif /* !ZLIB_IO_MAX */
#ifdef PNG_WRITE_SUPPORTED
/* The write compression control (allocated on demand).
* TODO: use this for the read state too.
*/
typedef struct png_zlib_state *png_zlib_statep;
#endif
#endif /* WRITE */
/* Colorspace support; structures used in png_struct, png_info and in internal
* functions to hold and communicate information about the color space.
@ -114,13 +114,13 @@ typedef struct png_colorspace
{
#ifdef PNG_GAMMA_SUPPORTED
png_fixed_point gamma; /* File gamma */
#endif
#endif /* GAMMA */
#ifdef PNG_COLORSPACE_SUPPORTED
png_xy end_points_xy; /* End points as chromaticities */
png_XYZ end_points_XYZ; /* End points as CIE XYZ colorant values */
png_uint_16 rendering_intent; /* Rendering intent of a profile */
#endif
#endif /* COLORSPACE */
/* Flags are always defined to simplify the code. */
png_uint_16 flags; /* As defined below */
@ -182,7 +182,7 @@ typedef struct
* during PNG_TC_INIT_FINAL. The field is only used on read; write
* transforms do not modify the gamma of the data.
*/
# endif
# endif /* READ_GAMMA */
unsigned int format; /* As pngstruct::row_format below */
unsigned int range; /* Count of range transforms */
# define PNG_TC_CHANNELS(tc) PNG_FORMAT_CHANNELS((tc).format)
@ -364,7 +364,7 @@ struct png_struct_def
* accessed.)
*/
jmp_buf jmp_buf_local;
#endif
#endif /* SETJMP */
/* Next the frequently accessed fields. Many processors perform arithmetic
* in the address pipeline, but frequently the amount of addition or
@ -420,7 +420,7 @@ struct png_struct_def
/* Options */
#ifdef PNG_SET_OPTION_SUPPORTED
png_uint_32 options; /* On/off state (up to 16 options) */
#endif
#endif /* SET_OPTIONS */
#ifdef PNG_READ_SUPPORTED
#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
@ -428,13 +428,13 @@ struct png_struct_def
* is in (just) the info_struct.
*/
png_colorspace colorspace;
#endif
#endif /* COLORSPACE || GAMMA */
#endif /* READ */
/* Transform handling */
#ifdef PNG_TRANSFORM_MECH_SUPPORTED
png_transformp transform_list; /* List of transformation to perform. */
#endif
#endif /* TRANSFORM_MECH */
/* ROW BUFFERS and CONTROL
*
@ -442,9 +442,9 @@ struct png_struct_def
* filter byte (which is in next_filter.) All fields are only used during
* IDAT processing and start of 0.
*/
#if defined(PNG_WRITE_FILTER_SUPPORTED) || defined(PNG_READ_SUPPORTED)
#ifdef PNG_READ_SUPPORTED
png_bytep row_buffer; /* primary row buffer */
#endif /* WRITE_FILTER || READ */
#endif /* READ */
#if (defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\
defined(PNG_READ_INTERLACING_SUPPORTED)) &&\
defined(PNG_TRANSFORM_MECH_SUPPORTED)
@ -482,10 +482,12 @@ struct png_struct_def
unsigned int invalid_info; /* PNG_INFO_* for invalidated chunks */
unsigned int palette_updated:1; /* png_struct::palette changed */
#endif /* READ_TRANSFORMS */
#ifdef PNG_WRITE_SUPPORTED
unsigned int write_rows :1; /* libpng has complete rows to write */
#endif /* WRITE */
#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
unsigned int read_started :1; /* at least one call to png_read_row */
#endif
#endif /* SEQUENTIAL_READ */
#if defined (PNG_READ_INTERLACING_SUPPORTED) ||\
defined (PNG_WRITE_INTERLACING_SUPPORTED)
unsigned int do_interlace :1; /* libpng handles the interlace */
@ -574,7 +576,7 @@ struct png_struct_def
* available to zlib during read decompression.
*/
png_alloc_size_t read_buffer_size; /* current size of the buffer */
#endif
#endif /* READ */
#if defined(PNG_SEQUENTIAL_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
png_uint_32 IDAT_size; /* limit on IDAT read and write IDAT size */
@ -585,13 +587,13 @@ struct png_struct_def
jmp_buf *jmp_buf_ptr; /* passed to longjmp_fn */
png_longjmp_ptr longjmp_fn; /* setjmp non-local goto function. */
size_t jmp_buf_size; /* size of *jmp_buf_ptr, if allocated */
#endif
#endif /* SETJMP */
/* Error/warning callbacks */
png_error_ptr error_fn; /* print an error message and abort */
#ifdef PNG_WARNINGS_SUPPORTED
png_error_ptr warning_fn; /* print a warning and continue */
#endif
#endif /* WARNINGS */
png_voidp error_ptr; /* user supplied data for the above */
/* MEMORY ALLOCATION */
@ -599,7 +601,7 @@ struct png_struct_def
png_malloc_ptr malloc_fn; /* allocate memory */
png_free_ptr free_fn; /* free memory */
png_voidp mem_ptr; /* user supplied data for the above */
#endif
#endif /* USER_MEM */
/* IO and BASIC READ/WRITE SUPPORT */
png_voidp io_ptr; /* user supplied data for IO callbacks */
@ -622,8 +624,7 @@ struct png_struct_def
#ifdef PNG_WRITE_FLUSH_SUPPORTED
png_flush_ptr output_flush_fn; /* Function for flushing output */
#endif
#endif /* WRITE_FLUSH */
#endif /* WRITE */
#ifdef PNG_SET_USER_LIMITS_SUPPORTED
@ -634,14 +635,14 @@ struct png_struct_def
* - it is decremented as memory is allocated.
*/
png_alloc_size_t user_chunk_malloc_max;
#endif
#endif /* SET_USER_LIMITS */
#ifdef PNG_USER_LIMITS_SUPPORTED
/* limit on total *number* of sPLT, text and unknown chunks that can be
* stored. 0 means unlimited. This field is a counter - it is decremented
* as chunks are encountered.
*/
png_uint_32 user_chunk_cache_max;
#endif
#endif /* USER_LIMITS */
/* The progressive reader gets passed data and calls application handling
* functions when appropriate.
@ -672,7 +673,7 @@ struct png_struct_def
#ifdef PNG_IO_STATE_SUPPORTED
png_uint_32 io_state; /* tells the app read/write progress */
#endif
#endif /* IO_STATE */
/* UNKNOWN CHUNK HANDLING */
/* TODO: this is excessively complicated, there are multiple ways of doing
@ -686,12 +687,12 @@ struct png_struct_def
* set_unknown_chunks interface.)
*/
png_voidp user_chunk_ptr;
#endif
#endif /* USER_CHUNKS */
#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
/* This is called back from the unknown chunk handling */
png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */
#endif
#endif /* READ_USER_CHUNKS */
#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
png_uint_32 known_unknown; /* Bit mask of known chunks to be treated as
@ -703,7 +704,7 @@ struct png_struct_def
* 'known & ~save'.
*/
# define png_IDATs_skipped(pp) (((pp)->known_unknown & ~(pp)->save_unknown)&1U)
#else
#else /* !SAVE_UNKNOWN_CHUNKS */
# define png_IDATs_skipped(pp) ((pp)->known_unknown & 1U)
#endif /* !SAVE_UNKNOWN_CHUNKS */
#endif /* HANDLE_AS_UNKNOWN */
@ -713,7 +714,7 @@ struct png_struct_def
* followed by a PNG_HANDLE_* byte */
unsigned int unknown_default :2; /* As PNG_HANDLE_* */
unsigned int num_chunk_list; /* Number of entries in the list */
#endif
#endif /* SET_UNKNOWN_CHUNKS */
/* COMPRESSION AND DECOMPRESSION SUPPORT.
*
@ -738,7 +739,7 @@ struct png_struct_def
/* MNG SUPPORT */
#ifdef PNG_MNG_FEATURES_SUPPORTED
unsigned int mng_features_permitted :3;
#endif
#endif /* MNG_FEATURES */
/* SCRATCH buffers, used when control returns to the application or a read
* loop.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -67,18 +67,7 @@ png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
}
#endif
/* This function is called to output any data pending writing (normally
* to disk). After png_flush is called, there should be no data pending
* writing in any buffers.
*/
#ifdef PNG_WRITE_FLUSH_SUPPORTED
void /* PRIVATE */
png_flush(png_structrp png_ptr)
{
if (png_ptr->output_flush_fn != NULL)
png_ptr->output_flush_fn(png_ptr);
}
# ifdef PNG_STDIO_SUPPORTED
void PNGCBAPI
png_default_flush(png_structp png_ptr)

View File

@ -487,7 +487,8 @@ png_write_end(png_structrp png_ptr, png_inforp info_ptr)
*/
# ifdef PNG_WRITE_FLUSH_SUPPORTED
# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
png_flush(png_ptr);
if (png_ptr->output_flush_fn != NULL)
png_ptr->output_flush_fn(png_ptr);
# endif
# endif
}
@ -570,65 +571,6 @@ png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
}
/* Write a few rows of image data. If the image is interlaced,
* either you will have to write the 7 sub images, or, if you
* have called png_set_interlace_handling(), you will have to
* "write" the image seven times.
*/
void PNGAPI
png_write_rows(png_structrp png_ptr, png_bytepp row,
png_uint_32 num_rows)
{
png_debug(1, "in png_write_rows");
if (png_ptr == NULL || row == NULL)
return;
/* Loop through the rows */
while (num_rows-- > 0)
png_write_row(png_ptr, *row++);
}
/* Write the image. You only need to call this function once, even
* if you are writing an interlaced image.
*/
void PNGAPI
png_write_image(png_structrp png_ptr, png_bytepp image)
{
int num_pass; /* pass variables */
if (png_ptr == NULL || image == NULL)
return;
png_debug(1, "in png_write_image");
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
/* Initialize interlace handling. If image is not interlaced,
* this will set pass to 1
*/
num_pass = png_set_interlace_handling(png_ptr);
#else
num_pass = 1;
if (png_ptr->interlaced)
{
png_app_error(png_ptr, "no interlace support");
return;
}
#endif
/* Loop through passes */
while (num_pass-- > 0)
{
png_bytepp rp = image; /* points to current row */
png_uint_32 num_rows = png_ptr->height;
/* Loop through image */
while (num_rows-- > 0)
png_write_row(png_ptr, *rp++);
}
}
#if defined(PNG_WRITE_INTERLACING_SUPPORTED) ||\
defined(PNG_WRITE_TRANSFORMS_SUPPORTED)
static void
@ -707,10 +649,10 @@ write_row_buffered(png_structrp png_ptr,
}
# endif /* WRITE_TRANSFORMS */
/* Call png_write_filter_row to write this block of data, the test on
/* Call png_write_png_data to write this block of data, the test on
* maxpixels says if this is the final block in the row.
*/
png_write_filter_row(png_ptr, prev_pixels, pixel_buffer.buffer, x,
png_write_png_data(png_ptr, prev_pixels, pixel_buffer.buffer, x,
max_pixels, row_info_flags);
}
}
@ -824,47 +766,7 @@ interlace_row_byte(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp,
}
#endif /* WRITE_INTERLACING */
static void
write_row_unbuffered(png_structrp png_ptr, png_const_bytep row,
unsigned int row_info_flags)
{
/* Split the row into blocks of the appropriate size: */
const unsigned int input_depth = png_ptr->row_input_pixel_depth;
unsigned int max_pixels = png_max_pixel_block(png_ptr);
png_uint_32 max_bytes = max_pixels;
const unsigned int pass = png_ptr->pass;
const png_uint_32 width = png_ptr->interlaced == PNG_INTERLACE_NONE ?
png_ptr->width : PNG_PASS_COLS(png_ptr->width, pass);
png_uint_32 x;
png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4 2-byte channels each */
/* max_pixels is at most 16 bits, input_depth is at most 64, so the product
* always fits in 32 bits:
*/
max_bytes *= input_depth; /* bits */
debug(input_depth <= 64 && (max_bytes & 7U) == 0U);
max_bytes >>= 3;
memset(prev_pixels, 0U, sizeof prev_pixels);
for (x = 0U; x < width; x += max_pixels, row += max_bytes)
{
if (max_pixels > width - x)
{
max_bytes = width - x;
max_pixels = (unsigned int)/*SAFE*/max_bytes;
max_bytes = (max_bytes * input_depth + 7U) >> 3;
}
debug((row_info_flags & png_row_end) == 0U); /* must be set here at end */
if (x + max_pixels >= width)
row_info_flags |= png_row_end;
png_write_filter_row(png_ptr, prev_pixels, row, x, max_pixels,
row_info_flags);
}
}
#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
static void
write_row_core(png_structrp png_ptr, png_const_bytep row,
unsigned int row_info_flags)
@ -880,7 +782,8 @@ write_row_core(png_structrp png_ptr, png_const_bytep row,
/* If control reaches this point the intermediate buffer is not required and
* the input data can be used unmodified.
*/
write_row_unbuffered(png_ptr, row, row_info_flags);
png_write_png_rows(png_ptr, &row, 1U);
PNG_UNUSED(row_info_flags)
}
/* Write a single non-interlaced row. */
@ -888,75 +791,53 @@ static void
write_row_non_interlaced(png_structrp png_ptr, png_const_bytep row)
{
const png_uint_32 row_number = png_ptr->row_number+1U;
const int last_pass_row = row_number == png_ptr->height;
/* There is only one pass, so this is the last pass: */
write_row_core(png_ptr, row,
(row_number == 1U ? png_pass_first_row : 0) |
(last_pass_row ? png_pass_last_row : 0) |
png_pass_last);
const unsigned int row_info_flags =
(row_number == 1U ? png_pass_first_row : 0) |
(row_number >= png_ptr->height ? png_pass_last_row : 0) |
png_pass_last;
if (!last_pass_row)
png_ptr->row_number = row_number;
debug(png_ptr->interlaced == PNG_INTERLACE_NONE);
else
{
png_ptr->row_number = 0U;
png_ptr->pass = 7U;
}
write_row_core(png_ptr, row, row_info_flags);
}
/* Write a single interlaced row. */
static void
write_row_interlaced(png_structrp png_ptr, png_const_bytep row)
{
/* row_number is the row in the pass. The app must only call png_write_row
* the correct number of times. 'pass' is set to 7U after the end.
*/
const png_uint_32 row_number = png_ptr->row_number+1U;
unsigned int pass = png_ptr->pass;
const int last_pass_row = row_number == PNG_PASS_ROWS(png_ptr->height, pass);
const png_uint_32 height = png_ptr->height;
const unsigned int pass = png_ptr->pass;
const unsigned int row_info_flags =
(row_number == 1U ? png_pass_first_row : 0) |
(row_number == PNG_PASS_ROWS(height, pass) ? png_pass_last_row : 0) |
(pass == PNG_LAST_PASS(png_ptr->width, height) ? png_pass_last : 0);
write_row_core(png_ptr, row,
(row_number == 1U ? png_pass_first_row : 0) |
(last_pass_row ? png_pass_last_row : 0) |
(pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height) ?
png_pass_last : 0));
# ifdef PNG_WRITE_INTERLACING_SUPPORTED
/* Check that libpng is not doing the interlace: */
debug(png_ptr->interlaced != PNG_INTERLACE_NONE &&
!png_ptr->do_interlace);
# endif /* WRITE_INTERLACING */
if (!last_pass_row)
png_ptr->row_number = row_number;
else
{
png_ptr->row_number = 0U;
do
++pass;
while (pass < 7U &&
!PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, pass));
png_ptr->pass = 0x7U & pass;
}
write_row_core(png_ptr, row, row_info_flags);
}
#endif /* WRITE_TRANSFORMS */
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
/* Interlace a row then write it out. */
static int
static void
interlace_row(png_structrp png_ptr, png_const_bytep row)
{
/* row_number is in the image, it may not be in the pass and, likewise, the
* pass may not exist.
*/
/* The row may not exist in the image (for this pass). */
const png_uint_32 row_number = png_ptr->row_number; /* in image */
const unsigned int pass = png_ptr->pass;
const int write_row = png_ptr->width > PNG_PASS_START_COL(pass) &&
PNG_ROW_IN_INTERLACE_PASS(row_number, pass);
if (write_row)
if (png_ptr->width > PNG_PASS_START_COL(pass) &&
PNG_ROW_IN_INTERLACE_PASS(row_number, pass))
{
const unsigned int row_info_flags =
(row_number == PNG_PASS_START_ROW(pass) ?
png_pass_first_row : 0) |
(row_number == PNG_PASS_START_ROW(pass) ? png_pass_first_row : 0) |
(PNG_LAST_PASS_ROW(row_number, pass, png_ptr->height) ?
png_pass_last_row : 0) |
(pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height) ?
@ -991,79 +872,214 @@ interlace_row(png_structrp png_ptr, png_const_bytep row)
interlace_row_byte, input_depth >> 3);
break;
}
}
} /* pass < 6 */
else /* pass 6; no interlacing required */
else /* pass 6: no interlacing required */
write_row_core(png_ptr, row, row_info_flags);
}
if (row_number+1U < png_ptr->height)
png_ptr->row_number = row_number+1U;
else
{
png_ptr->row_number = 0U;
png_ptr->pass = 0x7U & (pass+1U);
}
/* This code must advance row_number/pass itself; the row has been
* skipped.
*/
if (row_number+1U < png_ptr->height)
png_ptr->row_number = row_number+1U;
return write_row;
else
{
png_ptr->row_number = 0U;
png_ptr->pass = 0x7U & (pass+1U);
}
}
}
#endif /* WRITE_INTERLACING */
/* Called by user to write a row of image data */
void PNGAPI
png_write_row(png_structrp png_ptr, png_const_bytep row)
/* Bottleneck API to actually write a number of rows, only exists because the
* rows parameter to png_write_rows is wrong.
*/
static void
png_write_rows_internal(png_structrp png_ptr, png_const_bytep *rows,
png_uint_32 num_rows)
{
if (png_ptr != NULL)
if (png_ptr != NULL && num_rows > 0U && rows != NULL)
{
const unsigned int pass = png_ptr->pass;
const png_uint_32 row_number = png_ptr->row_number;
/* Unlike the read code initialization happens automatically: */
if (row_number == 0 && pass == 0)
png_write_start_IDAT(png_ptr); /* doesn't change row/pass/width */
if (png_ptr->row_number == 0U && png_ptr->pass == 0U)
{
png_init_row_info(png_ptr);
else if (pass == 7U) /* too many calls; write already ended */
# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
/* If the app takes a png_info from a read operation and if the app has
* performed transforms on the data the png_info can contain IHDR
* information that cannot be represented in PNG. The code that writes
* the IHDR takes the color type from the png_info::format. The app
* adds transforms, before or after writing the IHDR, then the IHDR
* color_type stored in png_struct::color_type is used in
* png_init_row_info above to work out the actual row format.
*
* Prior to 1.7.0 this was not verified (there was no easy way to do
* so). Now we can check it here, however this is an:
*
* API CHANGE: in 1.7.0 an error may be flagged against bogus
* info_struct formats even though the app had removed them itself.
* It's just a warning at present.
*
* The test is that either the row_format produced by the write
* transforms exactly matches that in the original info_struct::format
* or that the info_struct::format was a simple mapping of the
* color_type that ended up in the IHDR:
*/
if (png_ptr->row_format != png_ptr->info_format &&
PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type) !=
png_ptr->info_format)
png_app_warning(png_ptr, "info_struct format does not match IHDR");
# endif /* WRITE_TRANSFORMS */
/* Perform initialization required before IDATs are written. */
png_write_start_IDAT(png_ptr);
}
else if (png_ptr->pass >= 7U) /* too many calls; write already ended */
{
debug(png_ptr->row_number == 0U);
png_app_error(png_ptr, "Too many calls to png_write_row");
return;
}
/* Make sure there is a row to write: */
if (row == NULL)
{
png_app_error(png_ptr, "NULL row pointer to png_write_row");
return;
}
if (png_ptr->interlaced == PNG_INTERLACE_NONE)
write_row_non_interlaced(png_ptr, row);
/* The remainder of these tests detect internal errors in libpng */
else if (png_ptr->interlaced == PNG_INTERLACE_NONE)
affirm(png_ptr->row_number < png_ptr->height && png_ptr->pass == 0U);
# ifdef PNG_WRITE_INTERLACING_SUPPORTED
/* Optional: libpng does the interlacing, app passes every row of the
* image the required number of times.
*/
else if (png_ptr->do_interlace != 0U)
{
if (!interlace_row(png_ptr, row))
return; /* row skipped; skip the write callback */
}
# endif
else if (png_ptr->do_interlace)
affirm(png_ptr->row_number < png_ptr->height);
# endif /* WRITE_INTERLACING */
else /* app does the interlacing */
write_row_interlaced(png_ptr, row);
else /* app does interlace */
affirm(
PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, png_ptr->pass) &&
png_ptr->row_number < PNG_PASS_ROWS(png_ptr->height, png_ptr->pass)
);
/* API CHANGE: 1.7.0: this is now called after png_struct::row_number and
* png_struct::pass have been updated and, at the end of the image, after
* the deflate stream has been closed. The order of the call with respect
* to the flush operation has also changed. The callback can't discover
* any of this unless it relies on the write callbacks to find the row
* data, and that was never predictable.
/* First handle rows that require buffering because of the need to
* interlace them or the need to perform write transforms.
*/
if (png_ptr->write_row_fn != NULL)
(*(png_ptr->write_row_fn))(png_ptr, row_number, pass);
} /* png_ptr != NULL */
# ifdef PNG_WRITE_INTERLACING_SUPPORTED
/* libpng is doing the interlacing, but this only makes a difference to
* the first six passes (numbered, in libpng, 0..5); the seventh pass
* (numbered 6 by libpng) consists of complete image rows.
*/
if (png_ptr->do_interlace) while (num_rows > 0U && png_ptr->pass < 6)
interlace_row(png_ptr, *rows++), --num_rows;
# endif /* WRITE_INTERLACING */
# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
/* Transforms required however the row interlacing has already been
* handled and we have a complete (PNG) row.
*/
if (png_ptr->transform_list != NULL)
{
if (png_ptr->interlaced == PNG_INTERLACE_NONE)
while (num_rows > 0U)
write_row_non_interlaced(png_ptr, *rows++), --num_rows;
# ifdef PNG_WRITE_INTERLACING_SUPPORTED
else if (png_ptr->do_interlace)
while (num_rows > 0U)
interlace_row(png_ptr, *rows++), --num_rows;
# endif /* WRITE_INTERLACING */
else /* app does the interlacing */
while (num_rows > 0U)
write_row_interlaced(png_ptr, *rows++), --num_rows;
}
# endif /* WRITE_TRANSFORMS */
/* Finally handle any remaining rows that require no (libpng) interlace
* and no transforms.
*/
if (num_rows > 0U)
png_write_png_rows(png_ptr, rows, num_rows);
/* Repeat the checks above, but allow for end-of-image. */
if (png_ptr->pass < 7U)
{
if (png_ptr->interlaced == PNG_INTERLACE_NONE)
affirm(png_ptr->row_number < png_ptr->height &&
png_ptr->pass == 0U);
# ifdef PNG_WRITE_INTERLACING_SUPPORTED
else if (png_ptr->do_interlace)
affirm(png_ptr->row_number < png_ptr->height);
# endif /* WRITE_INTERLACING */
else /* app does interlace */
affirm(PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height,
png_ptr->pass) &&
png_ptr->row_number <
PNG_PASS_ROWS(png_ptr->height, png_ptr->pass));
}
} /* png_ptr, rows, num_rows all valid */
else if (png_ptr != NULL)
png_app_warning(png_ptr, "Missing rows to row write API");
}
/* ROW WRITE APIs */
/* Called by user to write a single row of image data */
void PNGAPI
png_write_row(png_structrp png_ptr, png_const_bytep row)
{
png_debug(1, "in png_write_row");
png_write_rows_internal(png_ptr, &row, 1U);
}
/* Write a few rows of image data. If the image is interlaced,
* either you will have to write the 7 sub images, or, if you
* have called png_set_interlace_handling(), you will have to
* "write" the image seven times.
*/
void PNGAPI
png_write_rows(png_structrp png_ptr, png_bytepp rows, png_uint_32 num_rows)
{
png_debug(1, "in png_write_rows");
if (png_ptr != NULL)
png_write_rows_internal(png_ptr, png_constcast(png_const_bytep*,rows),
num_rows);
}
/* Write the image. You only need to call this function once, even
* if you are writing an interlaced image.
*/
void PNGAPI
png_write_image(png_structrp png_ptr, png_bytepp image)
{
png_debug(1, "in png_write_image");
if (png_ptr != NULL)
{
int num_pass = 1;
/* The image is always an non-interlaced image. To write it as interlaced
* interlace handling must be present:
*/
if (png_ptr->interlaced)
{
# ifdef PNG_WRITE_INTERLACING_SUPPORTED
num_pass = png_set_interlace_handling(png_ptr);
# else /* !WRITE_INTERLACING */
/* There is no recovery because the IHDR has already been written.
*/
png_error(png_ptr, "No interlace support");
# endif /* !WRITE_INTERLACING */
}
/* And write the whole thing, 7 times if interlacing it: */
for (; num_pass > 0; --num_pass)
png_write_rows(png_ptr, image, png_ptr->height);
}
}
/* Free any memory used in png_ptr struct without freeing the struct itself. */
@ -1074,10 +1090,6 @@ png_write_destroy(png_structrp png_ptr)
png_deflate_destroy(png_ptr);
#ifdef PNG_WRITE_FILTER_SUPPORTED
png_free(png_ptr, png_ptr->row_buffer);
png_ptr->row_buffer = NULL;
#endif /* WRITE_FILTER */
#ifdef PNG_TRANSFORM_MECH_SUPPORTED
png_transform_free(png_ptr, &png_ptr->transform_list);
#endif

2546
pngwutil.c

File diff suppressed because it is too large Load Diff

View File

@ -930,20 +930,36 @@ option CONVERT_tIME requires WRITE_ANCILLARY_CHUNKS
# png_set_filter interface allowing the application to select the filter
# used for each row.
#
# SELECT_FILTER_HEURISTICALLY
# Enables code to cause libpng to choose a filter from a set passed to
# png_set_filter. Without this code libpng just chooses the first filter in
# the list if multiple are given.
# SELECT_FILTER
# Enables code to select between multiple filters on write. Without this
# the 'first' (lowest numbered) filter will be selected an this typically
# works out as PNG_FILTER_VALUE_NONE.
#
# SELECT_FILTER_METHODICALLY
# Enables code to try all the filters in the list passed to png_set_filter
# and choose the one which results in the least number of compressed bytes
# added by the current row.
# COMPRESSION_BUFFER_MAX
# WARNING: take care if you set this. This is the maximum amount of input
# data that the implementation of deflate can consume before it outputs a
# Huffman table for that data. I.e. before it commits to an encoding of the
# data it has read. This is used solely to implement a limit on the amount
# of image data buffering that occurs inside libpng before filter selection
# is done. Normally the limit is never reached because of the next setting,
# but this is a compile time limit and it is intended to prevent a potential
# DNS service as a result of an application setting the libpng equivalent of
# volume level 11 (read the wikipedia article on "Up to eleven").
#
# NOTE: the image of a black cat in a coal mine obviously requires this
# limit, but some more valid images can get very close; well over 8MByte.
#
# COMPRESSION_BUFFER_LIMIT
# This is the (overrideable) default for the amount of memory libpng will
# buffer before selecting a filter for a row. It is limited itself to
# COMPRESSION_BUFFER_MAX as values above that level make no change (see the
# previous paragraph.)
#
# See png.h for more description of these options.
option WRITE_FILTER requires WRITE
option SELECT_FILTER_HEURISTICALLY requires WRITE_FILTER enables SET_OPTION
option SELECT_FILTER_METHODICALLY requires WRITE_FILTER enables SET_OPTION
option SELECT_FILTER requires WRITE_FILTER disabled
setting COMPRESSION_BUFFER_MAX default 8453377
setting COMPRESSION_BUFFER_LIMIT default 8453377
# added at libpng-1.5.4

View File

@ -1,8 +1,7 @@
/* libpng 1.7.0beta80 STANDARD API DEFINITION */
/* pnglibconf.h - library build configuration */
/* Libpng version 1.7.0beta80, March 9, 2016 */
/* libpng version 1.7.0beta80, March 9, 2016 */
/* Copyright (c) 1998-2016 Glenn Randers-Pehrson */
@ -100,8 +99,7 @@
#define PNG_READ_tRNS_SUPPORTED
#define PNG_READ_zTXt_SUPPORTED
#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_SELECT_FILTER_HEURISTICALLY_SUPPORTED
#define PNG_SELECT_FILTER_METHODICALLY_SUPPORTED
/*#undef PNG_SELECT_FILTER_SUPPORTED*/
#define PNG_SEQUENTIAL_READ_SUPPORTED
#define PNG_SETJMP_SUPPORTED
#define PNG_SETTING_SUPPORTED
@ -193,6 +191,8 @@
/* settings */
#define PNG_ABORT { (abort()); }
#define PNG_API_RULE 0
#define PNG_COMPRESSION_BUFFER_LIMIT 8453377
#define PNG_COMPRESSION_BUFFER_MAX 8453377
#define PNG_DEFAULT_GAMMA_ACCURACY 665
#define PNG_DEFAULT_READ_MACROS 1
#define PNG_GAMMA_THRESHOLD_FIXED 153