[libpng16] Backported filter selection code from libpng-1.7.0beta51, to combine

sub_row, up_row, avg_row, and paeth_row into try_row and tst_row.
This commit is contained in:
Glenn Randers-Pehrson 2015-05-30 11:54:45 -05:00
parent 9fbdce8fb5
commit e6877671f1
5 changed files with 403 additions and 464 deletions

View File

@ -1,4 +1,4 @@
Libpng 1.6.18beta05 - May 29, 2015 Libpng 1.6.18beta05 - May 30, 2015
This is not intended to be a public release. It will be replaced This is not intended to be a public release. It will be replaced
within a few weeks by a public version or by another test version. within a few weeks by a public version or by another test version.
@ -51,9 +51,12 @@ Version 1.6.18beta04 [May 20, 2015]
type and can be defined on the command line, allowing testing in beta type and can be defined on the command line, allowing testing in beta
builds (John Bowler). builds (John Bowler).
Avoid Coverity issue 80858 (REVERSE NULL) in pngtest.c Avoid Coverity issue 80858 (REVERSE NULL) in pngtest.c
Avoid a harmless potential integer overflow in png_XYZ_from_xy(). Avoid a harmless potential integer overflow in png_XYZ_from_xy() (Bug
report from Christopher Ferris).
Version 1.6.18beta05 [May 29, 2015] Version 1.6.18beta05 [May 30, 2015]
Backport filter selection code from libpng-1.7.0beta51, to combine
sub_row, up_row, avg_row, and paeth_row into try_row and tst_row.
Send comments/corrections/commendations to png-mng-implement at lists.sf.net Send comments/corrections/commendations to png-mng-implement at lists.sf.net
(subscription required; visit (subscription required; visit

View File

@ -5239,7 +5239,9 @@ Version 1.6.18beta04 [May 20, 2015]
Avoid a harmless potential integer overflow in png_XYZ_from_xy() (Bug Avoid a harmless potential integer overflow in png_XYZ_from_xy() (Bug
report from Christopher Ferris). report from Christopher Ferris).
Version 1.6.18beta05 [May 29, 2015] Version 1.6.18beta05 [May 30, 2015]
Backport filter selection code from libpng-1.7.0beta51, to combine
sub_row, up_row, avg_row, and paeth_row into try_row and tst_row.
Send comments/corrections/commendations to png-mng-implement at lists.sf.net Send comments/corrections/commendations to png-mng-implement at lists.sf.net
(subscription required; visit (subscription required; visit

View File

@ -219,16 +219,18 @@ struct png_struct_def
png_uint_32 row_number; /* current row in interlace pass */ png_uint_32 row_number; /* current row in interlace pass */
png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */ png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */
png_bytep prev_row; /* buffer to save previous (unfiltered) row. png_bytep prev_row; /* buffer to save previous (unfiltered) row.
* This is a pointer into big_prev_row * While reading this is a pointer into
* big_prev_row; while writing it is separately
* allocated if needed.
*/ */
png_bytep row_buf; /* buffer to save current (unfiltered) row. png_bytep row_buf; /* buffer to save current (unfiltered) row.
* This is a pointer into big_row_buf * While reading, this is a pointer into
* big_row_buf; while writing it is separately
* allocated.
*/ */
#ifdef PNG_WRITE_SUPPORTED #ifdef PNG_WRITE_FILTER_SUPPORTED
png_bytep sub_row; /* buffer to save "sub" row when filtering */ png_bytep try_row; /* buffer to save trial row when filtering */
png_bytep up_row; /* buffer to save "up" row when filtering */ png_bytep tst_row; /* buffer to save best trial row when filtering */
png_bytep avg_row; /* buffer to save "avg" row when filtering */
png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */
#endif #endif
png_size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */ png_size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */

View File

@ -942,15 +942,11 @@ png_write_destroy(png_structrp png_ptr)
png_ptr->row_buf = NULL; png_ptr->row_buf = NULL;
#ifdef PNG_WRITE_FILTER_SUPPORTED #ifdef PNG_WRITE_FILTER_SUPPORTED
png_free(png_ptr, png_ptr->prev_row); png_free(png_ptr, png_ptr->prev_row);
png_free(png_ptr, png_ptr->sub_row); png_free(png_ptr, png_ptr->try_row);
png_free(png_ptr, png_ptr->up_row); png_free(png_ptr, png_ptr->tst_row);
png_free(png_ptr, png_ptr->avg_row);
png_free(png_ptr, png_ptr->paeth_row);
png_ptr->prev_row = NULL; png_ptr->prev_row = NULL;
png_ptr->sub_row = NULL; png_ptr->try_row = NULL;
png_ptr->up_row = NULL; png_ptr->tst_row = NULL;
png_ptr->avg_row = NULL;
png_ptr->paeth_row = NULL;
#endif #endif
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
@ -1057,76 +1053,68 @@ png_set_filter(png_structrp png_ptr, int method, int filters)
* wants to start and stop using particular filters during compression, * wants to start and stop using particular filters during compression,
* it should start out with all of the filters, and then remove them * it should start out with all of the filters, and then remove them
* or add them back after the start of compression. * or add them back after the start of compression.
*
* NOTE: this is a nasty constraint on the code, because it means that the
* prev_row buffer must be maintained even if there are currently no
* 'prev_row' requiring filters active.
*/ */
if (png_ptr->row_buf != NULL) if (png_ptr->row_buf != NULL)
{ {
#ifdef PNG_WRITE_FILTER_SUPPORTED int num_filters;
if ((png_ptr->do_filter & PNG_FILTER_SUB) != 0 && png_alloc_size_t buf_size;
png_ptr->sub_row == NULL)
/* Repeat the checks in png_write_start_row; 1 pixel high or wide
* images cannot benefit from certain filters. If this isn't done here
* the check below will fire on 1 pixel high images.
*/
if (png_ptr->height == 1)
filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
if (png_ptr->width == 1)
filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH);
if ((filters & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) != 0
&& png_ptr->prev_row == NULL)
{ {
png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, /* This is the error case, however it is benign - the previous row
(png_ptr->rowbytes + 1)); * is not available so the filter can't be used. Just warn here.
png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; */
png_app_warning(png_ptr,
"png_set_filter: UP/AVG/PAETH cannot be added after start");
filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
} }
if ((png_ptr->do_filter & PNG_FILTER_UP) != 0 && num_filters = 0;
png_ptr->up_row == NULL)
{
if (png_ptr->prev_row == NULL)
{
png_warning(png_ptr, "Can't add Up filter after starting");
png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
~PNG_FILTER_UP);
}
else if (filters & PNG_FILTER_SUB)
{ num_filters++;
png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
(png_ptr->rowbytes + 1));
png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
}
}
if ((png_ptr->do_filter & PNG_FILTER_AVG) != 0 && if (filters & PNG_FILTER_UP)
png_ptr->avg_row == NULL) num_filters++;
{
if (png_ptr->prev_row == NULL)
{
png_warning(png_ptr, "Can't add Average filter after starting");
png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
~PNG_FILTER_AVG);
}
else if (filters & PNG_FILTER_AVG)
{ num_filters++;
png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
(png_ptr->rowbytes + 1));
png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
}
}
if ((png_ptr->do_filter & PNG_FILTER_PAETH) != 0 && if (filters & PNG_FILTER_PAETH)
png_ptr->paeth_row == NULL) num_filters++;
{
if (png_ptr->prev_row == NULL)
{
png_warning(png_ptr, "Can't add Paeth filter after starting");
png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);
}
else /* Allocate needed row buffers if they have not already been
{ * allocated.
png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, */
(png_ptr->rowbytes + 1)); buf_size = PNG_ROWBYTES(png_ptr->usr_channels * png_ptr->usr_bit_depth,
png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; png_ptr->width) + 1;
}
}
if (png_ptr->do_filter == PNG_NO_FILTERS) if (png_ptr->try_row == NULL)
#endif /* WRITE_FILTER */ png_ptr->try_row = png_malloc(png_ptr, buf_size);
png_ptr->do_filter = PNG_FILTER_NONE;
if (num_filters > 1)
{
if (png_ptr->tst_row == NULL)
png_ptr->tst_row = png_malloc(png_ptr, buf_size);
} }
} }
png_ptr->do_filter = (png_byte)filters;
}
else else
png_error(png_ptr, "Unknown custom filter method"); png_error(png_ptr, "Unknown custom filter method");
} }

