pngtest.c 49.7 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.41 [November 2, 2009]
5
 * Copyright (c) 1998-2009 Glenn Randers-Pehrson
6 7
 * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
 * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
8
 *
9 10 11 12
 * This code is released under the libpng license.
 * For conditions of distribution and use, see the disclaimer
 * and license in png.h
 *
13 14 15 16 17 18
 * 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.
 *
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 23
 * 3) unknown unsafe-to-copy ancillary chunks or unknown critical chunks
 *    exist in the input file.
24 25
 * 4) others not listed here...
 * 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 29
 *
 * 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
30 31
 * 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 ..."
32
 */
G
Guy Schalnat 已提交
33

34 35
#include "png.h"

36
#ifdef _WIN32_WCE
37 38 39 40 41 42
#  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) \
43
     if (ReadFile(file, data, length, &check, NULL)) check = 0
44 45
#  define WRITEFILE(file, data, length, check)) \
     if (WriteFile(file, data, length, &check, NULL)) check = 0
46 47 48 49 50
#  define FCLOSE(file) CloseHandle(file)
#else
#  include <stdio.h>
#  include <stdlib.h>
#  define READFILE(file, data, length, check) \
51
     check=(png_size_t)fread(data, (png_size_t)1, length, file)
52
#  define WRITEFILE(file, data, length, check) \
53
     check=(png_size_t)fwrite(data, (png_size_t)1, length, file)
54 55
#  define FCLOSE(file) fclose(file)
#endif
A
Andreas Dilger 已提交
56

57
#ifndef PNG_STDIO_SUPPORTED
58
#  ifdef _WIN32_WCE
59 60 61 62
     typedef HANDLE                png_FILE_p;
#  else
     typedef FILE                * png_FILE_p;
#  endif
63 64
#endif

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

#if !PNG_DEBUG
71
#  define SINGLE_ROWBUF_ALLOC  /* Makes buffer overruns easier to nail */
72
#endif
A
Andreas Dilger 已提交
73

74 75 76 77
/* Turn on CPU timing
#define PNGTEST_TIMING
*/

78
#ifndef PNG_FLOATING_POINT_SUPPORTED
79 80 81
#undef PNGTEST_TIMING
#endif

82 83 84 85 86
#ifdef PNGTEST_TIMING
static float t_start, t_stop, t_decode, t_encode, t_misc;
#include <time.h>
#endif

87
#ifdef PNG_TIME_RFC1123_SUPPORTED
88
#define PNG_tIME_STRING_LENGTH 29
89
static int tIME_chunk_present = 0;
90
static char tIME_string[PNG_tIME_STRING_LENGTH] = "tIME chunk is not present";
91
#endif
92

93 94
static int verbose = 0;

95
int test_one_file PNGARG((PNG_CONST char *inname, PNG_CONST char *outname));
96

G
Guy Schalnat 已提交
97 98 99 100
#ifdef __TURBOC__
#include <mem.h>
#endif

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

105 106 107 108 109 110 111 112 113 114
/* 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

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

120
void
121 122 123
#ifdef PNG_1_0_X
PNGAPI
#endif
124
read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass);
125
void
126 127 128
#ifdef PNG_1_0_X
PNGAPI
#endif
129
read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass)
130
{
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
   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");
146
}
147

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


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

187
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
188 189 190
/* Example of using user transform callback (we don't transform anything,
 * but merely count the zero samples)
 */
191

192
static png_uint_32 zero_samples;
193

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

208
   /* Contents of row_info:
209 210 211 212 213 214 215 216
    *  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)
    */

217
    /* Counts the number of zero samples (or zero pixels if color_type is 3 */
218

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

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

298
static int wrote_question = 0;
299

300
#ifndef PNG_STDIO_SUPPORTED
301
/* START of code to validate stdio-free compilation */
302 303 304 305 306 307 308
/* 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.
 */
309

