pngtest.c 49.9 KB
Newer Older
A
Andreas Dilger 已提交
1

G
Guy Schalnat 已提交
2
/* pngtest.c - a simple test program to test libpng
3
 *
4
 * Last changed in libpng 1.2.37 [May 27, 2009]
5
 * For conditions of distribution and use, see copyright notice in png.h
6
 * Copyright (c) 1998-2009 Glenn Randers-Pehrson
7 8
 * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
 * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
9 10 11 12 13 14 15
 *
 * 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.
 *
16
 * The program will report "FAIL" in certain legitimate cases:
17
 * 1) when the compression level or filter selection method is changed.
18
 * 2) when the maximum IDAT size (PNG_ZBUF_SIZE in pngconf.h) is not 8192.
19 20
 * 3) unknown unsafe-to-copy ancillary chunks or unknown critical chunks
 *    exist in the input file.
21 22
 * 4) others not listed here...
 * In these cases, it is best to check with another tool such as "pngcheck"
23
 * to see what the differences between the two files are.
24 25 26
 *
 * 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
27 28
 * 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 ..."
29
 */
G
Guy Schalnat 已提交
30

31 32
#include "png.h"

33 34 35 36 37 38 39
#if defined(_WIN32_WCE)
#  if _WIN32_WCE < 211
     __error__ (f|w)printf functions are not supported on old WindowsCE.;
#  endif
#  include <windows.h>
#  include <stdlib.h>
#  define READFILE(file, data, length, check) \
40
     if (ReadFile(file, data, length, &check, NULL)) check = 0
41 42
#  define WRITEFILE(file, data, length, check)) \
     if (WriteFile(file, data, length, &check, NULL)) check = 0
43 44 45 46 47
#  define FCLOSE(file) CloseHandle(file)
#else
#  include <stdio.h>
#  include <stdlib.h>
#  define READFILE(file, data, length, check) \
48
     check=(png_size_t)fread(data, (png_size_t)1, length, file)
49
#  define WRITEFILE(file, data, length, check) \
50
     check=(png_size_t)fwrite(data, (png_size_t)1, length, file)
51 52
#  define FCLOSE(file) fclose(file)
#endif
A
Andreas Dilger 已提交
53

54
#if defined(PNG_NO_STDIO)
55 56 57 58 59
#  if defined(_WIN32_WCE)
     typedef HANDLE                png_FILE_p;
#  else
     typedef FILE                * png_FILE_p;
#  endif
60 61
#endif

A
Andreas Dilger 已提交
62 63
/* Makes pngtest verbose so we can find problems (needs to be before png.h) */
#ifndef PNG_DEBUG
64 65 66 67
#  define PNG_DEBUG 0
#endif

#if !PNG_DEBUG
68
#  define SINGLE_ROWBUF_ALLOC  /* Makes buffer overruns easier to nail */
69
#endif
A
Andreas Dilger 已提交
70

71 72 73 74
/* Turn on CPU timing
#define PNGTEST_TIMING
*/

75 76 77 78
#ifdef PNG_NO_FLOATING_POINT_SUPPORTED
#undef PNGTEST_TIMING
#endif

79 80 81 82 83
#ifdef PNGTEST_TIMING
static float t_start, t_stop, t_decode, t_encode, t_misc;
#include <time.h>
#endif

84
#if defined(PNG_TIME_RFC1123_SUPPORTED)
85
#define PNG_tIME_STRING_LENGTH 29
86
static int tIME_chunk_present = 0;
87
static char tIME_string[PNG_tIME_STRING_LENGTH] = "tIME chunk is not present";
88
#endif
89

90 91
static int verbose = 0;

92
int test_one_file PNGARG((PNG_CONST char *inname, PNG_CONST char *outname));
93

G
Guy Schalnat 已提交
94 95 96 97
#ifdef __TURBOC__
#include <mem.h>
#endif

98
/* Defined so I can write to a file on gui/windowing platforms */
G
Guy Schalnat 已提交
99
/*  #define STDERR stderr  */
100
#define STDERR stdout   /* For DOS */
G
Guy Schalnat 已提交
101

102 103 104 105 106 107 108 109 110 111
/* In case a system header (e.g., on AIX) defined jmpbuf */
#ifdef jmpbuf
#  undef jmpbuf
#endif

/* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */
#ifndef png_jmpbuf
#  define png_jmpbuf(png_ptr) png_ptr->jmpbuf
#endif

112
/* Example of using row callbacks to make a simple progress meter */
113 114 115
static int status_pass = 1;
static int status_dots_requested = 0;
static int status_dots = 1;
116

117
void
118 119 120
#ifdef PNG_1_0_X
PNGAPI
#endif
121
read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass);
122
void
123 124 125
#ifdef PNG_1_0_X
PNGAPI
#endif
126
read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass)
127
{
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
   if (png_ptr == NULL || row_number > PNG_UINT_31_MAX)
      return;
   if (status_pass != pass)
   {
      fprintf(stdout, "\n Pass %d: ", pass);
      status_pass = pass;
      status_dots = 31;
   }
   status_dots--;
   if (status_dots == 0)
   {
      fprintf(stdout, "\n         ");
      status_dots=30;
   }
   fprintf(stdout, "r");
143
}
144

145
void
146 147 148
#ifdef PNG_1_0_X
PNGAPI
#endif
149
write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass);
150
void
151 152 153
#ifdef PNG_1_0_X
PNGAPI
#endif
154
write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass)
155
{
156 157 158
   if (png_ptr == NULL || row_number > PNG_UINT_31_MAX || pass > 7)
      return;
   fprintf(stdout, "w");
159 160 161
}


162 163
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
/* Example of using user transform callback (we don't transform anything,
164 165 166
 * but merely examine the row filters.  We set this to 256 rather than
 * 5 in case illegal filter values are present.)
 */
167 168
static png_uint_32 filters_used[256];
void
169 170 171
#ifdef PNG_1_0_X
PNGAPI
#endif
172 173
count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data);
void
174 175 176
#ifdef PNG_1_0_X
PNGAPI
#endif
177 178
count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data)
{
179
   if (png_ptr != NULL && row_info != NULL)
180
      ++filters_used[*(data - 1)];
181 182 183
}
#endif

184
#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
185 186 187
/* Example of using user transform callback (we don't transform anything,
 * but merely count the zero samples)
 */
188

189
static png_uint_32 zero_samples;
190

191
void
192 193 194
#ifdef PNG_1_0_X
PNGAPI
#endif
195
count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data);
196
void
197 198 199
#ifdef PNG_1_0_X
PNGAPI
#endif
200
count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data)
201 202
{
   png_bytep dp = data;
203
   if (png_ptr == NULL)return;
204

205
   /* Contents of row_info:
206 207 208 209 210 211 212 213
    *  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)
    */

214
    /* Counts the number of zero samples (or zero pixels if color_type is 3 */
215

216
    if (row_info->color_type == 0 || row_info->color_type == 3)
217
    {
218
       int pos = 0;
219
       png_uint_32 n, nstop;
220
       for (n = 0, nstop=row_info->width; n<nstop; n++)
221
       {
222
          if (row_info->bit_depth == 1)
223
          {
224 225
             if (((*dp << pos++ ) & 0x80) == 0)
                zero_samples++;
226
             if (pos == 8)
227
             {
228
                pos = 0;
229 230
                dp++;
             }
231
          }
232
          if (row_info->bit_depth == 2)
233
          {
234 235
             if (((*dp << (pos+=2)) & 0xc0) == 0)
                zero_samples++;
236
             if (pos == 8)
237
             {
238
                pos = 0;
239 240
                dp++;
             }
241
          }
242
          if (row_info->bit_depth == 4)
243
          {
244 245
             if (((*dp << (pos+=4)) & 0xf0) == 0)
                zero_samples++;
246
             if (pos == 8)
247
             {
248
                pos = 0;
249 250
                dp++;
             }
251
          }
252
          if (row_info->bit_depth == 8)
253 254
             if (*dp++ == 0)
                zero_samples++;
255
          if (row_info->bit_depth == 16)
256
          {
257
             if ((*dp | *(dp+1)) == 0) zero_samples++;
258 259 260 261
             dp+=2;
          }
       }
    }
262
    else /* Other color types */
263
    {
264
       png_uint_32 n, nstop;
265 266
       int channel;
       int color_channels = row_info->channels;
267
       if (row_info->color_type > 3)color_channels--;
268

269
       for (n = 0, nstop=row_info->width; n<nstop; n++)
270 271 272
       {
          for (channel = 0; channel < color_channels; channel++)
          {
273
             if (row_info->bit_depth == 8)
274 275
                if (*dp++ == 0)
                   zero_samples++;
276
             if (row_info->bit_depth == 16)
277
             {
278
                if ((*dp | *(dp+1)) == 0) zero_samples++;
279 280 281
                dp+=2;
             }
          }
282
          if (row_info->color_type > 3)
283 284
          {
             dp++;
285 286
             if (row_info->bit_depth == 16)
                dp++;
287 288 289 290
          }
       }
    }
}
291
#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */
292

