pngtest.c revision f9a51917495bc8ba8b60632219652a7b122c1190
/* pngtest.c - a simple test program to test libpng
*
* libpng 1.2.8 - December 3, 2004
* For conditions of distribution and use, see copyright notice in png.h
* Copyright (c) 1998-2004 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
*
* This program reads in a PNG image, writes it out again, and then
* compares the two files. If the files are identical, this shows that
* the basic chunk handling, filtering, and (de)compression code is working
* properly. It does not currently test all of the transforms, although
* it probably should.
*
* The program will report "FAIL" in certain legitimate cases:
* 1) when the compression level or filter selection method is changed.
* 2) when the maximum IDAT size (PNG_ZBUF_SIZE in pngconf.h) is not 8192.
* 3) unknown unsafe-to-copy ancillary chunks or unknown critical chunks
* exist in the input file.
* 4) others not listed here...
* In these cases, it is best to check with another tool such as "pngcheck"
* to see what the differences between the two files are.
*
* If a filename is given on the command-line, then this file is used
* for the input, rather than the default "pngtest.png". This allows
* testing a wide variety of files easily. You can also test a number
* of files at once by typing "pngtest -m file1.png file2.png ..."
*/
#include "png.h"
#if defined(_WIN32_WCE)
# if _WIN32_WCE < 211
# endif
# include <windows.h>
# include <stdlib.h>
#else
# include <stdio.h>
# include <stdlib.h>
# include <assert.h>
#endif
#if defined(PNG_NO_STDIO)
# if defined(_WIN32_WCE)
typedef HANDLE png_FILE_p;
# else
typedef FILE * png_FILE_p;
# endif
#endif
/* Makes pngtest verbose so we can find problems (needs to be before png.h) */
#ifndef PNG_DEBUG
# define PNG_DEBUG 0
#endif
#if !PNG_DEBUG
# define SINGLE_ROWBUF_ALLOC /* makes buffer overruns easier to nail */
#endif
/* Turn on CPU timing
#define PNGTEST_TIMING
*/
#endif
#ifdef PNGTEST_TIMING
#include <time.h>
#endif
/* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */
#ifndef png_jmpbuf
#endif
#ifdef PNGTEST_TIMING
#if !defined(PNG_tIME_SUPPORTED)
#include <time.h>
#endif
#endif
#if defined(PNG_TIME_RFC1123_SUPPORTED)
static int tIME_chunk_present=0;
#endif
static int verbose = 0;
#ifdef __TURBOC__
#include <mem.h>
#endif
/* #define STDERR stderr */
/* example of using row callbacks to make a simple progress meter */
static int status_pass=1;
static int status_dots_requested=0;
static int status_dots=1;
void
#ifdef PNG_1_0_X
#endif
void
#ifdef PNG_1_0_X
#endif
{
if(status_pass != pass)
{
status_pass = pass;
status_dots = 31;
}
status_dots--;
if(status_dots == 0)
{
status_dots=30;
}
}
void
#ifdef PNG_1_0_X
#endif
void
#ifdef PNG_1_0_X
#endif
{
}
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
/* Example of using user transform callback (we don't transform anything,
but merely examine the row filters. We set this to 256 rather than
5 in case illegal filter values are present.) */
void
#ifdef PNG_1_0_X
#endif
void
#ifdef PNG_1_0_X
#endif
{
}
#endif
#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
/* example of using user transform callback (we don't transform anything,
but merely count the zero samples) */
static png_uint_32 zero_samples;
void
#ifdef PNG_1_0_X
#endif
void
#ifdef PNG_1_0_X
#endif
{
/* contents of row_info:
* png_uint_32 width width of row
* png_uint_32 rowbytes number of bytes in row
* png_byte color_type color type of pixels
* png_byte bit_depth bit depth of samples
* png_byte channels number of channels (1-4)
* png_byte pixel_depth bits per pixel (depth*channels)
*/
/* counts the number of zero samples (or zero pixels if color_type is 3 */
{
int pos=0;
png_uint_32 n, nstop;
{
{
if(pos == 8)
{
pos = 0;
dp++;
}
}
{
if(pos == 8)
{
pos = 0;
dp++;
}
}
{
if(pos == 8)
{
pos = 0;
dp++;
}
}
if(*dp++ == 0) zero_samples++;
{
dp+=2;
}
}
}
else /* other color types */
{
png_uint_32 n, nstop;
int channel;
{
{
if(*dp++ == 0) zero_samples++;
{
dp+=2;
}
}
{
dp++;
}
}
}
}
#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */
static int wrote_question = 0;
#if defined(PNG_NO_STDIO)
/* START of code to validate stdio-free compilation */
/* pngwio.c. They allow "don't include stdio" testing of the library. */
/* This is the function that does the actual reading of data. If you are
not reading from a standard C stream, you should create a replacement
read_data function and use it at run time with png_set_read_fn(), rather
than changing the library. */
#ifndef USE_FAR_KEYWORD
static void
{
/* fread() returns 0 on error, so it is OK to store this in a png_size_t
* instead of an int, which is what fread() actually returns.
*/
{
}
}
#else
/* this is the model-independent version. Since the standard I/O library
can't handle far buffers in the medium and small models, we have to copy
the data.
*/
#define NEAR_BUF_SIZE 1024
#define MIN(a,b) (a <= b ? a : b)
static void
{
int check;
/* Check if data really is near. If so, use usual code. */
{
}
else
{
check = 0;
do
{
break;
else
}
while (remaining != 0);
}
{
}
}
#endif /* USE_FAR_KEYWORD */
#if defined(PNG_WRITE_FLUSH_SUPPORTED)
static void
{
#if !defined(_WIN32_WCE)
#endif
}
#endif
/* This is the function that does the actual writing of data. If you are
not writing to a standard C stream, you should create a replacement
write_data function and use it at run time with png_set_write_fn(), rather
than changing the library. */
#ifndef USE_FAR_KEYWORD
static void
{
{
}
}
#else
/* this is the model-independent version. Since the standard I/O library
can't handle far buffers in the medium and small models, we have to copy
the data.
*/
#define NEAR_BUF_SIZE 1024
#define MIN(a,b) (a <= b ? a : b)
static void
{
/* Check if data really is near. If so, use usual code. */
{
}
else
{
check = 0;
do
{
break;
else
}
while (remaining != 0);
}
{
}
}
#endif /* USE_FAR_KEYWORD */
/* This function is called when there is a warning, but the library thinks
* it can continue anyway. Replacement functions don't have to do anything
* here if you don't want to. In the default configuration, png_ptr is
* not used, but it is passed in case it may be useful.
*/
static void
{
}
/* This is the default error handling function. Note that replacements for
* this function MUST NOT RETURN, or the program will likely crash. This
* function is used by default, or if the program supplies NULL for the
* error function pointer in png_set_error_fn().
*/
static void
{
/* We can return because png_error calls the default handler, which is
* actually OK in this case. */
}
#endif /* PNG_NO_STDIO */
/* END of code to validate stdio-free compilation */
/* START of code to validate memory allocation and deallocation */
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
/* Allocate memory. For reasonable files, size should never exceed
64K. However, zlib may allocate more then 64K if you don't tell
need to allocate exactly 64K, so whatever you call here must
have the ability to do that.
This piece of code can be compiled to validate max 64K allocations
by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K. */
typedef struct memory_information
{
static int current_allocation = 0;
static int maximum_allocation = 0;
static int total_allocation = 0;
static int num_allocations = 0;
{
/* png_malloc has already tested for NULL; png_create_struct calls
png_debug_malloc directly, with png_ptr == NULL which is OK */
if (size == 0)
return (NULL);
/* This calls the library allocator twice, once to get the requested
buffer and once to get a new free list entry. */
{
/* Disable malloc_fn and free_fn */
total_allocation += size;
num_allocations ++;
/* Restore malloc_fn and free_fn */
{
total_allocation -= size;
"out of memory in pngtest->png_debug_malloc.");
}
/* Make sure the caller isn't assuming zeroed memory. */
if(verbose)
}
}
/* Free a pointer. It is removed from the list at the same time. */
void
{
if (ptr == 0)
{
#if 0 /* This happens all the time. */
#endif
return;
}
/* Unlink the element from the list. */
{
for (;;)
{
{
if (current_allocation < 0)
/* We must free the list element too, but first kill
the memory that is to be freed. */
break;
}
{
break;
}
}
}
/* Finally free the data. */
if(verbose)
}
#endif /* PNG_USER_MEM_SUPPORTED && PNG_DEBUG */
/* END of code to test memory allocation/deallocation */
/* Test one file */
int
{
static png_FILE_p fpin;
#ifdef PNG_WRITE_SUPPORTED
#else
#endif
png_uint_32 y;
int bit_depth, color_type;
#ifdef PNG_SETJMP_SUPPORTED
#ifdef USE_FAR_KEYWORD
#endif
#endif
#if defined(_WIN32_WCE)
#endif
#if defined(_WIN32_WCE)
if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
#else
#endif
{
return (1);
}
#if defined(_WIN32_WCE)
if ((fpout = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL)) == INVALID_HANDLE_VALUE)
#else
#endif
{
return (1);
}
png_debug(0, "Allocating read and write structures\n");
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
#else
#endif
#if defined(PNG_NO_STDIO)
#endif
#ifdef PNG_WRITE_SUPPORTED
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
#else
#endif
#if defined(PNG_NO_STDIO)
#endif
#endif
png_debug(0, "Allocating read_info, write_info and end_info structures\n");
#ifdef PNG_WRITE_SUPPORTED
#endif
#ifdef PNG_SETJMP_SUPPORTED
png_debug(0, "Setting jmpbuf for read struct\n");
#ifdef USE_FAR_KEYWORD
#else
#endif
{
if (row_buf)
#ifdef PNG_WRITE_SUPPORTED
#endif
return (1);
}
#ifdef USE_FAR_KEYWORD
#endif
#ifdef PNG_WRITE_SUPPORTED
png_debug(0, "Setting jmpbuf for write struct\n");
#ifdef USE_FAR_KEYWORD
#else
#endif
{
#ifdef PNG_WRITE_SUPPORTED
#endif
return (1);
}
#ifdef USE_FAR_KEYWORD
#endif
#endif
#endif
png_debug(0, "Initializing input and output streams\n");
#if !defined(PNG_NO_STDIO)
# ifdef PNG_WRITE_SUPPORTED
# endif
#else
# ifdef PNG_WRITE_SUPPORTED
# if defined(PNG_WRITE_FLUSH_SUPPORTED)
# else
NULL);
# endif
# endif
#endif
if(status_dots_requested == 1)
{
#ifdef PNG_WRITE_SUPPORTED
#endif
}
else
{
#ifdef PNG_WRITE_SUPPORTED
#endif
}
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
{
int i;
for(i=0; i<256; i++)
filters_used[i]=0;
}
#endif
#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
zero_samples=0;
#endif
#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
# ifndef PNG_HANDLE_CHUNK_ALWAYS
# define PNG_HANDLE_CHUNK_ALWAYS 3
# endif
png_bytep_NULL, 0);
#endif
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
# ifndef PNG_HANDLE_CHUNK_IF_SAFE
# define PNG_HANDLE_CHUNK_IF_SAFE 2
# endif
png_bytep_NULL, 0);
#endif
png_debug(0, "Reading info struct\n");
png_debug(0, "Transferring info struct\n");
{
{
#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
#else
#endif
}
}
#if defined(PNG_FIXED_POINT_SUPPORTED)
#if defined(PNG_cHRM_SUPPORTED)
{
{
}
}
#endif
#if defined(PNG_gAMA_SUPPORTED)
{
{
}
}
#endif
#else /* Use floating point versions */
#if defined(PNG_FLOATING_POINT_SUPPORTED)
#if defined(PNG_cHRM_SUPPORTED)
{
{
}
}
#endif
#if defined(PNG_gAMA_SUPPORTED)
{
double gamma;
{
}
}
#endif
#endif /* floating point */
#endif /* fixed point */
#if defined(PNG_iCCP_SUPPORTED)
{
int compression_type;
{
}
}
#endif
#if defined(PNG_sRGB_SUPPORTED)
{
int intent;
{
}
}
#endif
{
int num_palette;
{
}
}
#if defined(PNG_bKGD_SUPPORTED)
{
{
}
}
#endif
#if defined(PNG_hIST_SUPPORTED)
{
{
}
}
#endif
#if defined(PNG_oFFs_SUPPORTED)
{
int unit_type;
{
}
}
#endif
#if defined(PNG_pCAL_SUPPORTED)
{
{
}
}
#endif
#if defined(PNG_pHYs_SUPPORTED)
{
int unit_type;
{
}
}
#endif
#if defined(PNG_sBIT_SUPPORTED)
{
{
}
}
#endif
#if defined(PNG_sCAL_SUPPORTED)
#ifdef PNG_FLOATING_POINT_SUPPORTED
{
int unit;
double scal_width, scal_height;
&scal_height))
{
}
}
#else
#ifdef PNG_FIXED_POINT_SUPPORTED
{
int unit;
&scal_height))
{
}
}
#endif
#endif
#endif
#if defined(PNG_TEXT_SUPPORTED)
{
int num_text;
{
}
}
#endif
#if defined(PNG_tIME_SUPPORTED)
{
{
#if defined(PNG_TIME_RFC1123_SUPPORTED)
/* we have to use png_strcpy instead of "=" because the string
pointed to by png_convert_to_rfc1123() gets free'ed before
we use it */
#endif /* PNG_TIME_RFC1123_SUPPORTED */
}
}
#endif
#if defined(PNG_tRNS_SUPPORTED)
{
int num_trans;
&trans_values))
{
}
}
#endif
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
{
&unknowns);
if (num_unknowns)
{
png_size_t i;
/* copy the locations from the read_info_ptr. The automatically
generated locations in write_info_ptr are wrong because we
haven't written anything yet */
for (i = 0; i < (png_size_t)num_unknowns; i++)
}
}
#endif
#ifdef PNG_WRITE_SUPPORTED
png_debug(0, "\nWriting info struct\n");
/* If we wanted, we could write info in two steps:
png_write_info_before_PLTE(write_ptr, write_info_ptr);
*/
#endif
#ifdef SINGLE_ROWBUF_ALLOC
png_debug(0, "\nAllocating row buffer...");
#endif /* SINGLE_ROWBUF_ALLOC */
png_debug(0, "Writing row data\n");
#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
defined(PNG_WRITE_INTERLACING_SUPPORTED)
# ifdef PNG_WRITE_SUPPORTED
# endif
#else
num_pass=1;
#endif
#ifdef PNGTEST_TIMING
#endif
{
for (y = 0; y < height; y++)
{
#ifndef SINGLE_ROWBUF_ALLOC
#endif /* !SINGLE_ROWBUF_ALLOC */
#ifdef PNG_WRITE_SUPPORTED
#ifdef PNGTEST_TIMING
#endif
#ifdef PNGTEST_TIMING
#endif
#endif /* PNG_WRITE_SUPPORTED */
#ifndef SINGLE_ROWBUF_ALLOC
#endif /* !SINGLE_ROWBUF_ALLOC */
}
}
#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
#endif
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
#endif
png_debug(0, "Reading and writing end_info data\n");
#if defined(PNG_TEXT_SUPPORTED)
{
int num_text;
{
}
}
#endif
#if defined(PNG_tIME_SUPPORTED)
{
{
#if defined(PNG_TIME_RFC1123_SUPPORTED)
/* we have to use png_strcpy instead of "=" because the string
pointed to by png_convert_to_rfc1123() gets free'ed before
we use it */
#endif /* PNG_TIME_RFC1123_SUPPORTED */
}
}
#endif
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
{
int num_unknowns;
&unknowns);
if (num_unknowns)
{
png_size_t i;
/* copy the locations from the read_info_ptr. The automatically
generated locations in write_end_info_ptr are wrong because we
haven't written the end_info yet */
for (i = 0; i < (png_size_t)num_unknowns; i++)
}
}
#endif
#ifdef PNG_WRITE_SUPPORTED
#endif
#ifdef PNG_EASY_ACCESS_SUPPORTED
if(verbose)
{
}
#endif
png_debug(0, "Destroying data structs\n");
#ifdef SINGLE_ROWBUF_ALLOC
#endif /* SINGLE_ROWBUF_ALLOC */
#ifdef PNG_WRITE_SUPPORTED
#endif
png_debug(0, "Destruction complete.\n");
png_debug(0, "Opening files for comparison\n");
#if defined(_WIN32_WCE)
if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
#else
#endif
{
return (1);
}
#if defined(_WIN32_WCE)
if ((fpout = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
#else
#endif
{
return (1);
}
for(;;)
{
{
if(wrote_question == 0)
{
" Was %s written with the same maximum IDAT chunk size (%d bytes),",
"\n filtering heuristic (libpng default), compression");
" level (zlib default),\n and zlib version (%s)?\n\n",
}
return (0);
}
if (!num_in)
break;
{
if(wrote_question == 0)
{
" Was %s written with the same maximum IDAT chunk size (%d bytes),",
"\n filtering heuristic (libpng default), compression");
" level (zlib default),\n and zlib version (%s)?\n\n",
}
return (0);
}
}
return (0);
}
/* input and output filenames */
#ifdef RISCOS
#else
#endif
int
{
int multiple = 0;
int ierror = 0;
/* Show the version of libpng used in building the library */
/* Show the version of libpng used in building the application */
/* Do some consistency checking on the memory allocation settings, I'm
not sure this matters, but it is nice to know, the first of these
tests should be impossible because of the way the macros are set
in pngconf.h */
#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K)
#endif
/* I think the following can happen. */
#if !defined(MAXSEG_64K) && defined(PNG_MAX_MALLOC_64K)
#endif
{
++ierror;
}
if (argc > 1)
{
{
multiple = 1;
}
{
multiple = 1;
verbose = 1;
}
{
verbose = 1;
}
else
{
}
}
{
"usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n",
" with -m %s is used as a temporary file\n", outname);
exit(1);
}
if (multiple)
{
int i;
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
int allocation_now = current_allocation;
#endif
for (i=2; i<argc; ++i)
{
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
int k;
#endif
int kerror;
if (kerror == 0)
{
#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
#else
#endif
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
for (k=0; k<256; k++)
if(filters_used[k])
k,filters_used[k]);
#endif
#if defined(PNG_TIME_RFC1123_SUPPORTED)
if(tIME_chunk_present != 0)
tIME_chunk_present = 0;
#endif /* PNG_TIME_RFC1123_SUPPORTED */
}
else
{
}
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
if (allocation_now != current_allocation)
if (current_allocation != 0)
{
{
}
}
#endif
}
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
#endif
}
else
{
int i;
for (i=0; i<3; ++i)
{
int kerror;
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
int allocation_now = current_allocation;
#endif
else if(verbose == 0)status_dots_requested = 0;
if(kerror == 0)
{
{
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
int k;
#endif
#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
#else
#endif
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
for (k=0; k<256; k++)
if(filters_used[k])
k,filters_used[k]);
#endif
#if defined(PNG_TIME_RFC1123_SUPPORTED)
if(tIME_chunk_present != 0)
#endif /* PNG_TIME_RFC1123_SUPPORTED */
}
}
else
{
if(verbose == 0 && i != 2)
}
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
if (allocation_now != current_allocation)
if (current_allocation != 0)
{
{
}
}
#endif
}
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
#endif
}
#ifdef PNGTEST_TIMING
t_decode/(float)CLOCKS_PER_SEC);
t_encode/(float)CLOCKS_PER_SEC);
t_misc/(float)CLOCKS_PER_SEC);
#endif
if (ierror == 0)
else
return (int)(ierror != 0);
}
/* Generate a compiler error if there is an old png.h in the search path. */