310 311
#ifndef USE_FAR_KEYWORD
static void
312
pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
313 314 315 316 317 318
{
   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.
    */
319
   READFILE((png_FILE_p)png_ptr->io_ptr, data, length, check);
320 321 322

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

332 333
#define NEAR_BUF_SIZE 1024
#define MIN(a,b) (a <= b ? a : b)
334

335
static void
336
pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
337 338 339
{
   int check;
   png_byte *n_data;
340
   png_FILE_p io_ptr;
341 342 343

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

374
#ifdef PNG_WRITE_FLUSH_SUPPORTED
375
static void
376
pngtest_flush(png_structp png_ptr)
377
{
378
   /* Do nothing; fflush() is said to be just a waste of energy. */
379
   png_ptr = png_ptr;  /* Stifle compiler warning */
380 381
}
#endif
G
Guy Schalnat 已提交
382

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

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

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

/* 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
456
pngtest_warning(png_structp png_ptr, png_const_charp message)
457 458 459 460 461 462 463 464 465 466 467 468 469
{
   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
470
pngtest_error(png_structp png_ptr, png_const_charp message)
471
{
472
   pngtest_warning(png_ptr, message);
473
   /* We can return because png_error calls the default handler, which is
474 475
    * actually OK in this case.
    */
476
}
477
#endif /* !PNG_STDIO_SUPPORTED */
478
/* END of code to validate stdio-free compilation */
479

480
/* START of code to validate memory allocation and deallocation */
481
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
482 483

/* Allocate memory.  For reasonable files, size should never exceed
484 485 486 487 488 489 490 491
 * 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.
 */
492 493
typedef struct memory_information
{
494
   png_uint_32               size;
495
   png_voidp                 pointer;
496 497 498 499 500 501 502
   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;
503 504
static int total_allocation = 0;
static int num_allocations = 0;
505

506 507
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));
508 509

png_voidp
510 511
png_debug_malloc(png_structp png_ptr, png_uint_32 size)
{
512 513

   /* png_malloc has already tested for NULL; png_create_struct calls
514 515
    * png_debug_malloc directly, with png_ptr == NULL which is OK
    */
516

517
   if (size == 0)
518
      return (NULL);
519 520 521 522

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

/* Free a pointer.  It is removed from the list at the same time. */
void
559
png_debug_free(png_structp png_ptr, png_voidp ptr)
560 561
{
   if (png_ptr == NULL)
562
      fprintf(STDERR, "NULL pointer to png_debug_free.\n");
563 564
   if (ptr == 0)
   {
565 566 567 568 569 570 571 572 573
#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;
574 575
      for (;;)
      {
576
         memory_infop pinfo = *ppinfo;
577 578
         if (pinfo->pointer == ptr)
         {
579 580 581 582 583
            *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
584
               the memory that is to be freed. */
585
            png_memset(ptr, 0x55, pinfo->size);
586
            png_free_default(png_ptr, pinfo);
587
            pinfo = NULL;
588 589
            break;
         }
590 591
         if (pinfo->next == NULL)
         {
592
            fprintf(STDERR, "Pointer %x not found\n", (unsigned int)ptr);
593 594 595 596 597 598 599
            break;
         }
         ppinfo = &pinfo->next;
      }
   }

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

608 609

/* Demonstration of user chunk support of the sTER and vpAg chunks */
610
#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
611

612
/* (sTER is a public chunk not yet known by libpng.  vpAg is a private
613 614 615 616 617 618 619 620 621 622 623 624 625
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)
{
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 666 667 668 669 670
   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);
671 672 673 674 675

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

676
/* Test one file */
677 678
int
test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
G
Guy Schalnat 已提交
679
{
680 681
   static png_FILE_p fpin;
   static png_FILE_p fpout;  /* "static" prevents setjmp corruption */
682 683 684 685 686 687 688 689 690 691 692
   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 已提交
693
   png_bytep row_buf;
G
Guy Schalnat 已提交
694
   png_uint_32 y;
A
Andreas Dilger 已提交
695 696 697
   png_uint_32 width, height;
   int num_pass, pass;
   int bit_depth, color_type;
698
#ifdef PNG_SETJMP_SUPPORTED
A
Andreas Dilger 已提交
699
#ifdef USE_FAR_KEYWORD
700
   jmp_buf jmpbuf;
701
#endif
702
#endif
703

704
#ifdef _WIN32_WCE
705 706
   TCHAR path[MAX_PATH];
#endif
707
   char inbuf[256], outbuf[256];
G
Guy Schalnat 已提交
708

709
   row_buf = NULL;
G
Guy Schalnat 已提交
710

711
#ifdef _WIN32_WCE
712 713 714
   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 已提交
715
   if ((fpin = fopen(inname, "rb")) == NULL)
716
#endif
G
Guy Schalnat 已提交
717 718
   {
      fprintf(STDERR, "Could not find input file %s\n", inname);
719
      return (1);
G
Guy Schalnat 已提交
720 721
   }

722
#ifdef _WIN32_WCE
723 724 725
   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 已提交
726
   if ((fpout = fopen(outname, "wb")) == NULL)
727
#endif
G
Guy Schalnat 已提交
728
   {
G
Guy Schalnat 已提交
729
      fprintf(STDERR, "Could not open output file %s\n", outname);
730
      FCLOSE(fpin);
731
      return (1);
G
Guy Schalnat 已提交
732 733
   }

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

750
#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
751 752 753 754 755 756
   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);
757 758

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

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

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

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

863
#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
864
   {
865 866 867 868
      int i;
      for (i = 0; i<256; i++)
         filters_used[i] = 0;
      png_set_read_user_transform_fn(read_ptr, count_filters);
869 870
   }
#endif
871
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
872
   zero_samples = 0;
873 874
   png_set_write_user_transform_fn(write_ptr, count_zero_samples);
#endif
G
Guy Schalnat 已提交
875

876
#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
877 878 879 880
#  ifndef PNG_HANDLE_CHUNK_ALWAYS
#    define PNG_HANDLE_CHUNK_ALWAYS       3
#  endif
   png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS,
881
      png_bytep_NULL, 0);
882
#endif
883
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
884 885 886 887
#  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,
888
      png_bytep_NULL, 0);