293
static int wrote_question = 0;
294

295
#if defined(PNG_NO_STDIO)
296
/* START of code to validate stdio-free compilation */
297 298 299 300 301 302 303
/* These copies of the default read/write functions come from pngrio.c and
 * 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.
 */
304

305 306
#ifndef USE_FAR_KEYWORD
static void
307
pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
308 309 310 311 312 313
{
   png_size_t check;

   /* 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.
    */
314
   READFILE((png_FILE_p)png_ptr->io_ptr, data, length, check);
315 316 317

   if (check != length)
   {
318
      png_error(png_ptr, "Read Error!");
319 320
   }
}
G
Guy Schalnat 已提交
321
#else
322
/* This is the model-independent version. Since the standard I/O library
323 324 325
   can't handle far buffers in the medium and small models, we have to copy
   the data.
*/
326

327 328
#define NEAR_BUF_SIZE 1024
#define MIN(a,b) (a <= b ? a : b)
329

330
static void
331
pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
332 333 334
{
   int check;
   png_byte *n_data;
335
   png_FILE_p io_ptr;
336 337 338

   /* Check if data really is near. If so, use usual code. */
   n_data = (png_byte *)CVT_PTR_NOCHECK(data);
339
   io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr);
340 341
   if ((png_bytep)n_data == data)
   {
342
      READFILE(io_ptr, n_data, length, check);
343 344 345 346 347 348 349 350 351 352
   }
   else
   {
      png_byte buf[NEAR_BUF_SIZE];
      png_size_t read, remaining, err;
      check = 0;
      remaining = length;
      do
      {
         read = MIN(NEAR_BUF_SIZE, remaining);
353
         READFILE(io_ptr, buf, 1, err);
354
         png_memcpy(data, buf, read); /* Copy far buffer to near buffer */
355
         if (err != read)
356 357 358 359 360 361 362 363 364 365 366
            break;
         else
            check += err;
         data += read;
         remaining -= read;
      }
      while (remaining != 0);
   }
   if (check != length)
      png_error(png_ptr, "read Error");
}
367
#endif /* USE_FAR_KEYWORD */
G
Guy Schalnat 已提交
368

369
#if defined(PNG_WRITE_FLUSH_SUPPORTED)
370
static void
371
pngtest_flush(png_structp png_ptr)
372
{
373
   /* Do nothing; fflush() is said to be just a waste of energy. */
374
   png_ptr = png_ptr;  /* Stifle compiler warning */
375 376
}
#endif
G
Guy Schalnat 已提交
377

378
/* This is the function that does the actual writing of data.  If you are
379 380 381 382
 * 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.
 */
383 384
#ifndef USE_FAR_KEYWORD
static void
385
pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
386 387 388
{
   png_uint_32 check;

389
   WRITEFILE((png_FILE_p)png_ptr->io_ptr,  data, length, check);
390 391 392 393 394 395
   if (check != length)
   {
      png_error(png_ptr, "Write Error");
   }
}
#else
396
/* This is the model-independent version. Since the standard I/O library
397 398 399 400 401 402 403 404
   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
405
pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
406 407 408
{
   png_uint_32 check;
   png_byte *near_data;  /* Needs to be "png_byte *" instead of "png_bytep" */
409
   png_FILE_p io_ptr;
410 411 412

   /* Check if data really is near. If so, use usual code. */
   near_data = (png_byte *)CVT_PTR_NOCHECK(data);
413
   io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr);
414 415
   if ((png_bytep)near_data == data)
   {
416
      WRITEFILE(io_ptr, near_data, length, check);
417 418 419 420 421 422 423 424 425 426
   }
   else
   {
      png_byte buf[NEAR_BUF_SIZE];
      png_size_t written, remaining, err;
      check = 0;
      remaining = length;
      do
      {
         written = MIN(NEAR_BUF_SIZE, remaining);
427
         png_memcpy(buf, data, written); /* Copy far buffer to near buffer */
428
         WRITEFILE(io_ptr, buf, written, err);
429 430 431 432 433 434 435 436 437 438 439 440 441 442
         if (err != written)
            break;
         else
            check += err;
         data += written;
         remaining -= written;
      }
      while (remaining != 0);
   }
   if (check != length)
   {
      png_error(png_ptr, "Write Error");
   }
}
443
#endif /* USE_FAR_KEYWORD */
444 445 446 447 448 449 450

/* 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
451
pngtest_warning(png_structp png_ptr, png_const_charp message)
452 453 454 455 456 457 458 459 460 461 462 463 464
{
   PNG_CONST char *name = "UNKNOWN (ERROR!)";
   if (png_ptr != NULL && png_ptr->error_ptr != NULL)
      name = png_ptr->error_ptr;
   fprintf(STDERR, "%s: libpng warning: %s\n", name, message);
}

/* 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
465
pngtest_error(png_structp png_ptr, png_const_charp message)
466
{
467
   pngtest_warning(png_ptr, message);
468
   /* We can return because png_error calls the default handler, which is
469 470
    * actually OK in this case.
    */
471
}
472 473
#endif /* PNG_NO_STDIO */
/* END of code to validate stdio-free compilation */
474

475
/* START of code to validate memory allocation and deallocation */
476
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
477 478

/* Allocate memory.  For reasonable files, size should never exceed
479 480 481 482 483 484 485 486
 * 64K.  However, zlib may allocate more then 64K if you don't tell
 * it not to.  See zconf.h and png.h for more information.  zlib does
 * 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.
 */
487 488
typedef struct memory_information
{
489
   png_uint_32               size;
490
   png_voidp                 pointer;
491 492 493 494 495 496 497
   struct memory_information FAR *next;
} memory_information;
typedef memory_information FAR *memory_infop;

static memory_infop pinformation = NULL;
static int current_allocation = 0;
static int maximum_allocation = 0;
498 499
static int total_allocation = 0;
static int num_allocations = 0;
500

501 502
png_voidp png_debug_malloc PNGARG((png_structp png_ptr, png_uint_32 size));
void png_debug_free PNGARG((png_structp png_ptr, png_voidp ptr));
503 504

