/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* pngtest.c - a simple test program to test libpng
*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file and, per its terms, should not be removed:
*
* Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 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 code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*
* 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 "zlib.h"
#include "png.h"
/* Copied from pngpriv.h but only used in error messages below. */
#ifndef PNG_ZBUF_SIZE
#endif
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
#ifndef PNG_STDIO_SUPPORTED
#endif
/* Makes pngtest verbose so we can find problems. */
#ifndef PNG_DEBUG
# define PNG_DEBUG 0
#endif
#if PNG_DEBUG > 1
#else
# define pngtest_debug(m) ((void)0)
#endif
#if !PNG_DEBUG
#endif
/* The code uses memcmp and memcpy on large objects (typically row pointers) so
* it is necessary to do soemthing special on certain architectures, note that
* the actual support for this was effectively removed in 1.4, so only the
* memory remains in this program:
*/
/* Turn on CPU timing
#define PNGTEST_TIMING
*/
#ifndef PNG_FLOATING_POINT_SUPPORTED
#endif
#ifdef PNGTEST_TIMING
#include <time.h>
#endif
#ifdef PNG_TIME_RFC1123_SUPPORTED
static int tIME_chunk_present = 0;
#endif
static int verbose = 0;
#ifdef __TURBOC__
#include <mem.h>
#endif
/* #define STDERR stderr */
/* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */
#ifndef png_jmpbuf
#endif
/* Example of using row callbacks to make a simple progress meter */
static int status_dots_requested = 0;
void PNGCBAPI
void PNGCBAPI
{
return;
if (status_pass != pass)
{
status_pass = pass;
status_dots = 31;
}
status_dots--;
if (status_dots == 0)
{
status_dots=30;
}
}
void PNGCBAPI
void PNGCBAPI
{
return;
}
/* 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 PNGCBAPI
void PNGCBAPI
{
}
#endif
/* Example of using user transform callback (we don't transform anything,
* but merely count the zero samples)
*/
void PNGCBAPI
void PNGCBAPI
{
return;
/* 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;
{
{
zero_samples++;
if (pos == 8)
{
pos = 0;
dp++;
}
}
{
zero_samples++;
if (pos == 8)
{
pos = 0;
dp++;
}
}
{
zero_samples++;
if (pos == 8)
{
pos = 0;
dp++;
}
}
if (*dp++ == 0)
zero_samples++;
{
zero_samples++;
dp+=2;
}
}
}
else /* Other color types */
{
int channel;
{
{
if (*dp++ == 0)
zero_samples++;
{
zero_samples++;
dp+=2;
}
}
{
dp++;
dp++;
}
}
}
}
#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */
static int wrote_question = 0;
#ifndef PNG_STDIO_SUPPORTED
/* 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.
*/
#ifdef PNG_IO_STATE_SUPPORTED
void
void
{
int err = 0;
/* Check if the current operation (reading / writing) is as expected. */
/* Check if the buffer size specific to the current location
* (file signature / header / data / crc) is as expected.
*/
switch (io_state & PNG_IO_MASK_LOC)
{
case PNG_IO_SIGNATURE:
if (data_length > 8)
err = 1;
break;
case PNG_IO_CHUNK_HDR:
if (data_length != 8)
err = 1;
break;
case PNG_IO_CHUNK_DATA:
break; /* no restrictions here */
case PNG_IO_CHUNK_CRC:
if (data_length != 4)
err = 1;
break;
default:
}
if (err)
}
#endif
#ifndef USE_FAR_KEYWORD
static void PNGCBAPI
{
/* 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.
*/
{
}
{
}
#ifdef PNG_IO_STATE_SUPPORTED
#endif
}
#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 MIN(a,b) (a <= b ? a : b)
static void PNGCBAPI
{
/* Check if data really is near. If so, use usual code. */
{
}
else
{
check = 0;
do
{
break;
else
}
while (remaining != 0);
}
#ifdef PNG_IO_STATE_SUPPORTED
#endif
}
#endif /* USE_FAR_KEYWORD */
#ifdef PNG_WRITE_FLUSH_SUPPORTED
static void PNGCBAPI
{
/* Do nothing; fflush() is said to be just a waste of energy. */
}
#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 PNGCBAPI
{
{
}
#ifdef PNG_IO_STATE_SUPPORTED
#endif
}
#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 MIN(a,b) (a <= b ? a : b)
static void PNGCBAPI
{
/* Check if data really is near. If so, use usual code. */
{
}
else
{
check = 0;
do
{
break;
else
}
while (remaining != 0);
}
{
}
#ifdef PNG_IO_STATE_SUPPORTED
#endif
}
#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 PNGCBAPI
{
char *test;
else
}
/* 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 PNGCBAPI
{
/* We can return because png_error calls the default handler, which is
* actually OK in this case.
*/
}
#endif /* !PNG_STDIO_SUPPORTED */
/* 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 */
png_sizeof(*pinfo));
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 PNGCBAPI
{
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 */
/* Demonstration of user chunk support of the sTER and vpAg chunks */
#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
/* (sTER is a public chunk not yet known by libpng. vpAg is a private
chunk used in ImageMagick to store "virtual page" size). */
/* 0: sTER mode + 1
* 1: vpAg width
* 2: vpAg height
* 3: vpAg units
*/
{
/* Return one of the following:
* return (-n); chunk had an error
* return (0); did not recognize
* return (n); success
*
* The unknown chunk structure contains the chunk data:
* png_byte name[5];
* png_byte *data;
* png_size_t size;
*
* Note that libpng has already taken care of the CRC handling.
*/
{
/* Found sTER chunk */
return (-1); /* Error return */
return (-1); /* Invalid mode */
return (1);
}
return (0); /* Did not recognize */
/* Found ImageMagick vpAg chunk */
return (-1); /* Error return */
return (1);
}
#endif
/* END of code to demonstrate user chunk support */
/* Test one file */
int
{
#ifdef PNG_WRITE_SUPPORTED
#else
#endif
png_uint_32 y;
#ifdef PNG_SETJMP_SUPPORTED
#ifdef USE_FAR_KEYWORD
#endif
#endif
{
return (1);
}
{
return (1);
}
pngtest_debug("Allocating read and write structures");
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
read_ptr =
#else
read_ptr =
#endif
#ifndef PNG_STDIO_SUPPORTED
#endif
#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
user_chunk_data[0] = 0;
user_chunk_data[1] = 0;
user_chunk_data[2] = 0;
user_chunk_data[3] = 0;
#endif
#ifdef PNG_WRITE_SUPPORTED
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
#else
#endif
#ifndef PNG_STDIO_SUPPORTED
#endif
#endif
pngtest_debug("Allocating read_info, write_info and end_info structures");
#ifdef PNG_WRITE_SUPPORTED
#endif
#ifdef PNG_SETJMP_SUPPORTED
pngtest_debug("Setting jmpbuf for read struct");
#ifdef USE_FAR_KEYWORD
if (setjmp(tmp_jmpbuf))
#else
#endif
{
#ifdef PNG_WRITE_SUPPORTED
#endif
return (1);
}
#ifdef USE_FAR_KEYWORD
#endif
#ifdef PNG_WRITE_SUPPORTED
pngtest_debug("Setting jmpbuf for write struct");
#ifdef USE_FAR_KEYWORD
if (setjmp(tmp_jmpbuf))
#else
#endif
{
#ifdef PNG_WRITE_SUPPORTED
#endif
return (1);
}
#ifdef USE_FAR_KEYWORD
#endif
#endif
#endif
pngtest_debug("Initializing input and output streams");
#ifdef PNG_STDIO_SUPPORTED
# ifdef PNG_WRITE_SUPPORTED
# endif
#else
# ifdef PNG_WRITE_SUPPORTED
# ifdef PNG_WRITE_FLUSH_SUPPORTED
# else
NULL);
# endif
# endif
#endif
/* Normally one would use Z_DEFAULT_STRATEGY for text compression.
* This is here just to make pngtest replicate the results from libpng
* versions prior to 1.5.4, and to test this new API.
*/
#endif
if (status_dots_requested == 1)
{
#ifdef PNG_WRITE_SUPPORTED
#endif
}
else
{
#ifdef PNG_WRITE_SUPPORTED
#endif
}
{
int i;
for (i = 0; i<256; i++)
filters_used[i] = 0;
}
#endif
zero_samples = 0;
#endif
# ifndef PNG_HANDLE_CHUNK_ALWAYS
# endif
NULL, 0);
#endif
# ifndef PNG_HANDLE_CHUNK_IF_SAFE
# endif
NULL, 0);
#endif
pngtest_debug("Reading info struct");
pngtest_debug("Transferring info struct");
{
{
#else
#endif
}
}
#ifdef PNG_FIXED_POINT_SUPPORTED
#ifdef PNG_cHRM_SUPPORTED
{
{
}
}
#endif
#ifdef PNG_gAMA_SUPPORTED
{
}
#endif
#else /* Use floating point versions */
#ifdef PNG_FLOATING_POINT_SUPPORTED
#ifdef PNG_cHRM_SUPPORTED
{
{
}
}
#endif
#ifdef PNG_gAMA_SUPPORTED
{
double gamma;
}
#endif
#endif /* Floating point */
#endif /* Fixed point */
#ifdef PNG_iCCP_SUPPORTED
{
int compression_type;
{
}
}
#endif
#ifdef PNG_sRGB_SUPPORTED
{
int intent;
}
#endif
{
int num_palette;
}
#ifdef PNG_bKGD_SUPPORTED
{
{
}
}
#endif
#ifdef PNG_hIST_SUPPORTED
{
}
#endif
#ifdef PNG_oFFs_SUPPORTED
{
int unit_type;
&unit_type))
{
}
}
#endif
#ifdef PNG_pCAL_SUPPORTED
{
{
}
}
#endif
#ifdef PNG_pHYs_SUPPORTED
{
int unit_type;
}
#endif
#ifdef PNG_sBIT_SUPPORTED
{
}
#endif
#ifdef PNG_sCAL_SUPPORTED
#ifdef PNG_FLOATING_POINT_SUPPORTED
{
int unit;
&scal_height))
{
}
}
#else
#ifdef PNG_FIXED_POINT_SUPPORTED
{
int unit;
&scal_height))
{
}
}
#endif
#endif
#endif
#ifdef PNG_TEXT_SUPPORTED
{
int num_text;
{
}
}
#endif
#ifdef PNG_tIME_SUPPORTED
{
{
#ifdef PNG_TIME_RFC1123_SUPPORTED
/* We have to use png_memcpy 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
#ifdef PNG_tRNS_SUPPORTED
{
int num_trans;
&trans_color))
{
/* libpng doesn't reject a tRNS chunk with out-of-range samples */
if (!((color_type == PNG_COLOR_TYPE_GRAY &&
(color_type == PNG_COLOR_TYPE_RGB &&
}
}
#endif
{
&unknowns);
if (num_unknowns)
{
int 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 < num_unknowns; i++)
}
}
#endif
#ifdef PNG_WRITE_SUPPORTED
pngtest_debug("Writing info struct");
/* If we wanted, we could write info in two steps:
* png_write_info_before_PLTE(write_ptr, write_info_ptr);
*/
#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
if (user_chunk_data[0] != 0)
{
unsigned char
if (verbose)
(unsigned long)(user_chunk_data[0] - 1));
}
{
unsigned char
if (verbose)
(unsigned long)user_chunk_data[1],
(unsigned long)user_chunk_data[2],
(unsigned long)user_chunk_data[3]);
}
#endif
#endif
#ifdef SINGLE_ROWBUF_ALLOC
pngtest_debug("Allocating row buffer...");
#endif /* SINGLE_ROWBUF_ALLOC */
pngtest_debug("Writing row data");
#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 */
}
}
#endif
#endif
pngtest_debug("Reading and writing end_info data");
#ifdef PNG_TEXT_SUPPORTED
{
int num_text;
{
}
}
#endif
#ifdef PNG_tIME_SUPPORTED
{
{
#ifdef PNG_TIME_RFC1123_SUPPORTED
/* We have to use png_memcpy 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
{
&unknowns);
if (num_unknowns)
{
int 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 < num_unknowns; i++)
}
}
#endif
#ifdef PNG_WRITE_SUPPORTED
#endif
#ifdef PNG_EASY_ACCESS_SUPPORTED
if (verbose)
{
}
#endif
pngtest_debug("Destroying data structs");
#ifdef SINGLE_ROWBUF_ALLOC
pngtest_debug("destroying row_buf for read_ptr");
#endif /* SINGLE_ROWBUF_ALLOC */
pngtest_debug("destroying read_ptr, read_info_ptr, end_info_ptr");
#ifdef PNG_WRITE_SUPPORTED
pngtest_debug("destroying write_end_info_ptr");
pngtest_debug("destroying write_ptr, write_info_ptr");
#endif
pngtest_debug("Destruction complete.");
pngtest_debug("Opening files for comparison");
{
return (1);
}
{
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",
wrote_question = 1;
}
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",
wrote_question = 1;
}
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 */
(unsigned long)png_access_version_number(),
/* 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
#endif
for (i=2; i<argc; ++i)
{
int kerror;
if (kerror == 0)
{
int k;
#endif
(unsigned long)zero_samples);
#else
#endif
for (k = 0; k<256; k++)
if (filters_used[k])
k, (unsigned long)filters_used[k]);
#endif
#ifdef 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
#endif
if (i == 1)
else if (verbose == 0)
if (kerror == 0)
{
{
int k;
#endif
(unsigned long)zero_samples);
#else
#endif
for (k = 0; k<256; k++)
if (filters_used[k])
k, (unsigned long)filters_used[k]);
#endif
#ifdef 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. */