889 890
#endif

891
   png_debug(0, "Reading info struct");
A
Andreas Dilger 已提交
892
   png_read_info(read_ptr, read_info_ptr);
G
Guy Schalnat 已提交
893

894
   png_debug(0, "Transferring info struct");
A
Andreas Dilger 已提交
895 896
   {
      int interlace_type, compression_type, filter_type;
G
Guy Schalnat 已提交
897

A
Andreas Dilger 已提交
898 899 900 901
      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,
902
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
A
Andreas Dilger 已提交
903
            color_type, interlace_type, compression_type, filter_type);
904 905 906
#else
            color_type, PNG_INTERLACE_NONE, compression_type, filter_type);
#endif
A
Andreas Dilger 已提交
907 908
      }
   }
909 910
#ifdef PNG_FIXED_POINT_SUPPORTED
#ifdef PNG_cHRM_SUPPORTED
G
Guy Schalnat 已提交
911
   {
912
      png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x,
913 914 915
         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 已提交
916
      {
917 918
         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 已提交
919
      }
G
Guy Schalnat 已提交
920
   }
A
Andreas Dilger 已提交
921
#endif
922
#ifdef PNG_gAMA_SUPPORTED
A
Andreas Dilger 已提交
923
   {
924
      png_fixed_point gamma;
G
Guy Schalnat 已提交
925

926 927
      if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma))
         png_set_gAMA_fixed(write_ptr, write_info_ptr, gamma);
A
Andreas Dilger 已提交
928 929
   }
#endif
930
#else /* Use floating point versions */
931 932
#ifdef PNG_FLOATING_POINT_SUPPORTED
#ifdef PNG_cHRM_SUPPORTED
933 934 935 936 937 938 939 940 941 942 943
   {
      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
944
#ifdef PNG_gAMA_SUPPORTED
945 946 947 948 949 950 951
   {
      double gamma;

      if (png_get_gAMA(read_ptr, read_info_ptr, &gamma))
         png_set_gAMA(write_ptr, write_info_ptr, gamma);
   }
#endif
952 953
#endif /* Floating point */
#endif /* Fixed point */
954
#ifdef PNG_iCCP_SUPPORTED
G
Guy Schalnat 已提交
955
   {
956 957
      png_charp name;
      png_charp profile;
958
      png_uint_32 proflen;
959
      int compression_type;
A
Andreas Dilger 已提交
960

961 962
      if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_type,
                      &profile, &proflen))
A
Andreas Dilger 已提交
963
      {
964 965
         png_set_iCCP(write_ptr, write_info_ptr, name, compression_type,
                      profile, proflen);
A
Andreas Dilger 已提交
966
      }
G
Guy Schalnat 已提交
967
   }