png_voidp
505 506
png_debug_malloc(png_structp png_ptr, png_uint_32 size)
{
507 508

   /* png_malloc has already tested for NULL; png_create_struct calls
509 510
    * png_debug_malloc directly, with png_ptr == NULL which is OK
    */
511

512
   if (size == 0)
513
      return (NULL);
514 515 516 517

   /* This calls the library allocator twice, once to get the requested
      buffer and once to get a new free list entry. */
   {
518
      /* Disable malloc_fn and free_fn */
519
      memory_infop pinfo;
520
      png_set_mem_fn(png_ptr, NULL, NULL, NULL);
521
      pinfo = (memory_infop)png_malloc(png_ptr,
522
         (png_uint_32)png_sizeof(*pinfo));
523 524
      pinfo->size = size;
      current_allocation += size;
525 526
      total_allocation += size;
      num_allocations ++;
527 528
      if (current_allocation > maximum_allocation)
         maximum_allocation = current_allocation;
529 530
      pinfo->pointer = (png_voidp)png_malloc(png_ptr, size);
      /* Restore malloc_fn and free_fn */
531 532 533
      png_set_mem_fn(png_ptr,
          png_voidp_NULL, (png_malloc_ptr)png_debug_malloc,
          (png_free_ptr)png_debug_free);
534 535 536 537
      if (size != 0 && pinfo->pointer == NULL)
      {
         current_allocation -= size;
         total_allocation -= size;
538 539
         png_error(png_ptr,
           "out of memory in pngtest->png_debug_malloc.");
540
      }
541 542 543 544
      pinfo->next = pinformation;
      pinformation = pinfo;
      /* Make sure the caller isn't assuming zeroed memory. */
      png_memset(pinfo->pointer, 0xdd, pinfo->size);
545
      if (verbose)
546
         printf("png_malloc %lu bytes at %x\n", (unsigned long)size,
547
            pinfo->pointer);
548
      return (png_voidp)(pinfo->pointer);
549 550 551 552 553
   }
}

/* Free a pointer.  It is removed from the list at the same time. */
void
554
png_debug_free(png_structp png_ptr, png_voidp ptr)
555 556
{
   if (png_ptr == NULL)
557
      fprintf(STDERR, "NULL pointer to png_debug_free.\n");
558 559
   if (ptr == 0)
   {
560 561 562 563 564 565 566 567 568
#if 0 /* This happens all the time. */
      fprintf(STDERR, "WARNING: freeing NULL pointer\n");
#endif
      return;
   }

   /* Unlink the element from the list. */
   {
      memory_infop FAR *ppinfo = &pinformation;
569 570
      for (;;)
      {
571
         memory_infop pinfo = *ppinfo;
572 573
         if (pinfo->pointer == ptr)
         {
574 575 576 577 578
            *ppinfo = pinfo->next;
            current_allocation -= pinfo->size;
            if (current_allocation < 0)
               fprintf(STDERR, "Duplicate free of memory\n");
            /* We must free the list element too, but first kill
579
               the memory that is to be freed. */
580
            png_memset(ptr, 0x55, pinfo->size);
581
            png_free_default(png_ptr, pinfo);
582
            pinfo = NULL;
583 584
            break;
         }
585 586
         if (pinfo->next == NULL)
         {
587
            fprintf(STDERR, "Pointer %x not found\n", (unsigned int)ptr);
588 589 590 591 592 593 594
            break;
         }
         ppinfo = &pinfo->next;
      }
   }

   /* Finally free the data. */
595
   if (verbose)
596
      printf("Freeing %x\n", ptr);
597
   png_free_default(png_ptr, ptr);
598
   ptr = NULL;
599
}
600
#endif /* PNG_USER_MEM_SUPPORTED && PNG_DEBUG */
601 602
/* END of code to test memory allocation/deallocation */

603 604 605 606

/* Demonstration of user chunk support of the sTER and vpAg chunks */
#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)

607
/* (sTER is a public chunk not yet known by libpng.  vpAg is a private
608 609 610 611 612 613 614 615 616 617 618 619 620
chunk used in ImageMagick to store "virtual page" size).  */

static png_uint_32 user_chunk_data[4];

    /* 0: sTER mode + 1
     * 1: vpAg width
     * 2: vpAg height
     * 3: vpAg units
     */

static int read_user_chunk_callback(png_struct *png_ptr,
   png_unknown_chunkp chunk)
{
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
   png_uint_32
     *my_user_chunk_data;

   /* 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.
    */

   if (chunk->name[0] == 115 && chunk->name[1] ==  84 &&     /* s  T */
       chunk->name[2] ==  69 && chunk->name[3] ==  82)       /* E  R */
      {
         /* Found sTER chunk */
         if (chunk->size != 1)
            return (-1); /* Error return */
         if (chunk->data[0] != 0 && chunk->data[0] != 1)
            return (-1);  /* Invalid mode */
         my_user_chunk_data=(png_uint_32 *) png_get_user_chunk_ptr(png_ptr);
         my_user_chunk_data[0]=chunk->data[0]+1;
         return (1);
      }

   if (chunk->name[0] != 118 || chunk->name[1] != 112 ||    /* v  p */
       chunk->name[2] !=  65 || chunk->name[3] != 103)      /* A  g */
      return (0); /* Did not recognize */

   /* Found ImageMagick vpAg chunk */

   if (chunk->size != 9)
      return (-1); /* Error return */

   my_user_chunk_data=(png_uint_32 *) png_get_user_chunk_ptr(png_ptr);

   my_user_chunk_data[1]=png_get_uint_31(png_ptr, chunk->data);
   my_user_chunk_data[2]=png_get_uint_31(png_ptr, chunk->data + 4);
   my_user_chunk_data[3]=(png_uint_32)chunk->data[8];

   return (1);
666 667 668 669 670

}
#endif
/* END of code to demonstrate user chunk support */

671
/* Test one file */
672 673
int
test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
G
Guy Schalnat 已提交
674
{
675 676
   static png_FILE_p fpin;
   static png_FILE_p fpout;  /* "static" prevents setjmp corruption */
677 678 679 680 681 682 683 684 685 686 687
   png_structp read_ptr;
   png_infop read_info_ptr, end_info_ptr;
#ifdef PNG_WRITE_SUPPORTED
   png_structp write_ptr;
   png_infop write_info_ptr;
   png_infop write_end_info_ptr;
#else
   png_structp write_ptr = NULL;
   png_infop write_info_ptr = NULL;
   png_infop write_end_info_ptr = NULL;
#endif
G
Guy Schalnat 已提交
688
   png_bytep row_buf;
G
Guy Schalnat 已提交
689
   png_uint_32 y;
A
Andreas Dilger 已提交
690 691 692
   png_uint_32 width, height;
   int num_pass, pass;
   int bit_depth, color_type;
693
#ifdef PNG_SETJMP_SUPPORTED
A
Andreas Dilger 已提交
694
#ifdef USE_FAR_KEYWORD
695
   jmp_buf jmpbuf;
696
#endif
697
#endif
698

699 700 701
#if defined(_WIN32_WCE)
   TCHAR path[MAX_PATH];
#endif
702
   char inbuf[256], outbuf[256];
G
Guy Schalnat 已提交
703

704
   row_buf = NULL;
G
Guy Schalnat 已提交
705

706 707 708 709
#if defined(_WIN32_WCE)
   MultiByteToWideChar(CP_ACP, 0, inname, -1, path, MAX_PATH);
   if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
#else
A
Andreas Dilger 已提交
710
   if ((fpin = fopen(inname, "rb")) == NULL)
711
#endif
G
Guy Schalnat 已提交
712 713
   {
      fprintf(STDERR, "Could not find input file %s\n", inname);
714
      return (1);
G
Guy Schalnat 已提交
715 716
   }

717 718 719 720
#if defined(_WIN32_WCE)
   MultiByteToWideChar(CP_ACP, 0, outname, -1, path, MAX_PATH);
   if ((fpout = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL)) == INVALID_HANDLE_VALUE)
#else
A
Andreas Dilger 已提交
721
   if ((fpout = fopen(outname, "wb")) == NULL)
722
#endif
G
Guy Schalnat 已提交
723
   {
G
Guy Schalnat 已提交
724
      fprintf(STDERR, "Could not open output file %s\n", outname);
725
      FCLOSE(fpin);
726
      return (1);
G
Guy Schalnat 已提交
727 728
   }

729
   png_debug(0, "Allocating read and write structures");
730
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
731 732
   read_ptr =
      png_create_read_struct_2(PNG_LIBPNG_VER_STRING, png_voidp_NULL,
733
      png_error_ptr_NULL, png_error_ptr_NULL, png_voidp_NULL,
734 735
      (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free);
#else
736 737
   read_ptr =
      png_create_read_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL,
738
      png_error_ptr_NULL, png_error_ptr_NULL);
