Checkpoint for 'methodical' filter selection

This checkpoint is to allow the massive merge with the recent mainline libpng17
changes.

Signed-off-by: John Bowler <jbowler@acm.org>
This commit is contained in:
John Bowler 2015-12-03 13:25:11 -08:00
parent 9fce04fcd6
commit 86499967c3
4 changed files with 181 additions and 143 deletions

View File

@ -959,12 +959,6 @@ PNG_INTERNAL_FUNCTION(void, png_zstream_error,(z_stream *zstream, int ret),
* set before they return. * set before they return.
*/ */
#ifdef PNG_WRITE_SUPPORTED
PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr,
png_compression_bufferp *list),PNG_EMPTY);
/* Free the buffer list used by the compressed write code. */
#endif
#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ #if defined(PNG_FLOATING_POINT_SUPPORTED) && \
!defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \
(defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \ (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \
@ -1206,14 +1200,30 @@ PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr,
* *
* This may be called multiple times per row, but calls must be in 'x' order; * This may be called multiple times per row, but calls must be in 'x' order;
* first a call with x 0 to mark the start of the row and, at the end, one with * first a call with x 0 to mark the start of the row and, at the end, one with
* 'end_of_row' set (this can be done in the same function call if the whole row * PNG_ROW_END set (this can be done in the same function call if the whole row
* is passed.) * is passed.) The following flags are used internally to control pass
* filtering and deflate:
*/ */
PNG_INTERNAL_FUNCTION(unsigned int, png_write_filter_row, enum
(png_structrp png_ptr, png_bytep prev_pixels, png_const_bytep unfiltered_row, {
png_uint_32 x, unsigned int width/*pixels*/, int first_row_in_pass, png_row_end =0x1U, /* This is the last block in the row */
int last_pass_row, unsigned int filters_to_try/*from previous call*/, png_pass_first_row =0x2U, /* This is the first row in a pass */
int end_of_image), png_pass_last_row =0x4U, /* This is the last row in a pass */
png_pass_last =0x8U /* This is the last pass in the image */
/* A useful macro; return true if this is the last block of the last row in
* the image.
*/
# 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_bytep prev_pixels, png_const_bytep unfiltered_row, png_uint_32 x,
unsigned int width/*pixels*/, unsigned int row_info_flags),
PNG_EMPTY);
/* Release memory used by the deflate mechanism */
PNG_INTERNAL_FUNCTION(void, png_deflate_destroy, (png_structrp png_ptr),
PNG_EMPTY); PNG_EMPTY);
#ifdef PNG_TRANSFORM_MECH_SUPPORTED #ifdef PNG_TRANSFORM_MECH_SUPPORTED

View File

@ -67,6 +67,11 @@
typedef struct png_compression_buffer *png_compression_bufferp; typedef struct png_compression_buffer *png_compression_bufferp;
#endif #endif
#ifdef PNG_SELECT_FILTER_METHODICALLY_SUPPORTED
/* Type of the data cache used when selecting filters methodicially */
typedef struct png_filter_select *png_filter_selectp;
#endif
/* Colorspace support; structures used in png_struct, png_info and in internal /* Colorspace support; structures used in png_struct, png_info and in internal
* functions to hold and communicate information about the color space. * functions to hold and communicate information about the color space.
* *
@ -751,8 +756,9 @@ struct png_struct_def
png_compression_bufferp *zbuffer_end; /* 'next' field of current buffer */ png_compression_bufferp *zbuffer_end; /* 'next' field of current buffer */
png_uint_32 zbuffer_len; /* Length of data in list */ png_uint_32 zbuffer_len; /* Length of data in list */
unsigned int zbuffer_start; /* Bytes written from start */ unsigned int zbuffer_start; /* Bytes written from start */
unsigned int zbuffer_filters;/*Filters for this row */
# ifdef PNG_SELECT_FILTER_METHODICALLY_SUPPORTED # ifdef PNG_SELECT_FILTER_METHODICALLY_SUPPORTED
png_voidp zbuffer_select; png_filter_selectp zbuffer_select;
# endif /* SELECT_FILTER_METHODICALLY */ # endif /* SELECT_FILTER_METHODICALLY */
# endif /* WRITE */ # endif /* WRITE */
# ifdef PNG_READ_SUPPORTED # ifdef PNG_READ_SUPPORTED

View File

@ -629,8 +629,8 @@ png_write_image(png_structrp png_ptr, png_bytepp image)
#if defined(PNG_WRITE_INTERLACING_SUPPORTED) ||\ #if defined(PNG_WRITE_INTERLACING_SUPPORTED) ||\
defined(PNG_WRITE_TRANSFORMS_SUPPORTED) defined(PNG_WRITE_TRANSFORMS_SUPPORTED)
static void static void
write_row_buffered(png_structrp png_ptr, png_const_bytep row, write_row_buffered(png_structrp png_ptr,
int first_row_in_pass, int last_pass_row, int end_of_image, png_const_bytep row, unsigned int row_info_flags,
void (*copy_fn)(png_const_structrp png_ptr, png_bytep row_buffer, void (*copy_fn)(png_const_structrp png_ptr, png_bytep row_buffer,
png_const_bytep row, png_uint_32 x, unsigned int count, unsigned int p), png_const_bytep row, png_uint_32 x, unsigned int count, unsigned int p),
unsigned int copy_parameter) unsigned int copy_parameter)
@ -640,15 +640,12 @@ write_row_buffered(png_structrp png_ptr, png_const_bytep row,
const png_uint_32 width = png_ptr->interlaced == PNG_INTERLACE_NONE ? const png_uint_32 width = png_ptr->interlaced == PNG_INTERLACE_NONE ?
png_ptr->width : PNG_PASS_COLS(png_ptr->width, pass); png_ptr->width : PNG_PASS_COLS(png_ptr->width, pass);
png_uint_32 x; png_uint_32 x;
unsigned int filters;
png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4 2-byte channels each */ png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4 2-byte channels each */
memset(prev_pixels, 0U, sizeof prev_pixels); memset(prev_pixels, 0U, sizeof prev_pixels);
for (x = 0U, filters = 0U; x < width; x += max_pixels) for (x = 0U; x < width; x += max_pixels)
{ {
int finish = 0;
union union
{ {
PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment; PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment;
@ -658,8 +655,9 @@ write_row_buffered(png_structrp png_ptr, png_const_bytep row,
if (max_pixels > width - x) if (max_pixels > width - x)
max_pixels = (unsigned int)/*SAFE*/(width - x); max_pixels = (unsigned int)/*SAFE*/(width - x);
if (end_of_image && x + max_pixels >= width) debug((row_info_flags & png_row_end) == 0U); /* must be set here at end */
finish = 1; if (x + max_pixels >= width)
row_info_flags |= png_row_end;
/* Copy a block of input pixels into the buffer, effecting the interlace /* Copy a block of input pixels into the buffer, effecting the interlace
* on the way if required. The argument is the number of pixels in the * on the way if required. The argument is the number of pixels in the
@ -707,11 +705,10 @@ write_row_buffered(png_structrp png_ptr, png_const_bytep row,
# endif /* WRITE_TRANSFORMS */ # endif /* WRITE_TRANSFORMS */
/* Call png_write_filter_row to write this block of data, the test on /* Call png_write_filter_row to write this block of data, the test on
* maxpixels says if this is the final block in the row, 'filters' is * maxpixels says if this is the final block in the row.
* initialized when 0 is 0 then preserved here for later blocks:
*/ */
filters = png_write_filter_row(png_ptr, prev_pixels, pixel_buffer.buffer, png_write_filter_row(png_ptr, prev_pixels, pixel_buffer.buffer, x,
x, max_pixels, first_row_in_pass, last_pass_row, filters, finish); max_pixels, row_info_flags);
} }
} }
#endif /* WRITE { INTERLACING || TRANSFORMS } */ #endif /* WRITE { INTERLACING || TRANSFORMS } */
@ -825,7 +822,7 @@ interlace_row_byte(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp,
static void static void
write_row_unbuffered(png_structrp png_ptr, png_const_bytep row, write_row_unbuffered(png_structrp png_ptr, png_const_bytep row,
int first_row_in_pass, int last_pass_row, int end_of_image) unsigned int row_info_flags)
{ {
/* Split the row into blocks of the appropriate size: */ /* Split the row into blocks of the appropriate size: */
const unsigned int input_depth = png_ptr->row_input_pixel_depth; const unsigned int input_depth = png_ptr->row_input_pixel_depth;
@ -835,7 +832,6 @@ write_row_unbuffered(png_structrp png_ptr, png_const_bytep row,
const png_uint_32 width = png_ptr->interlaced == PNG_INTERLACE_NONE ? const png_uint_32 width = png_ptr->interlaced == PNG_INTERLACE_NONE ?
png_ptr->width : PNG_PASS_COLS(png_ptr->width, pass); png_ptr->width : PNG_PASS_COLS(png_ptr->width, pass);
png_uint_32 x; png_uint_32 x;
unsigned int filters;
png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4 2-byte channels each */ 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 /* max_pixels is at most 16 bits, input_depth is at most 64, so the product
@ -847,10 +843,8 @@ write_row_unbuffered(png_structrp png_ptr, png_const_bytep row,
memset(prev_pixels, 0U, sizeof prev_pixels); memset(prev_pixels, 0U, sizeof prev_pixels);
for (x = 0U, filters = 0U; x < width; x += max_pixels, row += max_bytes) for (x = 0U; x < width; x += max_pixels, row += max_bytes)
{ {
int finish = 0;
if (max_pixels > width - x) if (max_pixels > width - x)
{ {
max_bytes = width - x; max_bytes = width - x;
@ -858,22 +852,23 @@ write_row_unbuffered(png_structrp png_ptr, png_const_bytep row,
max_bytes = (max_bytes * input_depth + 7U) >> 3; max_bytes = (max_bytes * input_depth + 7U) >> 3;
} }
if (end_of_image && x + max_pixels >= width) debug((row_info_flags & png_row_end) == 0U); /* must be set here at end */
finish = 1; if (x + max_pixels >= width)
row_info_flags |= png_row_end;
filters = png_write_filter_row(png_ptr, prev_pixels, row, x, max_pixels, png_write_filter_row(png_ptr, prev_pixels, row, x, max_pixels,
first_row_in_pass, last_pass_row, filters, finish); row_info_flags);
} }
} }
static void static void
write_row_core(png_structrp png_ptr, png_const_bytep row, write_row_core(png_structrp png_ptr, png_const_bytep row,
int first_row_in_pass, int last_pass_row, int end_of_image) unsigned int row_info_flags)
{ {
# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED # ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
if (png_ptr->transform_list != NULL) if (png_ptr->transform_list != NULL)
write_row_buffered(png_ptr, row, first_row_in_pass, last_pass_row, write_row_buffered(png_ptr, row, row_info_flags,
end_of_image, copy_row, png_ptr->row_input_pixel_depth); copy_row, png_ptr->row_input_pixel_depth);
else else
# endif /* WRITE_TRANSFORMS */ # endif /* WRITE_TRANSFORMS */
@ -881,8 +876,7 @@ write_row_core(png_structrp png_ptr, png_const_bytep row,
/* If control reaches this point the intermediate buffer is not required and /* If control reaches this point the intermediate buffer is not required and
* the input data can be used unmodified. * the input data can be used unmodified.
*/ */
write_row_unbuffered(png_ptr, row, first_row_in_pass, last_pass_row, write_row_unbuffered(png_ptr, row, row_info_flags);
end_of_image);
} }
/* Write a single non-interlaced row. */ /* Write a single non-interlaced row. */
@ -892,8 +886,11 @@ write_row_non_interlaced(png_structrp png_ptr, png_const_bytep row)
const png_uint_32 row_number = png_ptr->row_number+1U; const png_uint_32 row_number = png_ptr->row_number+1U;
const int last_pass_row = row_number == png_ptr->height; const int last_pass_row = row_number == png_ptr->height;
write_row_core(png_ptr, row, row_number == 1U, last_pass_row, /* There is only one pass, so this is the last pass: */
last_pass_row); write_row_core(png_ptr, row,
(row_number == 1U ? png_pass_first_row : 0U) |
(last_pass_row ? png_pass_last_row : 0U) |
png_pass_last);
if (!last_pass_row) if (!last_pass_row)
png_ptr->row_number = row_number; png_ptr->row_number = row_number;
@ -910,14 +907,17 @@ static void
write_row_interlaced(png_structrp png_ptr, png_const_bytep row) 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 /* 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 at the end. * the correct number of times. 'pass' is set to 7U after the end.
*/ */
const png_uint_32 row_number = png_ptr->row_number+1U; const png_uint_32 row_number = png_ptr->row_number+1U;
unsigned int pass = png_ptr->pass; unsigned int pass = png_ptr->pass;
const int last_pass_row = row_number == PNG_PASS_ROWS(png_ptr->height, pass); const int last_pass_row = row_number == PNG_PASS_ROWS(png_ptr->height, pass);
write_row_core(png_ptr, row, row_number == 1U, last_pass_row, write_row_core(png_ptr, row,
last_pass_row && pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height)); (row_number == 1U ? png_pass_first_row : 0U) |
(last_pass_row ? png_pass_last_row : 0U) |
(pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height) ?
png_pass_last : 0U));
if (!last_pass_row) if (!last_pass_row)
png_ptr->row_number = row_number; png_ptr->row_number = row_number;
@ -950,8 +950,13 @@ interlace_row(png_structrp png_ptr, png_const_bytep row)
if (write_row) if (write_row)
{ {
const int last_pass_row = const unsigned int row_info_flags =
PNG_LAST_PASS_ROW(row_number, pass, png_ptr->height); (row_number == PNG_PASS_START_ROW(pass) ?
png_pass_first_row : 0U) |
(PNG_LAST_PASS_ROW(row_number, pass, png_ptr->height) ?
png_pass_last_row : 0U) |
(pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height) ?
png_pass_last : 0U);
if (pass < 6) if (pass < 6)
{ {
@ -963,8 +968,6 @@ interlace_row(png_structrp png_ptr, png_const_bytep row)
*/ */
const unsigned int input_depth = png_ptr->row_input_pixel_depth; const unsigned int input_depth = png_ptr->row_input_pixel_depth;
unsigned int B = 0; /* log2(input_depth) */ unsigned int B = 0; /* log2(input_depth) */
const int end_of_image = last_pass_row &&
pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height);
switch (input_depth) switch (input_depth)
{ {
@ -975,22 +978,19 @@ interlace_row(png_structrp png_ptr, png_const_bytep row)
++B; ++B;
/*FALL THROUGH*/ /*FALL THROUGH*/
case 1U: /* B will be 0 */ case 1U: /* B will be 0 */
write_row_buffered(png_ptr, row, write_row_buffered(png_ptr, row, row_info_flags,
row_number == PNG_PASS_START_ROW(pass), last_pass_row, interlace_row_lbd, B);
end_of_image, interlace_row_lbd, B);
break; break;
default: /* Parameter is the pixel size in bytes */ default: /* Parameter is the pixel size in bytes */
write_row_buffered(png_ptr, row, write_row_buffered(png_ptr, row, row_info_flags,
row_number == PNG_PASS_START_ROW(pass), last_pass_row, interlace_row_byte, input_depth >> 3);
end_of_image, interlace_row_byte, input_depth >> 3);
break; break;
} }
} }
else /* pass 6; no interlacing required */ else /* pass 6; no interlacing required */
write_row_core(png_ptr, row, row_number == 1U, last_pass_row, write_row_core(png_ptr, row, row_info_flags);
last_pass_row);
} }
if (row_number+1U < png_ptr->height) if (row_number+1U < png_ptr->height)
@ -1099,20 +1099,8 @@ png_write_destroy(png_structrp png_ptr)
{ {
png_debug(1, "in png_write_destroy"); png_debug(1, "in png_write_destroy");
/* Free any memory zlib uses */ png_deflate_destroy(png_ptr);
if (png_ptr->zstream.state != NULL)
{
int ret = deflateEnd(&png_ptr->zstream);
if (ret != Z_OK)
{
png_zstream_error(&png_ptr->zstream, ret);
png_warning(png_ptr, png_ptr->zstream.msg);
}
}
/* Free our memory. png_free checks NULL for us. */
png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
#ifdef PNG_WRITE_FILTER_SUPPORTED #ifdef PNG_WRITE_FILTER_SUPPORTED
png_free(png_ptr, png_ptr->row_buffer); png_free(png_ptr, png_ptr->row_buffer);
png_ptr->row_buffer = NULL; png_ptr->row_buffer = NULL;

View File

@ -458,7 +458,7 @@ png_deflate_claim(png_structrp png_ptr, png_uint_32 owner,
} }
/* Clean up (or trim) a linked list of compression buffers. */ /* Clean up (or trim) a linked list of compression buffers. */
void /* PRIVATE */ static void
png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp) png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp)
{ {
png_compression_bufferp list = *listp; png_compression_bufferp list = *listp;
@ -478,6 +478,26 @@ png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp)
} }
} }
/* Release memory used by the deflate mechanism */
void /* PRIVATE */
png_deflate_destroy(png_structrp png_ptr)
{
/* Free any memory zlib uses */
if (png_ptr->zstream.state != NULL)
{
int ret = deflateEnd(&png_ptr->zstream);
if (ret != Z_OK)
{
png_zstream_error(&png_ptr->zstream, ret);
png_warning(png_ptr, png_ptr->zstream.msg);
}
}
/* Free our memory. png_free checks NULL for us. */
png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
}
/* Compress the given data given a compression buffer list. The passed in /* Compress the given data given a compression buffer list. The passed in
* z_stream must have already been claimed (if required) and the compression * z_stream must have already been claimed (if required) and the compression
* buffer list pointer initialized to NULL or an existing list. * buffer list pointer initialized to NULL or an existing list.
@ -2140,7 +2160,7 @@ png_write_flush(png_structrp png_ptr)
static void static void
write_filtered_row(png_structrp png_ptr, png_const_voidp filtered_row, write_filtered_row(png_structrp png_ptr, png_const_voidp filtered_row,
unsigned int row_bytes, png_byte filter /*if at start of row*/, unsigned int row_bytes, unsigned int filter /*if at start of row*/,
int end_of_image) int end_of_image)
{ {
/* This handles writing a row that has been filtered, or did not need to be /* This handles writing a row that has been filtered, or did not need to be
@ -2155,7 +2175,7 @@ write_filtered_row(png_structrp png_ptr, png_const_voidp filtered_row,
{ {
png_byte buffer[1]; png_byte buffer[1];
buffer[0] = filter; buffer[0] = PNG_BYTE(filter);
png_compress_IDAT(png_ptr, buffer, 1U/*len*/, Z_NO_FLUSH); png_compress_IDAT(png_ptr, buffer, 1U/*len*/, Z_NO_FLUSH);
} }
@ -2341,35 +2361,25 @@ filter_block(png_const_bytep prev_row, png_bytep prev_pixels,
static void static void
filter_row(png_structrp png_ptr, png_const_bytep prev_row, filter_row(png_structrp png_ptr, png_const_bytep prev_row,
png_bytep prev_pixels, png_const_bytep unfiltered_row, png_bytep prev_pixels, png_const_bytep unfiltered_row,
unsigned int row_bits, unsigned int bpp, unsigned int filters_to_try, unsigned int row_bits, unsigned int bpp, unsigned int filter,
int start_of_row, int end_of_image) int start_of_row, int end_of_image)
{ {
/* filters_to_try identifies a single filter and it is not PNG_FILTER_NONE. /* filters_to_try identifies a single filter and it is not PNG_FILTER_NONE.
*/ */
png_byte filter = PNG_FILTER_VALUE_LAST /* not at start */;
png_byte filtered_row[PNG_ROW_BUFFER_SIZE]; png_byte filtered_row[PNG_ROW_BUFFER_SIZE];
affirm((row_bits+7U) >> 3 <= PNG_ROW_BUFFER_SIZE); affirm((row_bits+7U) >> 3 <= PNG_ROW_BUFFER_SIZE &&
filter >= PNG_FILTER_VALUE_SUB && filter <= PNG_FILTER_VALUE_PAETH);
debug((row_bits % bpp) == 0U); debug((row_bits % bpp) == 0U);
if (start_of_row) switch (filters_to_try)
{
case PNG_FILTER_SUB: filter = PNG_FILTER_VALUE_SUB; break;
case PNG_FILTER_UP: filter = PNG_FILTER_VALUE_UP; break;
case PNG_FILTER_AVG: filter = PNG_FILTER_VALUE_AVG; break;
case PNG_FILTER_PAETH: filter = PNG_FILTER_VALUE_PAETH; break;
default:
impossible("filter list");
}
filter_block(prev_row, prev_pixels, unfiltered_row, row_bits, bpp, filter_block(prev_row, prev_pixels, unfiltered_row, row_bits, bpp,
filters_to_try & PNG_FILTER_SUB ? filtered_row : NULL, filter == PNG_FILTER_VALUE_SUB ? filtered_row : NULL,
filters_to_try & PNG_FILTER_UP ? filtered_row : NULL, filter == PNG_FILTER_VALUE_UP ? filtered_row : NULL,
filters_to_try & PNG_FILTER_AVG ? filtered_row : NULL, filter == PNG_FILTER_VALUE_AVG ? filtered_row : NULL,
filters_to_try & PNG_FILTER_PAETH ? filtered_row : NULL); filter == PNG_FILTER_VALUE_PAETH ? filtered_row : NULL);
write_filtered_row(png_ptr, filtered_row, (row_bits+7U)>>3, filter, write_filtered_row(png_ptr, filtered_row, (row_bits+7U)>>3,
end_of_image); start_of_row ? filter : PNG_FILTER_VALUE_LAST, end_of_image);
} }
/* These two #defines simplify writing code that depends on one or the other of /* These two #defines simplify writing code that depends on one or the other of
@ -2386,11 +2396,10 @@ filter_row(png_structrp png_ptr, png_const_bytep prev_row,
# define heuristic_option\ # define heuristic_option\
((png_ptr->options >> PNG_SELECT_FILTER_HEURISTICALLY) & 3U) ((png_ptr->options >> PNG_SELECT_FILTER_HEURISTICALLY) & 3U)
static unsigned int static void
select_filter_heuristically(png_structrp png_ptr, png_const_bytep prev_row, select_filter_heuristically(png_structrp png_ptr, png_const_bytep prev_row,
png_bytep prev_pixels, png_const_bytep unfiltered_row, png_bytep prev_pixels, png_const_bytep unfiltered_row,
unsigned int row_bits, unsigned int bpp, unsigned int filters_to_try, unsigned int row_bits, unsigned int bpp, int end_of_image)
int end_of_image)
{ {
const unsigned int row_bytes = (row_bits+7U) >> 3; const unsigned int row_bytes = (row_bits+7U) >> 3;
png_byte test_buffers[4][PNG_ROW_BUFFER_SIZE]; /* for each filter */ png_byte test_buffers[4][PNG_ROW_BUFFER_SIZE]; /* for each filter */
@ -2411,6 +2420,7 @@ select_filter_heuristically(png_structrp png_ptr, png_const_bytep prev_row,
* generated or we expect a count of average 8 per code. * generated or we expect a count of average 8 per code.
*/ */
{ {
unsigned int filters_to_try = png_ptr->zbuffer_filters;
unsigned int filter_max = 257U; unsigned int filter_max = 257U;
png_byte best_filter, test_filter; png_byte best_filter, test_filter;
png_const_bytep best_row, test_row; png_const_bytep best_row, test_row;
@ -2437,6 +2447,9 @@ select_filter_heuristically(png_structrp png_ptr, png_const_bytep prev_row,
filter_max = count, best_filter = test_filter, best_row = test_row; filter_max = count, best_filter = test_filter, best_row = test_row;
} }
/* Store the best filter found: */
png_ptr->zbuffer_filters = best_filter;
/* Calling write_unfiltered_rowbits is necessary here to deal with the /* Calling write_unfiltered_rowbits is necessary here to deal with the
* clearly of a partial byte at the end. * clearly of a partial byte at the end.
*/ */
@ -2447,8 +2460,6 @@ select_filter_heuristically(png_structrp png_ptr, png_const_bytep prev_row,
else else
write_filtered_row(png_ptr, best_row, row_bytes, best_filter, write_filtered_row(png_ptr, best_row, row_bytes, best_filter,
end_of_image); end_of_image);
return PNG_FILTER_MASK(best_filter);
} }
} }
#else /* !SELECT_FILTER_HEURISTICALLY */ #else /* !SELECT_FILTER_HEURISTICALLY */
@ -2462,11 +2473,10 @@ select_filters_methodically_init(png_structrp png_ptr)
affirm(png_ptr->zbuffer_select == NULL); affirm(png_ptr->zbuffer_select == NULL);
} }
static int static void
select_filter_methodically(png_structrp png_ptr, png_const_bytep prev_row, select_filter_methodically(png_structrp png_ptr, png_const_bytep prev_row,
png_bytep prev_pixels, png_const_bytep unfiltered_row, png_bytep prev_pixels, png_const_bytep unfiltered_row,
unsigned int row_bits, unsigned int bpp, unsigned int filters_to_try, unsigned int row_bits, unsigned int bpp, int end_of_row, int end_of_image)
int end_of_image)
{ {
const unsigned int row_bytes = (row_bits+7U) >> 3; const unsigned int row_bytes = (row_bits+7U) >> 3;
png_byte test_buffers[4][PNG_ROW_BUFFER_SIZE]; /* for each filter */ png_byte test_buffers[4][PNG_ROW_BUFFER_SIZE]; /* for each filter */
@ -2482,8 +2492,7 @@ select_filter_methodically(png_structrp png_ptr, png_const_bytep prev_row,
write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits, write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits,
PNG_FILTER_VALUE_LAST, end_of_image); PNG_FILTER_VALUE_LAST, end_of_image);
PNG_UNUSED(end_of_row)
return filters_to_try;
} }
#endif /* SELECT_FILTER_METHODICALLY */ #endif /* SELECT_FILTER_METHODICALLY */
@ -2491,15 +2500,15 @@ select_filter_methodically(png_structrp png_ptr, png_const_bytep prev_row,
* been specified by the application, and then writes the row out with the * been specified by the application, and then writes the row out with the
* chosen filter. * chosen filter.
*/ */
unsigned int /* PRIVATE */ void /* PRIVATE */
png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels, png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels,
png_const_bytep unfiltered_row, png_uint_32 x, png_const_bytep unfiltered_row, png_uint_32 x,
unsigned int width/*pixels*/, int first_row_in_pass, int last_pass_row, unsigned int width/*pixels*/, unsigned int row_info_flags)
unsigned int filters_to_try, int end_of_image)
{ {
png_bytep prev_row = png_ptr->row_buffer; png_bytep prev_row = png_ptr->row_buffer;
const unsigned int bpp = png_ptr->row_output_pixel_depth; const unsigned int bpp = png_ptr->row_output_pixel_depth;
const unsigned int row_bits = width * bpp; const unsigned int row_bits = width * bpp;
unsigned int filters_to_try;
/* These invariants are expected from the caller: */ /* These invariants are expected from the caller: */
affirm(width < 65536U && bpp <= 64U && width < 65536U/bpp && affirm(width < 65536U && bpp <= 64U && width < 65536U/bpp &&
@ -2519,7 +2528,7 @@ png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels,
PNG_ALL_FILTERS : PNG_NO_FILTERS); PNG_ALL_FILTERS : PNG_NO_FILTERS);
/* Now work out the filters to try for this row: */ /* Now work out the filters to try for this row: */
filters_to_try = png_ptr->filter_mask; /* else caller must preserve */ filters_to_try = png_ptr->filter_mask;
/* If this has a previous row filter in the set to try ensure the row /* If this has a previous row filter in the set to try ensure the row
* buffer exists and ensure it is empty when first allocated and at * buffer exists and ensure it is empty when first allocated and at
@ -2541,7 +2550,7 @@ png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels,
* app warning and disable the filters that would have required * app warning and disable the filters that would have required
* the data. * the data.
*/ */
if (!first_row_in_pass) if (!(row_info_flags & png_pass_first_row))
{ {
png_app_warning(png_ptr, "Previous row filters ignored"); png_app_warning(png_ptr, "Previous row filters ignored");
/* And always turn off the filters, to prevent using /* And always turn off the filters, to prevent using
@ -2556,7 +2565,7 @@ png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels,
} }
} }
if (first_row_in_pass) if ((row_info_flags & png_pass_first_row) != 0U)
{ {
/* On the first row UP and NONE are the same, PAETH and SUB are the /* 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 * same, so if both members of a pair occur together eliminate the one
@ -2569,7 +2578,7 @@ png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels,
filters_to_try &= PNG_BIC_MASK(PNG_FILTER_UP); filters_to_try &= PNG_BIC_MASK(PNG_FILTER_UP);
if (match(PNG_FILTER_SUB+PNG_FILTER_PAETH)) if (match(PNG_FILTER_SUB+PNG_FILTER_PAETH))
filters_to_try &= PNG_BIC_MASK(PNG_FILTER_UP); filters_to_try &= PNG_BIC_MASK(PNG_FILTER_PAETH);
# undef match # undef match
} }
@ -2588,59 +2597,84 @@ png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels,
if (heuristic_option == PNG_OPTION_OFF) /* don't use heuristics */ if (heuristic_option == PNG_OPTION_OFF) /* don't use heuristics */
# endif /* SELECT_FILTER_HEURISTICALLY */ # endif /* SELECT_FILTER_HEURISTICALLY */
filters_to_try &= -filters_to_try; filters_to_try &= -filters_to_try;
/* If there is just one bit set in filters_to_try convert it to the filter
* value and store that.
*/
if ((filters_to_try & (filters_to_try-1U)) == 0U) switch (filters_to_try)
{
case PNG_FILTER_NONE: filters_to_try = PNG_FILTER_VALUE_NONE; break;
case PNG_FILTER_SUB: filters_to_try = PNG_FILTER_VALUE_SUB; break;
case PNG_FILTER_UP: filters_to_try = PNG_FILTER_VALUE_UP; break;
case PNG_FILTER_AVG: filters_to_try = PNG_FILTER_VALUE_AVG; break;
case PNG_FILTER_PAETH: filters_to_try = PNG_FILTER_VALUE_PAETH; break;
default:
impossible("bad filter mask");
}
png_ptr->zbuffer_filters = filters_to_try;
} /* start of row */ } /* start of row */
else if (prev_row != NULL) else
{ {
/* Advance prev_row to the corresponding pixel above row[x], must use if (prev_row != NULL)
* png_calc_rowbytes here otherwise the calculation using x might {
* overflow. /* Advance prev_row to the corresponding pixel above row[x], must use
*/ * png_calc_rowbytes here otherwise the calculation using x might
debug(((x * bpp) & 7U) == 0U); * overflow.
prev_row += png_calc_rowbytes(png_ptr, bpp, x); */
debug(((x * bpp) & 7U) == 0U);
prev_row += png_calc_rowbytes(png_ptr, bpp, x);
}
filters_to_try = png_ptr->zbuffer_filters;
} }
/* Now choose the correct filter implementation according to the number of /* Now choose the correct filter implementation according to the number of
* filters in the filters_to_try list. The prev_row parameter is made NULL * filters in the filters_to_try list. The prev_row parameter is made NULL
* on the first row because it is uninitialized at that point. * on the first row because it is uninitialized at that point.
*/ */
if (filters_to_try == PNG_FILTER_NONE) if (filters_to_try == PNG_FILTER_VALUE_NONE)
write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits, write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits,
x == 0 ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST, x == 0 ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST,
end_of_image); PNG_IDAT_END(row_info_flags));
else if ((filters_to_try & -filters_to_try) == filters_to_try) /* 1 filter */ else
filter_row(png_ptr, first_row_in_pass ? NULL : prev_row, {
prev_pixels, unfiltered_row, row_bits, bpp, filters_to_try, x == 0, png_const_bytep prev =
end_of_image); (row_info_flags & png_pass_first_row) ? NULL : prev_row;
# ifdef PNG_SELECT_FILTER_METHODICALLY_SUPPORTED /* Is just one bit set in 'filters_to_try'? */
else if (png_ptr->zbuffer_select != NULL) if (filters_to_try < PNG_FILTER_MASK(0))
filters_to_try = select_filter_methodically(png_ptr, filter_row(png_ptr, prev, prev_pixels, unfiltered_row, row_bits, bpp,
first_row_in_pass ? NULL : prev_row, prev_pixels, unfiltered_row, filters_to_try, x == 0, PNG_IDAT_END(row_info_flags));
row_bits, bpp, filters_to_try, end_of_image);
# endif /* SELECT_FILTER_METHODICALLY */ # ifdef PNG_SELECT_FILTER_METHODICALLY_SUPPORTED
# ifdef PNG_SELECT_FILTER_HEURISTICALLY_SUPPORTED else if (png_ptr->zbuffer_select != NULL)
/* The heuristic must select a single filter based on the first block of select_filter_methodically(png_ptr, prev, prev_pixels,
* pixels: unfiltered_row, row_bits, bpp,
*/ (row_info_flags & png_row_end) != 0U,
else PNG_IDAT_END(row_info_flags));
filters_to_try = select_filter_heuristically(png_ptr, # endif /* SELECT_FILTER_METHODICALLY */
first_row_in_pass ? NULL : prev_row, prev_pixels, unfiltered_row, # ifdef PNG_SELECT_FILTER_HEURISTICALLY_SUPPORTED
row_bits, bpp, filters_to_try, end_of_image); /* The heuristic must select a single filter based on the first block
# else /* !SELECT_FILTER_HEURISTICALLY */ * of pixels; it updates zbuffer_filter to a single filter value.
else */
impossible("bad filter select logic"); else
# endif /* !SELECT_FILTER_HEURISTICALLY */ select_filter_heuristically(png_ptr, prev, prev_pixels,
unfiltered_row, row_bits, bpp, PNG_IDAT_END(row_info_flags));
# else /* !SELECT_FILTER_HEURISTICALLY */
else
impossible("bad filter select logic");
# endif /* !SELECT_FILTER_HEURISTICALLY */
}
/* Copy the current row into the previous row buffer, if available, unless /* Copy the current row into the previous row buffer, if available, unless
* this is the last row in the pass, when there is no point. Note that * this is the last row in the pass, when there is no point. Note that
* prev_row may have garbage in a partial byte at the end. * prev_row may have garbage in a partial byte at the end.
*/ */
if (prev_row != NULL && !last_pass_row) if (prev_row != NULL && !(row_info_flags & png_pass_last_row))
memcpy(prev_row, unfiltered_row, (row_bits + 7U) >> 3); memcpy(prev_row, unfiltered_row, (row_bits + 7U) >> 3);
return filters_to_try;
} }
/* Allow the application to select one or more row filters to use. */ /* Allow the application to select one or more row filters to use. */