968
#endif
969
#ifdef PNG_sRGB_SUPPORTED
970
   {
971
      int intent;
972 973 974 975

      if (png_get_sRGB(read_ptr, read_info_ptr, &intent))
         png_set_sRGB(write_ptr, write_info_ptr, intent);
   }
A
Andreas Dilger 已提交
976
#endif
977 978 979 980 981 982 983
   {
      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);
   }
984
#ifdef PNG_bKGD_SUPPORTED
985 986 987 988 989 990 991 992 993
   {
      png_color_16p background;

      if (png_get_bKGD(read_ptr, read_info_ptr, &background))
      {
         png_set_bKGD(write_ptr, write_info_ptr, background);
      }
   }
#endif
994
#ifdef PNG_hIST_SUPPORTED
G
Guy Schalnat 已提交
995
   {
A
Andreas Dilger 已提交
996 997 998 999
      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 已提交
1000
   }
A
Andreas Dilger 已提交
1001
#endif
1002
#ifdef PNG_oFFs_SUPPORTED
A
Andreas Dilger 已提交
1003
   {
1004
      png_int_32 offset_x, offset_y;
A
Andreas Dilger 已提交
1005
      int unit_type;
G
Guy Schalnat 已提交
1006

1007
      if (png_get_oFFs(read_ptr, read_info_ptr, &offset_x, &offset_y,
1008
          &unit_type))
A
Andreas Dilger 已提交
1009 1010 1011 1012 1013
      {
         png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type);
      }
   }
#endif
1014
#ifdef PNG_pCAL_SUPPORTED
A
Andreas Dilger 已提交
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
   {
      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
1029
#ifdef PNG_pHYs_SUPPORTED
A
Andreas Dilger 已提交
1030 1031 1032 1033 1034 1035 1036 1037
   {
      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
1038
#ifdef PNG_sBIT_SUPPORTED
A
Andreas Dilger 已提交
1039
   {
1040
      png_color_8p sig_bit;
A
Andreas Dilger 已提交
1041

1042 1043
      if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit))
         png_set_sBIT(write_ptr, write_info_ptr, sig_bit);
A
Andreas Dilger 已提交
1044
   }
1045
#endif
1046
#ifdef PNG_sCAL_SUPPORTED
1047
#ifdef PNG_FLOATING_POINT_SUPPORTED
G
Guy Schalnat 已提交
1048
   {
1049
      int unit;
1050
      double scal_width, scal_height;
A
Andreas Dilger 已提交
1051

1052 1053
      if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width,
         &scal_height))
G
Guy Schalnat 已提交
1054
      {
1055
         png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height);
1056 1057 1058
      }
   }
#else
1059
#ifdef PNG_FIXED_POINT_SUPPORTED
1060
   {
1061
      int unit;
1062
      png_charp scal_width, scal_height;
1063

1064 1065
      if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width,
          &scal_height))
1066
      {
1067
         png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width, scal_height);
A
Andreas Dilger 已提交
1068 1069
      }
   }
G
Guy Schalnat 已提交
1070
#endif
1071 1072
#endif
#endif
1073
#ifdef PNG_TEXT_SUPPORTED
A
Andreas Dilger 已提交
1074 1075 1076 1077 1078 1079
   {
      png_textp text_ptr;
      int num_text;

      if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0)
      {
1080
         png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks", num_text);
A
Andreas Dilger 已提交
1081 1082 1083 1084
         png_set_text(write_ptr, write_info_ptr, text_ptr, num_text);
      }
   }
#endif
1085
#ifdef PNG_tIME_SUPPORTED
A
Andreas Dilger 已提交
1086 1087 1088 1089 1090 1091
   {
      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);
1092
#ifdef PNG_TIME_RFC1123_SUPPORTED
1093 1094 1095 1096
         /* 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.
          */
1097
         png_memcpy(tIME_string,
1098
                    png_convert_to_rfc1123(read_ptr, mod_time),
1099
                    png_sizeof(tIME_string));
1100
         tIME_string[png_sizeof(tIME_string) - 1] = '\0';
1101 1102
         tIME_chunk_present++;
#endif /* PNG_TIME_RFC1123_SUPPORTED */
1103
      }
A
Andreas Dilger 已提交
1104 1105
   }