739
#endif
740
#if defined(PNG_NO_STDIO)
741 742
   png_set_error_fn(read_ptr, (png_voidp)inname, pngtest_error,
       pngtest_warning);
743
#endif
744 745

#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
746 747 748 749 750 751
   user_chunk_data[0] = 0;
   user_chunk_data[1] = 0;
   user_chunk_data[2] = 0;
   user_chunk_data[3] = 0;
   png_set_read_user_chunk_fn(read_ptr, user_chunk_data,
     read_user_chunk_callback);
752 753

#endif
754
#ifdef PNG_WRITE_SUPPORTED
755
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
756 757
   write_ptr =
      png_create_write_struct_2(PNG_LIBPNG_VER_STRING, png_voidp_NULL,
758
      png_error_ptr_NULL, png_error_ptr_NULL, png_voidp_NULL,
759 760
      (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free);
#else
761 762
   write_ptr =
      png_create_write_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL,
763
      png_error_ptr_NULL, png_error_ptr_NULL);
764
#endif
765
#if defined(PNG_NO_STDIO)
766 767
   png_set_error_fn(write_ptr, (png_voidp)inname, pngtest_error,
       pngtest_warning);
768
#endif
769
#endif
770
   png_debug(0, "Allocating read_info, write_info and end_info structures");
A
Andreas Dilger 已提交
771
   read_info_ptr = png_create_info_struct(read_ptr);
772
   end_info_ptr = png_create_info_struct(read_ptr);
773 774
#ifdef PNG_WRITE_SUPPORTED
   write_info_ptr = png_create_info_struct(write_ptr);
775
   write_end_info_ptr = png_create_info_struct(write_ptr);
776
#endif
G
Guy Schalnat 已提交
777

778
#ifdef PNG_SETJMP_SUPPORTED
779
   png_debug(0, "Setting jmpbuf for read struct");
A
Andreas Dilger 已提交
780
#ifdef USE_FAR_KEYWORD
781
   if (setjmp(jmpbuf))
A
Andreas Dilger 已提交
782
#else
783
   if (setjmp(png_jmpbuf(read_ptr)))
A
Andreas Dilger 已提交
784
#endif
G
Guy Schalnat 已提交
785
   {
786
      fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname);
787 788
      png_free(read_ptr, row_buf);
      row_buf = NULL;
A
Andreas Dilger 已提交
789
      png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
790
#ifdef PNG_WRITE_SUPPORTED
791
      png_destroy_info_struct(write_ptr, &write_end_info_ptr);
A
Andreas Dilger 已提交
792
      png_destroy_write_struct(&write_ptr, &write_info_ptr);
793
#endif
794 795
      FCLOSE(fpin);
      FCLOSE(fpout);
796
      return (1);
G
Guy Schalnat 已提交
797
   }
798
#ifdef USE_FAR_KEYWORD
799
   png_memcpy(png_jmpbuf(read_ptr), jmpbuf, png_sizeof(jmp_buf));
800
#endif
A
Andreas Dilger 已提交
801

802
#ifdef PNG_WRITE_SUPPORTED
803
   png_debug(0, "Setting jmpbuf for write struct");
A
Andreas Dilger 已提交
804
#ifdef USE_FAR_KEYWORD
805
   if (setjmp(jmpbuf))
A
Andreas Dilger 已提交
806
#else
807
   if (setjmp(png_jmpbuf(write_ptr)))
A
Andreas Dilger 已提交
808
#endif
G
Guy Schalnat 已提交
809
   {
810
      fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname);
A
Andreas Dilger 已提交
811
      png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
812
      png_destroy_info_struct(write_ptr, &write_end_info_ptr);
813
#ifdef PNG_WRITE_SUPPORTED
A
Andreas Dilger 已提交
814
      png_destroy_write_struct(&write_ptr, &write_info_ptr);
815
#endif
816 817
      FCLOSE(fpin);
      FCLOSE(fpout);
818
      return (1);
G
Guy Schalnat 已提交
819
   }
A
Andreas Dilger 已提交
820
#ifdef USE_FAR_KEYWORD
821
   png_memcpy(png_jmpbuf(write_ptr), jmpbuf, png_sizeof(jmp_buf));
822
#endif
823
#endif
A
Andreas Dilger 已提交
824
#endif
825

826
   png_debug(0, "Initializing input and output streams");
827
#if !defined(PNG_NO_STDIO)
G
Guy Schalnat 已提交
828
   png_init_io(read_ptr, fpin);
829
#  ifdef PNG_WRITE_SUPPORTED
G
Guy Schalnat 已提交
830
   png_init_io(write_ptr, fpout);
831
#  endif
832
#else
833
   png_set_read_fn(read_ptr, (png_voidp)fpin, pngtest_read_data);
834
#  ifdef PNG_WRITE_SUPPORTED
835
   png_set_write_fn(write_ptr, (png_voidp)fpout,  pngtest_write_data,
836
#    if defined(PNG_WRITE_FLUSH_SUPPORTED)
837
      pngtest_flush);
838
#    else
839
      NULL);
840 841
#    endif
#  endif
842
#endif
843
   if (status_dots_requested == 1)
844
   {
845
#ifdef PNG_WRITE_SUPPORTED
846
      png_set_write_status_fn(write_ptr, write_row_callback);
847
#endif
848 849 850 851
      png_set_read_status_fn(read_ptr, read_row_callback);
   }
   else
   {
852
#ifdef PNG_WRITE_SUPPORTED
853
      png_set_write_status_fn(write_ptr, png_write_status_ptr_NULL);
854
#endif
855
      png_set_read_status_fn(read_ptr, png_read_status_ptr_NULL);
856 857
   }

858 859
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
   {
860 861 862 863
      int i;
      for (i = 0; i<256; i++)
         filters_used[i] = 0;
      png_set_read_user_transform_fn(read_ptr, count_filters);
864 865 866
   }
#endif
#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
867
   zero_samples = 0;
868 869
   png_set_write_user_transform_fn(write_ptr, count_zero_samples);
#endif
G
Guy Schalnat 已提交
870

871
#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
872 873 874 875
#  ifndef PNG_HANDLE_CHUNK_ALWAYS
#    define PNG_HANDLE_CHUNK_ALWAYS       3
#  endif
   png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS,
876
      png_bytep_NULL, 0);
877 878
#endif
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
879 880 881 882
#  ifndef PNG_HANDLE_CHUNK_IF_SAFE
#    define PNG_HANDLE_CHUNK_IF_SAFE      2
#  endif
   png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_IF_SAFE,
883
      png_bytep_NULL, 0);
884 885
#endif

886
   png_debug(0, "Reading info struct");
A
Andreas Dilger 已提交
887
   png_read_info(read_ptr, read_info_ptr);
G
Guy Schalnat 已提交
888

889
   png_debug(0, "Transferring info struct");
A
Andreas Dilger 已提交
890 891
   {
      int interlace_type, compression_type, filter_type;
G
Guy Schalnat 已提交
892

A
Andreas Dilger 已提交
893 894 895 896
      if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth,
          &color_type, &interlace_type, &compression_type, &filter_type))
      {
         png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth,
897
#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
A
Andreas Dilger 已提交
898
            color_type, interlace_type, compression_type, filter_type);
899 900 901
#else
            color_type, PNG_INTERLACE_NONE, compression_type, filter_type);
#endif
A
Andreas Dilger 已提交
902 903
      }
   }
904
#if defined(PNG_FIXED_POINT_SUPPORTED)
905
#if defined(PNG_cHRM_SUPPORTED)
G
Guy Schalnat 已提交
906
   {
907
      png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x,
908 909 910
         blue_y;
      if (png_get_cHRM_fixed(read_ptr, read_info_ptr, &white_x, &white_y, &red_x,
         &red_y, &green_x, &green_y, &blue_x, &blue_y))
A
Andreas Dilger 已提交
911
      {
912 913
         png_set_cHRM_fixed(write_ptr, write_info_ptr, white_x, white_y, red_x,
            red_y, green_x, green_y, blue_x, blue_y);
A
Andreas Dilger 已提交
914
      }
G
Guy Schalnat 已提交
915
   }
