1
2/* pngtest.c - a simple test program to test libpng
3 *
4 * Last changed in libpng 1.6.9 [February 6, 2014]
5 * Copyright (c) 1998-2014 Glenn Randers-Pehrson
6 * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
7 * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
8 *
9 * This code is released under the libpng license.
10 * For conditions of distribution and use, see the disclaimer
11 * and license in png.h
12 *
13 * This program reads in a PNG image, writes it out again, and then
14 * compares the two files.  If the files are identical, this shows that
15 * the basic chunk handling, filtering, and (de)compression code is working
16 * properly.  It does not currently test all of the transforms, although
17 * it probably should.
18 *
19 * The program will report "FAIL" in certain legitimate cases:
20 * 1) when the compression level or filter selection method is changed.
21 * 2) when the maximum IDAT size (PNG_ZBUF_SIZE in pngconf.h) is not 8192.
22 * 3) unknown unsafe-to-copy ancillary chunks or unknown critical chunks
23 *    exist in the input file.
24 * 4) others not listed here...
25 * In these cases, it is best to check with another tool such as "pngcheck"
26 * to see what the differences between the two files are.
27 *
28 * If a filename is given on the command-line, then this file is used
29 * for the input, rather than the default "pngtest.png".  This allows
30 * testing a wide variety of files easily.  You can also test a number
31 * of files at once by typing "pngtest -m file1.png file2.png ..."
32 */
33
34#define _POSIX_SOURCE 1
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39
40/* Defined so I can write to a file on gui/windowing platforms */
41/*  #define STDERR stderr  */
42#define STDERR stdout   /* For DOS */
43
44#include "png.h"
45
46/* Known chunks that exist in pngtest.png must be supported or pngtest will fail
47 * simply as a result of re-ordering them.  This may be fixed in 1.7
48 *
49 * pngtest allocates a single row buffer for each row and overwrites it,
50 * therefore if the write side doesn't support the writing of interlaced images
51 * nothing can be done for an interlaced image (and the code below will fail
52 * horribly trying to write extra data after writing garbage).
53 */
54#if defined PNG_READ_SUPPORTED && /* else nothing can be done */\
55   defined PNG_READ_bKGD_SUPPORTED &&\
56   defined PNG_READ_cHRM_SUPPORTED &&\
57   defined PNG_READ_gAMA_SUPPORTED &&\
58   defined PNG_READ_oFFs_SUPPORTED &&\
59   defined PNG_READ_pCAL_SUPPORTED &&\
60   defined PNG_READ_pHYs_SUPPORTED &&\
61   defined PNG_READ_sBIT_SUPPORTED &&\
62   defined PNG_READ_sCAL_SUPPORTED &&\
63   defined PNG_READ_sRGB_SUPPORTED &&\
64   defined PNG_READ_tEXt_SUPPORTED &&\
65   defined PNG_READ_tIME_SUPPORTED &&\
66   defined PNG_READ_zTXt_SUPPORTED &&\
67   defined PNG_WRITE_INTERLACING_SUPPORTED
68
69#ifdef PNG_ZLIB_HEADER
70#  include PNG_ZLIB_HEADER /* defined by pnglibconf.h from 1.7 */
71#else
72#  include "zlib.h"
73#endif
74
75/* Copied from pngpriv.h but only used in error messages below. */
76#ifndef PNG_ZBUF_SIZE
77#  define PNG_ZBUF_SIZE 8192
78#endif
79#define FCLOSE(file) fclose(file)
80
81#ifndef PNG_STDIO_SUPPORTED
82typedef FILE                * png_FILE_p;
83#endif
84
85/* Makes pngtest verbose so we can find problems. */
86#ifndef PNG_DEBUG
87#  define PNG_DEBUG 0
88#endif
89
90#if PNG_DEBUG > 1
91#  define pngtest_debug(m)        ((void)fprintf(stderr, m "\n"))
92#  define pngtest_debug1(m,p1)    ((void)fprintf(stderr, m "\n", p1))
93#  define pngtest_debug2(m,p1,p2) ((void)fprintf(stderr, m "\n", p1, p2))
94#else
95#  define pngtest_debug(m)        ((void)0)
96#  define pngtest_debug1(m,p1)    ((void)0)
97#  define pngtest_debug2(m,p1,p2) ((void)0)
98#endif
99
100#if !PNG_DEBUG
101#  define SINGLE_ROWBUF_ALLOC  /* Makes buffer overruns easier to nail */
102#endif
103
104/* Turn on CPU timing
105#define PNGTEST_TIMING
106*/
107
108#ifndef PNG_FLOATING_POINT_SUPPORTED
109#undef PNGTEST_TIMING
110#endif
111
112#ifdef PNGTEST_TIMING
113static float t_start, t_stop, t_decode, t_encode, t_misc;
114#include <time.h>
115#endif
116
117#ifdef PNG_TIME_RFC1123_SUPPORTED
118#define PNG_tIME_STRING_LENGTH 29
119static int tIME_chunk_present = 0;
120static char tIME_string[PNG_tIME_STRING_LENGTH] = "tIME chunk is not present";
121#endif
122
123static int verbose = 0;
124static int strict = 0;
125static int relaxed = 0;
126static int unsupported_chunks = 0; /* chunk unsupported by libpng in input */
127static int error_count = 0; /* count calls to png_error */
128static int warning_count = 0; /* count calls to png_warning */
129
130/* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */
131#ifndef png_jmpbuf
132#  define png_jmpbuf(png_ptr) png_ptr->jmpbuf
133#endif
134
135/* Defines for unknown chunk handling if required. */
136#ifndef PNG_HANDLE_CHUNK_ALWAYS
137#  define PNG_HANDLE_CHUNK_ALWAYS       3
138#endif
139#ifndef PNG_HANDLE_CHUNK_IF_SAFE
140#  define PNG_HANDLE_CHUNK_IF_SAFE      2
141#endif
142
143/* Utility to save typing/errors, the argument must be a name */
144#define MEMZERO(var) ((void)memset(&var, 0, sizeof var))
145
146/* Example of using row callbacks to make a simple progress meter */
147static int status_pass = 1;
148static int status_dots_requested = 0;
149static int status_dots = 1;
150
151static void PNGCBAPI
152read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass)
153{
154   if (png_ptr == NULL || row_number > PNG_UINT_31_MAX)
155      return;
156
157   if (status_pass != pass)
158   {
159      fprintf(stdout, "\n Pass %d: ", pass);
160      status_pass = pass;
161      status_dots = 31;
162   }
163
164   status_dots--;
165
166   if (status_dots == 0)
167   {
168      fprintf(stdout, "\n         ");
169      status_dots=30;
170   }
171
172   fprintf(stdout, "r");
173}
174
175#ifdef PNG_WRITE_SUPPORTED
176static void PNGCBAPI
177write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass)
178{
179   if (png_ptr == NULL || row_number > PNG_UINT_31_MAX || pass > 7)
180      return;
181
182   fprintf(stdout, "w");
183}
184#endif
185
186
187#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
188/* Example of using user transform callback (we don't transform anything,
189 * but merely examine the row filters.  We set this to 256 rather than
190 * 5 in case illegal filter values are present.)
191 */
192static png_uint_32 filters_used[256];
193static void PNGCBAPI
194count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data)
195{
196   if (png_ptr != NULL && row_info != NULL)
197      ++filters_used[*(data - 1)];
198}
199#endif
200
201#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
202/* Example of using user transform callback (we don't transform anything,
203 * but merely count the zero samples)
204 */
205
206static png_uint_32 zero_samples;
207
208static void PNGCBAPI
209count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data)
210{
211   png_bytep dp = data;
212   if (png_ptr == NULL)
213      return;
214
215   /* Contents of row_info:
216    *  png_uint_32 width      width of row
217    *  png_uint_32 rowbytes   number of bytes in row
218    *  png_byte color_type    color type of pixels
219    *  png_byte bit_depth     bit depth of samples
220    *  png_byte channels      number of channels (1-4)
221    *  png_byte pixel_depth   bits per pixel (depth*channels)
222    */
223
224    /* Counts the number of zero samples (or zero pixels if color_type is 3 */
225
226    if (row_info->color_type == 0 || row_info->color_type == 3)
227    {
228       int pos = 0;
229       png_uint_32 n, nstop;
230
231       for (n = 0, nstop=row_info->width; n<nstop; n++)
232       {
233          if (row_info->bit_depth == 1)
234          {
235             if (((*dp << pos++ ) & 0x80) == 0)
236                zero_samples++;
237
238             if (pos == 8)
239             {
240                pos = 0;
241                dp++;
242             }
243          }
244
245          if (row_info->bit_depth == 2)
246          {
247             if (((*dp << (pos+=2)) & 0xc0) == 0)
248                zero_samples++;
249
250             if (pos == 8)
251             {
252                pos = 0;
253                dp++;
254             }
255          }
256
257          if (row_info->bit_depth == 4)
258          {
259             if (((*dp << (pos+=4)) & 0xf0) == 0)
260                zero_samples++;
261
262             if (pos == 8)
263             {
264                pos = 0;
265                dp++;
266             }
267          }
268
269          if (row_info->bit_depth == 8)
270             if (*dp++ == 0)
271                zero_samples++;
272
273          if (row_info->bit_depth == 16)
274          {
275             if ((*dp | *(dp+1)) == 0)
276                zero_samples++;
277             dp+=2;
278          }
279       }
280    }
281    else /* Other color types */
282    {
283       png_uint_32 n, nstop;
284       int channel;
285       int color_channels = row_info->channels;
286       if (row_info->color_type > 3)color_channels--;
287
288       for (n = 0, nstop=row_info->width; n<nstop; n++)
289       {
290          for (channel = 0; channel < color_channels; channel++)
291          {
292             if (row_info->bit_depth == 8)
293                if (*dp++ == 0)
294                   zero_samples++;
295
296             if (row_info->bit_depth == 16)
297             {
298                if ((*dp | *(dp+1)) == 0)
299                   zero_samples++;
300
301                dp+=2;
302             }
303          }
304          if (row_info->color_type > 3)
305          {
306             dp++;
307             if (row_info->bit_depth == 16)
308                dp++;
309          }
310       }
311    }
312}
313#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */
314
315#ifndef PNG_STDIO_SUPPORTED
316/* START of code to validate stdio-free compilation */
317/* These copies of the default read/write functions come from pngrio.c and
318 * pngwio.c.  They allow "don't include stdio" testing of the library.
319 * This is the function that does the actual reading of data.  If you are
320 * not reading from a standard C stream, you should create a replacement
321 * read_data function and use it at run time with png_set_read_fn(), rather
322 * than changing the library.
323 */
324
325#ifdef PNG_IO_STATE_SUPPORTED
326void
327pngtest_check_io_state(png_structp png_ptr, png_size_t data_length,
328   png_uint_32 io_op);
329void
330pngtest_check_io_state(png_structp png_ptr, png_size_t data_length,
331   png_uint_32 io_op)
332{
333   png_uint_32 io_state = png_get_io_state(png_ptr);
334   int err = 0;
335
336   /* Check if the current operation (reading / writing) is as expected. */
337   if ((io_state & PNG_IO_MASK_OP) != io_op)
338      png_error(png_ptr, "Incorrect operation in I/O state");
339
340   /* Check if the buffer size specific to the current location
341    * (file signature / header / data / crc) is as expected.
342    */
343   switch (io_state & PNG_IO_MASK_LOC)
344   {
345   case PNG_IO_SIGNATURE:
346      if (data_length > 8)
347         err = 1;
348      break;
349   case PNG_IO_CHUNK_HDR:
350      if (data_length != 8)
351         err = 1;
352      break;
353   case PNG_IO_CHUNK_DATA:
354      break;  /* no restrictions here */
355   case PNG_IO_CHUNK_CRC:
356      if (data_length != 4)
357         err = 1;
358      break;
359   default:
360      err = 1;  /* uninitialized */
361   }
362   if (err)
363      png_error(png_ptr, "Bad I/O state or buffer size");
364}
365#endif
366
367static void PNGCBAPI
368pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
369{
370   png_size_t check = 0;
371   png_voidp io_ptr;
372
373   /* fread() returns 0 on error, so it is OK to store this in a png_size_t
374    * instead of an int, which is what fread() actually returns.
375    */
376   io_ptr = png_get_io_ptr(png_ptr);
377   if (io_ptr != NULL)
378   {
379      check = fread(data, 1, length, (png_FILE_p)io_ptr);
380   }
381
382   if (check != length)
383   {
384      png_error(png_ptr, "Read Error");
385   }
386
387#ifdef PNG_IO_STATE_SUPPORTED
388   pngtest_check_io_state(png_ptr, length, PNG_IO_READING);
389#endif
390}
391
392#ifdef PNG_WRITE_FLUSH_SUPPORTED
393static void PNGCBAPI
394pngtest_flush(png_structp png_ptr)
395{
396   /* Do nothing; fflush() is said to be just a waste of energy. */
397   PNG_UNUSED(png_ptr)   /* Stifle compiler warning */
398}
399#endif
400
401/* This is the function that does the actual writing of data.  If you are
402 * not writing to a standard C stream, you should create a replacement
403 * write_data function and use it at run time with png_set_write_fn(), rather
404 * than changing the library.
405 */
406static void PNGCBAPI
407pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
408{
409   png_size_t check;
410
411   check = fwrite(data, 1, length, (png_FILE_p)png_get_io_ptr(png_ptr));
412
413   if (check != length)
414   {
415      png_error(png_ptr, "Write Error");
416   }
417
418#ifdef PNG_IO_STATE_SUPPORTED
419   pngtest_check_io_state(png_ptr, length, PNG_IO_WRITING);
420#endif
421}
422#endif /* !PNG_STDIO_SUPPORTED */
423
424/* This function is called when there is a warning, but the library thinks
425 * it can continue anyway.  Replacement functions don't have to do anything
426 * here if you don't want to.  In the default configuration, png_ptr is
427 * not used, but it is passed in case it may be useful.
428 */
429typedef struct
430{
431   PNG_CONST char *file_name;
432}  pngtest_error_parameters;
433
434static void PNGCBAPI
435pngtest_warning(png_structp png_ptr, png_const_charp message)
436{
437   PNG_CONST char *name = "UNKNOWN (ERROR!)";
438   pngtest_error_parameters *test =
439      (pngtest_error_parameters*)png_get_error_ptr(png_ptr);
440
441   ++warning_count;
442
443   if (test != NULL && test->file_name != NULL)
444      name = test->file_name;
445
446   fprintf(STDERR, "%s: libpng warning: %s\n", name, message);
447}
448
449/* This is the default error handling function.  Note that replacements for
450 * this function MUST NOT RETURN, or the program will likely crash.  This
451 * function is used by default, or if the program supplies NULL for the
452 * error function pointer in png_set_error_fn().
453 */
454static void PNGCBAPI
455pngtest_error(png_structp png_ptr, png_const_charp message)
456{
457   ++error_count;
458
459   pngtest_warning(png_ptr, message);
460   /* We can return because png_error calls the default handler, which is
461    * actually OK in this case.
462    */
463}
464
465/* END of code to validate stdio-free compilation */
466
467/* START of code to validate memory allocation and deallocation */
468#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
469
470/* Allocate memory.  For reasonable files, size should never exceed
471 * 64K.  However, zlib may allocate more then 64K if you don't tell
472 * it not to.  See zconf.h and png.h for more information.  zlib does
473 * need to allocate exactly 64K, so whatever you call here must
474 * have the ability to do that.
475 *
476 * This piece of code can be compiled to validate max 64K allocations
477 * by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K.
478 */
479typedef struct memory_information
480{
481   png_alloc_size_t          size;
482   png_voidp                 pointer;
483   struct memory_information *next;
484} memory_information;
485typedef memory_information *memory_infop;
486
487static memory_infop pinformation = NULL;
488static int current_allocation = 0;
489static int maximum_allocation = 0;
490static int total_allocation = 0;
491static int num_allocations = 0;
492
493png_voidp PNGCBAPI png_debug_malloc PNGARG((png_structp png_ptr,
494    png_alloc_size_t size));
495void PNGCBAPI png_debug_free PNGARG((png_structp png_ptr, png_voidp ptr));
496
497png_voidp
498PNGCBAPI png_debug_malloc(png_structp png_ptr, png_alloc_size_t size)
499{
500
501   /* png_malloc has already tested for NULL; png_create_struct calls
502    * png_debug_malloc directly, with png_ptr == NULL which is OK
503    */
504
505   if (size == 0)
506      return (NULL);
507
508   /* This calls the library allocator twice, once to get the requested
509      buffer and once to get a new free list entry. */
510   {
511      /* Disable malloc_fn and free_fn */
512      memory_infop pinfo;
513      png_set_mem_fn(png_ptr, NULL, NULL, NULL);
514      pinfo = (memory_infop)png_malloc(png_ptr,
515         (sizeof *pinfo));
516      pinfo->size = size;
517      current_allocation += size;
518      total_allocation += size;
519      num_allocations ++;
520
521      if (current_allocation > maximum_allocation)
522         maximum_allocation = current_allocation;
523
524      pinfo->pointer = png_malloc(png_ptr, size);
525      /* Restore malloc_fn and free_fn */
526
527      png_set_mem_fn(png_ptr,
528          NULL, png_debug_malloc, png_debug_free);
529
530      if (size != 0 && pinfo->pointer == NULL)
531      {
532         current_allocation -= size;
533         total_allocation -= size;
534         png_error(png_ptr,
535           "out of memory in pngtest->png_debug_malloc");
536      }
537
538      pinfo->next = pinformation;
539      pinformation = pinfo;
540      /* Make sure the caller isn't assuming zeroed memory. */
541      memset(pinfo->pointer, 0xdd, pinfo->size);
542
543      if (verbose)
544         printf("png_malloc %lu bytes at %p\n", (unsigned long)size,
545            pinfo->pointer);
546
547      return (png_voidp)(pinfo->pointer);
548   }
549}
550
551/* Free a pointer.  It is removed from the list at the same time. */
552void PNGCBAPI
553png_debug_free(png_structp png_ptr, png_voidp ptr)
554{
555   if (png_ptr == NULL)
556      fprintf(STDERR, "NULL pointer to png_debug_free.\n");
557
558   if (ptr == 0)
559   {
560#if 0 /* This happens all the time. */
561      fprintf(STDERR, "WARNING: freeing NULL pointer\n");
562#endif
563      return;
564   }
565
566   /* Unlink the element from the list. */
567   {
568      memory_infop *ppinfo = &pinformation;
569
570      for (;;)
571      {
572         memory_infop pinfo = *ppinfo;
573
574         if (pinfo->pointer == ptr)
575         {
576            *ppinfo = pinfo->next;
577            current_allocation -= pinfo->size;
578            if (current_allocation < 0)
579               fprintf(STDERR, "Duplicate free of memory\n");
580            /* We must free the list element too, but first kill
581               the memory that is to be freed. */
582            memset(ptr, 0x55, pinfo->size);
583            if (pinfo)
584               free(pinfo);
585            pinfo = NULL;
586            break;
587         }
588
589         if (pinfo->next == NULL)
590         {
591            fprintf(STDERR, "Pointer %x not found\n", (unsigned int)ptr);
592            break;
593         }
594
595         ppinfo = &pinfo->next;
596      }
597   }
598
599   /* Finally free the data. */
600   if (verbose)
601      printf("Freeing %p\n", ptr);
602
603   if (ptr)
604      free(ptr);
605   ptr = NULL;
606}
607#endif /* PNG_USER_MEM_SUPPORTED && PNG_DEBUG */
608/* END of code to test memory allocation/deallocation */
609
610
611#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
612/* Demonstration of user chunk support of the sTER and vpAg chunks */
613
614/* (sTER is a public chunk not yet known by libpng.  vpAg is a private
615chunk used in ImageMagick to store "virtual page" size).  */
616
617static struct user_chunk_data
618{
619   png_const_infop info_ptr;
620   png_uint_32     vpAg_width, vpAg_height;
621   png_byte        vpAg_units;
622   png_byte        sTER_mode;
623   int             location[2];
624}
625user_chunk_data;
626
627/* Used for location and order; zero means nothing. */
628#define have_sTER   0x01
629#define have_vpAg   0x02
630#define before_PLTE 0x10
631#define before_IDAT 0x20
632#define after_IDAT  0x40
633
634static void
635init_callback_info(png_const_infop info_ptr)
636{
637   MEMZERO(user_chunk_data);
638   user_chunk_data.info_ptr = info_ptr;
639}
640
641static int
642set_location(png_structp png_ptr, struct user_chunk_data *data, int what)
643{
644   int location;
645
646   if ((data->location[0] & what) || (data->location[1] & what))
647      return 0; /* already have one of these */
648
649   /* Find where we are (the code below zeros info_ptr to indicate that the
650    * chunks before the first IDAT have been read.)
651    */
652   if (data->info_ptr == NULL) /* after IDAT */
653      location = what | after_IDAT;
654
655   else if (png_get_valid(png_ptr, data->info_ptr, PNG_INFO_PLTE))
656      location = what | before_IDAT;
657
658   else
659      location = what | before_PLTE;
660
661   if (data->location[0] == 0)
662      data->location[0] = location;
663
664   else
665      data->location[1] = location;
666
667   return 1; /* handled */
668}
669
670static int PNGCBAPI
671read_user_chunk_callback(png_struct *png_ptr, png_unknown_chunkp chunk)
672{
673   struct user_chunk_data *my_user_chunk_data =
674      (struct user_chunk_data*)png_get_user_chunk_ptr(png_ptr);
675
676   if (my_user_chunk_data == NULL)
677      png_error(png_ptr, "lost user chunk pointer");
678
679   /* Return one of the following:
680    *    return (-n);  chunk had an error
681    *    return (0);  did not recognize
682    *    return (n);  success
683    *
684    * The unknown chunk structure contains the chunk data:
685    * png_byte name[5];
686    * png_byte *data;
687    * png_size_t size;
688    *
689    * Note that libpng has already taken care of the CRC handling.
690    */
691
692   if (chunk->name[0] == 115 && chunk->name[1] ==  84 &&     /* s  T */
693       chunk->name[2] ==  69 && chunk->name[3] ==  82)       /* E  R */
694      {
695         /* Found sTER chunk */
696         if (chunk->size != 1)
697            return (-1); /* Error return */
698
699         if (chunk->data[0] != 0 && chunk->data[0] != 1)
700            return (-1);  /* Invalid mode */
701
702         if (set_location(png_ptr, my_user_chunk_data, have_sTER))
703         {
704            my_user_chunk_data->sTER_mode=chunk->data[0];
705            return (1);
706         }
707
708         else
709            return (0); /* duplicate sTER - give it to libpng */
710      }
711
712   if (chunk->name[0] != 118 || chunk->name[1] != 112 ||    /* v  p */
713       chunk->name[2] !=  65 || chunk->name[3] != 103)      /* A  g */
714      return (0); /* Did not recognize */
715
716   /* Found ImageMagick vpAg chunk */
717
718   if (chunk->size != 9)
719      return (-1); /* Error return */
720
721   if (!set_location(png_ptr, my_user_chunk_data, have_vpAg))
722      return (0);  /* duplicate vpAg */
723
724   my_user_chunk_data->vpAg_width = png_get_uint_31(png_ptr, chunk->data);
725   my_user_chunk_data->vpAg_height = png_get_uint_31(png_ptr, chunk->data + 4);
726   my_user_chunk_data->vpAg_units = chunk->data[8];
727
728   return (1);
729}
730
731#ifdef PNG_WRITE_SUPPORTED
732static void
733write_sTER_chunk(png_structp write_ptr)
734{
735   png_byte sTER[5] = {115,  84,  69,  82, '\0'};
736
737   if (verbose)
738      fprintf(STDERR, "\n stereo mode = %d\n", user_chunk_data.sTER_mode);
739
740   png_write_chunk(write_ptr, sTER, &user_chunk_data.sTER_mode, 1);
741}
742
743static void
744write_vpAg_chunk(png_structp write_ptr)
745{
746   png_byte vpAg[5] = {118, 112,  65, 103, '\0'};
747
748   png_byte vpag_chunk_data[9];
749
750   if (verbose)
751      fprintf(STDERR, " vpAg = %lu x %lu, units = %d\n",
752        (unsigned long)user_chunk_data.vpAg_width,
753        (unsigned long)user_chunk_data.vpAg_height,
754        user_chunk_data.vpAg_units);
755
756   png_save_uint_32(vpag_chunk_data, user_chunk_data.vpAg_width);
757   png_save_uint_32(vpag_chunk_data + 4, user_chunk_data.vpAg_height);
758   vpag_chunk_data[8] = user_chunk_data.vpAg_units;
759   png_write_chunk(write_ptr, vpAg, vpag_chunk_data, 9);
760}
761
762static void
763write_chunks(png_structp write_ptr, int location)
764{
765   int i;
766
767   /* Notice that this preserves the original chunk order, however chunks
768    * intercepted by the callback will be written *after* chunks passed to
769    * libpng.  This will actually reverse a pair of sTER chunks or a pair of
770    * vpAg chunks, resulting in an error later.  This is not worth worrying
771    * about - the chunks should not be duplicated!
772    */
773   for (i=0; i<2; ++i)
774   {
775      if (user_chunk_data.location[i] == (location | have_sTER))
776         write_sTER_chunk(write_ptr);
777
778      else if (user_chunk_data.location[i] == (location | have_vpAg))
779         write_vpAg_chunk(write_ptr);
780   }
781}
782#endif /* PNG_WRITE_SUPPORTED */
783#else /* !PNG_READ_USER_CHUNKS_SUPPORTED */
784#  define write_chunks(pp,loc) ((void)0)
785#endif
786/* END of code to demonstrate user chunk support */
787
788/* START of code to check that libpng has the required text support; this only
789 * checks for the write support because if read support is missing the chunk
790 * will simply not be reported back to pngtest.
791 */
792#ifdef PNG_TEXT_SUPPORTED
793static void
794pngtest_check_text_support(png_const_structp png_ptr, png_textp text_ptr,
795   int num_text)
796{
797   while (num_text > 0)
798   {
799      switch (text_ptr[--num_text].compression)
800      {
801         case PNG_TEXT_COMPRESSION_NONE:
802            break;
803
804         case PNG_TEXT_COMPRESSION_zTXt:
805#           ifndef PNG_WRITE_zTXt_SUPPORTED
806               ++unsupported_chunks;
807#           endif
808            break;
809
810         case PNG_ITXT_COMPRESSION_NONE:
811         case PNG_ITXT_COMPRESSION_zTXt:
812#           ifndef PNG_WRITE_iTXt_SUPPORTED
813               ++unsupported_chunks;
814#           endif
815            break;
816
817         default:
818            /* This is an error */
819            png_error(png_ptr, "invalid text chunk compression field");
820            break;
821      }
822   }
823}
824#endif
825/* END of code to check that libpng has the required text support */
826
827/* Test one file */
828static int
829test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
830{
831   static png_FILE_p fpin;
832   static png_FILE_p fpout;  /* "static" prevents setjmp corruption */
833   pngtest_error_parameters error_parameters;
834   png_structp read_ptr;
835   png_infop read_info_ptr, end_info_ptr;
836#ifdef PNG_WRITE_SUPPORTED
837   png_structp write_ptr;
838   png_infop write_info_ptr;
839   png_infop write_end_info_ptr;
840   int interlace_preserved = 1;
841#else
842   png_structp write_ptr = NULL;
843   png_infop write_info_ptr = NULL;
844   png_infop write_end_info_ptr = NULL;
845#endif
846   png_bytep row_buf;
847   png_uint_32 y;
848   png_uint_32 width, height;
849   int num_pass = 1, pass;
850   int bit_depth, color_type;
851
852   row_buf = NULL;
853   error_parameters.file_name = inname;
854
855   if ((fpin = fopen(inname, "rb")) == NULL)
856   {
857      fprintf(STDERR, "Could not find input file %s\n", inname);
858      return (1);
859   }
860
861   if ((fpout = fopen(outname, "wb")) == NULL)
862   {
863      fprintf(STDERR, "Could not open output file %s\n", outname);
864      FCLOSE(fpin);
865      return (1);
866   }
867
868   pngtest_debug("Allocating read and write structures");
869#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
870   read_ptr =
871      png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL,
872      NULL, NULL, NULL, png_debug_malloc, png_debug_free);
873#else
874   read_ptr =
875      png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
876#endif
877   png_set_error_fn(read_ptr, &error_parameters, pngtest_error,
878      pngtest_warning);
879
880#ifdef PNG_WRITE_SUPPORTED
881#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
882   write_ptr =
883      png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL,
884      NULL, NULL, NULL, png_debug_malloc, png_debug_free);
885#else
886   write_ptr =
887      png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
888#endif
889   png_set_error_fn(write_ptr, &error_parameters, pngtest_error,
890      pngtest_warning);
891#endif
892   pngtest_debug("Allocating read_info, write_info and end_info structures");
893   read_info_ptr = png_create_info_struct(read_ptr);
894   end_info_ptr = png_create_info_struct(read_ptr);
895#ifdef PNG_WRITE_SUPPORTED
896   write_info_ptr = png_create_info_struct(write_ptr);
897   write_end_info_ptr = png_create_info_struct(write_ptr);
898#endif
899
900#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
901   init_callback_info(read_info_ptr);
902   png_set_read_user_chunk_fn(read_ptr, &user_chunk_data,
903     read_user_chunk_callback);
904#endif
905
906#ifdef PNG_SETJMP_SUPPORTED
907   pngtest_debug("Setting jmpbuf for read struct");
908   if (setjmp(png_jmpbuf(read_ptr)))
909   {
910      fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname);
911      png_free(read_ptr, row_buf);
912      row_buf = NULL;
913      png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
914#ifdef PNG_WRITE_SUPPORTED
915      png_destroy_info_struct(write_ptr, &write_end_info_ptr);
916      png_destroy_write_struct(&write_ptr, &write_info_ptr);
917#endif
918      FCLOSE(fpin);
919      FCLOSE(fpout);
920      return (1);
921   }
922
923#ifdef PNG_WRITE_SUPPORTED
924   pngtest_debug("Setting jmpbuf for write struct");
925
926   if (setjmp(png_jmpbuf(write_ptr)))
927   {
928      fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname);
929      png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
930      png_destroy_info_struct(write_ptr, &write_end_info_ptr);
931#ifdef PNG_WRITE_SUPPORTED
932      png_destroy_write_struct(&write_ptr, &write_info_ptr);
933#endif
934      FCLOSE(fpin);
935      FCLOSE(fpout);
936      return (1);
937   }
938#endif
939#endif
940
941   if (strict)
942   {
943      /* Treat png_benign_error() as errors on read */
944      png_set_benign_errors(read_ptr, 0);
945
946#ifdef PNG_WRITE_SUPPORTED
947      /* Treat them as errors on write */
948      png_set_benign_errors(write_ptr, 0);
949#endif
950
951      /* if strict is not set, then app warnings and errors are treated as
952       * warnings in release builds, but not in unstable builds; this can be
953       * changed with '--relaxed'.
954       */
955   }
956
957   else if (relaxed)
958   {
959      /* Allow application (pngtest) errors and warnings to pass */
960      png_set_benign_errors(read_ptr, 1);
961
962#ifdef PNG_WRITE_SUPPORTED
963      png_set_benign_errors(write_ptr, 1);
964#endif
965   }
966
967   pngtest_debug("Initializing input and output streams");
968#ifdef PNG_STDIO_SUPPORTED
969   png_init_io(read_ptr, fpin);
970#  ifdef PNG_WRITE_SUPPORTED
971   png_init_io(write_ptr, fpout);
972#  endif
973#else
974   png_set_read_fn(read_ptr, (png_voidp)fpin, pngtest_read_data);
975#  ifdef PNG_WRITE_SUPPORTED
976   png_set_write_fn(write_ptr, (png_voidp)fpout,  pngtest_write_data,
977#    ifdef PNG_WRITE_FLUSH_SUPPORTED
978      pngtest_flush);
979#    else
980      NULL);
981#    endif
982#  endif
983#endif
984
985   if (status_dots_requested == 1)
986   {
987#ifdef PNG_WRITE_SUPPORTED
988      png_set_write_status_fn(write_ptr, write_row_callback);
989#endif
990      png_set_read_status_fn(read_ptr, read_row_callback);
991   }
992
993   else
994   {
995#ifdef PNG_WRITE_SUPPORTED
996      png_set_write_status_fn(write_ptr, NULL);
997#endif
998      png_set_read_status_fn(read_ptr, NULL);
999   }
1000
1001#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
1002   {
1003      int i;
1004
1005      for (i = 0; i<256; i++)
1006         filters_used[i] = 0;
1007
1008      png_set_read_user_transform_fn(read_ptr, count_filters);
1009   }
1010#endif
1011#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
1012   zero_samples = 0;
1013   png_set_write_user_transform_fn(write_ptr, count_zero_samples);
1014#endif
1015
1016#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
1017   /* Preserve all the unknown chunks, if possible.  If this is disabled then,
1018    * even if the png_{get,set}_unknown_chunks stuff is enabled, we can't use
1019    * libpng to *save* the unknown chunks on read (because we can't switch the
1020    * save option on!)
1021    *
1022    * Notice that if SET_UNKNOWN_CHUNKS is *not* supported read will discard all
1023    * unknown chunks and write will write them all.
1024    */
1025#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
1026   png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS,
1027      NULL, 0);
1028#endif
1029#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
1030   png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
1031      NULL, 0);
1032#endif
1033#endif
1034
1035   pngtest_debug("Reading info struct");
1036   png_read_info(read_ptr, read_info_ptr);
1037
1038#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
1039   /* This is a bit of a hack; there is no obvious way in the callback function
1040    * to determine that the chunks before the first IDAT have been read, so
1041    * remove the info_ptr (which is only used to determine position relative to
1042    * PLTE) here to indicate that we are after the IDAT.
1043    */
1044   user_chunk_data.info_ptr = NULL;
1045#endif
1046
1047   pngtest_debug("Transferring info struct");
1048   {
1049      int interlace_type, compression_type, filter_type;
1050
1051      if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth,
1052          &color_type, &interlace_type, &compression_type, &filter_type))
1053      {
1054         png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth,
1055            color_type, interlace_type, compression_type, filter_type);
1056#ifndef PNG_READ_INTERLACING_SUPPORTED
1057         /* num_pass will not be set below, set it here if the image is
1058          * interlaced: what happens is that write interlacing is *not* turned
1059          * on an the partial interlaced rows are written directly.
1060          */
1061         switch (interlace_type)
1062         {
1063            case PNG_INTERLACE_NONE:
1064               num_pass = 1;
1065               break;
1066
1067            case PNG_INTERLACE_ADAM7:
1068               num_pass = 7;
1069                break;
1070
1071            default:
1072                png_error(read_ptr, "invalid interlace type");
1073                /*NOT REACHED*/
1074         }
1075#endif
1076      }
1077   }
1078#ifdef PNG_FIXED_POINT_SUPPORTED
1079#ifdef PNG_cHRM_SUPPORTED
1080   {
1081      png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x,
1082         blue_y;
1083
1084      if (png_get_cHRM_fixed(read_ptr, read_info_ptr, &white_x, &white_y,
1085         &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y))
1086      {
1087         png_set_cHRM_fixed(write_ptr, write_info_ptr, white_x, white_y, red_x,
1088            red_y, green_x, green_y, blue_x, blue_y);
1089      }
1090   }
1091#endif
1092#ifdef PNG_gAMA_SUPPORTED
1093   {
1094      png_fixed_point gamma;
1095
1096      if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma))
1097         png_set_gAMA_fixed(write_ptr, write_info_ptr, gamma);
1098   }
1099#endif
1100#else /* Use floating point versions */
1101#ifdef PNG_FLOATING_POINT_SUPPORTED
1102#ifdef PNG_cHRM_SUPPORTED
1103   {
1104      double white_x, white_y, red_x, red_y, green_x, green_y, blue_x,
1105         blue_y;
1106
1107      if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x,
1108         &red_y, &green_x, &green_y, &blue_x, &blue_y))
1109      {
1110         png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y, red_x,
1111            red_y, green_x, green_y, blue_x, blue_y);
1112      }
1113   }
1114#endif
1115#ifdef PNG_gAMA_SUPPORTED
1116   {
1117      double gamma;
1118
1119      if (png_get_gAMA(read_ptr, read_info_ptr, &gamma))
1120         png_set_gAMA(write_ptr, write_info_ptr, gamma);
1121   }
1122#endif
1123#endif /* Floating point */
1124#endif /* Fixed point */
1125#ifdef PNG_iCCP_SUPPORTED
1126   {
1127      png_charp name;
1128      png_bytep profile;
1129      png_uint_32 proflen;
1130      int compression_type;
1131
1132      if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_type,
1133                      &profile, &proflen))
1134      {
1135         png_set_iCCP(write_ptr, write_info_ptr, name, compression_type,
1136                      profile, proflen);
1137      }
1138   }
1139#endif
1140#ifdef PNG_sRGB_SUPPORTED
1141   {
1142      int intent;
1143
1144      if (png_get_sRGB(read_ptr, read_info_ptr, &intent))
1145         png_set_sRGB(write_ptr, write_info_ptr, intent);
1146   }
1147#endif
1148   {
1149      png_colorp palette;
1150      int num_palette;
1151
1152      if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette))
1153         png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette);
1154   }
1155#ifdef PNG_bKGD_SUPPORTED
1156   {
1157      png_color_16p background;
1158
1159      if (png_get_bKGD(read_ptr, read_info_ptr, &background))
1160      {
1161         png_set_bKGD(write_ptr, write_info_ptr, background);
1162      }
1163   }
1164#endif
1165#ifdef PNG_hIST_SUPPORTED
1166   {
1167      png_uint_16p hist;
1168
1169      if (png_get_hIST(read_ptr, read_info_ptr, &hist))
1170         png_set_hIST(write_ptr, write_info_ptr, hist);
1171   }
1172#endif
1173#ifdef PNG_oFFs_SUPPORTED
1174   {
1175      png_int_32 offset_x, offset_y;
1176      int unit_type;
1177
1178      if (png_get_oFFs(read_ptr, read_info_ptr, &offset_x, &offset_y,
1179          &unit_type))
1180      {
1181         png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type);
1182      }
1183   }
1184#endif
1185#ifdef PNG_pCAL_SUPPORTED
1186   {
1187      png_charp purpose, units;
1188      png_charpp params;
1189      png_int_32 X0, X1;
1190      int type, nparams;
1191
1192      if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type,
1193         &nparams, &units, &params))
1194      {
1195         png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type,
1196            nparams, units, params);
1197      }
1198   }
1199#endif
1200#ifdef PNG_pHYs_SUPPORTED
1201   {
1202      png_uint_32 res_x, res_y;
1203      int unit_type;
1204
1205      if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, &unit_type))
1206         png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type);
1207   }
1208#endif
1209#ifdef PNG_sBIT_SUPPORTED
1210   {
1211      png_color_8p sig_bit;
1212
1213      if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit))
1214         png_set_sBIT(write_ptr, write_info_ptr, sig_bit);
1215   }
1216#endif
1217#ifdef PNG_sCAL_SUPPORTED
1218#if defined(PNG_FLOATING_POINT_SUPPORTED) && \
1219   defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)
1220   {
1221      int unit;
1222      double scal_width, scal_height;
1223
1224      if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width,
1225         &scal_height))
1226      {
1227         png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height);
1228      }
1229   }
1230#else
1231#ifdef PNG_FIXED_POINT_SUPPORTED
1232   {
1233      int unit;
1234      png_charp scal_width, scal_height;
1235
1236      if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width,
1237          &scal_height))
1238      {
1239         png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width,
1240             scal_height);
1241      }
1242   }
1243#endif
1244#endif
1245#endif
1246#ifdef PNG_TEXT_SUPPORTED
1247   {
1248      png_textp text_ptr;
1249      int num_text;
1250
1251      if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0)
1252      {
1253         pngtest_debug1("Handling %d iTXt/tEXt/zTXt chunks", num_text);
1254
1255         pngtest_check_text_support(read_ptr, text_ptr, num_text);
1256
1257         if (verbose)
1258         {
1259            int i;
1260
1261            printf("\n");
1262            for (i=0; i<num_text; i++)
1263            {
1264               printf("   Text compression[%d]=%d\n",
1265                     i, text_ptr[i].compression);
1266            }
1267         }
1268
1269         png_set_text(write_ptr, write_info_ptr, text_ptr, num_text);
1270      }
1271   }
1272#endif
1273#ifdef PNG_tIME_SUPPORTED
1274   {
1275      png_timep mod_time;
1276
1277      if (png_get_tIME(read_ptr, read_info_ptr, &mod_time))
1278      {
1279         png_set_tIME(write_ptr, write_info_ptr, mod_time);
1280#ifdef PNG_TIME_RFC1123_SUPPORTED
1281         if (png_convert_to_rfc1123_buffer(tIME_string, mod_time))
1282            tIME_string[(sizeof tIME_string) - 1] = '\0';
1283
1284         else
1285         {
1286            strncpy(tIME_string, "*** invalid time ***", (sizeof tIME_string));
1287            tIME_string[(sizeof tIME_string) - 1] = '\0';
1288         }
1289
1290         tIME_chunk_present++;
1291#endif /* PNG_TIME_RFC1123_SUPPORTED */
1292      }
1293   }
1294#endif
1295#ifdef PNG_tRNS_SUPPORTED
1296   {
1297      png_bytep trans_alpha;
1298      int num_trans;
1299      png_color_16p trans_color;
1300
1301      if (png_get_tRNS(read_ptr, read_info_ptr, &trans_alpha, &num_trans,
1302         &trans_color))
1303      {
1304         int sample_max = (1 << bit_depth);
1305         /* libpng doesn't reject a tRNS chunk with out-of-range samples */
1306         if (!((color_type == PNG_COLOR_TYPE_GRAY &&
1307             (int)trans_color->gray > sample_max) ||
1308             (color_type == PNG_COLOR_TYPE_RGB &&
1309             ((int)trans_color->red > sample_max ||
1310             (int)trans_color->green > sample_max ||
1311             (int)trans_color->blue > sample_max))))
1312            png_set_tRNS(write_ptr, write_info_ptr, trans_alpha, num_trans,
1313               trans_color);
1314      }
1315   }
1316#endif
1317#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
1318   {
1319      png_unknown_chunkp unknowns;
1320      int num_unknowns = png_get_unknown_chunks(read_ptr, read_info_ptr,
1321         &unknowns);
1322
1323      if (num_unknowns)
1324      {
1325         png_set_unknown_chunks(write_ptr, write_info_ptr, unknowns,
1326           num_unknowns);
1327#if PNG_LIBPNG_VER < 10600
1328         /* Copy the locations from the read_info_ptr.  The automatically
1329          * generated locations in write_end_info_ptr are wrong prior to 1.6.0
1330          * because they are reset from the write pointer (removed in 1.6.0).
1331          */
1332         {
1333            int i;
1334            for (i = 0; i < num_unknowns; i++)
1335              png_set_unknown_chunk_location(write_ptr, write_info_ptr, i,
1336                unknowns[i].location);
1337         }
1338#endif
1339      }
1340   }
1341#endif
1342
1343#ifdef PNG_WRITE_SUPPORTED
1344   pngtest_debug("Writing info struct");
1345
1346   /* Write the info in two steps so that if we write the 'unknown' chunks here
1347    * they go to the correct place.
1348    */
1349   png_write_info_before_PLTE(write_ptr, write_info_ptr);
1350
1351   write_chunks(write_ptr, before_PLTE); /* before PLTE */
1352
1353   png_write_info(write_ptr, write_info_ptr);
1354
1355   write_chunks(write_ptr, before_IDAT); /* after PLTE */
1356#endif
1357
1358#ifdef SINGLE_ROWBUF_ALLOC
1359   pngtest_debug("Allocating row buffer...");
1360   row_buf = (png_bytep)png_malloc(read_ptr,
1361      png_get_rowbytes(read_ptr, read_info_ptr));
1362
1363   pngtest_debug1("\t0x%08lx", (unsigned long)row_buf);
1364#endif /* SINGLE_ROWBUF_ALLOC */
1365   pngtest_debug("Writing row data");
1366
1367#ifdef PNG_READ_INTERLACING_SUPPORTED
1368   num_pass = png_set_interlace_handling(read_ptr);
1369   if (png_set_interlace_handling(write_ptr) != num_pass)
1370      png_error(write_ptr, "png_set_interlace_handling: inconsistent num_pass");
1371#endif
1372
1373#ifdef PNGTEST_TIMING
1374   t_stop = (float)clock();
1375   t_misc += (t_stop - t_start);
1376   t_start = t_stop;
1377#endif
1378   for (pass = 0; pass < num_pass; pass++)
1379   {
1380      pngtest_debug1("Writing row data for pass %d", pass);
1381      for (y = 0; y < height; y++)
1382      {
1383#ifndef SINGLE_ROWBUF_ALLOC
1384         pngtest_debug2("Allocating row buffer (pass %d, y = %u)...", pass, y);
1385         row_buf = (png_bytep)png_malloc(read_ptr,
1386            png_get_rowbytes(read_ptr, read_info_ptr));
1387
1388         pngtest_debug2("\t0x%08lx (%u bytes)", (unsigned long)row_buf,
1389            png_get_rowbytes(read_ptr, read_info_ptr));
1390
1391#endif /* !SINGLE_ROWBUF_ALLOC */
1392         png_read_rows(read_ptr, (png_bytepp)&row_buf, NULL, 1);
1393
1394#ifdef PNG_WRITE_SUPPORTED
1395#ifdef PNGTEST_TIMING
1396         t_stop = (float)clock();
1397         t_decode += (t_stop - t_start);
1398         t_start = t_stop;
1399#endif
1400         png_write_rows(write_ptr, (png_bytepp)&row_buf, 1);
1401#ifdef PNGTEST_TIMING
1402         t_stop = (float)clock();
1403         t_encode += (t_stop - t_start);
1404         t_start = t_stop;
1405#endif
1406#endif /* PNG_WRITE_SUPPORTED */
1407
1408#ifndef SINGLE_ROWBUF_ALLOC
1409         pngtest_debug2("Freeing row buffer (pass %d, y = %u)", pass, y);
1410         png_free(read_ptr, row_buf);
1411         row_buf = NULL;
1412#endif /* !SINGLE_ROWBUF_ALLOC */
1413      }
1414   }
1415
1416#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
1417#  ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
1418      png_free_data(read_ptr, read_info_ptr, PNG_FREE_UNKN, -1);
1419#  endif
1420#  ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
1421      png_free_data(write_ptr, write_info_ptr, PNG_FREE_UNKN, -1);
1422#  endif
1423#endif
1424
1425   pngtest_debug("Reading and writing end_info data");
1426
1427   png_read_end(read_ptr, end_info_ptr);
1428#ifdef PNG_TEXT_SUPPORTED
1429   {
1430      png_textp text_ptr;
1431      int num_text;
1432
1433      if (png_get_text(read_ptr, end_info_ptr, &text_ptr, &num_text) > 0)
1434      {
1435         pngtest_debug1("Handling %d iTXt/tEXt/zTXt chunks", num_text);
1436
1437         pngtest_check_text_support(read_ptr, text_ptr, num_text);
1438
1439         if (verbose)
1440         {
1441            int i;
1442
1443            printf("\n");
1444            for (i=0; i<num_text; i++)
1445            {
1446               printf("   Text compression[%d]=%d\n",
1447                     i, text_ptr[i].compression);
1448            }
1449         }
1450
1451         png_set_text(write_ptr, write_end_info_ptr, text_ptr, num_text);
1452      }
1453   }
1454#endif
1455#ifdef PNG_tIME_SUPPORTED
1456   {
1457      png_timep mod_time;
1458
1459      if (png_get_tIME(read_ptr, end_info_ptr, &mod_time))
1460      {
1461         png_set_tIME(write_ptr, write_end_info_ptr, mod_time);
1462#ifdef PNG_TIME_RFC1123_SUPPORTED
1463         if (png_convert_to_rfc1123_buffer(tIME_string, mod_time))
1464            tIME_string[(sizeof tIME_string) - 1] = '\0';
1465
1466         else
1467         {
1468            strncpy(tIME_string, "*** invalid time ***", sizeof tIME_string);
1469            tIME_string[(sizeof tIME_string)-1] = '\0';
1470         }
1471
1472         tIME_chunk_present++;
1473#endif /* PNG_TIME_RFC1123_SUPPORTED */
1474      }
1475   }
1476#endif
1477#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
1478   {
1479      png_unknown_chunkp unknowns;
1480      int num_unknowns = png_get_unknown_chunks(read_ptr, end_info_ptr,
1481         &unknowns);
1482
1483      if (num_unknowns)
1484      {
1485         png_set_unknown_chunks(write_ptr, write_end_info_ptr, unknowns,
1486           num_unknowns);
1487#if PNG_LIBPNG_VER < 10600
1488         /* Copy the locations from the read_info_ptr.  The automatically
1489          * generated locations in write_end_info_ptr are wrong prior to 1.6.0
1490          * because they are reset from the write pointer (removed in 1.6.0).
1491          */
1492         {
1493            int i;
1494            for (i = 0; i < num_unknowns; i++)
1495              png_set_unknown_chunk_location(write_ptr, write_end_info_ptr, i,
1496                unknowns[i].location);
1497         }
1498#endif
1499      }
1500   }
1501#endif
1502
1503#ifdef PNG_WRITE_SUPPORTED
1504#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
1505   /* Normally one would use Z_DEFAULT_STRATEGY for text compression.
1506    * This is here just to make pngtest replicate the results from libpng
1507    * versions prior to 1.5.4, and to test this new API.
1508    */
1509   png_set_text_compression_strategy(write_ptr, Z_FILTERED);
1510#endif
1511
1512   /* When the unknown vpAg/sTER chunks are written by pngtest the only way to
1513    * do it is to write them *before* calling png_write_end.  When unknown
1514    * chunks are written by libpng, however, they are written just before IEND.
1515    * There seems to be no way round this, however vpAg/sTER are not expected
1516    * after IDAT.
1517    */
1518   write_chunks(write_ptr, after_IDAT);
1519
1520   png_write_end(write_ptr, write_end_info_ptr);
1521#endif
1522
1523#ifdef PNG_EASY_ACCESS_SUPPORTED
1524   if (verbose)
1525   {
1526      png_uint_32 iwidth, iheight;
1527      iwidth = png_get_image_width(write_ptr, write_info_ptr);
1528      iheight = png_get_image_height(write_ptr, write_info_ptr);
1529      fprintf(STDERR, "\n Image width = %lu, height = %lu\n",
1530         (unsigned long)iwidth, (unsigned long)iheight);
1531   }
1532#endif
1533
1534   pngtest_debug("Destroying data structs");
1535#ifdef SINGLE_ROWBUF_ALLOC
1536   pngtest_debug("destroying row_buf for read_ptr");
1537   png_free(read_ptr, row_buf);
1538   row_buf = NULL;
1539#endif /* SINGLE_ROWBUF_ALLOC */
1540   pngtest_debug("destroying read_ptr, read_info_ptr, end_info_ptr");
1541   png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
1542#ifdef PNG_WRITE_SUPPORTED
1543   pngtest_debug("destroying write_end_info_ptr");
1544   png_destroy_info_struct(write_ptr, &write_end_info_ptr);
1545   pngtest_debug("destroying write_ptr, write_info_ptr");
1546   png_destroy_write_struct(&write_ptr, &write_info_ptr);
1547#endif
1548   pngtest_debug("Destruction complete.");
1549
1550   FCLOSE(fpin);
1551   FCLOSE(fpout);
1552
1553   /* Summarize any warnings or errors and in 'strict' mode fail the test.
1554    * Unsupported chunks can result in warnings, in that case ignore the strict
1555    * setting, otherwise fail the test on warnings as well as errors.
1556    */
1557   if (error_count > 0)
1558   {
1559      /* We don't really expect to get here because of the setjmp handling
1560       * above, but this is safe.
1561       */
1562      fprintf(STDERR, "\n  %s: %d libpng errors found (%d warnings)",
1563         inname, error_count, warning_count);
1564
1565      if (strict != 0)
1566         return (1);
1567   }
1568
1569#  ifdef PNG_WRITE_SUPPORTED
1570      /* If there we no write support nothing was written! */
1571      else if (unsupported_chunks > 0)
1572      {
1573         fprintf(STDERR, "\n  %s: unsupported chunks (%d)%s",
1574            inname, unsupported_chunks, strict ? ": IGNORED --strict!" : "");
1575      }
1576#  endif
1577
1578   else if (warning_count > 0)
1579   {
1580      fprintf(STDERR, "\n  %s: %d libpng warnings found",
1581         inname, warning_count);
1582
1583      if (strict != 0)
1584         return (1);
1585   }
1586
1587   pngtest_debug("Opening files for comparison");
1588   if ((fpin = fopen(inname, "rb")) == NULL)
1589   {
1590      fprintf(STDERR, "Could not find file %s\n", inname);
1591      return (1);
1592   }
1593
1594   if ((fpout = fopen(outname, "rb")) == NULL)
1595   {
1596      fprintf(STDERR, "Could not find file %s\n", outname);
1597      FCLOSE(fpin);
1598      return (1);
1599   }
1600
1601#ifdef PNG_WRITE_SUPPORTED /* else nothing was written */
1602   if (interlace_preserved) /* else the files will be changed */
1603   {
1604      for (;;)
1605      {
1606         static int wrote_question = 0;
1607         png_size_t num_in, num_out;
1608         char inbuf[256], outbuf[256];
1609
1610         num_in = fread(inbuf, 1, sizeof inbuf, fpin);
1611         num_out = fread(outbuf, 1, sizeof outbuf, fpout);
1612
1613         if (num_in != num_out)
1614         {
1615            fprintf(STDERR, "\nFiles %s and %s are of a different size\n",
1616                    inname, outname);
1617
1618            if (wrote_question == 0 && unsupported_chunks == 0)
1619            {
1620               fprintf(STDERR,
1621         "   Was %s written with the same maximum IDAT chunk size (%d bytes),",
1622                 inname, PNG_ZBUF_SIZE);
1623               fprintf(STDERR,
1624                 "\n   filtering heuristic (libpng default), compression");
1625               fprintf(STDERR,
1626                 " level (zlib default),\n   and zlib version (%s)?\n\n",
1627                 ZLIB_VERSION);
1628               wrote_question = 1;
1629            }
1630
1631            FCLOSE(fpin);
1632            FCLOSE(fpout);
1633
1634            if (strict != 0 && unsupported_chunks == 0)
1635              return (1);
1636
1637            else
1638              return (0);
1639         }
1640
1641         if (!num_in)
1642            break;
1643
1644         if (memcmp(inbuf, outbuf, num_in))
1645         {
1646            fprintf(STDERR, "\nFiles %s and %s are different\n", inname,
1647               outname);
1648
1649            if (wrote_question == 0 && unsupported_chunks == 0)
1650            {
1651               fprintf(STDERR,
1652         "   Was %s written with the same maximum IDAT chunk size (%d bytes),",
1653                    inname, PNG_ZBUF_SIZE);
1654               fprintf(STDERR,
1655                 "\n   filtering heuristic (libpng default), compression");
1656               fprintf(STDERR,
1657                 " level (zlib default),\n   and zlib version (%s)?\n\n",
1658                 ZLIB_VERSION);
1659               wrote_question = 1;
1660            }
1661
1662            FCLOSE(fpin);
1663            FCLOSE(fpout);
1664
1665            /* NOTE: the unsupported_chunks escape is permitted here because
1666             * unsupported text chunk compression will result in the compression
1667             * mode being changed (to NONE) yet, in the test case, the result
1668             * can be exactly the same size!
1669             */
1670            if (strict != 0 && unsupported_chunks == 0)
1671              return (1);
1672
1673            else
1674              return (0);
1675         }
1676      }
1677   }
1678#endif /* PNG_WRITE_SUPPORTED */
1679
1680   FCLOSE(fpin);
1681   FCLOSE(fpout);
1682
1683   return (0);
1684}
1685
1686/* Input and output filenames */
1687#ifdef RISCOS
1688static PNG_CONST char *inname = "pngtest/png";
1689static PNG_CONST char *outname = "pngout/png";
1690#else
1691static PNG_CONST char *inname = "pngtest.png";
1692static PNG_CONST char *outname = "pngout.png";
1693#endif
1694
1695int
1696main(int argc, char *argv[])
1697{
1698   int multiple = 0;
1699   int ierror = 0;
1700
1701   fprintf(STDERR, "\n Testing libpng version %s\n", PNG_LIBPNG_VER_STRING);
1702   fprintf(STDERR, "   with zlib   version %s\n", ZLIB_VERSION);
1703   fprintf(STDERR, "%s", png_get_copyright(NULL));
1704   /* Show the version of libpng used in building the library */
1705   fprintf(STDERR, " library (%lu):%s",
1706      (unsigned long)png_access_version_number(),
1707      png_get_header_version(NULL));
1708
1709   /* Show the version of libpng used in building the application */
1710   fprintf(STDERR, " pngtest (%lu):%s", (unsigned long)PNG_LIBPNG_VER,
1711      PNG_HEADER_VERSION_STRING);
1712
1713   /* Do some consistency checking on the memory allocation settings, I'm
1714    * not sure this matters, but it is nice to know, the first of these
1715    * tests should be impossible because of the way the macros are set
1716    * in pngconf.h
1717    */
1718#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K)
1719      fprintf(STDERR, " NOTE: Zlib compiled for max 64k, libpng not\n");
1720#endif
1721   /* I think the following can happen. */
1722#if !defined(MAXSEG_64K) && defined(PNG_MAX_MALLOC_64K)
1723      fprintf(STDERR, " NOTE: libpng compiled for max 64k, zlib not\n");
1724#endif
1725
1726   if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING))
1727   {
1728      fprintf(STDERR,
1729         "Warning: versions are different between png.h and png.c\n");
1730      fprintf(STDERR, "  png.h version: %s\n", PNG_LIBPNG_VER_STRING);
1731      fprintf(STDERR, "  png.c version: %s\n\n", png_libpng_ver);
1732      ++ierror;
1733   }
1734
1735   if (argc > 1)
1736   {
1737      if (strcmp(argv[1], "-m") == 0)
1738      {
1739         multiple = 1;
1740         status_dots_requested = 0;
1741      }
1742
1743      else if (strcmp(argv[1], "-mv") == 0 ||
1744               strcmp(argv[1], "-vm") == 0 )
1745      {
1746         multiple = 1;
1747         verbose = 1;
1748         status_dots_requested = 1;
1749      }
1750
1751      else if (strcmp(argv[1], "-v") == 0)
1752      {
1753         verbose = 1;
1754         status_dots_requested = 1;
1755         inname = argv[2];
1756      }
1757
1758      else if (strcmp(argv[1], "--strict") == 0)
1759      {
1760         status_dots_requested = 0;
1761         verbose = 1;
1762         inname = argv[2];
1763         strict++;
1764         relaxed = 0;
1765      }
1766
1767      else if (strcmp(argv[1], "--relaxed") == 0)
1768      {
1769         status_dots_requested = 0;
1770         verbose = 1;
1771         inname = argv[2];
1772         strict = 0;
1773         relaxed++;
1774      }
1775
1776      else
1777      {
1778         inname = argv[1];
1779         status_dots_requested = 0;
1780      }
1781   }
1782
1783   if (!multiple && argc == 3 + verbose)
1784     outname = argv[2 + verbose];
1785
1786   if ((!multiple && argc > 3 + verbose) || (multiple && argc < 2))
1787   {
1788     fprintf(STDERR,
1789       "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n",
1790        argv[0], argv[0]);
1791     fprintf(STDERR,
1792       "  reads/writes one PNG file (without -m) or multiple files (-m)\n");
1793     fprintf(STDERR,
1794       "  with -m %s is used as a temporary file\n", outname);
1795     exit(1);
1796   }
1797
1798   if (multiple)
1799   {
1800      int i;
1801#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
1802      int allocation_now = current_allocation;
1803#endif
1804      for (i=2; i<argc; ++i)
1805      {
1806         int kerror;
1807         fprintf(STDERR, "\n Testing %s:", argv[i]);
1808         kerror = test_one_file(argv[i], outname);
1809         if (kerror == 0)
1810         {
1811#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
1812            int k;
1813#endif
1814#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
1815            fprintf(STDERR, "\n PASS (%lu zero samples)\n",
1816               (unsigned long)zero_samples);
1817#else
1818            fprintf(STDERR, " PASS\n");
1819#endif
1820#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
1821            for (k = 0; k<256; k++)
1822               if (filters_used[k])
1823                  fprintf(STDERR, " Filter %d was used %lu times\n",
1824                     k, (unsigned long)filters_used[k]);
1825#endif
1826#ifdef PNG_TIME_RFC1123_SUPPORTED
1827         if (tIME_chunk_present != 0)
1828            fprintf(STDERR, " tIME = %s\n", tIME_string);
1829
1830         tIME_chunk_present = 0;
1831#endif /* PNG_TIME_RFC1123_SUPPORTED */
1832         }
1833
1834         else
1835         {
1836            fprintf(STDERR, " FAIL\n");
1837            ierror += kerror;
1838         }
1839#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
1840         if (allocation_now != current_allocation)
1841            fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n",
1842               current_allocation - allocation_now);
1843
1844         if (current_allocation != 0)
1845         {
1846            memory_infop pinfo = pinformation;
1847
1848            fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n",
1849               current_allocation);
1850
1851            while (pinfo != NULL)
1852            {
1853               fprintf(STDERR, " %lu bytes at %x\n",
1854                 (unsigned long)pinfo->size,
1855                 (unsigned int)pinfo->pointer);
1856               pinfo = pinfo->next;
1857            }
1858         }
1859#endif
1860      }
1861#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
1862         fprintf(STDERR, " Current memory allocation: %10d bytes\n",
1863            current_allocation);
1864         fprintf(STDERR, " Maximum memory allocation: %10d bytes\n",
1865            maximum_allocation);
1866         fprintf(STDERR, " Total   memory allocation: %10d bytes\n",
1867            total_allocation);
1868         fprintf(STDERR, "     Number of allocations: %10d\n",
1869            num_allocations);
1870#endif
1871   }
1872
1873   else
1874   {
1875      int i;
1876      for (i = 0; i<3; ++i)
1877      {
1878         int kerror;
1879#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
1880         int allocation_now = current_allocation;
1881#endif
1882         if (i == 1)
1883            status_dots_requested = 1;
1884
1885         else if (verbose == 0)
1886            status_dots_requested = 0;
1887
1888         if (i == 0 || verbose == 1 || ierror != 0)
1889            fprintf(STDERR, "\n Testing %s:", inname);
1890
1891         kerror = test_one_file(inname, outname);
1892
1893         if (kerror == 0)
1894         {
1895            if (verbose == 1 || i == 2)
1896            {
1897#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
1898                int k;
1899#endif
1900#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
1901                fprintf(STDERR, "\n PASS (%lu zero samples)\n",
1902                   (unsigned long)zero_samples);
1903#else
1904                fprintf(STDERR, " PASS\n");
1905#endif
1906#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
1907                for (k = 0; k<256; k++)
1908                   if (filters_used[k])
1909                      fprintf(STDERR, " Filter %d was used %lu times\n",
1910                         k, (unsigned long)filters_used[k]);
1911#endif
1912#ifdef PNG_TIME_RFC1123_SUPPORTED
1913             if (tIME_chunk_present != 0)
1914                fprintf(STDERR, " tIME = %s\n", tIME_string);
1915#endif /* PNG_TIME_RFC1123_SUPPORTED */
1916            }
1917         }
1918
1919         else
1920         {
1921            if (verbose == 0 && i != 2)
1922               fprintf(STDERR, "\n Testing %s:", inname);
1923
1924            fprintf(STDERR, " FAIL\n");
1925            ierror += kerror;
1926         }
1927#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
1928         if (allocation_now != current_allocation)
1929             fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n",
1930               current_allocation - allocation_now);
1931
1932         if (current_allocation != 0)
1933         {
1934             memory_infop pinfo = pinformation;
1935
1936             fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n",
1937                current_allocation);
1938
1939             while (pinfo != NULL)
1940             {
1941                fprintf(STDERR, " %lu bytes at %x\n",
1942                   (unsigned long)pinfo->size, (unsigned int)pinfo->pointer);
1943                pinfo = pinfo->next;
1944             }
1945          }
1946#endif
1947       }
1948#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
1949       fprintf(STDERR, " Current memory allocation: %10d bytes\n",
1950          current_allocation);
1951       fprintf(STDERR, " Maximum memory allocation: %10d bytes\n",
1952          maximum_allocation);
1953       fprintf(STDERR, " Total   memory allocation: %10d bytes\n",
1954          total_allocation);
1955       fprintf(STDERR, "     Number of allocations: %10d\n",
1956            num_allocations);
1957#endif
1958   }
1959
1960#ifdef PNGTEST_TIMING
1961   t_stop = (float)clock();
1962   t_misc += (t_stop - t_start);
1963   t_start = t_stop;
1964   fprintf(STDERR, " CPU time used = %.3f seconds",
1965      (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC);
1966   fprintf(STDERR, " (decoding %.3f,\n",
1967      t_decode/(float)CLOCKS_PER_SEC);
1968   fprintf(STDERR, "        encoding %.3f ,",
1969      t_encode/(float)CLOCKS_PER_SEC);
1970   fprintf(STDERR, " other %.3f seconds)\n\n",
1971      t_misc/(float)CLOCKS_PER_SEC);
1972#endif
1973
1974   if (ierror == 0)
1975      fprintf(STDERR, " libpng passes test\n");
1976
1977   else
1978      fprintf(STDERR, " libpng FAILS test\n");
1979
1980   return (int)(ierror != 0);
1981}
1982#else
1983int
1984main(void)
1985{
1986   fprintf(STDERR,
1987      " test ignored because libpng was not built with read support\n");
1988   /* And skip this test */
1989   return PNG_LIBPNG_VER < 10600 ? 0 : 77;
1990}
1991#endif
1992
1993/* Generate a compiler error if there is an old png.h in the search path. */
1994typedef png_libpng_version_1_6_10 Your_png_h_is_not_version_1_6_10;
1995