#endif
1106
#ifdef PNG_tRNS_SUPPORTED
A
Andreas Dilger 已提交
1107 1108 1109 1110 1111 1112 1113 1114
   {
      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))
      {
1115 1116 1117
         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 &&
1118 1119 1120 1121 1122 1123 1124
             (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 已提交
1125 1126 1127
      }
   }
#endif
1128
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
1129 1130 1131 1132 1133 1134 1135 1136 1137
   {
      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);
1138 1139 1140 1141
         /* 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.
          */
1142
         for (i = 0; i < (png_size_t)num_unknowns; i++)
1143 1144
           png_set_unknown_chunk_location(write_ptr, write_info_ptr, i,
             unknowns[i].location);
1145 1146 1147
      }
   }
#endif
A
Andreas Dilger 已提交
1148

1149
#ifdef PNG_WRITE_SUPPORTED
1150
   png_debug(0, "Writing info struct");
1151 1152

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

1157
#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
1158 1159
   if (user_chunk_data[0] != 0)
   {
1160
      png_byte png_sTER[5] = {115,  84,  69,  82, '\0'};
1161

1162 1163
      unsigned char
        ster_chunk_data[1];
1164

1165 1166 1167 1168 1169
      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);
1170 1171 1172
   }
   if (user_chunk_data[1] != 0 || user_chunk_data[2] != 0)
   {
1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
      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);
1187 1188 1189
   }

#endif
1190
#endif
A
Andreas Dilger 已提交
1191

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

1200 1201
#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
  defined(PNG_WRITE_INTERLACING_SUPPORTED)
A
Andreas Dilger 已提交
1202
   num_pass = png_set_interlace_handling(read_ptr);
1203
#  ifdef PNG_WRITE_SUPPORTED
A
Andreas Dilger 已提交
1204
   png_set_interlace_handling(write_ptr);
1205
#  endif
1206
#else
1207
   num_pass = 1;
1208
#endif
A
Andreas Dilger 已提交
1209

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

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

#ifndef SINGLE_ROWBUF_ALLOC
1244
         png_debug2(0, "Freeing row buffer (pass %d, y = %ld)", pass, y);
1245
         png_free(read_ptr, row_buf);
1246
         row_buf = NULL;
1247
#endif /* !SINGLE_ROWBUF_ALLOC */
G
Guy Schalnat 已提交
1248 1249 1250
      }
   }

1251
#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
1252
   png_free_data(read_ptr, read_info_ptr, PNG_FREE_UNKN, -1);
1253
#endif
1254
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
1255
   png_free_data(write_ptr, write_info_ptr, PNG_FREE_UNKN, -1);
1256 1257
#endif

1258
   png_debug(0, "Reading and writing end_info data");
1259

A
Andreas Dilger 已提交
1260
   png_read_end(read_ptr, end_info_ptr);
1261
#ifdef PNG_TEXT_SUPPORTED
1262 1263 1264 1265 1266 1267
   {
      png_textp text_ptr;
      int num_text;

      if (png_get_text(read_ptr, end_info_ptr, &text_ptr, &num_text) > 0)
      {
1268
         png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks", num_text);
1269 1270 1271 1272
         png_set_text(write_ptr, write_end_info_ptr, text_ptr, num_text);
      }
   }
#endif
1273
#ifdef PNG_tIME_SUPPORTED
1274 1275 1276 1277 1278 1279
   {
      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);
1280
#ifdef PNG_TIME_RFC1123_SUPPORTED
1281
         /* We have to use png_memcpy instead of "=" because the string
1282 1283
            pointed to by png_convert_to_rfc1123() gets free'ed before
            we use it */
1284 1285 1286
         png_memcpy(tIME_string,
                    png_convert_to_rfc1123(read_ptr, mod_time),
                    png_sizeof(tIME_string));
1287
         tIME_string[png_sizeof(tIME_string) - 1] = '\0';
1288 1289 1290 1291
         tIME_chunk_present++;
#endif /* PNG_TIME_RFC1123_SUPPORTED */
      }
   }
1292
#endif
1293
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
   {
      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);
1304 1305 1306 1307
         /* 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.
          */
1308
         for (i = 0; i < (png_size_t)num_unknowns; i++)
1309 1310
           png_set_unknown_chunk_location(write_ptr, write_end_info_ptr, i,
             unknowns[i].location);
1311 1312
      }
   }
1313
#endif
1314
#ifdef PNG_WRITE_SUPPORTED
1315
   png_write_end(write_ptr, write_end_info_ptr);