A
Andreas Dilger 已提交
916
#endif
917
#if defined(PNG_gAMA_SUPPORTED)
A
Andreas Dilger 已提交
918
   {
919
      png_fixed_point gamma;
G
Guy Schalnat 已提交
920

921 922
      if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma))
         png_set_gAMA_fixed(write_ptr, write_info_ptr, gamma);
A
Andreas Dilger 已提交
923 924
   }
#endif
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946
#else /* Use floating point versions */
#if defined(PNG_FLOATING_POINT_SUPPORTED)
#if defined(PNG_cHRM_SUPPORTED)
   {
      double white_x, white_y, red_x, red_y, green_x, green_y, blue_x,
         blue_y;
      if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x,
         &red_y, &green_x, &green_y, &blue_x, &blue_y))
      {
         png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y, red_x,
            red_y, green_x, green_y, blue_x, blue_y);
      }
   }
#endif
#if defined(PNG_gAMA_SUPPORTED)
   {
      double gamma;

      if (png_get_gAMA(read_ptr, read_info_ptr, &gamma))
         png_set_gAMA(write_ptr, write_info_ptr, gamma);
   }
#endif
947 948
#endif /* Floating point */
#endif /* Fixed point */
949
#if defined(PNG_iCCP_SUPPORTED)
G
Guy Schalnat 已提交
950
   {
951 952
      png_charp name;
      png_charp profile;
953
      png_uint_32 proflen;
954
      int compression_type;
A
Andreas Dilger 已提交
955

956 957
      if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_type,
                      &profile, &proflen))
A
Andreas Dilger 已提交
958
      {
959 960
         png_set_iCCP(write_ptr, write_info_ptr, name, compression_type,
                      profile, proflen);
A
Andreas Dilger 已提交
961
      }
G
Guy Schalnat 已提交
962
   }
963
#endif
964
#if defined(PNG_sRGB_SUPPORTED)
965
   {
966
      int intent;
967 968 969 970

      if (png_get_sRGB(read_ptr, read_info_ptr, &intent))
         png_set_sRGB(write_ptr, write_info_ptr, intent);
   }
A
Andreas Dilger 已提交
971
#endif
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989
   {
      png_colorp palette;
      int num_palette;

      if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette))
         png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette);
   }
#if defined(PNG_bKGD_SUPPORTED)
   {
      png_color_16p background;

      if (png_get_bKGD(read_ptr, read_info_ptr, &background))
      {
         png_set_bKGD(write_ptr, write_info_ptr, background);
      }
   }
#endif
#if defined(PNG_hIST_SUPPORTED)
G
Guy Schalnat 已提交
990
   {
A
Andreas Dilger 已提交
991 992 993 994
      png_uint_16p hist;

      if (png_get_hIST(read_ptr, read_info_ptr, &hist))
         png_set_hIST(write_ptr, write_info_ptr, hist);
G
Guy Schalnat 已提交
995
   }
A
Andreas Dilger 已提交
996
#endif
997
#if defined(PNG_oFFs_SUPPORTED)
A
Andreas Dilger 已提交
998
   {
999
      png_int_32 offset_x, offset_y;
A
Andreas Dilger 已提交
1000
      int unit_type;
G
Guy Schalnat 已提交
1001

1002
      if (png_get_oFFs(read_ptr, read_info_ptr, &offset_x, &offset_y,
1003
          &unit_type))
A
Andreas Dilger 已提交
1004 1005 1006 1007 1008
      {
         png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type);
      }
   }
#endif
1009
#if defined(PNG_pCAL_SUPPORTED)
A
Andreas Dilger 已提交
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
   {
      png_charp purpose, units;
      png_charpp params;
      png_int_32 X0, X1;
      int type, nparams;

      if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type,
         &nparams, &units, &params))
      {
         png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type,
            nparams, units, params);
      }
   }
#endif
1024
#if defined(PNG_pHYs_SUPPORTED)
A
Andreas Dilger 已提交
1025 1026 1027 1028 1029 1030 1031 1032
   {
      png_uint_32 res_x, res_y;
      int unit_type;

      if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, &unit_type))
         png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type);
   }
#endif
1033
#if defined(PNG_sBIT_SUPPORTED)
A
Andreas Dilger 已提交
1034
   {
1035
      png_color_8p sig_bit;
A
Andreas Dilger 已提交
1036

1037 1038
      if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit))
         png_set_sBIT(write_ptr, write_info_ptr, sig_bit);
A
Andreas Dilger 已提交
1039
   }
1040 1041 1042
#endif
#if defined(PNG_sCAL_SUPPORTED)
#ifdef PNG_FLOATING_POINT_SUPPORTED
G
Guy Schalnat 已提交
1043
   {
1044
      int unit;
1045
      double scal_width, scal_height;
A
Andreas Dilger 已提交
1046

1047 1048
      if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width,
         &scal_height))
G
Guy Schalnat 已提交
1049
      {
1050
         png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height);
1051 1052 1053
      }
   }
#else
1054
#ifdef PNG_FIXED_POINT_SUPPORTED
1055
   {
1056
      int unit;
1057
      png_charp scal_width, scal_height;
1058

1059 1060
      if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width,
          &scal_height))
1061
      {
1062
         png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width, scal_height);
A
Andreas Dilger 已提交
1063 1064
      }
   }
G
Guy Schalnat 已提交
1065
#endif
1066 1067
#endif
#endif
1068
#if defined(PNG_TEXT_SUPPORTED)
A
Andreas Dilger 已提交
1069 1070 1071 1072 1073 1074
   {
      png_textp text_ptr;
      int num_text;

      if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0)
      {
1075
         png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks", num_text);
A
Andreas Dilger 已提交
1076 1077 1078 1079
         png_set_text(write_ptr, write_info_ptr, text_ptr, num_text);
      }
   }
#endif
1080
#if defined(PNG_tIME_SUPPORTED)
A
Andreas Dilger 已提交
1081 1082 1083 1084 1085 1086
   {
      png_timep mod_time;

      if (png_get_tIME(read_ptr, read_info_ptr, &mod_time))
      {
         png_set_tIME(write_ptr, write_info_ptr, mod_time);
1087
#if defined(PNG_TIME_RFC1123_SUPPORTED)
1088 1089 1090 1091
         /* 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.
          */
1092
         png_memcpy(tIME_string,
1093
                    png_convert_to_rfc1123(read_ptr, mod_time),
1094
                    png_sizeof(tIME_string));
1095
         tIME_string[png_sizeof(tIME_string) - 1] = '\0';
1096 1097
         tIME_chunk_present++;
#endif /* PNG_TIME_RFC1123_SUPPORTED */
1098
      }
A
Andreas Dilger 已提交
1099 1100
   }
#endif
1101
#if defined(PNG_tRNS_SUPPORTED)
A
Andreas Dilger 已提交
1102 1103 1104 1105 1106 1107 1108 1109
   {
      png_bytep trans;
      int num_trans;
      png_color_16p trans_values;

      if (png_get_tRNS(read_ptr, read_info_ptr, &trans, &num_trans,
         &trans_values))
      {
1110 1111 1112
         int sample_max = (1 << read_info_ptr->bit_depth);
         /* libpng doesn't reject a tRNS chunk with out-of-range samples */
         if (!((read_info_ptr->color_type == PNG_COLOR_TYPE_GRAY &&
1113 1114 1115 1116 1117 1118 1119
             (int)trans_values->gray > sample_max) ||
             (read_info_ptr->color_type == PNG_COLOR_TYPE_RGB &&
             ((int)trans_values->red > sample_max ||
             (int)trans_values->green > sample_max ||
             (int)trans_values->blue > sample_max))))
            png_set_tRNS(write_ptr, write_info_ptr, trans, num_trans,
               trans_values);
A
Andreas Dilger 已提交
1120 1121 1122
      }
   }