View File

@ -1961,6 +1961,10 @@ png_write_start_row(png_structrp png_ptr)
png_alloc_size_t buf_size; png_alloc_size_t buf_size;
int usr_pixel_depth; int usr_pixel_depth;
#ifdef PNG_WRITE_FILTER_SUPPORTED
int filters;
#endif
png_debug(1, "in png_write_start_row"); png_debug(1, "in png_write_start_row");
usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth; usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth;
@ -1971,50 +1975,53 @@ png_write_start_row(png_structrp png_ptr)
png_ptr->maximum_pixel_depth = (png_byte)usr_pixel_depth; png_ptr->maximum_pixel_depth = (png_byte)usr_pixel_depth;
/* Set up row buffer */ /* Set up row buffer */
png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, buf_size); png_ptr->row_buf = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size));
png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE;
#ifdef PNG_WRITE_FILTER_SUPPORTED #ifdef PNG_WRITE_FILTER_SUPPORTED
/* Set up filtering buffer, if using this filter */ filters = png_ptr->do_filter;
if (png_ptr->do_filter & PNG_FILTER_SUB)
{
png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, png_ptr->rowbytes + 1);
png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; if (png_ptr->height == 1)
filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
if (png_ptr->width == 1)
filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH);
if (filters == 0)
filters = PNG_FILTER_NONE;
png_ptr->do_filter = filters;
if (((filters & (PNG_FILTER_SUB | PNG_FILTER_UP | PNG_FILTER_AVG |
PNG_FILTER_PAETH)) != 0) && png_ptr->try_row == NULL)
{
int num_filters = 0;
png_ptr->try_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size));
if (filters & PNG_FILTER_SUB)
num_filters++;
if (filters & PNG_FILTER_UP)
num_filters++;
if (filters & PNG_FILTER_AVG)
num_filters++;
if (filters & PNG_FILTER_PAETH)
num_filters++;
if (num_filters > 1)
png_ptr->tst_row = png_voidcast(png_bytep, png_malloc(png_ptr,
buf_size));
} }
/* We only need to keep the previous row if we are using one of these. */ /* We only need to keep the previous row if we are using one of the following
if ((png_ptr->do_filter & * filters.
(PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) != 0) */
{ if ((filters & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) != 0)
/* Set up previous row buffer */ png_ptr->prev_row = png_calloc(png_ptr, buf_size);
png_ptr->prev_row = (png_bytep)png_calloc(png_ptr, buf_size);
if ((png_ptr->do_filter & PNG_FILTER_UP) != 0)
{
png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
png_ptr->rowbytes + 1);
png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
}
if ((png_ptr->do_filter & PNG_FILTER_AVG) != 0)
{
png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
png_ptr->rowbytes + 1);
png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
}
if ((png_ptr->do_filter & PNG_FILTER_PAETH) != 0)
{
png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
png_ptr->rowbytes + 1);
png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
}
}
#endif /* WRITE_FILTER */ #endif /* WRITE_FILTER */
#ifdef PNG_WRITE_INTERLACING_SUPPORTED #ifdef PNG_WRITE_INTERLACING_SUPPORTED
@ -2310,6 +2317,7 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass)
} }
#endif #endif
/* This filters the row, chooses which filter to use, if it has not already /* This filters the row, chooses which filter to use, if it has not already
* 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.
@ -2318,22 +2326,215 @@ static void /* PRIVATE */
png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
png_size_t row_bytes); png_size_t row_bytes);
#ifdef PNG_WRITE_FILTER_SUPPORTED
static png_size_t /* PRIVATE */
png_setup_sub_row(png_structrp png_ptr, const png_uint_32 bpp,
const png_size_t row_bytes, const png_size_t lmins)
{
png_bytep rp, dp, lp;
png_size_t i;
png_size_t sum = 0;
int v;
png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB;
for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1; i < bpp;
i++, rp++, dp++)
{
v = *dp = *rp;
sum += (v < 128) ? v : 256 - v;
}
for (lp = png_ptr->row_buf + 1; i < row_bytes;
i++, rp++, lp++, dp++)
{
v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
sum += (v < 128) ? v : 256 - v;
if (sum > lmins) /* We are already worse, don't continue. */
break;
}
return (sum);
}
static png_size_t /* PRIVATE */
png_setup_up_row(png_structrp png_ptr, const png_size_t row_bytes,
const png_size_t lmins)
{
png_bytep rp, dp, pp;
png_size_t i;
png_size_t sum = 0;
int v;
png_ptr->try_row[0] = PNG_FILTER_VALUE_UP;
for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
pp = png_ptr->prev_row + 1; i < row_bytes;
i++, rp++, pp++, dp++)
{
v = *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
sum += (v < 128) ? v : 256 - v;
if (sum > lmins) /* We are already worse, don't continue. */
break;
}
return (sum);
}
static png_size_t /* PRIVATE */
png_setup_avg_row(png_structrp png_ptr, const png_uint_32 bpp,
const png_size_t row_bytes, const png_size_t lmins)
{
png_bytep rp, dp, pp, lp;
png_uint_32 i;
png_size_t sum = 0;
int v;
png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG;
for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
pp = png_ptr->prev_row + 1; i < bpp; i++)
{
v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
sum += (v < 128) ? v : 256 - v;
}
for (lp = png_ptr->row_buf + 1; i < row_bytes; i++)
{
v = *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
& 0xff);
sum += (v < 128) ? v : 256 - v;
if (sum > lmins) /* We are already worse, don't continue. */
break;
}
return (sum);
}
static png_size_t /* PRIVATE */
png_setup_paeth_row(png_structrp png_ptr, const png_uint_32 bpp,
const png_size_t row_bytes, const png_size_t lmins)
{
png_bytep rp, dp, pp, cp, lp;
png_size_t i;
png_size_t sum = 0;
int v;
png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH;
for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
pp = png_ptr->prev_row + 1; i < bpp; i++)
{
v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
sum += (v < 128) ? v : 256 - v;
}
for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes;
i++)
{
int a, b, c, pa, pb, pc, p;
b = *pp++;
c = *cp++;
a = *lp++;
p = b - c;
pc = a - c;
#ifdef PNG_USE_ABS
pa = abs(p);
pb = abs(pc);
pc = abs(p + pc);
#else
pa = p < 0 ? -p : p;
pb = pc < 0 ? -pc : pc;
pc = (p + pc) < 0 ? -(p + pc) : p + pc;
#endif
p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
sum += (v < 128) ? v : 256 - v;
if (sum > lmins) /* We are already worse, don't continue. */
break;
}
return (sum);
}
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) #define PNG_MAXSUM (((png_uint_32)(-1)) >> 1)
#define PNG_HISHIFT 10 #define PNG_HISHIFT 10
#define PNG_LOMASK ((png_uint_32)0xffffL) #define PNG_LOMASK ((png_uint_32)0xffffL)
#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) #define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT))
static png_size_t /* PRIVATE */
png_increase_lmins(png_structrp png_ptr, png_size_t lmins)
{
/* We temporarily increase the "minimum sum" by the factor we
* would reduce the sum of this filter, so that we can do the
* early exit comparison without scaling the sum each time.
*/
int j;
png_uint_32 lmhi, lmlo;
lmlo = lmins & PNG_LOMASK;
lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
for (j = 0; j < png_ptr->num_prev_filters; j++)
{
if (png_ptr->prev_filters[j] == png_ptr->try_row[0])
{
lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
PNG_WEIGHT_SHIFT;
lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
PNG_WEIGHT_SHIFT;
}
}
lmlo = (lmlo * png_ptr->inv_filter_costs[png_ptr->try_row[0]]) >>
PNG_COST_SHIFT;
lmhi = (lmhi * png_ptr->inv_filter_costs[png_ptr->try_row[0]]) >>
PNG_COST_SHIFT;
if (lmhi > PNG_HIMASK)
lmins = PNG_MAXSUM;
else
lmins = (lmhi << PNG_HISHIFT) + lmlo;
return (lmins);
}
#endif /* WRITE_WEIGHTED_FILTER */
#endif /* WRITE_FILTER */
void /* PRIVATE */ void /* PRIVATE */
png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
{ {
png_bytep best_row; #ifndef PNG_WRITE_FILTER_SUPPORTED
#ifdef PNG_WRITE_FILTER_SUPPORTED png_write_filtered_row(png_ptr, png_ptr->row_buf, row_info->rowbytes+1);
png_bytep prev_row, row_buf; #else
png_uint_32 mins, bpp;
png_byte filter_to_do = png_ptr->do_filter; png_byte filter_to_do = png_ptr->do_filter;
png_size_t row_bytes = row_info->rowbytes; png_bytep row_buf;
png_bytep best_row;
png_uint_32 bpp;
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
int num_p_filters = png_ptr->num_prev_filters; int num_p_filters = png_ptr->num_prev_filters;
#endif #endif
png_size_t mins;
png_size_t row_bytes = row_info->rowbytes;
png_debug(1, "in png_write_find_filter"); png_debug(1, "in png_write_find_filter");
@ -2348,12 +2549,9 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
/* Find out how many bytes offset each pixel is */ /* Find out how many bytes offset each pixel is */
bpp = (row_info->pixel_depth + 7) >> 3; bpp = (row_info->pixel_depth + 7) >> 3;
prev_row = png_ptr->prev_row; row_buf = png_ptr->row_buf;
#endif mins = PNG_SIZE_MAX - 256/* so we can detect potential overflow of the
best_row = png_ptr->row_buf; running sum */;
#ifdef PNG_WRITE_FILTER_SUPPORTED
row_buf = best_row;
mins = PNG_MAXSUM;
/* The prediction method we use is to find which method provides the /* The prediction method we use is to find which method provides the
* smallest value when summing the absolute values of the distances * smallest value when summing the absolute values of the distances
@ -2383,19 +2581,16 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
/* We don't need to test the 'no filter' case if this is the only filter /* We don't need to test the 'no filter' case if this is the only filter
* that has been chosen, as it doesn't actually do anything to the data. * that has been chosen, as it doesn't actually do anything to the data.
*/ */
best_row = png_ptr->row_buf;
if ((filter_to_do & PNG_FILTER_NONE) != 0 && filter_to_do != PNG_FILTER_NONE) if ((filter_to_do & PNG_FILTER_NONE) != 0 && filter_to_do != PNG_FILTER_NONE)
{ {
png_bytep rp; png_bytep rp;
png_uint_32 sum = 0; png_size_t sum = 0;
png_size_t i; png_size_t i;
int v; int v;
for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
{
v = *rp;
sum += (v < 128) ? v : 256 - v;
}
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
{ {
@ -2434,6 +2629,28 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
sum = (sumhi << PNG_HISHIFT) + sumlo; sum = (sumhi << PNG_HISHIFT) + sumlo;
} }
#endif #endif
if (PNG_SIZE_MAX/128 <= row_bytes)
{
for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
{
/* Check for overflow */
if (sum > PNG_SIZE_MAX/128 - 256)
break;
v = *rp;
sum += (v < 128) ? v : 256 - v;
}
}
else /* Overflow is not possible */
{
for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
{
v = *rp;
sum += (v < 128) ? v : 256 - v;
}
}
mins = sum; mins = sum;
} }
@ -2441,87 +2658,21 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
if (filter_to_do == PNG_FILTER_SUB) if (filter_to_do == PNG_FILTER_SUB)
/* It's the only filter so no testing is needed */ /* It's the only filter so no testing is needed */
{ {
png_bytep rp, lp, dp; (void) png_setup_sub_row(png_ptr, bpp, row_bytes, mins);
png_size_t i; best_row = png_ptr->try_row;
for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
i++, rp++, dp++)
{
*dp = *rp;
}
for (lp = row_buf + 1; i < row_bytes;
i++, rp++, lp++, dp++)
{
*dp = (png_byte)((int)*rp - (int)*lp);
}
best_row = png_ptr->sub_row;
} }
else if ((filter_to_do & PNG_FILTER_SUB) != 0) else if ((filter_to_do & PNG_FILTER_SUB) != 0)
{ {
png_bytep rp, dp, lp; png_size_t sum;
png_uint_32 sum = 0, lmins = mins; png_size_t lmins = mins;
png_size_t i;
int v;
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
/* We temporarily increase the "minimum sum" by the factor we png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB;
* would reduce the sum of this filter, so that we can do the
* early exit comparison without scaling the sum each time.
*/
if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
{ lmins = png_increase_lmins(png_ptr, lmins);
int j;
png_uint_32 lmhi, lmlo;
lmlo = lmins & PNG_LOMASK;
lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
for (j = 0; j < num_p_filters; j++)
{
if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
{
lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
PNG_WEIGHT_SHIFT;
lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
PNG_WEIGHT_SHIFT;
}
}
lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
PNG_COST_SHIFT;
lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
PNG_COST_SHIFT;
if (lmhi > PNG_HIMASK)
lmins = PNG_MAXSUM;
else
lmins = (lmhi << PNG_HISHIFT) + lmlo;
}
#endif #endif
for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; sum = png_setup_sub_row(png_ptr, bpp, row_bytes, lmins);
i++, rp++, dp++)
{
v = *dp = *rp;
sum += (v < 128) ? v : 256 - v;
}
for (lp = row_buf + 1; i < row_bytes;
i++, rp++, lp++, dp++)
{
v = *dp = (png_byte)((int)*rp - (int)*lp);
sum += (v < 128) ? v : 256 - v;
if (sum > lmins) /* We are already worse, don't continue. */
break;
}
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
@ -2560,78 +2711,33 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
if (sum < mins) if (sum < mins)
{ {
mins = sum; mins = sum;
best_row = png_ptr->sub_row; best_row = png_ptr->try_row;
if (png_ptr->tst_row != NULL)
{
png_ptr->try_row = png_ptr->tst_row;
png_ptr->tst_row = best_row;
}
} }
} }
/* Up filter */ /* Up filter */
if (filter_to_do == PNG_FILTER_UP) if (filter_to_do == PNG_FILTER_UP)
{ {
png_bytep rp, dp, pp; (void) png_setup_up_row(png_ptr, row_bytes, mins);
png_size_t i; best_row = png_ptr->try_row;
for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
pp = prev_row + 1; i < row_bytes;
i++, rp++, pp++, dp++)
{
*dp = (png_byte)((int)*rp - (int)*pp);
}
best_row = png_ptr->up_row;
} }
else if ((filter_to_do & PNG_FILTER_UP) != 0) else if ((filter_to_do & PNG_FILTER_UP) != 0)
{ {
png_bytep rp, dp, pp; png_size_t sum;
png_uint_32 sum = 0, lmins = mins; png_size_t lmins = mins;
png_size_t i;
int v;
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
png_ptr->try_row[0] = PNG_FILTER_VALUE_UP;
if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
{ lmins = png_increase_lmins(png_ptr, lmins);
int j;
png_uint_32 lmhi, lmlo;
lmlo = lmins & PNG_LOMASK;
lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
for (j = 0; j < num_p_filters; j++)
{
if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
{
lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
PNG_WEIGHT_SHIFT;
lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
PNG_WEIGHT_SHIFT;
}
}
lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
PNG_COST_SHIFT;
lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
PNG_COST_SHIFT;
if (lmhi > PNG_HIMASK)
lmins = PNG_MAXSUM;
else
lmins = (lmhi << PNG_HISHIFT) + lmlo;
}
#endif #endif
for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, sum = png_setup_up_row(png_ptr, row_bytes, lmins);
pp = prev_row + 1; i < row_bytes; i++)
{
v = *dp++ = (png_byte)((int)*rp++ - (int)*pp++);
sum += (v < 128) ? v : 256 - v;
if (sum > lmins) /* We are already worse, don't continue. */
break;
}
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
@ -2670,89 +2776,33 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
if (sum < mins) if (sum < mins)
{ {
mins = sum; mins = sum;
best_row = png_ptr->up_row; best_row = png_ptr->try_row;
if (png_ptr->tst_row != NULL)
{
png_ptr->try_row = png_ptr->tst_row;
png_ptr->tst_row = best_row;
}
} }
} }
/* Avg filter */ /* Avg filter */
if (filter_to_do == PNG_FILTER_AVG) if (filter_to_do == PNG_FILTER_AVG)
{ {
png_bytep rp, dp, pp, lp; (void) png_setup_avg_row(png_ptr, bpp, row_bytes, mins);
png_uint_32 i; best_row = png_ptr->try_row;
for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
pp = prev_row + 1; i < bpp; i++)
{
*dp++ = (png_byte)((int)*rp++ - ((int)*pp++ / 2));
}
for (lp = row_buf + 1; i < row_bytes; i++)
{
*dp++ =
(png_byte)((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2));
}
best_row = png_ptr->avg_row;
} }
else if ((filter_to_do & PNG_FILTER_AVG) != 0) else if ((filter_to_do & PNG_FILTER_AVG) != 0)
{ {
png_bytep rp, dp, pp, lp; png_size_t sum;
png_uint_32 sum = 0, lmins = mins; png_size_t lmins = mins;
png_size_t i;
int v;
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG;
if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
{ lmins = png_increase_lmins(png_ptr, lmins);
int j;
png_uint_32 lmhi, lmlo;
lmlo = lmins & PNG_LOMASK;
lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
for (j = 0; j < num_p_filters; j++)
{
if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG)
{
lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
PNG_WEIGHT_SHIFT;
lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
PNG_WEIGHT_SHIFT;
}
}
lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
PNG_COST_SHIFT;
lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
PNG_COST_SHIFT;
if (lmhi > PNG_HIMASK)
lmins = PNG_MAXSUM;
else
lmins = (lmhi << PNG_HISHIFT) + lmlo;
}
#endif #endif
for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, sum= png_setup_avg_row(png_ptr, bpp, row_bytes, lmins);
pp = prev_row + 1; i < bpp; i++)
{
v = *dp++ = (png_byte)((int)*rp++ - ((int)*pp++ / 2));
sum += (v < 128) ? v : 256 - v;
}
for (lp = row_buf + 1; i < row_bytes; i++)
{
v = *dp++ =
(png_byte)(((int)*rp++ - ((int)*pp++ + (int)*lp++) / 2));
sum += (v < 128) ? v : 256 - v;
if (sum > lmins) /* We are already worse, don't continue. */
break;
}
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
@ -2791,143 +2841,33 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
if (sum < mins) if (sum < mins)
{ {
mins = sum; mins = sum;
best_row = png_ptr->avg_row; best_row = png_ptr->try_row;
if (png_ptr->tst_row != NULL)
{
png_ptr->try_row = png_ptr->tst_row;
png_ptr->tst_row = best_row;
}
} }
} }
/* Paeth filter */ /* Paeth filter */
if ((filter_to_do == PNG_FILTER_PAETH) != 0) if ((filter_to_do == PNG_FILTER_PAETH) != 0)
{ {
png_bytep rp, dp, pp, cp, lp; (void) png_setup_paeth_row(png_ptr, bpp, row_bytes, mins);
png_size_t i; best_row = png_ptr->try_row;
for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
pp = prev_row + 1; i < bpp; i++)
{
*dp++ = (png_byte)((int)*rp++ - (int)*pp++);
}
for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
{
int a, b, c, pa, pb, pc, p;
b = *pp++;
c = *cp++;
a = *lp++;
p = b - c;
pc = a - c;
#ifdef PNG_USE_ABS
pa = abs(p);
pb = abs(pc);
pc = abs(p + pc);
#else
pa = p < 0 ? -p : p;
pb = pc < 0 ? -pc : pc;
pc = (p + pc) < 0 ? -(p + pc) : p + pc;
#endif
p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
*dp++ = (png_byte)((int)*rp++ - p);
}
best_row = png_ptr->paeth_row;
} }
else if ((filter_to_do & PNG_FILTER_PAETH) != 0) else if ((filter_to_do & PNG_FILTER_PAETH) != 0)
{ {
png_bytep rp, dp, pp, cp, lp; png_size_t sum;
png_uint_32 sum = 0, lmins = mins; png_size_t lmins = mins;
png_size_t i;
int v;
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH;
if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
{ lmins = png_increase_lmins(png_ptr, lmins);
int j;
png_uint_32 lmhi, lmlo;
lmlo = lmins & PNG_LOMASK;
lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
for (j = 0; j < num_p_filters; j++)
{
if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
{
lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
PNG_WEIGHT_SHIFT;
lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
PNG_WEIGHT_SHIFT;
}
}
lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
PNG_COST_SHIFT;
lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
PNG_COST_SHIFT;
if (lmhi > PNG_HIMASK)
lmins = PNG_MAXSUM;
else
lmins = (lmhi << PNG_HISHIFT) + lmlo;
}
#endif #endif
for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, sum = png_setup_paeth_row(png_ptr, bpp, row_bytes, lmins);
pp = prev_row + 1; i < bpp; i++)
{
v = *dp++ = (png_byte)((int)*rp++ - (int)*pp++);
sum += (v < 128) ? v : 256 - v;
}
for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
{
int a, b, c, pa, pb, pc, p;
b = *pp++;
c = *cp++;
a = *lp++;
#ifndef PNG_SLOW_PAETH
p = b - c;
pc = a - c;
#ifdef PNG_USE_ABS
pa = abs(p);
pb = abs(pc);
pc = abs(p + pc);
#else
pa = p < 0 ? -p : p;
pb = pc < 0 ? -pc : pc;
pc = (p + pc) < 0 ? -(p + pc) : p + pc;
#endif
p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
#else /* SLOW_PAETH */
p = a + b - c;
pa = abs(p - a);
pb = abs(p - b);
pc = abs(p - c);
if (pa <= pb && pa <= pc)
p = a;
else if (pb <= pc)
p = b;
else
p = c;
#endif /* SLOW_PAETH */
v = *dp++ = (png_byte)((int)*rp++ - p);
sum += (v < 128) ? v : 256 - v;
if (sum > lmins) /* We are already worse, don't continue. */
break;
}
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
@ -2965,15 +2905,19 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
if (sum < mins) if (sum < mins)
{ {
best_row = png_ptr->paeth_row; mins = sum;
best_row = png_ptr->try_row;
if (png_ptr->tst_row != NULL)
{
png_ptr->try_row = png_ptr->tst_row;
png_ptr->tst_row = best_row;
}
} }
} }
#endif /* WRITE_FILTER */
/* Do the actual writing of the filtered row data from the chosen filter. */ /* Do the actual writing of the filtered row data from the chosen filter. */
png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1); png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1);
#ifdef PNG_WRITE_FILTER_SUPPORTED
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
/* Save the type of filter we picked this time for future calculations */ /* Save the type of filter we picked this time for future calculations */
if (png_ptr->num_prev_filters > 0) if (png_ptr->num_prev_filters > 0)
@ -2987,7 +2931,7 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
png_ptr->prev_filters[j] = best_row[0]; png_ptr->prev_filters[j] = best_row[0];
} }
#endif #endif /* WRITE_WEIGHTED_FILTER */
#endif /* WRITE_FILTER */ #endif /* WRITE_FILTER */
} }