1316
#endif
1317

1318
#ifdef PNG_EASY_ACCESS_SUPPORTED
1319
   if (verbose)
1320 1321 1322 1323
   {
      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);
1324
      fprintf(STDERR, "\n Image width = %lu, height = %lu\n",
1325
         (unsigned long)iwidth, (unsigned long)iheight);
1326 1327
   }
#endif
G
Guy Schalnat 已提交
1328

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

1345 1346
   FCLOSE(fpin);
   FCLOSE(fpout);
G
Guy Schalnat 已提交
1347

1348
   png_debug(0, "Opening files for comparison");
1349
#ifdef _WIN32_WCE
1350 1351 1352
   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 已提交
1353
   if ((fpin = fopen(inname, "rb")) == NULL)
1354
#endif
G
Guy Schalnat 已提交
1355
   {
G
Guy Schalnat 已提交
1356
      fprintf(STDERR, "Could not find file %s\n", inname);
1357
      return (1);
G
Guy Schalnat 已提交
1358 1359
   }

1360
#ifdef _WIN32_WCE
1361 1362 1363
   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 已提交
1364
   if ((fpout = fopen(outname, "rb")) == NULL)
1365
#endif
G
Guy Schalnat 已提交
1366
   {
G
Guy Schalnat 已提交
1367
      fprintf(STDERR, "Could not find file %s\n", outname);
1368
      FCLOSE(fpin);
1369
      return (1);
G
Guy Schalnat 已提交
1370
   }
A
Andreas Dilger 已提交
1371

1372
   for (;;)
G
Guy Schalnat 已提交
1373
   {
A
Andreas Dilger 已提交
1374
      png_size_t num_in, num_out;
G
Guy Schalnat 已提交
1375

1376 1377
         READFILE(fpin, inbuf, 1, num_in);
         READFILE(fpout, outbuf, 1, num_out);
G
Guy Schalnat 已提交
1378 1379 1380

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

      if (!num_in)
         break;

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

1424 1425
   FCLOSE(fpin);
   FCLOSE(fpout);
G
Guy Schalnat 已提交
1426

1427
   return (0);
G
Guy Schalnat 已提交
1428
}
G
Guy Schalnat 已提交
1429

1430
/* Input and output filenames */
1431
#ifdef RISCOS
1432 1433
static PNG_CONST char *inname = "pngtest/png";
static PNG_CONST char *outname = "pngout/png";
1434
#else
1435 1436
static PNG_CONST char *inname = "pngtest.png";
static PNG_CONST char *outname = "pngout.png";
1437 1438 1439 1440 1441 1442 1443 1444
#endif

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

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

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

   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)
   {
1482
      if (strcmp(argv[1], "-m") == 0)
1483
      {
1484
         multiple = 1;
1485 1486
         status_dots_requested = 0;
      }
1487 1488 1489 1490 1491
      else if (strcmp(argv[1], "-mv") == 0 ||
               strcmp(argv[1], "-vm") == 0 )
      {
         multiple = 1;
         verbose = 1;
1492
         status_dots_requested = 1;
1493 1494 1495 1496
      }
      else if (strcmp(argv[1], "-v") == 0)
      {
         verbose = 1;
1497
         status_dots_requested = 1;
1498 1499 1500
         inname = argv[2];
      }
      else
1501
      {
1502
         inname = argv[1];
1503 1504
         status_dots_requested = 0;
      }
1505 1506
   }

1507 1508
   if (!multiple && argc == 3 + verbose)
     outname = argv[2 + verbose];
1509

1510
   if ((!multiple && argc > 3 + verbose) || (multiple && argc < 2))
1511
   {
1512 1513
     fprintf(STDERR,
       "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n",
1514
        argv[0], argv[0]);
1515 1516 1517 1518
     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);
1519 1520 1521 1522 1523 1524
     exit(1);
   }

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

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

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

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

1684
   if (ierror == 0)
1685
      fprintf(STDERR, " libpng passes test\n");
1686
   else
1687
      fprintf(STDERR, " libpng FAILS test\n");
1688
   return (int)(ierror != 0);
1689
}
1690

1691
/* Generate a compiler error if there is an old png.h in the search path. */
1692
typedef version_1_2_41beta13 your_png_h_is_not_version_1_2_41beta13;