#endif
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
   {
      png_unknown_chunkp unknowns;
      int num_unknowns = (int)png_get_unknown_chunks(read_ptr, read_info_ptr,
         &unknowns);
      if (num_unknowns)
      {
         png_size_t i;
         png_set_unknown_chunks(write_ptr, write_info_ptr, unknowns,
           num_unknowns);
1133 1134 1135 1136
         /* 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.
          */
1137
         for (i = 0; i < (png_size_t)num_unknowns; i++)
1138 1139
           png_set_unknown_chunk_location(write_ptr, write_info_ptr, i,
             unknowns[i].location);
1140 1141 1142
      }
   }
#endif
A
Andreas Dilger 已提交
1143

1144
#ifdef PNG_WRITE_SUPPORTED
1145
   png_debug(0, "Writing info struct");
1146 1147

/* If we wanted, we could write info in two steps:
1148
 * png_write_info_before_PLTE(write_ptr, write_info_ptr);
1149
 */
A
Andreas Dilger 已提交
1150
   png_write_info(write_ptr, write_info_ptr);
1151 1152 1153 1154

#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
   if (user_chunk_data[0] != 0)
   {
1155
      png_byte png_sTER[5] = {115,  84,  69,  82, '\0'};
1156

1157 1158
      unsigned char
        ster_chunk_data[1];
1159

1160 1161 1162 1163 1164
      if (verbose)
         fprintf(STDERR, "\n stereo mode = %lu\n",
           (unsigned long)(user_chunk_data[0] - 1));
      ster_chunk_data[0]=(unsigned char)(user_chunk_data[0] - 1);
      png_write_chunk(write_ptr, png_sTER, ster_chunk_data, 1);
1165 1166 1167
   }
   if (user_chunk_data[1] != 0 || user_chunk_data[2] != 0)
   {
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
      png_byte png_vpAg[5] = {118, 112,  65, 103, '\0'};

      unsigned char
        vpag_chunk_data[9];

      if (verbose)
         fprintf(STDERR, " vpAg = %lu x %lu, units = %lu\n",
           (unsigned long)user_chunk_data[1],
           (unsigned long)user_chunk_data[2],
           (unsigned long)user_chunk_data[3]);
      png_save_uint_32(vpag_chunk_data, user_chunk_data[1]);
      png_save_uint_32(vpag_chunk_data + 4, user_chunk_data[2]);
      vpag_chunk_data[8] = (unsigned char)(user_chunk_data[3] & 0xff);
      png_write_chunk(write_ptr, png_vpAg, vpag_chunk_data, 9);
1182 1183 1184
   }

#endif
1185
#endif
A
Andreas Dilger 已提交
1186

1187
#ifdef SINGLE_ROWBUF_ALLOC
1188
   png_debug(0, "Allocating row buffer...");
1189
   row_buf = (png_bytep)png_malloc(read_ptr,
A
Andreas Dilger 已提交
1190
      png_get_rowbytes(read_ptr, read_info_ptr));
1191
   png_debug1(0, "0x%08lx", (unsigned long)row_buf);
1192
#endif /* SINGLE_ROWBUF_ALLOC */
1193
   png_debug(0, "Writing row data");
A
Andreas Dilger 已提交
1194

1195 1196
#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
  defined(PNG_WRITE_INTERLACING_SUPPORTED)
A
Andreas Dilger 已提交
1197
   num_pass = png_set_interlace_handling(read_ptr);
1198
#  ifdef PNG_WRITE_SUPPORTED
A
Andreas Dilger 已提交
1199
   png_set_interlace_handling(write_ptr);
1200
#  endif
1201
#else
1202
   num_pass = 1;
1203
#endif
A
Andreas Dilger 已提交
1204

1205 1206 1207 1208 1209
#ifdef PNGTEST_TIMING
   t_stop = (float)clock();
   t_misc += (t_stop - t_start);
   t_start = t_stop;
#endif
A
Andreas Dilger 已提交
1210 1211
   for (pass = 0; pass < num_pass; pass++)
   {
1212
      png_debug1(0, "Writing row data for pass %d", pass);
A
Andreas Dilger 已提交
1213 1214
      for (y = 0; y < height; y++)
      {
1215
#ifndef SINGLE_ROWBUF_ALLOC
1216
         png_debug2(0, "Allocating row buffer (pass %d, y = %ld)...", pass, y);
1217 1218
         row_buf = (png_bytep)png_malloc(read_ptr,
            png_get_rowbytes(read_ptr, read_info_ptr));
1219
         png_debug2(0, "0x%08lx (%ld bytes)", (unsigned long)row_buf,
1220 1221
            png_get_rowbytes(read_ptr, read_info_ptr));
#endif /* !SINGLE_ROWBUF_ALLOC */
1222
         png_read_rows(read_ptr, (png_bytepp)&row_buf, png_bytepp_NULL, 1);
1223 1224

#ifdef PNG_WRITE_SUPPORTED
1225 1226 1227 1228 1229
#ifdef PNGTEST_TIMING
         t_stop = (float)clock();
         t_decode += (t_stop - t_start);
         t_start = t_stop;
#endif
G
Guy Schalnat 已提交
1230
         png_write_rows(write_ptr, (png_bytepp)&row_buf, 1);
1231 1232 1233 1234 1235
#ifdef PNGTEST_TIMING
         t_stop = (float)clock();
         t_encode += (t_stop - t_start);
         t_start = t_stop;
#endif
1236 1237 1238
#endif /* PNG_WRITE_SUPPORTED */

#ifndef SINGLE_ROWBUF_ALLOC
1239
         png_debug2(0, "Freeing row buffer (pass %d, y = %ld)", pass, y);
1240
         png_free(read_ptr, row_buf);
1241
         row_buf = NULL;
1242
#endif /* !SINGLE_ROWBUF_ALLOC */
G
Guy Schalnat 已提交
1243 1244 1245
      }
   }

1246
#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
1247
   png_free_data(read_ptr, read_info_ptr, PNG_FREE_UNKN, -1);
1248 1249
#endif
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
1250
   png_free_data(write_ptr, write_info_ptr, PNG_FREE_UNKN, -1);
1251 1252
#endif

1253
   png_debug(0, "Reading and writing end_info data");
1254

A
Andreas Dilger 已提交
1255
   png_read_end(read_ptr, end_info_ptr);
1256
#if defined(PNG_TEXT_SUPPORTED)
1257 1258 1259 1260 1261 1262
   {
      png_textp text_ptr;
      int num_text;

      if (png_get_text(read_ptr, end_info_ptr, &text_ptr, &num_text) > 0)
      {
1263
         png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks", num_text);
1264 1265 1266 1267
         png_set_text(write_ptr, write_end_info_ptr, text_ptr, num_text);
      }
   }
#endif
1268
#if defined(PNG_tIME_SUPPORTED)
1269 1270 1271 1272 1273 1274 1275
   {
      png_timep mod_time;

      if (png_get_tIME(read_ptr, end_info_ptr, &mod_time))
      {
         png_set_tIME(write_ptr, write_end_info_ptr, mod_time);
#if defined(PNG_TIME_RFC1123_SUPPORTED)
1276
         /* We have to use png_memcpy instead of "=" because the string
1277 1278
            pointed to by png_convert_to_rfc1123() gets free'ed before
            we use it */
1279 1280 1281
         png_memcpy(tIME_string,
                    png_convert_to_rfc1123(read_ptr, mod_time),
                    png_sizeof(tIME_string));
1282
         tIME_string[png_sizeof(tIME_string) - 1] = '\0';
1283 1284 1285 1286
         tIME_chunk_present++;
#endif /* PNG_TIME_RFC1123_SUPPORTED */
      }
   }
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298
#endif
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
   {
      png_unknown_chunkp unknowns;
      int num_unknowns;
      num_unknowns = (int)png_get_unknown_chunks(read_ptr, end_info_ptr,
         &unknowns);
      if (num_unknowns)
      {
         png_size_t i;
         png_set_unknown_chunks(write_ptr, write_end_info_ptr, unknowns,
           num_unknowns);
1299 1300 1301 1302
         /* 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.
          */
1303
         for (i = 0; i < (png_size_t)num_unknowns; i++)
1304 1305
           png_set_unknown_chunk_location(write_ptr, write_end_info_ptr, i,
             unknowns[i].location);
1306 1307
      }
   }
