mirror of
https://git.code.sf.net/p/libpng/code.git
synced 2025-07-10 18:04:09 +02:00
Added an accurate 'methodical' measure
Also fix incorrect references to 'pngimage' in pngcp. Signed-off-by: John Bowler <jbowler@acm.org>
This commit is contained in:
parent
8d48a512bd
commit
156006bb1a
@ -163,7 +163,7 @@ get_dp(png_structp pp)
|
|||||||
|
|
||||||
if (dp == NULL)
|
if (dp == NULL)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "pngimage: internal error (no display)\n");
|
fprintf(stderr, "pngcp: internal error (no display)\n");
|
||||||
exit(99); /* prevents a crash */
|
exit(99); /* prevents a crash */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,16 +196,16 @@ display_log(struct display *dp, error_level level, const char *fmt, ...)
|
|||||||
{
|
{
|
||||||
case INFORMATION: lp = "information"; break;
|
case INFORMATION: lp = "information"; break;
|
||||||
case LIBPNG_WARNING: lp = "warning(libpng)"; break;
|
case LIBPNG_WARNING: lp = "warning(libpng)"; break;
|
||||||
case APP_WARNING: lp = "warning(pngimage)"; break;
|
case APP_WARNING: lp = "warning(pngcp)"; break;
|
||||||
case APP_FAIL: lp = "error(continuable)"; break;
|
case APP_FAIL: lp = "error(continuable)"; break;
|
||||||
case LIBPNG_ERROR: lp = "error(libpng)"; break;
|
case LIBPNG_ERROR: lp = "error(libpng)"; break;
|
||||||
case LIBPNG_BUG: lp = "bug(libpng)"; break;
|
case LIBPNG_BUG: lp = "bug(libpng)"; break;
|
||||||
case APP_ERROR: lp = "error(pngimage)"; break;
|
case APP_ERROR: lp = "error(pngcp)"; break;
|
||||||
case USER_ERROR: lp = "error(user)"; break;
|
case USER_ERROR: lp = "error(user)"; break;
|
||||||
|
|
||||||
case INTERNAL_ERROR: /* anything unexpected is an internal error: */
|
case INTERNAL_ERROR: /* anything unexpected is an internal error: */
|
||||||
case VERBOSE: case WARNINGS: case ERRORS: case QUIET:
|
case VERBOSE: case WARNINGS: case ERRORS: case QUIET:
|
||||||
default: lp = "bug(pngimage)"; break;
|
default: lp = "bug(pngcp)"; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "%s: %s: %s",
|
fprintf(stderr, "%s: %s: %s",
|
||||||
@ -461,7 +461,7 @@ main(const int argc, const char * const * const argv)
|
|||||||
|
|
||||||
else if (name[0] == '-' && name[1] == '-')
|
else if (name[0] == '-' && name[1] == '-')
|
||||||
{
|
{
|
||||||
fprintf(stderr, "pngimage: %s: unknown option\n", name);
|
fprintf(stderr, "pngcp: %s: unknown option\n", name);
|
||||||
return 99;
|
return 99;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,7 +504,7 @@ main(const int argc, const char * const * const argv)
|
|||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
printf("%s: pngimage", pass ? "PASS" : "FAIL");
|
printf("%s: pngcp", pass ? "PASS" : "FAIL");
|
||||||
|
|
||||||
for (j=1; j<option_end; ++j) if (j != ilog)
|
for (j=1; j<option_end; ++j) if (j != ilog)
|
||||||
printf(" %s", argv[j]);
|
printf(" %s", argv[j]);
|
||||||
|
|||||||
BIN
pngtest.png
BIN
pngtest.png
Binary file not shown.
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.6 KiB |
204
pngwutil.c
204
pngwutil.c
@ -469,6 +469,56 @@ png_zlib_compress_init(png_structrp png_ptr, png_zlib_compressp pz)
|
|||||||
*/
|
*/
|
||||||
#define png_ptr png_voidcast(png_const_structrp, pz->zs.opaque)
|
#define png_ptr png_voidcast(png_const_structrp, pz->zs.opaque)
|
||||||
|
|
||||||
|
#if PNG_RELEASE_BUILD
|
||||||
|
# define png_zlib_compress_vaidate(pz) ((void)0)
|
||||||
|
#else /* !RELEASE_BUILD */
|
||||||
|
static void
|
||||||
|
png_zlib_compress_validate(png_zlib_compressp pz, int in_use)
|
||||||
|
{
|
||||||
|
const uInt o_size = sizeof pz->list->output;
|
||||||
|
|
||||||
|
affirm(pz->end != NULL && (in_use || (pz->zs.next_in == NULL &&
|
||||||
|
pz->zs.avail_in == 0U && *pz->end == NULL)));
|
||||||
|
|
||||||
|
if (pz->overflow == 0U && pz->len == 0U && pz->start == 0U) /* empty */
|
||||||
|
{
|
||||||
|
affirm((pz->end == &pz->list->next && pz->zs.next_out == pz->list->output
|
||||||
|
&& pz->zs.avail_out == o_size) ||
|
||||||
|
(pz->end == &pz->list && pz->zs.next_out == NULL
|
||||||
|
&& pz->zs.avail_out == 0U));
|
||||||
|
}
|
||||||
|
|
||||||
|
else /* not empty */
|
||||||
|
{
|
||||||
|
png_compression_bufferp *ep = &pz->list, list;
|
||||||
|
png_uint_32 o, l;
|
||||||
|
|
||||||
|
affirm(*ep != NULL && pz->zs.next_out != NULL);
|
||||||
|
|
||||||
|
/* Check the list length: */
|
||||||
|
o = pz->overflow;
|
||||||
|
l = pz->len;
|
||||||
|
affirm((l & 0x80000000U) == 0U && (o & 0x80000000U) == 0U);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
list = *ep;
|
||||||
|
l -= o_size;
|
||||||
|
if (l & 0x80000000U) --o, l &= 0x7FFFFFFFU;
|
||||||
|
ep = &list->next;
|
||||||
|
}
|
||||||
|
while (ep != pz->end);
|
||||||
|
|
||||||
|
l += pz->start;
|
||||||
|
l += pz->zs.avail_out;
|
||||||
|
if (l & 0x80000000U) ++o, l &= 0x7FFFFFFFU;
|
||||||
|
|
||||||
|
affirm(o == 0U && l == 0U && pz->zs.next_out >= list->output &&
|
||||||
|
pz->zs.next_out + pz->zs.avail_out == list->output + o_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* !RELEASE_BUILD */
|
||||||
|
|
||||||
/* Destroy one zlib compress structure. */
|
/* Destroy one zlib compress structure. */
|
||||||
static void
|
static void
|
||||||
png_zlib_compress_destroy(png_zlib_compressp pz, int check)
|
png_zlib_compress_destroy(png_zlib_compressp pz, int check)
|
||||||
@ -479,7 +529,12 @@ png_zlib_compress_destroy(png_zlib_compressp pz, int check)
|
|||||||
if (png_ptr != NULL)
|
if (png_ptr != NULL)
|
||||||
{
|
{
|
||||||
if (pz->zs.state != NULL)
|
if (pz->zs.state != NULL)
|
||||||
|
{
|
||||||
|
if (check)
|
||||||
|
png_zlib_compress_validate(pz, 0/*in_use*/);
|
||||||
|
|
||||||
png_deflateEnd(png_ptr, &pz->zs, check);
|
png_deflateEnd(png_ptr, &pz->zs, check);
|
||||||
|
}
|
||||||
|
|
||||||
pz->end = &pz->list; /* safety */
|
pz->end = &pz->list; /* safety */
|
||||||
png_free_compression_buffer(png_ptr, &pz->list);
|
png_free_compression_buffer(png_ptr, &pz->list);
|
||||||
@ -494,6 +549,8 @@ png_zlib_compress_avail_out(png_zlib_compressp pz)
|
|||||||
{
|
{
|
||||||
uInt avail_out = pz->zs.avail_out;
|
uInt avail_out = pz->zs.avail_out;
|
||||||
|
|
||||||
|
png_zlib_compress_validate(pz, 1/*in_use*/);
|
||||||
|
|
||||||
if (avail_out == 0U)
|
if (avail_out == 0U)
|
||||||
{
|
{
|
||||||
png_compression_bufferp next;
|
png_compression_bufferp next;
|
||||||
@ -2257,6 +2314,7 @@ png_write_IDAT(png_structrp png_ptr, int flush)
|
|||||||
* pointer is NULL means that the end of the list can be easily detected.
|
* pointer is NULL means that the end of the list can be easily detected.
|
||||||
*/
|
*/
|
||||||
affirm(ps != NULL && ps->s.end != NULL && *ps->s.end == NULL);
|
affirm(ps != NULL && ps->s.end != NULL && *ps->s.end == NULL);
|
||||||
|
png_zlib_compress_validate(&png_ptr->zlib_state->s, 0/*in_use*/);
|
||||||
|
|
||||||
/* Write IDAT chunks while either 'flush' is true or there are at
|
/* Write IDAT chunks while either 'flush' is true or there are at
|
||||||
* least png_ptr->IDAT_size bytes available to be written.
|
* least png_ptr->IDAT_size bytes available to be written.
|
||||||
@ -2277,7 +2335,7 @@ png_write_IDAT(png_structrp png_ptr, int flush)
|
|||||||
if (!flush)
|
if (!flush)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (avail == 0)
|
if (avail == 0U)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
len = avail;
|
len = avail;
|
||||||
@ -2309,8 +2367,9 @@ png_write_IDAT(png_structrp png_ptr, int flush)
|
|||||||
}
|
}
|
||||||
|
|
||||||
else /* not end of list */
|
else /* not end of list */
|
||||||
debug(ps->s.zs.next_out < next->output ||
|
debug((ps->s.zs.next_out < next->output ||
|
||||||
ps->s.zs.next_out > next->output + sizeof next->output);
|
ps->s.zs.next_out > next->output + sizeof next->output) &&
|
||||||
|
(ps->s.overflow > 0 || ps->s.len >= sizeof next->output));
|
||||||
|
|
||||||
/* First, if this is the very first IDAT (PNG_HAVE_IDAT not set)
|
/* First, if this is the very first IDAT (PNG_HAVE_IDAT not set)
|
||||||
* optimize the CINFO field:
|
* optimize the CINFO field:
|
||||||
@ -2375,6 +2434,7 @@ png_write_IDAT(png_structrp png_ptr, int flush)
|
|||||||
affirm(ps->s.overflow > 0U);
|
affirm(ps->s.overflow > 0U);
|
||||||
--ps->s.overflow;
|
--ps->s.overflow;
|
||||||
ps->s.len += 0x80000000U - written;
|
ps->s.len += 0x80000000U - written;
|
||||||
|
UNTESTED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (len > 0U);
|
while (len > 0U);
|
||||||
@ -2424,6 +2484,7 @@ png_compress_IDAT_data(png_const_structrp png_ptr, png_zlib_statep ps,
|
|||||||
(ret == Z_STREAM_END) == (flush == Z_FINISH));
|
(ret == Z_STREAM_END) == (flush == Z_FINISH));
|
||||||
pz->zs.next_in = NULL;
|
pz->zs.next_in = NULL;
|
||||||
pz->zs.avail_in = 0U; /* safety */
|
pz->zs.avail_in = 0U; /* safety */
|
||||||
|
png_zlib_compress_validate(pz, 0/*in_use*/);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -2983,9 +3044,10 @@ png_zlib_filter_revert(png_structrp png_ptr, png_zlib_statep ps, png_byte i)
|
|||||||
png_zlib_compressp pz = &ps->filter[i];
|
png_zlib_compressp pz = &ps->filter[i];
|
||||||
|
|
||||||
affirm(pz->zs.opaque != NULL);
|
affirm(pz->zs.opaque != NULL);
|
||||||
|
png_zlib_compress_validate(pz, 0/*in_use*/);
|
||||||
|
|
||||||
/* First merge the buffer lists. */
|
/* First merge the buffer lists. */
|
||||||
if (pz->overflow || pz->len > 0U)
|
if (pz->overflow > 0U || pz->len > 0U)
|
||||||
{
|
{
|
||||||
affirm(pz->list != NULL);
|
affirm(pz->list != NULL);
|
||||||
debug(ps->s.end != NULL && *ps->s.end == NULL);
|
debug(ps->s.end != NULL && *ps->s.end == NULL);
|
||||||
@ -2995,8 +3057,9 @@ png_zlib_filter_revert(png_structrp png_ptr, png_zlib_statep ps, png_byte i)
|
|||||||
* the main z_stream, if pz->zs.next_out still points into this buffer the
|
* the main z_stream, if pz->zs.next_out still points into this buffer the
|
||||||
* pointer must be updated to point to the old buffer.
|
* pointer must be updated to point to the old buffer.
|
||||||
*/
|
*/
|
||||||
if (pz->start > 0U)
|
if (ps->s.zs.avail_out > 0U)
|
||||||
{
|
{
|
||||||
|
affirm(ps->s.zs.avail_out + pz->start == sizeof ps->s.list->output);
|
||||||
/* Copy everything after pz->start into the old buffer. */
|
/* Copy everything after pz->start into the old buffer. */
|
||||||
memcpy(ps->s.zs.next_out, pz->list->output + pz->start,
|
memcpy(ps->s.zs.next_out, pz->list->output + pz->start,
|
||||||
ps->s.zs.avail_out);
|
ps->s.zs.avail_out);
|
||||||
@ -3022,6 +3085,8 @@ png_zlib_filter_revert(png_structrp png_ptr, png_zlib_statep ps, png_byte i)
|
|||||||
{
|
{
|
||||||
debug(pz->overflow == 0U &&
|
debug(pz->overflow == 0U &&
|
||||||
pz->len + pz->start < (sizeof pz->list->output) &&
|
pz->len + pz->start < (sizeof pz->list->output) &&
|
||||||
|
pz->zs.next_out + pz->zs.avail_out ==
|
||||||
|
pz->list->output + (sizeof pz->list->output) &&
|
||||||
ps->s.zs.avail_out > pz->zs.avail_out);
|
ps->s.zs.avail_out > pz->zs.avail_out);
|
||||||
pz->zs.next_out = ps->s.zs.next_out + ps->s.zs.avail_out -
|
pz->zs.next_out = ps->s.zs.next_out + ps->s.zs.avail_out -
|
||||||
pz->zs.avail_out;
|
pz->zs.avail_out;
|
||||||
@ -3030,6 +3095,8 @@ png_zlib_filter_revert(png_structrp png_ptr, png_zlib_statep ps, png_byte i)
|
|||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
affirm(pz->start == 0U);
|
||||||
|
|
||||||
/* Nothing to copy, the whole new list is appended to the existing one.
|
/* Nothing to copy, the whole new list is appended to the existing one.
|
||||||
*/
|
*/
|
||||||
*ps->s.end = pz->list;
|
*ps->s.end = pz->list;
|
||||||
@ -3059,11 +3126,11 @@ png_zlib_filter_revert(png_structrp png_ptr, png_zlib_statep ps, png_byte i)
|
|||||||
{
|
{
|
||||||
z_stream zs = ps->s.zs;
|
z_stream zs = ps->s.zs;
|
||||||
|
|
||||||
|
ps->s.zs = pz->zs;
|
||||||
|
png_zlib_compress_validate(&ps->s, 0/*in_use*/);
|
||||||
zs.next_in = zs.next_out = NULL;
|
zs.next_in = zs.next_out = NULL;
|
||||||
zs.avail_in = zs.avail_out = 0U;
|
zs.avail_in = zs.avail_out = 0U;
|
||||||
zs.msg = PNGZ_MSG_CAST("invalid");
|
zs.msg = PNGZ_MSG_CAST("invalid");
|
||||||
|
|
||||||
ps->s.zs = pz->zs;
|
|
||||||
pz->zs = zs;
|
pz->zs = zs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3137,6 +3204,113 @@ select_filter_methodically_init(png_structrp png_ptr,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
select_filter_methodically_better(png_structrp png_ptr, png_zlib_compressp pz,
|
||||||
|
png_uint_32p op/*high 32 bits*/, png_uint_32p lp/*low 31 bits*/,
|
||||||
|
Bytef *scratch_out, uInt avail_out, int flush)
|
||||||
|
/* Called at the end of a row for each filter being tested to work out if
|
||||||
|
* this filter is apparently producing better results than {*op,*lp}, which
|
||||||
|
* is preset to a number larger than any possible 63-bit value and then set,
|
||||||
|
* here, as required to {overflow,len} from a selected filter.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
/* The pre-check here is that the data already produced by the compression
|
||||||
|
* engine does not exceed the best count found so far:
|
||||||
|
*/
|
||||||
|
png_uint_32 o = pz->overflow, l = pz->len;
|
||||||
|
|
||||||
|
png_zlib_compress_validate(pz, 0/*in_use*/);
|
||||||
|
|
||||||
|
if (o < *op || (o == *op && l < *lp))
|
||||||
|
{
|
||||||
|
/* But if the stream hasn't been flushed this proves nothing; test the
|
||||||
|
* pending output by using an appropriate flush:
|
||||||
|
*/
|
||||||
|
if (flush == Z_NO_FLUSH)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
z_stream zs;
|
||||||
|
|
||||||
|
ret = deflateCopy(&zs, &pz->zs);
|
||||||
|
|
||||||
|
if (ret == Z_OK)
|
||||||
|
{
|
||||||
|
zs.next_in = NULL;
|
||||||
|
zs.avail_in = 0U;
|
||||||
|
|
||||||
|
/* Extract all the output from zlib by doing dummy deflates. Note
|
||||||
|
* that all the flush possibilites give approximately the same
|
||||||
|
* result but PARTIAL, SYNC and FULL seem to be mildly better
|
||||||
|
* probably because they avoid the rounding and block overhead.
|
||||||
|
*
|
||||||
|
* Z_PARTIAL_FLUSH 1
|
||||||
|
* Z_SYNC_FLUSH 2
|
||||||
|
* Z_FULL_FLUSH 3
|
||||||
|
* Z_FINISH 4
|
||||||
|
* Z_BLOCK 5
|
||||||
|
*/
|
||||||
|
flush = Z_PARTIAL_FLUSH;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (l & 0x80000000U)
|
||||||
|
++o, l &= 0x7FFFFFFFU;
|
||||||
|
|
||||||
|
zs.next_out = scratch_out;
|
||||||
|
zs.avail_out = avail_out;
|
||||||
|
l += avail_out;
|
||||||
|
|
||||||
|
ret = deflate(&zs, flush);
|
||||||
|
} while (ret == Z_OK && zs.avail_out == 0U);
|
||||||
|
|
||||||
|
if (ret == (flush == Z_FINISH ? Z_STREAM_END : Z_OK))
|
||||||
|
{
|
||||||
|
/* This cannot underflow because the check above is performed
|
||||||
|
* before adding 'avail_out' to l:
|
||||||
|
*/
|
||||||
|
l -= zs.avail_out;
|
||||||
|
(void)deflateEnd(&zs);
|
||||||
|
png_zlib_compress_validate(pz, 0/*in_use*/);
|
||||||
|
|
||||||
|
if (l & 0x80000000U)
|
||||||
|
++o, l &= 0x7FFFFFFFU;
|
||||||
|
|
||||||
|
if (o < *op || (o == *op && l < *lp))
|
||||||
|
{
|
||||||
|
*op = o;
|
||||||
|
*lp = l;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No errors but the result was longer (this can't be the first
|
||||||
|
* filter.)
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
else /* problem in deflate */
|
||||||
|
(void)deflateEnd(&zs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We arrive here if there was an error somewhere inside zlib. */
|
||||||
|
png_zstream_error(&zs, ret);
|
||||||
|
png_warning(png_ptr, zs.msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
else /* flush already performed */
|
||||||
|
{
|
||||||
|
*op = o;
|
||||||
|
*lp = l;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is the failure case, however if this is the first filter to be tested
|
||||||
|
* return success anyway, without resetting {op,lp}:
|
||||||
|
*/
|
||||||
|
return *op == 0xFFFFFFFFU && *lp == 0xFFFFFFFFU;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
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,
|
||||||
@ -3167,8 +3341,7 @@ select_filter_methodically(png_structrp png_ptr, png_const_bytep prev_row,
|
|||||||
{
|
{
|
||||||
if (png_zlib_filter_compress(png_ptr, ps, filter,
|
if (png_zlib_filter_compress(png_ptr, ps, filter,
|
||||||
filter == PNG_FILTER_VALUE_NONE ?
|
filter == PNG_FILTER_VALUE_NONE ?
|
||||||
unfiltered_row : test_buffers[filter-1],
|
unfiltered_row : test_buffers[filter-1], row_bytes, flush))
|
||||||
row_bytes, flush))
|
|
||||||
ok_filter = filter;
|
ok_filter = filter;
|
||||||
|
|
||||||
else /* remove this filter from the test list: */
|
else /* remove this filter from the test list: */
|
||||||
@ -3188,15 +3361,10 @@ select_filter_methodically(png_structrp png_ptr, png_const_bytep prev_row,
|
|||||||
ok_filter = PNG_FILTER_VALUE_LAST;
|
ok_filter = PNG_FILTER_VALUE_LAST;
|
||||||
|
|
||||||
for (filter=0U; filter < PNG_FILTER_VALUE_LAST; ++filter)
|
for (filter=0U; filter < PNG_FILTER_VALUE_LAST; ++filter)
|
||||||
if ((filters_to_try & PNG_FILTER_MASK(filter)) != 0U)
|
if ((filters_to_try & PNG_FILTER_MASK(filter)) != 0U &&
|
||||||
if (ps->filter[filter].overflow < o ||
|
select_filter_methodically_better(png_ptr, &ps->filter[filter],
|
||||||
(ps->filter[filter].overflow == o &&
|
&o, &l, test_buffers[0], sizeof test_buffers, flush))
|
||||||
ps->filter[filter].len < l))
|
ok_filter = filter;
|
||||||
{
|
|
||||||
ok_filter = filter;
|
|
||||||
o = ps->filter[filter].overflow;
|
|
||||||
l = ps->filter[filter].len;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Keep going if there is more than one filter left, otherwise, if there
|
/* Keep going if there is more than one filter left, otherwise, if there
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user