1308
#endif
1309
#ifdef PNG_WRITE_SUPPORTED
1310
   png_write_end(write_ptr, write_end_info_ptr);
1311
#endif
1312

1313
#ifdef PNG_EASY_ACCESS_SUPPORTED
1314
   if (verbose)
1315 1316 1317 1318
   {
      png_uint_32 iwidth, iheight;
      iwidth = png_get_image_width(write_ptr, write_info_ptr);
      iheight = png_get_image_height(write_ptr, write_info_ptr);
1319
      fprintf(STDERR, "\n Image width = %lu, height = %lu\n",
1320
         (unsigned long)iwidth, (unsigned long)iheight);
1321 1322
   }
#endif
G
Guy Schalnat 已提交
1323

1324
   png_debug(0, "Destroying data structs");
1325
#ifdef SINGLE_ROWBUF_ALLOC
1326
   png_debug(1, "destroying row_buf for read_ptr");
1327
   png_free(read_ptr, row_buf);
1328
   row_buf = NULL;
1329
#endif /* SINGLE_ROWBUF_ALLOC */
1330
   png_debug(1, "destroying read_ptr, read_info_ptr, end_info_ptr");
A
Andreas Dilger 已提交
1331
   png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
1332
#ifdef PNG_WRITE_SUPPORTED
1333
   png_debug(1, "destroying write_end_info_ptr");
1334
   png_destroy_info_struct(write_ptr, &write_end_info_ptr);
1335
   png_debug(1, "destroying write_ptr, write_info_ptr");
A
Andreas Dilger 已提交
1336
   png_destroy_write_struct(&write_ptr, &write_info_ptr);
1337
#endif
1338
   png_debug(0, "Destruction complete.");
G
Guy Schalnat 已提交
1339

1340 1341
   FCLOSE(fpin);
   FCLOSE(fpout);
G
Guy Schalnat 已提交
1342

1343
   png_debug(0, "Opening files for comparison");
1344 1345 1346 1347
#if defined(_WIN32_WCE)
   MultiByteToWideChar(CP_ACP, 0, inname, -1, path, MAX_PATH);
   if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
#else
A
Andreas Dilger 已提交
1348
   if ((fpin = fopen(inname, "rb")) == NULL)
1349
#endif
G
Guy Schalnat 已提交
1350
   {
G
Guy Schalnat 已提交
1351
      fprintf(STDERR, "Could not find file %s\n", inname);
1352
      return (1);
G
Guy Schalnat 已提交
1353 1354
   }

1355 1356 1357 1358
#if defined(_WIN32_WCE)
   MultiByteToWideChar(CP_ACP, 0, outname, -1, path, MAX_PATH);
   if ((fpout = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
#else
A
Andreas Dilger 已提交
1359
   if ((fpout = fopen(outname, "rb")) == NULL)
1360
#endif
G
Guy Schalnat 已提交
1361
   {
G
Guy Schalnat 已提交
1362
      fprintf(STDERR, "Could not find file %s\n", outname);
1363
      FCLOSE(fpin);
1364
      return (1);
G
Guy Schalnat 已提交
1365
   }
A
Andreas Dilger 已提交
1366

1367
   for (;;)
G
Guy Schalnat 已提交
1368
   {
A
Andreas Dilger 已提交
1369
      png_size_t num_in, num_out;
G
Guy Schalnat 已提交
1370

1371 1372
         READFILE(fpin, inbuf, 1, num_in);
         READFILE(fpout, outbuf, 1, num_out);
G
Guy Schalnat 已提交
1373 1374 1375

      if (num_in != num_out)
      {
1376
         fprintf(STDERR, "\nFiles %s and %s are of a different size\n",
G
Guy Schalnat 已提交
1377
                 inname, outname);
1378
         if (wrote_question == 0)
1379 1380
         {
            fprintf(STDERR,
1381
         "   Was %s written with the same maximum IDAT chunk size (%d bytes),",
1382
              inname, PNG_ZBUF_SIZE);
1383
            fprintf(STDERR,
1384
              "\n   filtering heuristic (libpng default), compression");
1385
            fprintf(STDERR,
1386
              " level (zlib default),\n   and zlib version (%s)?\n\n",
1387
              ZLIB_VERSION);
1388
            wrote_question = 1;
1389
         }
1390 1391
         FCLOSE(fpin);
         FCLOSE(fpout);
1392
         return (0);
G
Guy Schalnat 已提交
1393 1394 1395 1396 1397
      }

      if (!num_in)
         break;

A
Andreas Dilger 已提交
1398
      if (png_memcmp(inbuf, outbuf, num_in))
G
Guy Schalnat 已提交
1399
      {
1400
         fprintf(STDERR, "\nFiles %s and %s are different\n", inname, outname);
1401
         if (wrote_question == 0)
1402 1403
         {
            fprintf(STDERR,
1404
         "   Was %s written with the same maximum IDAT chunk size (%d bytes),",
1405
                 inname, PNG_ZBUF_SIZE);
1406
            fprintf(STDERR,
1407
              "\n   filtering heuristic (libpng default), compression");
1408
            fprintf(STDERR,
1409
              " level (zlib default),\n   and zlib version (%s)?\n\n",
1410
              ZLIB_VERSION);
1411
            wrote_question = 1;
1412
         }
1413 1414
         FCLOSE(fpin);
         FCLOSE(fpout);
1415
         return (0);
G
Guy Schalnat 已提交
1416 1417 1418
      }
   }

1419 1420
   FCLOSE(fpin);
   FCLOSE(fpout);
G
Guy Schalnat 已提交
1421

1422
   return (0);
G
Guy Schalnat 已提交
1423
}
G
Guy Schalnat 已提交
1424

1425
/* Input and output filenames */
1426
#ifdef RISCOS
1427 1428
static PNG_CONST char *inname = "pngtest/png";
static PNG_CONST char *outname = "pngout/png";
1429
#else
1430 1431
static PNG_CONST char *inname = "pngtest.png";
static PNG_CONST char *outname = "pngout.png";
1432 1433 1434 1435 1436 1437 1438 1439
#endif

int
main(int argc, char *argv[])
{
   int multiple = 0;
   int ierror = 0;

1440
   fprintf(STDERR, "\n Testing libpng version %s\n", PNG_LIBPNG_VER_STRING);
1441
   fprintf(STDERR, "   with zlib   version %s\n", ZLIB_VERSION);
1442
   fprintf(STDERR, "%s", png_get_copyright(NULL));
1443
   /* Show the version of libpng used in building the library */
1444
   fprintf(STDERR, " library (%lu):%s",
1445
      (unsigned long)png_access_version_number(),
1446
      png_get_header_version(NULL));
1447
   /* Show the version of libpng used in building the application */
1448
   fprintf(STDERR, " pngtest (%lu):%s", (unsigned long)PNG_LIBPNG_VER,
1449
      PNG_HEADER_VERSION_STRING);
1450
   fprintf(STDERR, " sizeof(png_struct)=%ld, sizeof(png_info)=%ld\n",
1451
                    (long)png_sizeof(png_struct), (long)png_sizeof(png_info));
1452 1453

   /* Do some consistency checking on the memory allocation settings, I'm
1454 1455 1456 1457
    * 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
    */
1458
#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K)
1459
      fprintf(STDERR, " NOTE: Zlib compiled for max 64k, libpng not\n");
1460
#endif
1461
   /* I think the following can happen. */
1462
#if !defined(MAXSEG_64K) && defined(PNG_MAX_MALLOC_64K)
1463
      fprintf(STDERR, " NOTE: libpng compiled for max 64k, zlib not\n");
1464
#endif
1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476

   if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING))
   {
      fprintf(STDERR,
         "Warning: versions are different between png.h and png.c\n");
      fprintf(STDERR, "  png.h version: %s\n", PNG_LIBPNG_VER_STRING);
      fprintf(STDERR, "  png.c version: %s\n\n", png_libpng_ver);
      ++ierror;
   }

   if (argc > 1)
   {
1477
      if (strcmp(argv[1], "-m") == 0)
1478
      {
1479
         multiple = 1;
1480 1481
         status_dots_requested = 0;
      }
1482 1483 1484 1485 1486
      else if (strcmp(argv[1], "-mv") == 0 ||
               strcmp(argv[1], "-vm") == 0 )
      {
         multiple = 1;
         verbose = 1;
1487
         status_dots_requested = 1;
1488 1489 1490 1491
      }
      else if (strcmp(argv[1], "-v") == 0)
      {
         verbose = 1;
1492
         status_dots_requested = 1;
1493 1494 1495
         inname = argv[2];
      }
      else
1496
      {
1497
         inname = argv[1];
1498 1499
         status_dots_requested = 0;
      }
1500 1501
   }

1502 1503
   if (!multiple && argc == 3 + verbose)
     outname = argv[2 + verbose];
1504

1505
   if ((!multiple && argc > 3 + verbose) || (multiple && argc < 2))
1506
   {
1507 1508
     fprintf(STDERR,
       "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n",
1509
        argv[0], argv[0]);
1510 1511 1512 1513
     fprintf(STDERR,
       "  reads/writes one PNG file (without -m) or multiple files (-m)\n");
     fprintf(STDERR,
       "  with -m %s is used as a temporary file\n", outname);
1514 1515 1516 1517 1518 1519
     exit(1);
   }

   if (multiple)
   {
      int i;
1520
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
1521 1522
      int allocation_now = current_allocation;
#endif
1523
      for (i=2; i<argc; ++i)
1524
      {
1525 1526 1527 1528
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
         int k;
#endif
         int kerror;
1529
         fprintf(STDERR, "\n Testing %s:", argv[i]);
1530
         kerror = test_one_file(argv[i], outname);
1531
         if (kerror == 0)
1532
         {
1533
#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
1534 1535
            fprintf(STDERR, "\n PASS (%lu zero samples)\n",
               (unsigned long)zero_samples);
1536
#else
1537
            fprintf(STDERR, " PASS\n");
1538
#endif
1539
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
1540 1541
            for (k = 0; k<256; k++)
               if (filters_used[k])
1542
                  fprintf(STDERR, " Filter %d was used %lu times\n",
1543
                     k, (unsigned long)filters_used[k]);
1544
#endif
1545
#if defined(PNG_TIME_RFC1123_SUPPORTED)
1546
         if (tIME_chunk_present != 0)
1547
            fprintf(STDERR, " tIME = %s\n", tIME_string);
1548 1549 1550 1551 1552
         tIME_chunk_present = 0;
#endif /* PNG_TIME_RFC1123_SUPPORTED */
         }
         else
         {
1553 1554
            fprintf(STDERR, " FAIL\n");
            ierror += kerror;
1555
         }
1556
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
1557 1558
         if (allocation_now != current_allocation)
            fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n",
1559
               current_allocation - allocation_now);
1560 1561
         if (current_allocation != 0)
         {
1562 1563 1564 1565
            memory_infop pinfo = pinformation;

            fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n",
               current_allocation);
1566 1567
            while (pinfo != NULL)
            {
1568 1569
               fprintf(STDERR, " %lu bytes at %x\n",
                 (unsigned long)pinfo->size,
1570
                 (unsigned int) pinfo->pointer);
1571
               pinfo = pinfo->next;
1572
            }
1573
         }
1574 1575
#endif
      }
1576
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
1577
         fprintf(STDERR, " Current memory allocation: %10d bytes\n",
1578
            current_allocation);
1579
         fprintf(STDERR, " Maximum memory allocation: %10d bytes\n",
1580
            maximum_allocation);
1581 1582 1583 1584
         fprintf(STDERR, " Total   memory allocation: %10d bytes\n",
            total_allocation);
         fprintf(STDERR, "     Number of allocations: %10d\n",
            num_allocations);
1585
#endif
1586 1587 1588
   }
   else
   {
1589
      int i;
1590
      for (i = 0; i<3; ++i)
1591
      {
1592
         int kerror;
1593
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
1594 1595
         int allocation_now = current_allocation;
#endif
1596
         if (i == 1) status_dots_requested = 1;
1597
         else if (verbose == 0)status_dots_requested = 0;
1598
         if (i == 0 || verbose == 1 || ierror != 0)
1599
            fprintf(STDERR, "\n Testing %s:", inname);
1600
         kerror = test_one_file(inname, outname);
1601
         if (kerror == 0)
1602
         {
1603
            if (verbose == 1 || i == 2)
1604
            {
1605
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
1606
                int k;
1607
#endif
1608
#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
1609 1610
                fprintf(STDERR, "\n PASS (%lu zero samples)\n",
                   (unsigned long)zero_samples);
1611 1612 1613
#else
                fprintf(STDERR, " PASS\n");
#endif
1614
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
1615 1616
                for (k = 0; k<256; k++)
                   if (filters_used[k])
1617
                      fprintf(STDERR, " Filter %d was used %lu times\n",
1618 1619
                         k,
                         (unsigned long)filters_used[k]);
1620
#endif
1621
#if defined(PNG_TIME_RFC1123_SUPPORTED)
1622
             if (tIME_chunk_present != 0)
1623
                fprintf(STDERR, " tIME = %s\n", tIME_string);
1624 1625
#endif /* PNG_TIME_RFC1123_SUPPORTED */
            }
1626 1627 1628
         }
         else
         {
1629
            if (verbose == 0 && i != 2)
1630
               fprintf(STDERR, "\n Testing %s:", inname);
1631 1632
            fprintf(STDERR, " FAIL\n");
            ierror += kerror;
1633
         }
1634
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
1635 1636
         if (allocation_now != current_allocation)
             fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n",
1637
               current_allocation - allocation_now);
1638 1639
         if (current_allocation != 0)
         {
1640
             memory_infop pinfo = pinformation;
1641

1642 1643
             fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n",
                current_allocation);
1644 1645
             while (pinfo != NULL)
             {
1646
                fprintf(STDERR, " %lu bytes at %x\n",
1647
                   (unsigned long)pinfo->size, (unsigned int)pinfo->pointer);
1648 1649 1650 1651
                pinfo = pinfo->next;
             }
          }
#endif
1652
       }
1653
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
1654
       fprintf(STDERR, " Current memory allocation: %10d bytes\n",
1655
          current_allocation);
1656
       fprintf(STDERR, " Maximum memory allocation: %10d bytes\n",
1657
          maximum_allocation);
1658 1659 1660 1661
       fprintf(STDERR, " Total   memory allocation: %10d bytes\n",
          total_allocation);
       fprintf(STDERR, "     Number of allocations: %10d\n",
            num_allocations);
1662
#endif
1663 1664
   }

1665 1666 1667 1668
#ifdef PNGTEST_TIMING
   t_stop = (float)clock();
   t_misc += (t_stop - t_start);
   t_start = t_stop;
1669
   fprintf(STDERR, " CPU time used = %.3f seconds",
1670
      (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC);
1671
   fprintf(STDERR, " (decoding %.3f,\n",
1672
      t_decode/(float)CLOCKS_PER_SEC);
1673
   fprintf(STDERR, "        encoding %.3f ,",
1674
      t_encode/(float)CLOCKS_PER_SEC);
1675
   fprintf(STDERR, " other %.3f seconds)\n\n",
1676 1677 1678
      t_misc/(float)CLOCKS_PER_SEC);
#endif

1679
   if (ierror == 0)
1680
      fprintf(STDERR, " libpng passes test\n");
1681
   else
1682
      fprintf(STDERR, " libpng FAILS test\n");
1683
   return (int)(ierror != 0);
1684
}
1685

1686
/* Generate a compiler error if there is an old png.h in the search path. */
1687
typedef version_1_2_37rc01 your_png_h_is_not_version_1_2_37rc01;