]> git.saurik.com Git - wxWidgets.git/commitdiff
readded previsouly removed files (needed for merge after import of libpng-1.2.4)
authorGilles Depeyrot <gilles_depeyrot@mac.com>
Mon, 8 Jul 2002 21:35:05 +0000 (21:35 +0000)
committerGilles Depeyrot <gilles_depeyrot@mac.com>
Mon, 8 Jul 2002 21:35:05 +0000 (21:35 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@16099 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

29 files changed:
src/png/ANNOUNCE [new file with mode: 0644]
src/png/CHANGES [new file with mode: 0644]
src/png/INSTALL [new file with mode: 0644]
src/png/README [new file with mode: 0644]
src/png/TODO [new file with mode: 0644]
src/png/Y2KINFO [new file with mode: 0644]
src/png/example.c [new file with mode: 0644]
src/png/libpng.3 [new file with mode: 0644]
src/png/libpng.txt [new file with mode: 0644]
src/png/libpngpf.3 [new file with mode: 0644]
src/png/png.5 [new file with mode: 0644]
src/png/png.c [new file with mode: 0644]
src/png/png.h [new file with mode: 0644]
src/png/pngconf.h [new file with mode: 0644]
src/png/pngerror.c [new file with mode: 0644]
src/png/pngget.c [new file with mode: 0644]
src/png/pngmem.c [new file with mode: 0644]
src/png/pngpread.c [new file with mode: 0644]
src/png/pngread.c [new file with mode: 0644]
src/png/pngrio.c [new file with mode: 0644]
src/png/pngrtran.c [new file with mode: 0644]
src/png/pngrutil.c [new file with mode: 0644]
src/png/pngset.c [new file with mode: 0644]
src/png/pngtest.c [new file with mode: 0644]
src/png/pngtrans.c [new file with mode: 0644]
src/png/pngwio.c [new file with mode: 0644]
src/png/pngwrite.c [new file with mode: 0644]
src/png/pngwtran.c [new file with mode: 0644]
src/png/pngwutil.c [new file with mode: 0644]

diff --git a/src/png/ANNOUNCE b/src/png/ANNOUNCE
new file mode 100644 (file)
index 0000000..77eea6e
--- /dev/null
@@ -0,0 +1,53 @@
+
+Libpng 1.0.3 - January 14, 1999
+
+This is a public release of libpng, intended for use in production codes.
+
+Changes since the previous public release (1.0.2):
+
+libpng-1.0.3:
+
+  Replaced and extended code that was removed from png_set_filler() in 1.0.1a.
+  Fixed a bug in png_do_filler() that made it fail to write filler bytes in
+    the left-most pixel of each row (Kevin Bracey).
+  Changed "static pngcharp tIME_string" to "static char tIME_string[30]"
+    in pngtest.c (Duncan Simpson).
+  Fixed a bug in pngtest.c that caused pngtest to try to write a tIME chunk
+    even when no tIME chunk was present in the source file.
+  Fixed a problem in pngrutil.c: gray_to_rgb didn't always work with 16-bit.
+  Fixed a problem in png_read_push_finish_row(), which would not skip some
+    passes that it should skip, for images that are less than 3 pixels high.
+  Interchanged the order of calls to png_do_swap() and png_do_shift()
+    in pngwtran.c (John Cromer).
+  Added #ifdef PNG_DEBUG/#endif surrounding use of PNG_DEBUG in png.h .
+  Changed "bad adaptive filter type" from error to warning in pngrutil.c .
+  Fixed a documentation error about default filtering with 8-bit indexed-color.
+  Separated the PNG_NO_STDIO macro into PNG_NO_STDIO and PNG_NO_CONSOLE_IO
+    (L. Peter Deutsch).
+  Added png_set_rgb_to_gray() and png_get_rgb_to_gray_status() functions.
+  Added png_get_copyright() and png_get_header_version() functions.
+  Revised comments on png_set_progressive_read_fn() in libpng.txt and example.c
+  Added information about debugging in libpng.txt and libpng.3 .
+  Changed "ln -sf" to "ln -s -f" in makefile.s2x, makefile.lnx, and makefile.sco.
+  Removed lines after Dynamic Dependencies" in makefile.aco .
+  Revised makefile.dec to make a shared library (Jeremie Petit).
+  Removed trailing blanks from all files. 
+  Removed misplaced #endif and #ifdef PNG_NO_EXTERN near the end of png.h
+  Added "if" tests to silence complaints about unused png_ptr in png.h and png.c
+  Changed "check_if_png" function in example.c to return true (nonzero) if PNG.
+  Changed libpng.txt to demonstrate png_sig_cmp() instead of png_check_sig()
+    which is obsolete.
+  Added makefile.hux, for Hewlett Packard HPUX 10.20 and 11.00 (Jim Rice)
+  Added a statement of Y2K compliance in png.h, libpng.1, and Y2KINFO.
+
+Send comments/corrections/commendations to
+png-implement@dworkin.wustl.edu or to randeg@alumni.rpi.edu
+
+Glenn R-P
+
+Send comments/corrections/commendations to
+png-implement@dworkin.wustl.edu or to randeg@alumni.rpi.edu
+
+Glenn Randers-Pehrson
+libpng maintainer
+PNG Development Group
diff --git a/src/png/CHANGES b/src/png/CHANGES
new file mode 100644 (file)
index 0000000..d2dcde3
--- /dev/null
@@ -0,0 +1,389 @@
+CHANGES - changes for libpng
+
+version 0.2
+  added reader into png.h
+  fixed small problems in stub file
+version 0.3
+  added pull reader
+  split up pngwrite.c to several files
+  added pnglib.txt
+  added example.c
+  cleaned up writer, adding a few new tranformations
+  fixed some bugs in writer
+  interfaced with zlib 0.5
+  added K&R support
+  added check for 64 KB blocks for 16 bit machines
+version 0.4
+  cleaned up code and commented code
+  simplified time handling into png_time
+  created png_color_16 and png_color_8 to handle color needs
+  cleaned up color type defines
+  fixed various bugs
+  made various names more consistant
+  interfaced with zlib 0.71
+  cleaned up zTXt reader and writer (using zlib's Reset functions)
+  split transformations into pngrtran.c and pngwtran.c
+version 0.5
+  interfaced with zlib 0.8
+  fixed many reading and writing bugs
+  saved using 3 spaces instead of tabs
+version 0.6
+  added png_large_malloc() and png_large_free()
+  added png_size_t
+  cleaned up some compiler warnings
+  added png_start_read_image()
+version 0.7
+  cleaned up lots of bugs
+  finished dithering and other stuff
+  added test program
+  changed name from pnglib to libpng
+version 0.71 [June, 1995]
+  changed pngtest.png for zlib 0.93
+  fixed error in libpng.txt and example.c
+version 0.8
+  cleaned up some bugs
+  added png_set_filler()
+  split up pngstub.c into pngmem.c, pngio.c, and pngerror.c
+  added #define's to remove unwanted code
+  moved png_info_init() to png.c
+  added old_size into png_realloc()
+  added functions to manually set filtering and compression info
+  changed compression parameters based on image type
+  optimized filter selection code
+  added version info
+  changed external functions passing floats to doubles (k&r problems?)
+  put all the configurable stuff in pngconf.h
+  enabled png_set_shift to work with paletted images on read
+  added png_read_update_info() - updates info structure with
+     transformations
+version 0.81 [August, 1995]
+  incorporated Tim Wegner's medium model code (thanks, Tim)
+version 0.82 [September, 1995]
+  [unspecified changes]
+version 0.85 [December, 1995]
+  added more medium model code (almost everything's a far)
+  added i/o, error, and memory callback functions
+  fixed some bugs (16 bit, 4 bit interlaced, etc.)
+  added first run progressive reader (barely tested)
+version 0.86 [January, 1996]
+  fixed bugs
+  improved documentation
+version 0.87 [January, 1996]
+  fixed medium model bugs
+  fixed other bugs introduced in 0.85 and 0.86
+  added some minor documentation
+version 0.88 [January, 1996]
+  fixed progressive bugs
+  replaced tabs with spaces
+  cleaned up documentation
+  added callbacks for read/write and warning/error functions
+version 0.89 [July, 1996]
+  added new initialization API to make libpng work better with shared libs
+     we now have png_create_read_struct(), png_create_write_struct(),
+     png_create_info_struct(), png_destroy_read_struct(), and
+     png_destroy_write_struct() instead of the separate calls to
+     malloc and png_read_init(), png_info_init(), and png_write_init()
+  changed warning/error callback functions to fix bug - this means you
+     should use the new initialization API if you were using the old
+     png_set_message_fn() calls, and that the old API no longer exists
+     so that people are aware that they need to change their code
+  changed filter selection API to allow selection of multiple filters
+     since it didn't work in previous versions of libpng anyways
+  optimized filter selection code
+  fixed png_set_background() to allow using an arbitrary RGB color for
+     paletted images
+  fixed gamma and background correction for paletted images, so
+     png_correct_palette is not needed unless you are correcting an
+     external palette (you will need to #define PNG_CORRECT_PALETTE_SUPPORTED
+     in pngconf.h) - if nobody uses this, it may disappear in the future.
+  fixed bug with Borland 64K memory allocation (Alexander Lehmann)
+  fixed bug in interlace handling (Smarasderagd, I think)
+  added more error checking for writing and image to reduce invalid files
+  separated read and write functions so that they won't both be linked
+     into a binary when only reading or writing functionality is used
+  new pngtest image also has interlacing and zTXt
+  updated documentation to reflect new API
+version 0.90 [January, 1997]
+  made CRC errors/warnings on critical and ancillary chunks configurable
+  libpng will use the zlib CRC routines by (compile-time) default
+  changed DOS small/medium model memory support - needs zlib 1.04 (Tim Wegner)
+  added external C++ wrapper statements to png.h (Gilles Dauphin)
+  allow PNG file to be read when some or all of file signature has already
+     been read from the beginning of the stream.  ****This affects the size
+     of info_struct and invalidates all programs that use a shared libpng****
+  fixed png_filler() declarations
+  fixed? background color conversions
+  fixed order of error function pointers to match documentation
+  current chunk name is now available in png_struct to reduce the number
+     of nearly identical error messages (will simplify multi-lingual
+     support when available)
+  try to get ready for unknown-chunk callback functions:
+     - previously read critical chunks are flagged, so the chunk handling
+       routines can determine if the chunk is in the right place
+     - all chunk handling routines have the same prototypes, so we will
+       be able to handle all chunks via a callback mechanism
+  try to fix Linux "setjmp" buffer size problems
+version 0.95 [March, 1997]
+  fixed bug in pngwutil.c allocating "up_row" twice and "avg_row" never
+  fixed bug in PNG file signature compares when start != 0
+  changed parameter type of png_set_filler(...filler...) from png_byte
+     to png_uint_32
+  added test for MACOS to ensure that both math.h and fp.h are not #included
+  added macros for libpng to be compiled as a Windows DLL (Andreas Kupries)
+  added "packswap" transformation, which changes the endianness of
+     packed-pixel bytes (Kevin Bracey)
+  added "strip_alpha" transformation, which removes the alpha channel of
+     input images without using it (not neccesarily a good idea)
+  added "swap_alpha" transformation, which puts the alpha channel in front
+     of the color bytes instead of after
+  removed all implicit variable tests which assume NULL == 0 (I think)
+  changed several variables to "png_size_t" to show 16/32-bit limitations
+  added new pCAL chunk read/write support
+  added experimental filter selection weighting (Greg Roelofs)
+  removed old png_set_rgbx() and png_set_xrgb() functions that have been
+     obsolete for about 2 years now (use png_set_filler() instead)
+  added macros to read 16- and 32-bit ints directly from buffer, to be
+     used only on those systems that support it (namely PowerPC and 680x0)
+     With some testing, this may become the default for MACOS/PPC systems.
+  only calculate CRC on data if we are going to use it
+  added macros for zTXt compression type PNG_zTXt_COMPRESSION_???
+  added macros for simple libpng debugging output selectable at compile time
+  removed PNG_READ_END_MODE in progressive reader (Smarasderagd)
+  more description of info_struct in libpng.txt and png.h
+  more instructions in example.c
+  more chunk types tested in pngtest.c
+  renamed pngrcb.c to pngset.c, and all png_read_<chunk> functions to be
+     png_set_<chunk>.  We now have corresponding png_get_<chunk>
+     functions in pngget.c to get infomation in info_ptr.  This isolates
+     the application from the internal organization of png_info_struct
+     (good for shared library implementations).
+version 0.96 [May, 1997]
+  fixed serious bug with < 8bpp images introduced in 0.95
+  fixed 256-color transparency bug (Greg Roelofs)
+  fixed up documentation (Greg Roelofs, Laszlo Nyul)
+  fixed "error" in pngconf.h for Linux setjmp() behaviour
+  fixed DOS medium model support (Tim Wegner)
+  fixed png_check_keyword() for case with error in static string text
+  added read of CRC after IEND chunk for embedded PNGs (Laszlo Nyul)
+  added typecasts to quiet compiler errors
+  added more debugging info
+version 0.97 [January, 1998]
+  removed PNG_USE_OWN_CRC capability
+  relocated png_set_crc_action from pngrutil.c to pngrtran.c
+  fixed typecasts of "new_key", etc. (Andreas Dilger)
+  added RFC 1152 [sic] date support
+  fixed bug in gamma handling of 4-bit grayscale
+  added 2-bit grayscale gamma handling (Glenn R-P)
+  added more typecasts. 65536L becomes (png_uint_32)65536L, etc. (Glenn R-P)
+  minor corrections in libpng.txt
+  added simple sRGB support (Glenn R-P)
+  easier conditional compiling, e.g. define PNG_READ/WRITE_NOT_FULLY_SUPPORTED;
+     all configurable options can be selected from command-line instead
+     of having to edit pngconf.h (Glenn R-P)
+  fixed memory leak in pngwrite.c (free info_ptr->text) (Glenn R-P)
+  added more conditions for png_do_background, to avoid changing
+     black pixels to background when a background is supplied and
+     no pixels are transparent
+  repaired PNG_NO_STDIO behaviour
+  tested NODIV support and made it default behaviour (Greg Roelofs)
+  added "-m" option and PNGTEST_DEBUG_MEMORY to pngtest (John Bowler)
+  regularized version numbering scheme and bumped shared-library major
+     version number to 2 to avoid problems with libpng 0.89 apps (Greg Roelofs)
+version 0.98 [January, 1998]
+  cleaned up some typos in libpng.txt and in code documentation
+  fixed memory leaks in pCAL chunk processing (Glenn R-P and John Bowler)
+  cosmetic change "display_gamma" to "screen_gamma" in pngrtran.c
+  changed recommendation about file_gamma for PC images to .51 from .45,
+     in example.c and libpng.txt, added comments to distinguish between
+     screen_gamma, viewing_gamma, and display_gamma.
+  changed all references to RFC1152 to read RFC1123 and changed the
+     PNG_TIME_RFC1152_SUPPORTED macro to PNG_TIME_RFC1123_SUPPORTED
+  added png_invert_alpha capability (Glenn R-P -- suggestion by Jon Vincent)
+  changed srgb_intent from png_byte to int to avoid compiler bugs
+version 0.99 [January 30, 1998]
+  free info_ptr->text instead of end_info_ptr->text in pngread.c (John Bowler)
+  fixed a longstanding "packswap" bug in pngtrans.c
+  fixed some inconsistencies in pngconf.h that prevented compiling with
+     PNG_READ_GAMMA_SUPPORTED and PNG_READ_hIST_SUPPORTED undefined
+  fixed some typos and made other minor rearrangement of libpng.txt (Andreas)
+  changed recommendation about file_gamma for PC images to .50 from .51 in
+     example.c and libpng.txt, and changed file_gamma for sRGB images to .45
+  added a number of functions to access information from the png structure
+     png_get_image_height(), etc. (Glenn R-P, suggestion by Brad Pettit)
+  added TARGET_MACOS similar to zlib-1.0.8
+  define PNG_ALWAYS_EXTERN when __MWERKS__ && WIN32 are defined
+  added type casting to all png_malloc() function calls
+version 0.99a [January 31, 1998]
+  Added type casts and parentheses to all returns that return a value.(Tim W.)
+version 0.99b [February 4, 1998]
+  Added type cast png_uint_32 on malloc function calls where needed.
+  Changed type of num_hist from png_uint_32 to int (same as num_palette).
+  Added checks for rowbytes overflow, in case png_size_t is less than 32 bits.
+  Renamed makefile.elf to makefile.lnx.
+version 0.99c [February 7, 1998]
+  More type casting.  Removed erroneous overflow test in pngmem.c.
+  Added png_buffered_memcpy() and png_buffered_memset(), apply them to rowbytes.
+  Added UNIX manual pages libpng.3 (incorporating libpng.txt) and  png.5.
+version 0.99d [February 11, 1998]
+  Renamed "far_to_near()" "png_far_to_near()"
+  Revised libpng.3
+  Version 99c "buffered" operations didn't work as intended.  Replaced them
+    with png_memcpy_check() and png_memset_check().
+  Added many "if (png_ptr == NULL) return" to quell compiler warnings about
+    unused png_ptr, mostly in pngget.c and pngset.c.
+  Check for overlength tRNS chunk present when indexed-color PLTE is read.
+  Cleaned up spelling errors in libpng.3/libpng.txt
+  Corrected a problem with png_get_tRNS() which returned undefined trans array
+version 0.99e [February 28, 1998]
+  Corrected png_get_tRNS() again.
+  Add parentheses for easier reading of pngget.c, fixed "||" should be "&&".
+  Touched up example.c to make more of it compileable, although the entire
+    file still can't be compiled (Willem van Schaik)
+  Fixed a bug in png_do_shift() (Bryan Tsai)
+  Added a space in png.h prototype for png_write_chunk_start()
+  Replaced pngtest.png with one created with zlib 1.1.1
+  Changed pngtest to report PASS even when file size is different (Jean-loup G.)
+  Corrected some logic errors in png_do_invert_alpha() (Chris Patterson)
+version 0.99f [March 5, 1998]
+  Corrected a bug in pngpread() introduced in version 99c (Kevin Bracey)
+  Moved makefiles into a "scripts" directory, and added INSTALL instruction file
+  Added makefile.os2 and pngos2.def (A. Zabolotny) and makefile.s2x (W. Sebok)
+  Added pointers to "note on libpng versions" in makefile.lnx and README
+  Added row callback feature when reading and writing nonprogressive rows
+     and added a test of this feature in pngtest.c
+  Added user transform callbacks, with test of the feature in pngtest.c
+version 0.99g [March 6, 1998, morning]
+  Minor changes to pngtest.c to suppress compiler warnings.
+  Removed "beta" language from documentation.
+version 0.99h [March 6, 1998, evening]
+  Minor changes to previous minor changes to pngtest.c
+  Changed PNG_READ_NOT_FULLY_SUPPORTED to PNG_READ_TRANSFORMS_NOT_SUPPORTED
+  and added PNG_PROGRESSIVE_READ_NOT_SUPPORTED macro
+version 1.00 [March 7, 1998]
+  Changed several typedefs in pngrutil.c
+  Added makefile.wat (Pawel Mrochen), updated makefile.tc3 (Willem van Schaik)
+  replaced "while(1)" with "for(;;)"
+  added PNGARG() to prototypes in pngtest.c and removed some prototypes
+  updated some of the makefiles (Tom Lane)
+  changed some typedefs (s_start, etc.) in pngrutil.c
+  fixed dimensions of "short_months" array in pngwrite.c
+  Replaced ansi2knr.c with the one from jpeg-v6
+version 1.0.0 [March 8, 1998]
+  Changed name from 1.00 to 1.0.0 (Adam Costello)
+  Added smakefile.ppc (with SCOPTIONS.ppc) for Amiga PPC (Andreas Kleinert)
+version 1.0.0a [March 9, 1998]
+  Fixed three bugs in pngrtran.c to make gamma+background handling consistent
+  (Greg Roelofs)
+  Changed format of the PNG_LIBPNG_VER integer to xyyzz instead of xyz
+  for major, minor, and bugfix releases.  This is 10001. (Adam Costello,
+  Tom Lane)
+  Make months range from 1-12 in png_convert_to_rfc1123
+version 1.0.0b [March 13, 1998]
+  Quieted compiler complaints about two empty "for" loops in pngrutil.c
+  Minor changes to makefile.s2x
+  Removed #ifdef/#endif around a png_free() in pngread.c
+version 1.0.1 [March 14, 1998]
+  Changed makefile.s2x to reduce security risk of using a relative pathname
+  Fixed some typos in the documentation (Greg).
+  Fixed a problem with value of "channels" returned by png_read_update_info()
+version 1.0.1a [April 21, 1998]
+  Optimized Paeth calculations by replacing abs() function calls with intrinsics
+  plus other loop optimizations. Improves avg decoding speed by about 20%.
+  Commented out i386istic "align" compiler flags in makefile.lnx.
+  Reduced the default warning level in some makefiles, to make them consistent.
+  Removed references to IJG and JPEG in the ansi2knr.c copyright statement.
+  Fixed a bug in png_do_strip_filler with XXRRGGBB => RRGGBB transformation.
+  Added grayscale and 16-bit capability to png_do_read_filler().
+  Fixed a bug in pngset.c, introduced in version 0.99c, that sets rowbytes
+    too large when writing an image with bit_depth < 8 (Bob Dellaca).
+  Corrected some bugs in the experimental weighted filtering heuristics.
+  Moved a misplaced pngrutil code block that truncates tRNS if it has more
+    than num_palette entries -- test was done before num_palette was defined.
+  Fixed a png_convert_to_rfc1123() bug that converts day 31 to 0 (Steve Eddins).
+  Changed compiler flags in makefile.wat for better optimization (Pawel Mrochen).
+version 1.0.1b [May 2, 1998]
+  Relocated png_do_gray_to_rgb() within png_do_read_transformations() (Greg).
+  Relocated the png_composite macros from pngrtran.c to png.h (Greg).
+  Added makefile.sco (contributed by Mike Hopkirk).
+  Fixed two bugs (missing definitions of "istop") introduced in libpng-1.0.1a.
+  Fixed a bug in pngrtran.c that would set channels=5 under some circumstances.
+  More work on the Paeth-filtering, achieving imperceptible speedup (A Kleinert).
+  More work on loop optimization which may help when compiled with C++ compilers.
+  Added warnings when people try to use transforms they've defined out.
+  Collapsed 4 "i" and "c" loops into single "i" loops in pngrtran and pngwtran.
+  Revised paragraph about png_set_expand() in libpng.txt and libpng.3 (Greg)
+version 1.0.1c [May 11, 1998]
+  Fixed a bug in pngrtran.c (introduced in libpng-1.0.1a) where the masks for
+    filler bytes should have been 0xff instead of 0xf.
+  Added max_pixel_depth=32 in pngrutil.c when using FILLER with palette images.
+  Moved PNG_WRITE_WEIGHTED_FILTER_SUPPORTED and PNG_WRITE_FLUSH_SUPPORTED
+    out of the PNG_WRITE_TRANSFORMS_NOT_SUPPORTED block of pngconf.h
+  Added "PNG_NO_WRITE_TRANSFORMS" etc., as alternatives for *_NOT_SUPPORTED,
+    for consistency, in pngconf.h
+  Added individual "ifndef PNG_NO_[CAPABILITY]" in pngconf.h to make it easier
+    to remove unwanted capabilities via the compile line
+  Made some corrections to grammar (which, it's) in documentation (Greg).
+  Corrected example.c, use of row_pointers in png_write_image().
+version 1.0.1d [May 24, 1998]
+  Corrected several statements that used side effects illegally in pngrutil.c
+    and pngtrans.c, that were introduced in version 1.0.1b
+  Revised png_read_rows() to avoid repeated if-testing for NULL (A Kleinert)
+  More corrections to example.c, use of row_pointers in png_write_image()
+    and png_read_rows().
+  Added pngdll.mak and pngdef.pas to scripts directory, contributed by
+    Bob Dellaca, to make a png32bd.dll with Borland C++ 4.5
+  Fixed error in example.c with png_set_text: num_text is 3, not 2 (Guido V.)
+  Changed several loops from count-down to count-up, for consistency.
+version 1.0.1e [June 6, 1998]
+  Revised libpng.txt and libpng.3 description of png_set_read|write_fn(), and
+    added warnings when people try to set png_read_fn and png_write_fn in
+    the same structure.
+  Added a test such that png_do_gamma will be done when num_trans==0
+    for truecolor images that have defined a background.  This corrects an
+    error that was introduced in libpng-0.90 that can cause gamma processing
+    to be skipped.
+  Added tests in png.h to include "trans" and "trans_values" in structures
+    when PNG_READ_BACKGROUND_SUPPORTED or PNG_READ_EXPAND_SUPPORTED is defined.
+  Add png_free(png_ptr->time_buffer) in png_destroy_read_struct()
+  Moved png_convert_to_rfc_1123() from pngwrite.c to png.c
+  Added capability for user-provided malloc_fn() and free_fn() functions,
+    and revised pngtest.c to demonstrate their use, replacing the
+    PNGTEST_DEBUG_MEM feature.
+  Added makefile.w32, for Microsoft C++ 4.0 and later (Tim Wegner).
+version 1.0.2 [June 14, 1998]
+  Fixed two bugs in makefile.bor .
+version 1.0.2a [December 30, 1998]
+  Replaced and extended code that was removed from png_set_filler() in 1.0.1a.
+  Fixed a bug in png_do_filler() that made it fail to write filler bytes in
+    the left-most pixel of each row (Kevin Bracey).
+  Changed "static pngcharp tIME_string" to "static char tIME_string[30]"
+    in pngtest.c (Duncan Simpson).
+  Fixed a bug in pngtest.c that caused pngtest to try to write a tIME chunk
+    even when no tIME chunk was present in the source file.
+  Fixed a problem in pngrutil.c: gray_to_rgb didn't always work with 16-bit.
+  Fixed a problem in png_read_push_finish_row(), which would not skip some
+    passes that it should skip, for images that are less than 3 pixels high.
+  Interchanged the order of calls to png_do_swap() and png_do_shift()
+    in pngwtran.c (John Cromer).
+  Added #ifdef PNG_DEBUG/#endif surrounding use of PNG_DEBUG in png.h .
+  Changed "bad adaptive filter type" from error to warning in pngrutil.c .
+  Fixed a documentation error about default filtering with 8-bit indexed-color.
+  Separated the PNG_NO_STDIO macro into PNG_NO_STDIO and PNG_NO_CONSOLE_IO
+    (L. Peter Deutsch).
+  Added png_set_rgb_to_gray() and png_get_rgb_to_gray_status() functions.
+  Added png_get_copyright() and png_get_header_version() functions.
+  Revised comments on png_set_progressive_read_fn() in libpng.txt and example.c
+  Added information about debugging in libpng.txt and libpng.3 .
+  Changed "ln -sf" to "ln -s -f" in makefile.s2x, makefile.lnx, and makefile.sco.
+  Removed lines after Dynamic Dependencies" in makefile.aco .
+  Revised makefile.dec to make a shared library (Jeremie Petit).
+  Removed trailing blanks from all files. 
+version 1.0.2a [January 6, 1999]
+  Removed misplaced #endif and #ifdef PNG_NO_EXTERN near the end of png.h
+  Added "if" tests to silence complaints about unused png_ptr in png.h and png.c
+  Changed "check_if_png" function in example.c to return true (nonzero) if PNG.
+  Changed libpng.txt to demonstrate png_sig_cmp() instead of png_check_sig()
+    which is obsolete.
+version 1.0.3 [January 14, 1999]
+  Added makefile.hux, for Hewlett Packard HPUX 10.20 and 11.00 (Jim Rice)
+  Added a statement of Y2K compliance in png.h, libpng.1, and Y2KINFO.
diff --git a/src/png/INSTALL b/src/png/INSTALL
new file mode 100644 (file)
index 0000000..554b771
--- /dev/null
@@ -0,0 +1,91 @@
+
+Installing libpng version 1.0.3 - January 14, 1999
+
+Before installing libpng, you must first install zlib.  zlib
+can usually be found wherever you got libpng.  zlib can be
+placed in another directory, at the same level as libpng.
+Note that your system might already have a preinstalled
+zlib, but you will still need to have access to the
+zlib.h and zconf.h include files that correspond to the
+version of zlib that's installed.
+
+You can rename the directories that you downloaded (they
+might be called "libpng-1.0.3" or "lpng103" and "zlib-1.1.3"
+or "zlib113") so that you have directories called "zlib" and "libpng".
+
+Your directory structure should look like this:
+
+   ..       (the parent directory)
+      libpng  (this directory)
+          INSTALL (this file)
+          README
+          *.h
+          *.c
+          scripts
+             makefile.*
+          pngtest.png
+          etc.
+      zlib
+          README
+          *.h
+          *.c
+          contrib
+          etc.
+
+First enter the zlib directory and follow the instructions
+in zlib/README.  Then come back here and choose the
+appropriate makefile.sys in the scripts directory.
+The files that are presently available in the scripts directory
+include
+
+      descrip.mms   =>  VMS makefile for MMS or MMK
+      makefile.std  =>  Generic UNIX makefile
+      makefile.knr  =>  Archaic UNIX Makefile that converts files with ansi2knr
+      makefile.dec  =>  DEC Alpha UNIX makefile
+      makefile.hux  =>  HPUX (10.20 and 11.00) makefile
+      makefile.sgi  =>  Silicon Graphics IRIX makefile
+      makefile.sun  =>  Sun makefile
+      makefile.s2x  =>  Solaris 2.X makefile (gcc, creates libpng.so.2.1.0)
+      makefile.lnx  =>  Linux/ELF makefile (gcc, creates libpng.so.2.1.0)
+      makefile.mip  =>  MIPS makefile
+      makefile.aco  =>  Acorn makefile
+      makefile.ama  =>  Amiga makefile
+      smakefile.ppc =>  AMIGA smakefile for SAS C V6.58/7.00 PPC compiler
+                        (Requires SCOPTIONS, copied from scripts/SCOPTIONS.ppc)
+      makefile.atr  =>  Atari makefile
+      makefile.bor  =>  Borland makefile
+      build.bat     =>  MS-DOS batch file for Borland compiler
+      makefile.dj2  =>  DJGPP 2 makefile
+      makefile.msc  =>  Microsoft C makefile
+      makefile.w32  =>  makefile for Microsoft Visual C++ 4.0 and later
+      makefile.tc3  =>  Turbo C 3.0 makefile
+      makefile.os2  =>  OS/2 Makefile (gcc and emx, requires pngos2.def)
+      pngos2.def    =>  OS/2 module definition file used by makefile.os2
+      makefile.wat  =>  Watcom 10a+ Makefile, 32-bit flat memory model
+      makevms.com   =>  VMS build script
+      pngdll.mak    =>  To make a png32bd.dll with Borland C++ 4.5
+      pngdef.pas    =>  Defines for a png32bd.dll with Borland C++ 4.5
+
+Copy the file (or files) that you need from the
+scripts directory into this directory, for example
+
+   MSDOS example: copy scripts\makefile.msd makefile
+   UNIX example:    cp scripts/makefile.std makefile
+
+Read the makefile to see if you need to change any source or
+target directories to match your preferences.
+
+Then read pngconf.h to see if you want to make any configuration
+changes.
+
+Then just run "make test" which will create the libpng library in
+this directory and run a quick test that reads the "pngtest.png"
+file and writes a "pngout.png" file that should be identical to it.
+
+Most of the makefiles will allow you to run "make install" to
+put the library in its final resting place (if you want to
+do that, run "make install" in the zlib directory first if necessary).
+
+Further information can be found in the README and libpng.txt
+files, in the individual makefiles, and in png.h, and the manual
+pages libpng.3 and png.5.
diff --git a/src/png/README b/src/png/README
new file mode 100644 (file)
index 0000000..3bd0893
--- /dev/null
@@ -0,0 +1,198 @@
+README for libpng 1.0.3 - January 14, 1999 (shared library 2.1)
+See the note about version numbers near the top of png.h
+
+See INSTALL for instructions on how to install libpng.
+
+This is the first official release of libpng.  Don't let the fact that
+it's the first release fool you.  The libpng library has been in
+extensive use and testing for about two and a half years.  However, it's
+finally gotten to the stage where there haven't been significant
+changes to the API in some time, and people have a bad feeling about
+libraries with versions < 1.0.
+
+****
+Note that some of the changes to the png_info structure render this
+version of the library binary incompatible with libpng-0.89 or
+earlier versions if you are using a shared library.  The type of the
+"filler" parameter for png_set_filler() has changed from png_byte to
+png_uint_32, which will affect shared-library applications that use
+this function.
+
+To avoid problems with changes to the internals of png_info_struct,
+new APIs have been made available in 0.95 to avoid direct application
+access to info_ptr.  These functions are the png_set_<chunk> and
+png_get_<chunk> functions.  These functions should be used when
+accessing/storing the info_struct data, rather than manipulating it
+directly, to avoid such problems in the future.
+
+It is important to note that the APIs do not make current programs
+that access the info struct directly incompatible with the new
+library.  However, it is strongly suggested that new programs use
+the new APIs (as shown in example.c), and older programs be converted
+to the new format, to facilitate upgrades in the future.
+****
+
+Additions since 0.90 include the ability to compile libpng as a
+Windows DLL, and new APIs for accessing data in the info struct.
+Experimental functions include the ability to set weighting and cost
+factors for row filter selection, direct reads of integers from buffers
+on big-endian processors that support misaligned data access, faster
+methods of doing alpha composition, and more accurate 16->8 bit color
+conversion.
+
+The additions since 0.89 include the ability to read from a PNG stream
+which has had some (or all) of the signature bytes read by the calling
+application.  This also allows the reading of embedded PNG streams that
+do not have the PNG file signature.  As well, it is now possible to set
+the library action on the detection of chunk CRC errors.  It is possible
+to set different actions based on whether the CRC error occurred in a
+critical or an ancillary chunk.
+
+The changes made to the library, and bugs fixed are based on discussions
+on the PNG implementation mailing list <png-implement@dworking.wustl.edu>
+and not on material submitted to Guy.
+
+For a detailed description on using libpng, read libpng.txt.  For
+examples of libpng in a program, see example.c and pngtest.c.  For usage
+information and restrictions (what little they are) on libpng, see
+png.h.  For a description on using zlib (the compression library used by
+libpng) and zlib's restrictions, see zlib.h
+
+I have included a general makefile, as well as several machine and
+compiler specific ones, but you may have to modify one for your own needs.
+
+You should use zlib 1.0.4 or later to run this, but it MAY work with
+versions as old as zlib 0.95.  Even so, there are bugs in older zlib
+versions which can cause the output of invalid compression streams for
+some images.  You will definitely need zlib 1.0.4 or later if you are
+taking advantage of the MS-DOS "far" structure allocation for the small
+and medium memory models.  You should also note that zlib is a
+compression library that is useful for more things than just PNG files.
+You can use zlib as a drop-in replacement for fread() and fwrite() if
+you are so inclined.
+
+zlib should be available at the same place that libpng is.
+If not, it should be at ftp.uu.net in /graphics/png
+Eventually, it will be at ftp.uu.net in /pub/archiving/zip/zlib
+
+You may also want a copy of the PNG specification.  It is available
+as an RFC and a W3C Recommendation.  Failing
+these resources you can try ftp.uu.net in the /graphics/png directory.
+
+This code is currently being archived at ftp.uu.net in the
+/graphics/png directory, and on CompuServe, Lib 20 (PNG SUPPORT)
+at GO GRAPHSUP.  If you can't find it in any of those places,
+e-mail me, and I'll help you find it.
+
+If you have any code changes, requests, problems, etc., please e-mail
+them to me.  Also, I'd appreciate any make files or project files,
+and any modifications you needed to make to get libpng to compile,
+along with a #define variable to tell what compiler/system you are on.
+If you needed to add transformations to libpng, or wish libpng would
+provide the image in a different way, drop me a note (and code, if
+possible), so I can consider supporting the transformation.
+Finally, if you get any warning messages when compiling libpng
+(note: not zlib), and they are easy to fix, I'd appreciate the
+fix.  Please mention "libpng" somewhere in the subject line.  Thanks.
+
+This release was created and will be supported by myself (of course
+based in a large way on Guy's and Andreas' earlier work), and the PNG group.
+
+randeg@alumni.rpi.edu
+png-implement@dworkin.wustl.edu
+
+You can't reach Guy, the original libpng author, at the addresses
+given in previous versions of this document.  He and Andreas will read mail
+addressed to the png-implement list, however.
+
+Please do not send general questions about PNG.  Send them to
+the address in the specification (png-group@w3.org).  At the same
+time, please do not send libpng questions to that address, send them to me
+or to png-implement@dworkin.wustl.edu.  I'll
+get them in the end anyway.  If you have a question about something
+in the PNG specification that is related to using libpng, send it
+to me.  Send me any questions that start with "I was using libpng,
+and ...".  If in doubt, send questions to me.  I'll bounce them
+to others, if necessary.
+
+Please do not send suggestions on how to change PNG.  We have
+been discussing PNG for three years now, and it is official and
+finished.  If you have suggestions for libpng, however, I'll
+gladly listen.  Even if your suggestion is not used for version
+1.0, it may be used later.
+
+Files in this distribution:
+
+      CHANGES       =>  Description of changes between libpng versions
+      README        =>  This file
+      TODO          =>  Things not implemented in the current library
+      ansi2knr.1    =>  Manual page for ansi2knr
+      ansi2knr.c    =>  Converts files to K&R style function declarations
+      build.bat     =>  MS-DOS batch file for Borland compiler
+      descrip.mms   =>  VMS project file
+      example.c     =>  Example code for using libpng functions
+      libpng.3      =>  manual page for libpng
+      libpng.txt    =>  Description of libpng and its functions
+      libpngpf.3    =>  manual page for libpng's private functions
+      png.5         =>  manual page for the PNG format
+      png.c         =>  Basic interface functions common to library
+      png.h         =>  Library function and interface declarations
+      pngconf.h     =>  System specific library configuration
+      pngerror.c    =>  Error/warning message I/O functions
+      pngget.c      =>  Functions for retrieving info from struct
+      pngmem.c      =>  Memory handling functions
+      pngpread.c    =>  Progressive reading functions
+      pngread.c     =>  Read data/helper high-level functions
+      pngrio.c      =>  Lowest-level data read I/O functions
+      pngrtran.c    =>  Read data transformation functions
+      pngrutil.c    =>  Read data utility functions
+      pngset.c      =>  Functions for storing data into the info_struct
+      pngtest.c     =>  Library test program
+      pngtest.png   =>  Library test sample image
+      pngtrans.c    =>  Common data transformation functions
+      pngwio.c      =>  Lowest-level write I/O functions
+      pngwrite.c    =>  High-level write functions
+      pngwtran.c    =>  Write data transformations
+      pngwutil.c    =>  Write utility functions
+      scripts       =>  Directory containing scripts for building libpng:
+        descrip.mms   =>  VMS makefile for MMS or MMK
+        makefile.std  =>  Generic UNIX makefile
+        makefile.knr  =>  Archaic UNIX Makefile that converts files with ansi2knr
+        makefile.dec  =>  DEC Alpha UNIX makefile
+        makefile.hux  =>  HPUX (10.20 and 11.00) makefile
+        makefile.sgi  =>  Silicon Graphics IRIX makefile
+        makefile.sun  =>  Sun makefile
+        makefile.s2x  =>  Solaris 2.X makefile (gcc, creates libpng.so.2.1.0)
+        makefile.lnx  =>  Linux/ELF makefile (gcc, creates libpng.so.2.1.0)
+        makefile.mip  =>  MIPS makefile
+        makefile.aco  =>  Acorn makefile
+        makefile.ama  =>  Amiga makefile
+        smakefile.ppc =>  AMIGA smakefile for SAS C V6.58/7.00 PPC compiler
+                          (Requires SCOPTIONS, copied from scripts/SCOPTIONS.ppc)
+        makefile.atr  =>  Atari makefile
+        makefile.bor  =>  Borland makefile
+        build.bat     =>  MS-DOS batch file for Borland compiler
+        makefile.dj2  =>  DJGPP 2 makefile
+        makefile.msc  =>  Microsoft C makefile
+        makefile.w32  =>  makefile for Microsoft Visual C++ 4.0 and later
+        makefile.tc3  =>  Turbo C 3.0 makefile
+        makefile.os2  =>  OS/2 Makefile (gcc and emx, requires pngos2.def)
+        makefile.wat  =>  Watcom 10a+ Makefile, 32-bit flat memory model
+        pngos2.def    =>  OS/2 module definition file used by makefile.os2
+        makevms.com   =>  VMS build script
+        pngdll.mak    =>  To make a png32bd.dll with Borland C++ 4.5
+        pngdef.pas    =>  Defines for a png32bd.dll with Borland C++ 4.5
+
+Good luck, and happy coding.
+
+-Glenn Randers-Pehrson
+ Internet: randeg@alumni.rpi.edu
+ Web: http://www.rpi.edu/~randeg/index.html
+
+-Andreas Eric Dilger
+ Internet: adilger@enel.ucalgary.ca
+ Web: http://www-mddsp.enel.ucalgary.ca/People/adilger/
+
+-Guy Eric Schalnat
+ (formerly of Group 42, Inc)
+ Internet: gschal@infinet.com
diff --git a/src/png/TODO b/src/png/TODO
new file mode 100644 (file)
index 0000000..a87e9ad
--- /dev/null
@@ -0,0 +1,9 @@
+foreach i (ANNOUNCE CHANGES INSTALL README TODO Y2KINFO example.c libpng.3 libpng.txt libpngpf.3 png.5 png.c png.h pngconf.h pngerror.c pngget.c pngmem.c pngpread.c pngread.c pngrio.c pngrtran.c pngrutil.c pngset.c pngtest.c pngtrans.c pngwio.c pngwrite.c pngwtran.c pngwutil.c)
+
+cvs up -rLIBPNG_1_0_3 $i
+cp $i $i.old
+cvs up -A $i
+mv $i.old $i
+cvs add $i
+
+end
diff --git a/src/png/Y2KINFO b/src/png/Y2KINFO
new file mode 100644 (file)
index 0000000..bf41676
--- /dev/null
@@ -0,0 +1,52 @@
+   Y2K compliance in libpng:
+   =========================
+      
+      January 13, 1999
+      
+      Since the PNG Development group is an ad-hoc body, we can't make
+      an official declaration.
+      
+      This is your unofficial assurance that libpng from version 0.81 and
+      upward are Y2K compliant.  It is my belief that earlier versions were
+      also Y2K compliant.
+      
+      Libpng only has three year fields.  One is a 2-byte unsigned integer
+      that will hold years up to 65535.  The other two hold the date in text
+      format, and will hold years up to 9999.
+      
+      The integer is
+          "png_uint_16 year" in png_time_struct.
+      
+      The strings are
+          "png_charp time_buffer" in png_struct and
+          "near_time_buffer", which is a local character string in png.c.
+      
+      There are seven time-related functions:
+
+          png_convert_to_rfc_1123() in png.c 
+            (formerly png_convert_to_rfc_1152() in error)
+          png_convert_from_struct_tm() in pngwrite.c, called in pngwrite.c
+          png_convert_from_time_t() in pngwrite.c
+          png_get_tIME() in pngget.c
+          png_handle_tIME() in pngrutil.c, called in pngread.c
+          png_set_tIME() in pngset.c
+          png_write_tIME() in pngwutil.c, called in pngwrite.c
+      
+      All appear to handle dates properly in a Y2K environment.  The 
+      png_convert_from_time_t() function calls gmtime() to convert from system
+      clock time, which returns (year - 1900), which we properly convert to
+      the full 4-digit year.  There is a possibility that applications using
+      libpng are not passing 4-digit years into the png_convert_to_rfc_1123()
+      function, or incorrectly passing only a 2-digit year instead of
+      "year - 1900" into the png_convert_from_struct_tm() function, but this
+      is not under our control.  The libpng documentation has always stated
+      that it works with 4-digit years, and the APIs have been documented as
+      such.
+      
+      The tIME chunk itself is also Y2K compliant.  It uses a 2-byte unsigned
+      integer to hold the year, and can hold years as large as 65535.
+      
+      
+         Glenn Randers-Pehrson
+         libpng maintainer
+         PNG Development Group
diff --git a/src/png/example.c b/src/png/example.c
new file mode 100644 (file)
index 0000000..088aad3
--- /dev/null
@@ -0,0 +1,702 @@
+
+/* example.c - an example of using libpng */
+
+/* This is an example of how to use libpng to read and write PNG files.
+ * The file libpng.txt is much more verbose then this.  If you have not
+ * read it, do so first.  This was designed to be a starting point of an
+ * implementation.  This is not officially part of libpng, and therefore
+ * does not require a copyright notice.
+ *
+ * This file does not currently compile, because it is missing certain
+ * parts, like allocating memory to hold an image.  You will have to
+ * supply these parts to get it to compile.  For an example of a minimal
+ * working PNG reader/writer, see pngtest.c, included in this distribution.
+ */
+
+#include "png.h"
+
+/* Check to see if a file is a PNG file using png_sig_cmp().  png_sig_cmp()
+ * returns zero if the image is a PNG and nonzero if it isn't a PNG.
+ *
+ * The function check_if_png() shown here, but not used, returns nonzero (true)
+ * if the file can be opened and is a PNG, 0 (false) otherwise.
+ *
+ * If this call is successful, and you are going to keep the file open,
+ * you should call png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); once
+ * you have created the png_ptr, so that libpng knows your application
+ * has read that many bytes from the start of the file.  Make sure you
+ * don't call png_set_sig_bytes() with more than 8 bytes read or give it
+ * an incorrect number of bytes read, or you will either have read too
+ * many bytes (your fault), or you are telling libpng to read the wrong
+ * number of magic bytes (also your fault).
+ *
+ * Many applications already read the first 2 or 4 bytes from the start
+ * of the image to determine the file type, so it would be easiest just
+ * to pass the bytes to png_sig_cmp() or even skip that if you know
+ * you have a PNG file, and call png_set_sig_bytes().
+ */
+#define PNG_BYTES_TO_CHECK 4
+int check_if_png(char *file_name, FILE **fp)
+{
+   char buf[PNG_BYTES_TO_CHECK];
+
+   /* Open the prospective PNG file. */
+   if ((*fp = fopen(file_name, "rb")) != NULL);
+      return 0;
+
+   /* Read in some of the signature bytes */
+   if (fread(buf, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK)
+      return 0;
+
+   /* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.
+      Return nonzero (true) if they match */
+
+   return(!png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK));
+}
+
+/* Read a PNG file.  You may want to return an error code if the read
+ * fails (depending upon the failure).  There are two "prototypes" given
+ * here - one where we are given the filename, and we need to open the
+ * file, and the other where we are given an open file (possibly with
+ * some or all of the magic bytes read - see comments above).
+ */
+#ifdef open_file /* prototype 1 */
+void read_png(char *file_name)  /* We need to open the file */
+{
+   png_structp png_ptr;
+   png_infop info_ptr;
+   unsigned int sig_read = 0;
+   png_uint_32 width, height;
+   int bit_depth, color_type, interlace_type;
+   FILE *fp;
+
+   if ((fp = fopen(file_name, "rb")) == NULL)
+      return;
+#else no_open_file /* prototype 2 */
+void read_png(FILE *fp, unsigned int sig_read)  /* file is already open */
+{
+   png_structp png_ptr;
+   png_infop info_ptr;
+   png_uint_32 width, height;
+   int bit_depth, color_type, interlace_type;
+#endif no_open_file /* only use one prototype! */
+
+   /* Create and initialize the png_struct with the desired error handler
+    * functions.  If you want to use the default stderr and longjump method,
+    * you can supply NULL for the last three parameters.  We also supply the
+    * the compiler header file version, so that we know if the application
+    * was compiled with a compatible version of the library.  REQUIRED
+    */
+   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+      png_voidp user_error_ptr, user_error_fn, user_warning_fn);
+
+   if (png_ptr == NULL)
+   {
+      fclose(fp);
+      return;
+   }
+
+   /* Allocate/initialize the memory for image information.  REQUIRED. */
+   info_ptr = png_create_info_struct(png_ptr);
+   if (info_ptr == NULL)
+   {
+      fclose(fp);
+      png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
+      return;
+   }
+
+   /* Set error handling if you are using the setjmp/longjmp method (this is
+    * the normal method of doing things with libpng).  REQUIRED unless you
+    * set up your own error handlers in the png_create_read_struct() earlier.
+    */
+   if (setjmp(png_ptr->jmpbuf))
+   {
+      /* Free all of the memory associated with the png_ptr and info_ptr */
+      png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+      fclose(fp);
+      /* If we get here, we had a problem reading the file */
+      return;
+   }
+
+   /* One of the following I/O initialization methods is REQUIRED */
+#ifdef streams /* PNG file I/O method 1 */
+   /* Set up the input control if you are using standard C streams */
+   png_init_io(png_ptr, fp);
+
+#else no_streams /* PNG file I/O method 2 */
+   /* If you are using replacement read functions, instead of calling
+    * png_init_io() here you would call:
+    */
+   png_set_read_fn(png_ptr, (void *)user_io_ptr, user_read_fn);
+   /* where user_io_ptr is a structure you want available to the callbacks */
+#endif no_streams /* Use only one I/O method! */
+
+   /* If we have already read some of the signature */
+   png_set_sig_bytes(png_ptr, sig_read);
+
+   /* The call to png_read_info() gives us all of the information from the
+    * PNG file before the first IDAT (image data chunk).  REQUIRED
+    */
+   png_read_info(png_ptr, info_ptr);
+
+   png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+       &interlace_type, NULL, NULL);
+
+/**** Set up the data transformations you want.  Note that these are all
+ **** optional.  Only call them if you want/need them.  Many of the
+ **** transformations only work on specific types of images, and many
+ **** are mutually exclusive.
+ ****/
+
+   /* tell libpng to strip 16 bit/color files down to 8 bits/color */
+   png_set_strip_16(png_ptr);
+
+   /* Strip alpha bytes from the input data without combining with th
+    * background (not recommended).
+    */
+   png_set_strip_alpha(png_ptr);
+
+   /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
+    * byte into separate bytes (useful for paletted and grayscale images).
+    */
+   png_set_packing(png_ptr);
+
+   /* Change the order of packed pixels to least significant bit first
+    * (not useful if you are using png_set_packing). */
+   png_set_packswap(png_ptr);
+
+   /* Expand paletted colors into true RGB triplets */
+   if (color_type == PNG_COLOR_TYPE_PALETTE)
+      png_set_expand(png_ptr);
+
+   /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
+   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+      png_set_expand(png_ptr);
+
+   /* Expand paletted or RGB images with transparency to full alpha channels
+    * so the data will be available as RGBA quartets.
+    */
+   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+      png_set_expand(png_ptr);
+
+   /* Set the background color to draw transparent and alpha images over.
+    * It is possible to set the red, green, and blue components directly
+    * for paletted images instead of supplying a palette index.  Note that
+    * even if the PNG file supplies a background, you are not required to
+    * use it - you should use the (solid) application background if it has one.
+    */
+
+   png_color_16 my_background, *image_background;
+
+   if (png_get_bKGD(png_ptr, info_ptr, &image_background))
+      png_set_background(png_ptr, image_background,
+                         PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
+   else
+      png_set_background(png_ptr, &my_background,
+                         PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
+
+   /* Some suggestions as to how to get a screen gamma value */
+
+   /* Note that screen gamma is (display_gamma/viewing_gamma) */
+   if (/* We have a user-defined screen gamma value */)
+   {
+      screen_gamma = user-defined screen_gamma;
+   }
+   /* This is one way that applications share the same screen gamma value */
+   else if ((gamma_str = getenv("SCREEN_GAMMA")) != NULL)
+   {
+      screen_gamma = atof(gamma_str);
+   }
+   /* If we don't have another value */
+   else
+   {
+      screen_gamma = 2.2;  /* A good guess for a PC monitors in a dimly
+                              lit room */
+      screen_gamma = 1.7 or 1.0;  /* A good guess for Mac systems */
+   }
+
+   /* Tell libpng to handle the gamma conversion for you.  The second call
+    * is a good guess for PC generated images, but it should be configurable
+    * by the user at run time by the user.  It is strongly suggested that
+    * your application support gamma correction.
+    */
+
+   int intent;
+
+   if (png_get_sRGB(png_ptr, info_ptr, &intent))
+      png_set_sRGB(png_ptr, intent, 0);
+   else
+   {
+      double image_gamma;
+      if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
+         png_set_gamma(png_ptr, screen_gamma, image_gamma);
+      else
+         png_set_gamma(png_ptr, screen_gamma, 0.45455);
+   }
+
+   /* Dither RGB files down to 8 bit palette or reduce palettes
+    * to the number of colors available on your screen.
+    */
+   if (color_type & PNG_COLOR_MASK_COLOR)
+   {
+      png_uint_32 num_palette;
+      png_colorp palette;
+
+      /* This reduces the image to the application supplied palette */
+      if (/* we have our own palette */)
+      {
+         /* An array of colors to which the image should be dithered */
+         png_color std_color_cube[MAX_SCREEN_COLORS];
+
+         png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS,
+            MAX_SCREEN_COLORS, NULL, 0);
+      }
+      /* This reduces the image to the palette supplied in the file */
+      else if (png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette))
+      {
+         png_color16p histogram;
+
+         png_get_hIST(png_ptr, info_ptr, &histogram);
+
+         png_set_dither(png_ptr, palette, num_palette,
+                        max_screen_colors, histogram, 0);
+      }
+   }
+
+   /* invert monocrome files to have 0 as white and 1 as black */
+   png_set_invert_mono(png_ptr);
+
+   /* If you want to shift the pixel values from the range [0,255] or
+    * [0,65535] to the original [0,7] or [0,31], or whatever range the
+    * colors were originally in:
+    */
+   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT))
+   {
+      png_color8p sig_bit;
+
+      png_get_sBIT(png_ptr, info_ptr, &sig_bit);
+      png_set_shift(png_ptr, sig_bit);
+   }
+
+   /* flip the RGB pixels to BGR (or RGBA to BGRA) */
+   png_set_bgr(png_ptr);
+
+   /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
+   png_set_swap_alpha(png_ptr);
+
+   /* swap bytes of 16 bit files to least significant byte first */
+   png_set_swap(png_ptr);
+
+   /* Add filler (or alpha) byte (before/after each RGB triplet) */
+   png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+
+   /* Turn on interlace handling.  REQUIRED if you are not using
+    * png_read_image().  To see how to handle interlacing passes,
+    * see the png_read_row() method below:
+    */
+   number_passes = png_set_interlace_handling(png_ptr);
+
+   /* Optional call to gamma correct and add the background to the palette
+    * and update info structure.  REQUIRED if you are expecting libpng to
+    * update the palette for you (ie you selected such a transform above).
+    */
+   png_read_update_info(png_ptr, info_ptr);
+
+   /* Allocate the memory to hold the image using the fields of info_ptr. */
+
+   /* The easiest way to read the image: */
+   png_bytep row_pointers[height];
+
+   for (row = 0; row < height; row++)
+   {
+      row_pointers[row] = malloc(png_get_rowbytes(png_ptr, info_ptr));
+   }
+
+   /* Now it's time to read the image.  One of these methods is REQUIRED */
+#ifdef entire /* Read the entire image in one go */
+   png_read_image(png_ptr, row_pointers);
+
+#else no_entire /* Read the image one or more scanlines at a time */
+   /* The other way to read images - deal with interlacing: */
+
+   for (pass = 0; pass < number_passes; pass++)
+   {
+#ifdef single /* Read the image a single row at a time */
+      for (y = 0; y < height; y++)
+      {
+         png_read_rows(png_ptr, &row_pointers[y], NULL, 1);
+      }
+
+#else no_single /* Read the image several rows at a time */
+      for (y = 0; y < height; y += number_of_rows)
+      {
+#ifdef sparkle /* Read the image using the "sparkle" effect. */
+         png_read_rows(png_ptr, &row_pointers[y], NULL, number_of_rows);
+
+         png_read_rows(png_ptr, NULL, row_pointers[y], number_of_rows);
+#else no_sparkle /* Read the image using the "rectangle" effect */
+         png_read_rows(png_ptr, NULL, &row_pointers[y], number_of_rows);
+#endif no_sparkle /* use only one of these two methods */
+      }
+
+      /* if you want to display the image after every pass, do
+         so here */
+#endif no_single /* use only one of these two methods */
+   }
+#endif no_entire /* use only one of these two methods */
+
+   /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
+   png_read_end(png_ptr, info_ptr);
+
+   /* clean up after the read, and free any memory allocated - REQUIRED */
+   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+
+   /* close the file */
+   fclose(fp);
+
+   /* that's it */
+   return;
+}
+
+/* progressively read a file */
+
+int
+initialize_png_reader(png_structp *png_ptr, png_infop *info_ptr)
+{
+   /* Create and initialize the png_struct with the desired error handler
+    * functions.  If you want to use the default stderr and longjump method,
+    * you can supply NULL for the last three parameters.  We also check that
+    * the library version is compatible in case we are using dynamically
+    * linked libraries.
+    */
+   *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+       png_voidp user_error_ptr, user_error_fn, user_warning_fn);
+
+   if (*png_ptr == NULL)
+   {
+      *info_ptr = NULL;
+      return ERROR;
+   }
+
+   *info_ptr = png_create_info_struct(png_ptr);
+
+   if (*info_ptr == NULL)
+   {
+      png_destroy_read_struct(png_ptr, info_ptr, (png_infopp)NULL);
+      return ERROR;
+   }
+
+   if (setjmp((*png_ptr)->jmpbuf))
+   {
+      png_destroy_read_struct(png_ptr, info_ptr, (png_infopp)NULL);
+      return ERROR;
+   }
+
+   /* This one's new.  You will need to provide all three
+    * function callbacks, even if you aren't using them all.
+    * If you aren't using all functions, you can specify NULL
+    * parameters.  Even when all three functions are NULL,
+    * you need to call png_set_progressive_read_fn().
+    * These functions shouldn't be dependent on global or
+    * static variables if you are decoding several images
+    * simultaneously.  You should store stream specific data
+    * in a separate struct, given as the second parameter,
+    * and retrieve the pointer from inside the callbacks using
+    * the function png_get_progressive_ptr(png_ptr).
+    */
+   png_set_progressive_read_fn(*png_ptr, (void *)stream_data,
+      info_callback, row_callback, end_callback);
+
+   return OK;
+}
+
+int
+process_data(png_structp *png_ptr, png_infop *info_ptr,
+   png_bytep buffer, png_uint_32 length)
+{
+   if (setjmp((*png_ptr)->jmpbuf))
+   {
+      /* Free the png_ptr and info_ptr memory on error */
+      png_destroy_read_struct(png_ptr, info_ptr, (png_infopp)NULL);
+      return ERROR;
+   }
+
+   /* This one's new also.  Simply give it chunks of data as
+    * they arrive from the data stream (in order, of course).
+    * On Segmented machines, don't give it any more than 64K.
+    * The library seems to run fine with sizes of 4K, although
+    * you can give it much less if necessary (I assume you can
+    * give it chunks of 1 byte, but I haven't tried with less
+    * than 256 bytes yet).  When this function returns, you may
+    * want to display any rows that were generated in the row
+    * callback, if you aren't already displaying them there.
+    */
+   png_process_data(*png_ptr, *info_ptr, buffer, length);
+   return OK;
+}
+
+info_callback(png_structp png_ptr, png_infop info)
+{
+/* do any setup here, including setting any of the transformations
+ * mentioned in the Reading PNG files section.  For now, you _must_
+ * call either png_start_read_image() or png_read_update_info()
+ * after all the transformations are set (even if you don't set
+ * any).  You may start getting rows before png_process_data()
+ * returns, so this is your last chance to prepare for that.
+ */
+}
+
+row_callback(png_structp png_ptr, png_bytep new_row,
+   png_uint_32 row_num, int pass)
+{
+/* this function is called for every row in the image.  If the
+ * image is interlacing, and you turned on the interlace handler,
+ * this function will be called for every row in every pass.
+ * Some of these rows will not be changed from the previous pass.
+ * When the row is not changed, the new_row variable will be NULL.
+ * The rows and passes are called in order, so you don't really
+ * need the row_num and pass, but I'm supplying them because it
+ * may make your life easier.
+ *
+ * For the non-NULL rows of interlaced images, you must call
+ * png_progressive_combine_row() passing in the row and the
+ * old row.  You can call this function for NULL rows (it will
+ * just return) and for non-interlaced images (it just does the
+ * memcpy for you) if it will make the code easier.  Thus, you
+ * can just do this for all cases:
+ */
+
+   png_progressive_combine_row(png_ptr, old_row, new_row);
+
+/* where old_row is what was displayed for previous rows.  Note
+ * that the first pass (pass == 0 really) will completely cover
+ * the old row, so the rows do not have to be initialized.  After
+ * the first pass (and only for interlaced images), you will have
+ * to pass the current row, and the function will combine the
+ * old row and the new row.
+ */
+}
+
+end_callback(png_structp png_ptr, png_infop info)
+{
+/* this function is called when the whole image has been read,
+ * including any chunks after the image (up to and including
+ * the IEND).  You will usually have the same info chunk as you
+ * had in the header, although some data may have been added
+ * to the comments and time fields.
+ *
+ * Most people won't do much here, perhaps setting a flag that
+ * marks the image as finished.
+ */
+}
+
+/* write a png file */
+void write_png(char *file_name /* , ... other image information ... */)
+{
+   FILE *fp;
+   png_structp png_ptr;
+   png_infop info_ptr;
+
+   /* open the file */
+   fp = fopen(file_name, "wb");
+   if (fp == NULL)
+      return;
+
+   /* Create and initialize the png_struct with the desired error handler
+    * functions.  If you want to use the default stderr and longjump method,
+    * you can supply NULL for the last three parameters.  We also check that
+    * the library version is compatible with the one used at compile time,
+    * in case we are using dynamically linked libraries.  REQUIRED.
+    */
+   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+      png_voidp user_error_ptr, user_error_fn, user_warning_fn);
+
+   if (png_ptr == NULL)
+   {
+      fclose(fp);
+      return;
+   }
+
+   /* Allocate/initialize the image information data.  REQUIRED */
+   info_ptr = png_create_info_struct(png_ptr);
+   if (info_ptr == NULL)
+   {
+      fclose(fp);
+      png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
+      return;
+   }
+
+   /* Set error handling.  REQUIRED if you aren't supplying your own
+    * error hadnling functions in the png_create_write_struct() call.
+    */
+   if (setjmp(png_ptr->jmpbuf))
+   {
+      /* If we get here, we had a problem reading the file */
+      fclose(fp);
+      png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
+      return;
+   }
+
+   /* One of the following I/O initialization functions is REQUIRED */
+#ifdef streams /* I/O initialization method 1 */
+   /* set up the output control if you are using standard C streams */
+   png_init_io(png_ptr, fp);
+#else no_streams /* I/O initialization method 2 */
+   /* If you are using replacement read functions, instead of calling
+    * png_init_io() here you would call */
+   png_set_write_fn(png_ptr, (void *)user_io_ptr, user_write_fn,
+      user_IO_flush_function);
+   /* where user_io_ptr is a structure you want available to the callbacks */
+#endif no_streams /* only use one initialization method */
+
+   /* Set the image information here.  Width and height are up to 2^31,
+    * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
+    * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
+    * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
+    * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
+    * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
+    * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
+    */
+   png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_???,
+      PNG_INTERLACE_????, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+   /* set the palette if there is one.  REQUIRED for indexed-color images */
+   palette = (png_colorp)png_malloc(png_ptr, 256 * sizeof (png_color));
+   /* ... set palette colors ... */
+   png_set_PLTE(png_ptr, info_ptr, palette, 256);
+
+   /* optional significant bit chunk */
+   /* if we are dealing with a grayscale image then */
+   sig_bit.gray = true_bit_depth;
+   /* otherwise, if we are dealing with a color image then */
+   sig_bit.red = true_red_bit_depth;
+   sig_bit.green = true_green_bit_depth;
+   sig_bit.blue = true_blue_bit_depth;
+   /* if the image has an alpha channel then */
+   sig_bit.alpha = true_alpha_bit_depth;
+   png_set_sBIT(png_ptr, info_ptr, sig_bit);
+
+
+   /* Optional gamma chunk is strongly suggested if you have any guess
+    * as to the correct gamma of the image.
+    */
+   png_set_gAMA(png_ptr, info_ptr, gamma);
+
+   /* Optionally write comments into the image */
+   text_ptr[0].key = "Title";
+   text_ptr[0].text = "Mona Lisa";
+   text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
+   text_ptr[1].key = "Author";
+   text_ptr[1].text = "Leonardo DaVinci";
+   text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
+   text_ptr[2].key = "Description";
+   text_ptr[2].text = "<long text>";
+   text_ptr[2].compression = PNG_TEXT_COMPRESSION_zTXt;
+   png_set_text(png_ptr, info_ptr, text_ptr, 3);
+
+   /* other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs, */
+   /* note that if sRGB is present the cHRM chunk must be ignored
+    * on read and must be written in accordance with the sRGB profile */
+
+   /* Write the file header information.  REQUIRED */
+   png_write_info(png_ptr, info_ptr);
+
+   /* Once we write out the header, the compression type on the text
+    * chunks gets changed to PNG_TEXT_COMPRESSION_NONE_WR or
+    * PNG_TEXT_COMPRESSION_zTXt_WR, so it doesn't get written out again
+    * at the end.
+    */
+
+   /* set up the transformations you want.  Note that these are
+    * all optional.  Only call them if you want them.
+    */
+
+   /* invert monocrome pixels */
+   png_set_invert_mono(png_ptr);
+
+   /* Shift the pixels up to a legal bit depth and fill in
+    * as appropriate to correctly scale the image.
+    */
+   png_set_shift(png_ptr, &sig_bit);
+
+   /* pack pixels into bytes */
+   png_set_packing(png_ptr);
+
+   /* swap location of alpha bytes from ARGB to RGBA */
+   png_set_swap_alpha(png_ptr);
+
+   /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into
+    * RGB (4 channels -> 3 channels). The second parameter is not used.
+    */
+   png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
+
+   /* flip BGR pixels to RGB */
+   png_set_bgr(png_ptr);
+
+   /* swap bytes of 16-bit files to most significant byte first */
+   png_set_swap(png_ptr);
+
+   /* swap bits of 1, 2, 4 bit packed pixel formats */
+   png_set_packswap(png_ptr);
+
+   /* turn on interlace handling if you are not using png_write_image() */
+   if (interlacing)
+      number_passes = png_set_interlace_handling(png_ptr);
+   else
+      number_passes = 1;
+
+   /* The easiest way to write the image (you may have a different memory
+    * layout, however, so choose what fits your needs best).  You need to
+    * use the first method if you aren't handling interlacing yourself.
+    */
+   png_uint_32 k, height, width;
+   png_byte image[height][width];
+   png_bytep row_pointers[height];
+   for (k = 0; k < height; k++)
+     row_pointers[k] = image + k*width;
+
+   /* One of the following output methods is REQUIRED */
+#ifdef entire /* write out the entire image data in one call */
+   png_write_image(png_ptr, row_pointers);
+
+   /* the other way to write the image - deal with interlacing */
+
+#else no_entire /* write out the image data by one or more scanlines */
+   /* The number of passes is either 1 for non-interlaced images,
+    * or 7 for interlaced images.
+    */
+   for (pass = 0; pass < number_passes; pass++)
+   {
+      /* Write a few rows at a time. */
+      png_write_rows(png_ptr, &row_pointers[first_row], number_of_rows);
+
+      /* If you are only writing one row at a time, this works */
+      for (y = 0; y < height; y++)
+      {
+         png_write_rows(png_ptr, &row_pointers[y], 1);
+      }
+   }
+#endif no_entire /* use only one output method */
+
+   /* You can write optional chunks like tEXt, zTXt, and tIME at the end
+    * as well.
+    */
+
+   /* It is REQUIRED to call this to finish writing the rest of the file */
+   png_write_end(png_ptr, info_ptr);
+
+   /* if you malloced the palette, free it here */
+   free(info_ptr->palette);
+
+   /* if you allocated any text comments, free them here */
+
+   /* clean up after the write, and free any memory allocated */
+   png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+
+   /* close the file */
+   fclose(fp);
+
+   /* that's it */
+   return;
+}
+
diff --git a/src/png/libpng.3 b/src/png/libpng.3
new file mode 100644 (file)
index 0000000..f970736
--- /dev/null
@@ -0,0 +1,2621 @@
+.TH LIBPNG 3 "January 14, 1999"
+.SH NAME
+libpng \- Portable Network Graphics (PNG) Reference Library 1.0.3 - January 14, 1999
+.SH SYNOPSIS
+
+#include <png.h>
+
+int png_check_sig (png_bytep sig, int num);
+
+void png_chunk_error (png_structp png_ptr, png_const_charp
+error);
+
+void png_chunk_warning (png_structp png_ptr, png_const_charp
+message);
+
+void png_convert_from_struct_tm (png_timep ptime, struct tm FAR
+* ttime);
+
+void png_convert_from_time_t (png_timep ptime, time_t ttime);
+
+png_charp png_convert_to_rfc1123 (png_structp png_ptr,
+png_timep ptime);
+
+png_infop png_create_info_struct (png_structp png_ptr);
+
+png_structp png_create_read_struct (png_const_charp
+user_png_ver, voidp error_ptr, png_error_ptr error_fn,
+png_error_ptr warn_fn);
+
+png_structp png_create_read_struct_2(png_const_charp user_png_ver,
+png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr
+warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn,
+png_free_ptr free_fn)
+
+png_structp png_create_write_struct (png_const_charp
+user_png_ver, voidp error_ptr, png_error_ptr error_fn,
+png_error_ptr warn_fn);
+
+png_structp png_create_write_struct_2(png_const_charp
+user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
+png_error_ptr warn_fn, png_voidp mem_ptr,
+png_malloc_ptr malloc_fn, png_free_ptr free_fn)
+
+int png_debug(int level, png_const_charp message)
+
+int png_debug1(int level, png_const_charp message, p1)
+
+int png_debug2(int level, png_const_charp message, p1, p2)
+
+void png_destroy_info_struct (png_structp png_ptr, png_infopp
+info_ptr_ptr);
+
+void png_destroy_read_struct (png_structpp png_ptr_ptr,
+png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr);
+
+void png_destroy_write_struct (png_structpp png_ptr_ptr,
+png_infopp info_ptr_ptr);
+
+void png_error (png_structp png_ptr, png_const_charp error);
+
+void png_free (png_structp png_ptr, png_voidp ptr);
+
+void png_free_default(png_structp png_ptr, png_voidp ptr)
+
+png_byte png_get_bit_depth (png_structp png_ptr, png_infop
+info_ptr);
+
+png_uint_32 png_get_bKGD (png_structp png_ptr, png_infop
+info_ptr, png_color_16p *background);
+
+png_byte png_get_channels (png_structp png_ptr, png_infop
+info_ptr);
+
+png_uint_32 png_get_cHRM (png_structp png_ptr, png_infop
+info_ptr, double *white_x, double *white_y, double *red_x,
+double *red_y, double *green_x, double *green_y, double
+*blue_x, double *blue_y);
+
+png_byte png_get_color_type (png_structp png_ptr, png_infop
+info_ptr);
+
+png_byte png_get_compression_type (png_structp png_ptr,
+png_infop info_ptr);
+
+png_byte png_get_copyright (png_structp png_ptr);
+
+png_voidp png_get_error_ptr (png_structp png_ptr);
+
+png_byte png_get_filter_type (png_structp png_ptr, png_infop
+info_ptr);
+
+png_uint_32 png_get_gAMA (png_structp png_ptr, png_infop
+info_ptr, double *file_gamma);
+
+png_byte png_get_header_version (png_structp png_ptr);
+
+png_uint_32 png_get_hIST (png_structp png_ptr, png_infop
+info_ptr, png_uint_16p *hist);
+
+png_uint_32 png_get_IHDR (png_structp png_ptr, png_infop
+info_ptr, png_uint_32 *width, png_uint_32 *height, int
+*bit_depth, int *color_type, int *interlace_type, int
+*compression_type, int *filter_type);
+
+png_uint_32 png_get_image_height (png_structp png_ptr,
+png_infop info_ptr);
+
+png_uint_32 png_get_image_width (png_structp png_ptr, png_infop
+info_ptr);
+
+png_byte png_get_interlace_type (png_structp png_ptr, png_infop
+info_ptr);
+
+png_voidp png_get_io_ptr (png_structp png_ptr);
+
+png_voidp png_get_mem_ptr(png_structp png_ptr)
+
+png_uint_32 png_get_oFFs (png_structp png_ptr, png_infop
+info_ptr, png_uint_32 *offset_x, png_uint_32 *offset_y, int
+*unit_type);
+
+png_uint_32 png_get_pCAL (png_structp png_ptr, png_infop
+info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1,
+int *type, int *nparams, png_charp *units, png_charpp *params);
+
+png_uint_32 png_get_pHYs (png_structp png_ptr, png_infop
+info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int
+*unit_type);
+
+float png_get_pixel_aspect_ratio (png_structp png_ptr,
+png_infop info_ptr);
+
+png_uint_32 png_get_pixels_per_meter (png_structp png_ptr,
+png_infop info_ptr);
+
+png_voidp png_get_progressive_ptr (png_structp png_ptr);
+
+png_uint_32 png_get_PLTE (png_structp png_ptr, png_infop
+info_ptr, png_colorp *palette, int *num_palette);
+
+png_byte png_get_rgb_to_gray_status (png_structp png_ptr)
+
+png_uint_32 png_get_rowbytes (png_structp png_ptr, png_infop
+info_ptr);
+
+png_uint_32 png_get_sBIT (png_structp png_ptr, png_infop
+info_ptr, png_color_8p *sig_bit);
+
+png_bytep png_get_signature (png_structp png_ptr, png_infop
+info_ptr);
+
+png_uint_32 png_get_sRGB (png_structp png_ptr, png_infop
+info_ptr, int *intent);
+
+png_uint_32 png_get_text (png_structp png_ptr, png_infop
+info_ptr, png_textp *text_ptr, int *num_text);
+
+png_uint_32 png_get_tIME (png_structp png_ptr, png_infop
+info_ptr, png_timep *mod_time);
+
+png_uint_32 png_get_tRNS (png_structp png_ptr, png_infop
+info_ptr, png_bytep *trans, int *num_trans, png_color_16p
+*trans_values);
+
+png_uint_32 png_get_valid (png_structp png_ptr, png_infop
+info_ptr, png_uint_32 flag);
+
+png_uint_32 png_get_x_offset_microns (png_structp png_ptr,
+png_infop info_ptr);
+
+png_uint_32 png_get_x_offset_pixels (png_structp png_ptr,
+png_infop info_ptr);
+
+png_uint_32 png_get_x_pixels_per_meter (png_structp png_ptr,
+png_infop info_ptr);
+
+png_uint_32 png_get_y_offset_microns (png_structp png_ptr,
+png_infop info_ptr);
+
+png_uint_32 png_get_y_offset_pixels (png_structp png_ptr,
+png_infop info_ptr);
+
+png_uint_32 png_get_y_pixels_per_meter (png_structp png_ptr,
+png_infop info_ptr);
+
+void png_info_init (png_infop info_ptr);
+
+void png_init_io (png_structp png_ptr, FILE *fp);
+
+png_voidp png_malloc (png_structp png_ptr, png_uint_32 size);
+
+png_voidp png_malloc_default(png_structp png_ptr,
+png_uint_32 size)
+
+voidp png_memcpy (png_voidp s1, png_voidp s2, png_size_t size);
+
+png_voidp png_memcpy_check (png_structp png_ptr, png_voidp s1,
+png_voidp s2, png_uint_32 size);
+
+voidp png_memset (png_voidp s1, int value, png_size_t size);
+
+png_voidp png_memset_check (png_structp png_ptr, png_voidp
+s1, int value, png_uint_32 size);
+
+void png_process_data (png_structp png_ptr, png_infop info_ptr,
+png_bytep buffer, png_size_t buffer_size);
+
+void png_progressive_combine_row (png_structp png_ptr,
+png_bytep old_row, png_bytep new_row);
+
+void png_read_destroy (png_structp png_ptr, png_infop info_ptr,
+png_infop end_info_ptr);
+
+void png_read_end (png_structp png_ptr, png_infop info_ptr);
+
+void png_read_image (png_structp png_ptr, png_bytepp image);
+
+void png_read_info (png_structp png_ptr, png_infop info_ptr);
+
+void png_read_row (png_structp png_ptr, png_bytep row,
+png_bytep display_row);
+
+void png_read_rows (png_structp png_ptr, png_bytepp row,
+png_bytepp display_row, png_uint_32 num_rows);
+
+void png_read_update_info (png_structp png_ptr, png_infop
+info_ptr);
+
+void png_set_background (png_structp png_ptr, png_color_16p
+background_color, int background_gamma_code, int need_expand,
+double background_gamma);
+
+void png_set_bgr (png_structp png_ptr);
+
+void png_set_bKGD (png_structp png_ptr, png_infop info_ptr,
+png_color_16p background);
+
+void png_set_cHRM (png_structp png_ptr, png_infop info_ptr,
+double white_x, double white_y, double red_x, double red_y,
+double green_x, double green_y, double blue_x, double blue_y);
+
+void png_set_compression_level (png_structp png_ptr, int
+level);
+
+void png_set_compression_mem_level (png_structp png_ptr, int
+mem_level);
+
+void png_set_compression_method (png_structp png_ptr, int
+method);
+
+void png_set_compression_strategy (png_structp png_ptr, int
+strategy);
+
+void png_set_compression_window_bits (png_structp png_ptr, int
+window_bits);
+
+void png_set_crc_action (png_structp png_ptr, int crit_action,
+int ancil_action);
+
+void png_set_dither (png_structp png_ptr, png_colorp palette,
+int num_palette, int maximum_colors, png_uint_16p histogram,
+int full_dither);
+
+void png_set_error_fn (png_structp png_ptr, png_voidp
+error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn);
+
+void png_set_expand (png_structp png_ptr);
+
+void png_set_filler (png_structp png_ptr, png_uint_32 filler,
+int flags);
+
+void png_set_filter (png_structp png_ptr, int method, int
+filters);
+
+void png_set_filter_heuristics (png_structp png_ptr, int
+heuristic_method, int num_weights, png_doublep filter_weights,
+png_doublep filter_costs);
+
+void png_set_flush (png_structp png_ptr, int nrows);
+
+void png_set_gamma (png_structp png_ptr, double screen_gamma,
+double default_file_gamma);
+
+void png_set_gAMA (png_structp png_ptr, png_infop info_ptr,
+double file_gamma);
+
+void png_set_gray_to_rgb (png_structp png_ptr);
+
+void png_set_hIST (png_structp png_ptr, png_infop info_ptr,
+png_uint_16p hist);
+
+int png_set_interlace_handling (png_structp png_ptr);
+
+void png_set_invert_alpha (png_structp png_ptr);
+
+void png_set_invert_mono (png_structp png_ptr);
+
+void png_set_IHDR (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 width, png_uint_32 height, int bit_depth, int
+color_type, int interlace_type, int compression_type, int
+filter_type);
+
+void png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr,
+png_malloc_ptr malloc_fn, png_free_ptr free_fn)
+
+void png_set_oFFs (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 offset_x, png_uint_32 offset_y, int unit_type);
+
+void png_set_packing (png_structp png_ptr);
+
+void png_set_packswap (png_structp png_ptr);
+
+void png_set_pCAL (png_structp png_ptr, png_infop info_ptr,
+png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int
+nparams, png_charp units, png_charpp params);
+
+void png_set_pHYs (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 res_x, png_uint_32 res_y, int unit_type);
+
+void png_set_progressive_read_fn (png_structp png_ptr,
+png_voidp progressive_ptr, png_progressive_info_ptr info_fn,
+png_progressive_row_ptr row_fn, png_progressive_end_ptr
+end_fn);
+
+void png_set_PLTE (png_structp png_ptr, png_infop info_ptr,
+png_colorp palette, int num_palette);
+
+void png_set_read_fn (png_structp png_ptr, png_voidp io_ptr,
+png_rw_ptr read_data_fn);
+
+void png_set_read_status_fn (png_structp png_ptr, png_read_status_ptr
+read_row_fn);
+
+void png_set_read_user_transform_fn (png_structp png_ptr,
+png_user_transform_ptr read_user_transform_fn);
+
+void png_set_rgb_to_gray (png_structp png_ptr, int error_action);
+
+void png_set_sBIT (png_structp png_ptr, png_infop info_ptr,
+png_color_8p sig_bit);
+
+void png_set_shift (png_structp png_ptr, png_color_8p
+true_bits);
+
+void png_set_sig_bytes (png_structp png_ptr, int num_bytes);
+
+void png_set_sRGB (png_structp png_ptr, png_infop info_ptr, int
+intent);
+
+void png_set_sRGB_gAMA_and_cHRM (png_structp png_ptr, png_infop
+info_ptr, int intent);
+
+void png_set_strip_16 (png_structp png_ptr);
+
+void png_set_strip_alpha (png_structp png_ptr);
+
+void png_set_swap (png_structp png_ptr);
+
+void png_set_swap_alpha (png_structp png_ptr);
+
+void png_set_text (png_structp png_ptr, png_infop info_ptr,
+png_textp text_ptr, int num_text);
+
+void png_set_tIME (png_structp png_ptr, png_infop info_ptr,
+png_timep mod_time);
+
+void png_set_tRNS (png_structp png_ptr, png_infop info_ptr,
+png_bytep trans, int num_trans, png_color_16p trans_values);
+
+void png_set_write_fn (png_structp png_ptr, png_voidp io_ptr,
+png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn);
+
+void png_set_write_status_fn (png_structp png_ptr, png_write_status_ptr
+write_row_fn);
+
+void png_set_write_user_transform_fn (png_structp png_ptr,
+png_user_transform_ptr write_user_transform_fn);
+
+int png_sig_cmp (png_bytep sig, png_size_t start, png_size_t
+num_to_check);
+
+void png_start_read_image (png_structp png_ptr);
+
+void png_warning (png_structp png_ptr, png_const_charp
+message);
+
+void png_write_chunk (png_structp png_ptr, png_bytep
+chunk_name, png_bytep data, png_size_t length);
+
+void png_write_chunk_data (png_structp png_ptr, png_bytep data,
+png_size_t length);
+
+void png_write_chunk_end (png_structp png_ptr);
+
+void png_write_chunk_start (png_structp png_ptr, png_bytep
+chunk_name, png_uint_32 length);
+
+void png_write_destroy (png_structp png_ptr);
+
+void png_write_destroy_info (png_infop info_ptr);
+
+void png_write_end (png_structp png_ptr, png_infop info_ptr);
+
+void png_write_flush (png_structp png_ptr);
+
+void png_write_image (png_structp png_ptr, png_bytepp image);
+
+void png_write_info (png_structp png_ptr, png_infop info_ptr);
+
+void png_write_row (png_structp png_ptr, png_bytep row);
+
+void png_write_rows (png_structp png_ptr, png_bytepp row,
+png_uint_32 num_rows);
+
+.SH DESCRIPTION
+The
+.I libpng
+library supports encoding, decoding, and various manipulations of
+the Portable Network Graphics (PNG) format image files.  It uses the
+.IR zlib(3)
+compression library.
+Following is a copy of the libpng.txt file that accompanies libpng.
+.SH LIBPNG.TXT
+libpng.txt - A description on how to use and modify libpng
+
+ libpng version 1.0.3 - January 14, 1999
+ Updated and distributed by Glenn Randers-Pehrson
+ <randeg@alumni.rpi.edu>
+ Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ For conditions of distribution and use, see copyright
+ notice in png.h.
+
+ based on:
+
+ libpng 1.0 beta 6  version 0.96 May 28, 1997
+ Updated and distributed by Andreas Dilger
+ Copyright (c) 1996, 1997 Andreas Dilger
+
+ libpng 1.0 beta 2 - version 0.88  January 26, 1996
+ For conditions of distribution and use, see copyright
+ notice in png.h. Copyright (c) 1995, 1996 Guy Eric
+ Schalnat, Group 42, Inc.
+
+ Updated/rewritten per request in the libpng FAQ
+ Copyright (c) 1995 Frank J. T. Wojcik
+ December 18, 1995 && January 20, 1996
+
+.SH I. Introduction
+
+This file describes how to use and modify the PNG reference library
+(known as libpng) for your own use.  There are five sections to this
+file: introduction, structures, reading, writing, and modification and
+configuration notes for various special platforms.  In addition to this
+file, example.c is a good starting point for using the library, as
+it is heavily commented and should include everything most people
+will need.  We assume that libpng is already installed; see the
+INSTALL file for instructions on how to install libpng.
+
+Libpng was written as a companion to the PNG specification, as a way
+of reducing the amount of time and effort it takes to support the PNG
+file format in application programs.  The PNG specification is available
+as RFC 2083 <ftp://ftp.uu.net/graphics/png/documents/> and as a
+W3C Recommendation <http://www.w3.org/TR/REC.png.html>. Some
+additional chunks are described in the special-purpose public chunks
+documents at <ftp://ftp.uu.net/graphics/png/documents/>.  Other information
+about PNG, and the latest version of libpng, can be found at the PNG home
+page, <http://www.cdrom.com/pub/png/>.
+
+Most users will not have to modify the library significantly; advanced
+users may want to modify it more.  All attempts were made to make it as
+complete as possible, while keeping the code easy to understand.
+Currently, this library only supports C.  Support for other languages
+is being considered.
+
+Libpng has been designed to handle multiple sessions at one time,
+to be easily modifiable, to be portable to the vast majority of
+machines (ANSI, K&R, 16-, 32-, and 64-bit) available, and to be easy
+to use.  The ultimate goal of libpng is to promote the acceptance of
+the PNG file format in whatever way possible.  While there is still
+work to be done (see the TODO file), libpng should cover the
+majority of the needs of its users.
+
+Libpng uses zlib for its compression and decompression of PNG files.
+Further information about zlib, and the latest version of zlib, can
+be found at the zlib home page, <http://www.cdrom.com/pub/infozip/zlib/>.
+The zlib compression utility is a general purpose utility that is
+useful for more than PNG files, and can be used without libpng.
+See the documentation delivered with zlib for more details.
+You can usually find the source files for the zlib utility wherever you
+find the libpng source files.
+
+Libpng is thread safe, provided the threads are using different
+instances of the structures.  Each thread should have its own
+png_struct and png_info instances, and thus its own image.
+Libpng does not protect itself against two threads using the
+same instance of a structure.
+
+
+.SH II. Structures
+
+There are two main structures that are important to libpng, png_struct
+and png_info.  The first, png_struct, is an internal structure that
+will not, for the most part, be used by a user except as the first
+variable passed to every libpng function call.
+
+The png_info structure is designed to provide information about the
+PNG file.  At one time, the fields of png_info were intended to be
+directly accessible to the user.  However, this tended to cause problems
+with applications using dynamically loaded libraries, and as a result
+a set of interface functions for png_info was developed.  The fields
+of png_info are still available for older applications, but it is
+suggested that applications use the new interfaces if at all possible.
+
+The png.h header file is an invaluable reference for programming with libpng.
+And while I'm on the topic, make sure you include the libpng header file:
+
+#include <png.h>
+
+.SH III. Reading
+
+Reading PNG files:
+
+We'll now walk you through the possible functions to call when reading
+in a PNG file, briefly explaining the syntax and purpose of each one.
+See example.c and png.h for more detail.  While Progressive reading
+is covered in the next section, you will still need some of the
+functions discussed in this section to read a PNG file.
+
+You will want to do the I/O initialization(*) before you get into libpng,
+so if it doesn't work, you don't have much to undo.  Of course, you
+will also want to insure that you are, in fact, dealing with a PNG
+file.  Libpng provides a simple check to see if a file is a PNG file.
+To use it, pass in the first 1 to 8 bytes of the file, and it will
+return true or false (1 or 0) depending on whether the bytes could be
+part of a PNG file.  Of course, the more bytes you pass in, the
+greater the accuracy of the prediction.
+
+If you are intending to keep the file pointer open for use in libpng,
+you must ensure you don't read more than 8 bytes from the beginning
+of the file, and you also have to make a call to png_set_sig_bytes_read()
+with the number of bytes you read from the beginning.  Libpng will
+then only check the bytes (if any) that your program didn't read.
+
+(*): If you are not using the standard I/O functions, you will need
+to replace them with custom functions.  See the discussion under
+Customizing libpng.
+
+
+    FILE *fp = fopen(file_name, "rb");
+    if (!fp)
+    {
+        return;
+    }
+    fread(header, 1, number, fp);
+    is_png = !png_sig_cmp(header, 0, number);
+    if (!is_png)
+    {
+        return;
+    }
+
+
+Next, png_struct and png_info need to be allocated and initialized.  In
+order to ensure that the size of these structures is correct even with a
+dynamically linked libpng, there are functions to initialize and
+allocate the structures.  We also pass the library version, optional
+pointers to error handling functions, and a pointer to a data struct for
+use by the error functions, if necessary (the pointer and functions can
+be NULL if the default error handlers are to be used).  See the section
+on Changes to Libpng below regarding the old initialization functions.
+
+    png_structp png_ptr = png_create_read_struct
+       (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+        user_error_fn, user_warning_fn);
+    if (!png_ptr)
+        return;
+
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr)
+    {
+        png_destroy_read_struct(&png_ptr,
+           (png_infopp)NULL, (png_infopp)NULL);
+        return;
+    }
+
+    png_infop end_info = png_create_info_struct(png_ptr);
+    if (!end_info)
+    {
+        png_destroy_read_struct(&png_ptr, &info_ptr,
+          (png_infopp)NULL);
+        return;
+    }
+
+If you want to use your own memory allocation routines,
+define PNG_USER_MEM_SUPPORTED and use
+png_create_read_struct_2() instead of png_create_read_struct():
+
+    png_structp png_ptr = png_create_read_struct_2
+       (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+        user_error_fn, user_warning_fn, (png_voidp)
+        user_mem_ptr, user_malloc_fn, user_free_fn);
+
+The error handling routines passed to png_create_read_struct()
+and the memory alloc/free routines passed to png_create_struct_2()
+are only necessary if you are not using the libpng supplied error
+handling and memory alloc/free functions.
+
+When libpng encounters an error, it expects to longjmp back
+to your routine.  Therefore, you will need to call setjmp and pass
+your png_ptr->jmpbuf.  If you read the file from different
+routines, you will need to update the jmpbuf field every time you enter
+a new routine that will call a png_ function.
+
+See your documentation of setjmp/longjmp for your compiler for more
+handling in the Customizing Libpng section below for more information on
+the libpng error handling.  If an error occurs, and libpng longjmp's
+back to your setjmp, you will want to call png_destroy_read_struct() to
+free any memory.
+
+    if (setjmp(png_ptr->jmpbuf))
+    {
+        png_destroy_read_struct(&png_ptr, &info_ptr,
+           &end_info);
+        fclose(fp);
+        return;
+    }
+
+Now you need to set up the input code.  The default for libpng is to
+use the C function fread().  If you use this, you will need to pass a
+valid FILE * in the function png_init_io().  Be sure that the file is
+opened in binary mode.  If you wish to handle reading data in another
+way, you need not call the png_init_io() function, but you must then
+implement the libpng I/O methods discussed in the Customizing Libpng
+section below.
+
+    png_init_io(png_ptr, fp);
+
+If you had previously opened the file and read any of the signature from
+the beginning in order to see if this was a PNG file, you need to let
+libpng know that there are some bytes missing from the start of the file.
+
+    png_set_sig_bytes(png_ptr, number);
+
+At this point, you can set up a callback function that will be
+called after each row has been read, which you can use to control
+a progress meter or the like.  It's demonstrated in pngtest.c.
+You must supply a function
+
+    void read_row_callback(png_ptr, png_uint_32 row, int pass);
+    {
+      /* put your code here */
+    }
+
+(You can give it another name that you like instead of "read_row_callback")
+
+To inform libpng about your function, use
+
+    png_set_read_status_fn(png_ptr, read_row_callback);
+
+In PNG files, the alpha channel in an image is the level of opacity.
+If you need the alpha channel in an image to be the level of transparency
+instead of opacity, you can invert the alpha channel (or the tRNS chunk
+data) after it's read, so that 0 is fully opaque and 255 (in 8-bit or
+paletted images) or 65535 (in 16-bit images) is fully transparent, with
+
+    png_set_invert_alpha(png_ptr);
+
+This has to appear here rather than later with the other transformations
+because the tRNS chunk data must be modified in the case of paletted images.
+If your image is not a paletted image, the tRNS data (which in such cases
+represents a single color to be rendered as transparent) won't be changed.
+
+Finally, you can write your own transformation function if none of
+the existing ones meets your needs.  This is done by setting a callback
+with
+
+    png_set_read_user_transform_fn(png_ptr,
+       read_transform_fn);
+
+You must supply the function
+
+    void read_transform_fn(png_ptr ptr, row_info_ptr
+       row_info, png_bytep data)
+
+See pngtest.c for a working example.  Your function will be called
+after all of the other transformations have been processed.
+
+You are now ready to read all the file information up to the actual
+image data.  You do this with a call to png_read_info().
+
+    png_read_info(png_ptr, info_ptr);
+
+Functions are used to get the information from the info_ptr:
+
+    png_get_IHDR(png_ptr, info_ptr, &width, &height,
+       &bit_depth, &color_type, &interlace_type,
+       &compression_type, &filter_type);
+
+    width          - holds the width of the image
+                     in pixels (up to 2^31).
+    height         - holds the height of the image
+                     in pixels (up to 2^31).
+    bit_depth      - holds the bit depth of one of the
+                     image channels.  (valid values are
+                     1, 2, 4, 8, 16 and depend also on
+                     the color_type.  See also
+                     significant bits (sBIT) below).
+    color_type     - describes which color/alpha channels
+                         are present.
+                     PNG_COLOR_TYPE_GRAY
+                        (bit depths 1, 2, 4, 8, 16)
+                     PNG_COLOR_TYPE_GRAY_ALPHA
+                        (bit depths 8, 16)
+                     PNG_COLOR_TYPE_PALETTE
+                        (bit depths 1, 2, 4, 8)
+                     PNG_COLOR_TYPE_RGB
+                        (bit_depths 8, 16)
+                     PNG_COLOR_TYPE_RGB_ALPHA
+                        (bit_depths 8, 16)
+
+                     PNG_COLOR_MASK_PALETTE
+                     PNG_COLOR_MASK_COLOR
+                     PNG_COLOR_MASK_ALPHA
+
+    filter_type    - (must be PNG_FILTER_TYPE_BASE
+                     for PNG 1.0)
+    compression_type - (must be PNG_COMPRESSION_TYPE_BASE
+                     for PNG 1.0)
+    interlace_type - (PNG_INTERLACE_NONE or
+                     PNG_INTERLACE_ADAM7)
+    Any or all of interlace_type, compression_type, of
+                     filter_type can be
+    NULL if you are not interested in their values.
+
+    channels = png_get_channels(png_ptr, info_ptr);
+    channels       - number of channels of info for the
+                     color type (valid values are 1 (GRAY,
+                     PALETTE), 2 (GRAY_ALPHA), 3 (RGB),
+                     4 (RGB_ALPHA or RGB + filler byte))
+    rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+    rowbytes       - number of bytes needed to hold a row
+
+    signature = png_get_signature(png_ptr, info_ptr);
+    signature      - holds the signature read from the
+                     file (if any).  The data is kept in
+                     the same offset it would be if the
+                     whole signature were read (i.e. if an
+                     application had already read in 4
+                     bytes of signature before starting
+                     libpng, the remaining 4 bytes would
+                     be in signature[4] through signature[7]
+                     (see png_set_sig_bytes())).
+
+
+    width            = png_get_image_width(png_ptr,
+                         info_ptr);
+    height           = png_get_image_height(png_ptr,
+                         info_ptr);
+    bit_depth        = png_get_bit_depth(png_ptr,
+                         info_ptr);
+    color_type       = png_get_color_type(png_ptr,
+                         info_ptr);
+    filter_type      = png_get_filter_type(png_ptr,
+                         info_ptr);
+    compression_type = png_get_compression_type(png_ptr,
+                         info_ptr);
+    interlace_type   = png_get_interlace_type(png_ptr,
+                         info_ptr);
+
+
+These are also important, but their validity depends on whether the chunk
+has been read.  The png_get_valid(png_ptr, info_ptr, PNG_INFO_<chunk>) and
+png_get_<chunk>(png_ptr, info_ptr, ...) functions return non-zero if the
+data has been read, or zero if it is missing.  The parameters to the
+png_get_<chunk> are set directly if they are simple data types, or a pointer
+into the info_ptr is returned for any complex types.
+
+    png_get_PLTE(png_ptr, info_ptr, &palette,
+                     &num_palette);
+    palette        - the palette for the file
+                     (array of png_color)
+    num_palette    - number of entries in the palette
+
+    png_get_gAMA(png_ptr, info_ptr, &gamma);
+    gamma          - the gamma the file is written
+                     at (PNG_INFO_gAMA)
+
+    png_get_sRGB(png_ptr, info_ptr, &srgb_intent);
+    srgb_intent    - the rendering intent (PNG_INFO_sRGB)
+                     The presence of the sRGB chunk
+                     means that the pixel data is in the
+                     sRGB color space.  This chunk also
+                     implies specific values of gAMA and
+                     cHRM.
+
+    png_get_sBIT(png_ptr, info_ptr, &sig_bit);
+    sig_bit        - the number of significant bits for
+                     (PNG_INFO_sBIT) each of the gray,
+                     red, green, and blue channels,
+                     whichever are appropriate for the
+                     given color type (png_color_16)
+
+    png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans,
+                     &trans_values);
+    trans          - array of transparent entries for
+                     palette (PNG_INFO_tRNS)
+    trans_values   - transparent pixel for non-paletted
+                     images (PNG_INFO_tRNS)
+    num_trans      - number of transparent entries
+                     (PNG_INFO_tRNS)
+
+    png_get_hIST(png_ptr, info_ptr, &hist);
+                     (PNG_INFO_hIST)
+    hist           - histogram of palette (array of
+                     png_color_16)
+
+    png_get_tIME(png_ptr, info_ptr, &mod_time);
+    mod_time       - time image was last modified
+                    (PNG_VALID_tIME)
+
+    png_get_bKGD(png_ptr, info_ptr, &background);
+    background     - background color (PNG_VALID_bKGD)
+
+    num_text = png_get_text(png_ptr, info_ptr, &text_ptr);
+    text_ptr       - array of png_text holding image
+                     comments
+    text_ptr[i]->key   - keyword for comment.
+    text_ptr[i]->text  - text comments for current
+                         keyword.
+    text_ptr[i]->compression - type of compression used
+                     on "text" PNG_TEXT_COMPRESSION_NONE
+                     or PNG_TEXT_COMPRESSION_zTXt
+    num_text       - number of comments
+
+    png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y,
+                     &unit_type);
+    offset_x       - positive offset from the left edge
+                     of the screen
+    offset_y       - positive offset from the top edge
+                     of the screen
+    unit_type      - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER
+
+    png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y,
+                     &unit_type);
+    res_x          - pixels/unit physical resolution in
+                     x direction
+    res_y          - pixels/unit physical resolution in
+                     x direction
+    unit_type      - PNG_RESOLUTION_UNKNOWN,
+                     PNG_RESOLUTION_METER
+
+The data from the pHYs chunk can be retrieved in several convenient
+forms:
+
+    res_x = png_get_x_pixels_per_meter(png_ptr,
+                  info_ptr)
+    res_y = png_get_y_pixels_per_meter(png_ptr,
+                  info_ptr)
+    res_x_and_y = png_get_pixels_per_meter(png_ptr,
+                  info_ptr)
+    aspect_ratio = png_get_pixel_aspect_ratio(png_ptr,
+                  info_ptr)
+
+   (Each of these returns 0 [signifying "unknown"] if
+       the data is not present or if res_x is 0;
+       res_x_and_y is 0 if res_x != res_y)
+
+For more information, see the png_info definition in png.h and the
+PNG specification for chunk contents.  Be careful with trusting
+rowbytes, as some of the transformations could increase the space
+needed to hold a row (expand, filler, gray_to_rgb, etc.).
+See png_read_update_info(), below.
+
+A quick word about text_ptr and num_text.  PNG stores comments in
+keyword/text pairs, one pair per chunk, with no limit on the number
+of text chunks, and a 2^31 byte limit on their size.  While there are
+suggested keywords, there is no requirement to restrict the use to these
+strings.  It is strongly suggested that keywords and text be sensible
+to humans (that's the point), so don't use abbreviations.  Non-printing
+symbols are not allowed.  See the PNG specification for more details.
+There is also no requirement to have text after the keyword.
+
+Keywords should be limited to 79 Latin-1 characters without leading or
+trailing spaces, but non-consecutive spaces are allowed within the
+keyword.  It is possible to have the same keyword any number of times.
+The text_ptr is an array of png_text structures, each holding pointer
+to a keyword and a pointer to a text string.  Only the text string may
+be null.  The keyword/text pairs are put into the array in the order
+that they are received.  However, some or all of the text chunks may be
+after the image, so, to make sure you have read all the text chunks,
+don't mess with these until after you read the stuff after the image.
+This will be mentioned again below in the discussion that goes with
+png_read_end().
+
+After you've read the header information, you can set up the library
+to handle any special transformations of the image data.  The various
+ways to transform the data will be described in the order that they
+should occur.  This is important, as some of these change the color
+type and/or bit depth of the data, and some others only work on
+certain color types and bit depths.  Even though each transformation
+checks to see if it has data that it can do something with, you should
+make sure to only enable a transformation if it will be valid for the
+data.  For example, don't swap red and blue on grayscale data.
+
+The colors used for the background and transparency values should be
+supplied in the same format/depth as the current image data.  They
+are stored in the same format/depth as the image data in a bKGD or tRNS
+chunk, so this is what libpng expects for this data.  The colors are
+transformed to keep in sync with the image data when an application
+calls the png_read_update_info() routine (see below).
+
+Data will be decoded into the supplied row buffers packed into bytes
+unless the library has been told to transform it into another format.
+For example, 4 bit/pixel paletted or grayscale data will be returned
+2 pixels/byte with the leftmost pixel in the high-order bits of the
+byte, unless png_set_packing() is called.  8-bit RGB data will be stored
+in RGB RGB RGB format unless png_set_filler() is called to insert filler
+bytes, either before or after each RGB triplet.  16-bit RGB data will
+be returned RRGGBB RRGGBB, with the most significant byte of the color
+value first, unless png_set_strip_16() is called to transform it to
+regular RGB RGB triplets, or png_set_filler() is called to insert
+filler bytes, either before or after each RRGGBB triplet.  Similarly,
+8-bit or 16-bit grayscale data can be modified with png_set_filler()
+or png_set_strip_16().
+
+The following code transforms grayscale images of less than 8 to 8 bits,
+changes paletted images to RGB, and adds a full alpha channel if there is
+transparency information in a tRNS chunk.  This is most useful on
+grayscale images with bit depths of 2 or 4 or if there is a multiple-image
+viewing application that wishes to treat all images in the same way.
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE &&
+        bit_depth <= 8) png_set_expand(png_ptr);
+
+    if (color_type == PNG_COLOR_TYPE_GRAY &&
+        bit_depth < 8) png_set_expand(png_ptr);
+
+    if (png_get_valid(png_ptr, info_ptr,
+        PNG_INFO_tRNS)) png_set_expand(png_ptr);
+
+PNG can have files with 16 bits per channel.  If you only can handle
+8 bits per channel, this will strip the pixels down to 8 bit.
+
+    if (bit_depth == 16)
+        png_set_strip_16(png_ptr);
+
+The png_set_background() function tells libpng to composite images
+with alpha or simple transparency against the supplied background
+color.  If the PNG file contains a bKGD chunk (PNG_INFO_bKGD valid),
+you may use this color, or supply another color more suitable for
+the current display (e.g., the background color from a web page).  You
+need to tell libpng whether the color is in the gamma space of the
+display (PNG_BACKGROUND_GAMMA_SCREEN for colors you supply), the file
+(PNG_BACKGROUND_GAMMA_FILE for colors from the bKGD chunk), or one
+that is neither of these gammas (PNG_BACKGROUND_GAMMA_UNIQUE - I don't
+know why anyone would use this, but it's here).
+
+If, for some reason, you don't need the alpha channel on an image,
+and you want to remove it rather than combining it with the background
+(but the image author certainly had in mind that you *would* combine
+it with the background, so that's what you should probably do):
+
+    if (color_type & PNG_COLOR_MASK_ALPHA)
+        png_set_strip_alpha(png_ptr);
+
+PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as
+they can, resulting in, for example, 8 pixels per byte for 1 bit
+files.  This code expands to 1 pixel per byte without changing the
+values of the pixels:
+
+    if (bit_depth < 8)
+        png_set_packing(png_ptr);
+
+PNG files have possible bit depths of 1, 2, 4, 8, and 16.  All pixels
+stored in a PNG image have been "scaled" or "shifted" up to the next
+higher possible bit depth (e.g. from 5 bits/sample in the range [0,31] to
+8 bits/sample in the range [0, 255]).  However, it is also possible to
+convert the PNG pixel data back to the original bit depth of the image.
+This call reduces the pixels back down to the original bit depth:
+
+    png_color_16p sig_bit;
+
+    if (png_get_sBIT(png_ptr, info_ptr, &sig_bit))
+        png_set_shift(png_ptr, sig_bit);
+
+PNG files store 3-color pixels in red, green, blue order.  This code
+changes the storage of the pixels to blue, green, red:
+
+    if (color_type == PNG_COLOR_TYPE_RGB ||
+        color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+        png_set_bgr(png_ptr);
+
+PNG files store RGB pixels packed into 3 bytes. This code expands them
+into 4 bytes for windowing systems that need them in this format:
+
+    if (bit_depth == 8 && color_type ==
+        PNG_COLOR_TYPE_RGB) png_set_filler(png_ptr,
+        filler, PNG_FILLER_BEFORE);
+
+where "filler" is the 8 or 16-bit number to fill with, and the location is
+either PNG_FILLER_BEFORE or PNG_FILLER_AFTER, depending upon whether
+you want the filler before the RGB or after.  This transformation
+does not affect images that already have full alpha channels.
+
+If you are reading an image with an alpha channel, and you need the
+data as ARGB instead of the normal PNG format RGBA:
+
+    if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+        png_set_swap_alpha(png_ptr);
+
+For some uses, you may want a grayscale image to be represented as
+RGB.  This code will do that conversion:
+
+    if (color_type == PNG_COLOR_TYPE_GRAY ||
+        color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+          png_set_gray_to_rgb(png_ptr);
+
+Conversely, you can convert an RGB or RGBA image to grayscale or grayscale
+with alpha.  This is intended for conversion of images that really are
+gray (red == green == blue), so the function simply strips out the red
+and blue channels, leaving the green channel in the gray position.
+
+    if (color_type == PNG_COLOR_TYPE_RGB ||
+        color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+          png_set_rgb_to_gray(png_ptr, error_action,
+             float red_weight, float green_weight);
+
+    error_action = 1: silently do the conversion
+    error_action = 2: issue a warning if the original
+                      image has any pixel where
+                      red != green or red != blue
+    error_action = 3: issue an error and abort the
+                      conversion if the original
+                      image has any pixel where
+                      red != green or red != blue
+
+    red_weight:       weight of red component
+                      (NULL -> default 54/256)
+    green_weight:     weight of green component
+                      (NULL -> default 183/256)
+
+If you have set error_action = 1 or 2, you can
+later check whether the image really was gray, after processing
+the image rows, with the png_get_rgb_to_gray_status(png_ptr) function.
+It will return a png_byte that is zero if the image was gray or
+1 if there were any non-gray pixels.  bKGD and sBIT data
+will be silently converted to grayscale, using the green channel
+data, regardless of the error_action setting.
+
+With 0.0<=red_weight+green_weight<=1.0,
+the normalized graylevel is computed:
+
+    int rw = red_weight * 256;
+    int gw = green_weight * 256;
+    int bw = 256 - (rw + gw);
+    gray = (rw*red + gw*green + bw*blue)/256;
+
+The default values approximate those recommended in the Charles
+Poynton's Color FAQ, <http://www.inforamp.net/~poynton/>
+Copyright (c) 1998-01-04 Charles Poynton poynton@inforamp.net
+
+    Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
+
+Libpng approximates this with
+
+    Y = 0.211 * R    + 0.715 * G    + 0.074 * B
+
+which can be expressed with integers as
+
+    Y = (54 * R + 183 * G + 19 * B)/256
+
+The calculation is done in a linear colorspace, if the image gamma
+is known.
+
+If you have a grayscale and you are using png_set_expand() to change to
+a higher bit-depth, you must either supply the background color as a gray
+value at the original file bit-depth (need_expand = 1) or else supply the
+background color as an RGB triplet at the final, expanded bit depth
+(need_expand = 0).  Similarly, if you are reading a paletted image, you
+must either supply the background color as a palette index (need_expand = 1)
+or as an RGB triplet that may or may not be in the palette (need_expand = 0).
+
+    png_color_16 my_background;
+    png_color_16p image_background;
+
+    if (png_get_bKGD(png_ptr, info_ptr, &image_background))
+        png_set_background(png_ptr, image_background,
+          PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
+    else
+        png_set_background(png_ptr, &my_background,
+          PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
+
+To properly display PNG images on any kind of system, the application needs
+to know what the display gamma is.  Ideally, the user will know this, and
+the application will allow them to set it.  One method of allowing the user
+to set the display gamma separately for each system is to check for the
+DISPLAY_GAMMA and VIEWING_GAMMA environment variables or for a SCREEN_GAMMA
+environment variable, which will hopefully be correctly set.
+
+Note that display_gamma is the gamma of your display, while screen_gamma is
+the overall gamma correction required to produce pleasing results,
+which depends on the lighting conditions in the surrounding environment.
+Screen_gamma is display_gamma/viewing_gamma, where viewing_gamma is
+the amount of additional gamma correction needed to compensate for
+a (viewing_gamma=1.25) environment.  In a dim or brightly lit room, no
+compensation other than the display_gamma is needed (viewing_gamma=1.0).
+
+   if (/* We have a user-defined screen
+       gamma value */)
+   {
+      screen_gamma = user_defined_screen_gamma;
+   }
+   /* One way that applications can share the same
+      screen gamma value */
+   else if ((gamma_str = getenv("SCREEN_GAMMA"))
+      != NULL)
+   {
+      screen_gamma = atof(gamma_str);
+   }
+   /* If we don't have another value */
+   else
+   {
+      screen_gamma = 2.2; /* A good guess for a
+           PC monitor in a bright office or a dim room */
+      screen_gamma = 2.0; /* A good guess for a
+           PC monitor in a dark room */
+      screen_gamma = 1.7 or 1.0;  /* A good
+           guess for Mac systems */
+   }
+
+The png_set_gamma() function handles gamma transformations of the data.
+Pass both the file gamma and the current screen_gamma.  If the file does
+not have a gamma value, you can pass one anyway if you have an idea what
+it is (usually 0.45455 is a good guess for GIF images on PCs).  Note
+that file gammas are inverted from screen gammas.  See the discussions
+on gamma in the PNG specification for an excellent description of what
+gamma is, and why all applications should support it.  It is strongly
+recommended that PNG viewers support gamma correction.
+
+   if (png_get_gAMA(png_ptr, info_ptr, &gamma))
+      png_set_gamma(png_ptr, screen_gamma, gamma);
+   else
+      png_set_gamma(png_ptr, screen_gamma, 0.45455);
+
+If you need to reduce an RGB file to a paletted file, or if a paletted
+file has more entries then will fit on your screen, png_set_dither()
+will do that.  Note that this is a simple match dither that merely
+finds the closest color available.  This should work fairly well with
+optimized palettes, and fairly badly with linear color cubes.  If you
+pass a palette that is larger then maximum_colors, the file will
+reduce the number of colors in the palette so it will fit into
+maximum_colors.  If there is a histogram, it will use it to make
+more intelligent choices when reducing the palette.  If there is no
+histogram, it may not do as good a job.
+
+   if (color_type & PNG_COLOR_MASK_COLOR)
+   {
+      if (png_get_valid(png_ptr, info_ptr,
+         PNG_INFO_PLTE))
+      {
+         png_color_16p histogram;
+
+         png_get_hIST(png_ptr, info_ptr,
+            &histogram);
+         png_set_dither(png_ptr, palette, num_palette,
+            max_screen_colors, histogram, 1);
+      }
+      else
+      {
+         png_color std_color_cube[MAX_SCREEN_COLORS] =
+            { ... colors ... };
+
+         png_set_dither(png_ptr, std_color_cube,
+            MAX_SCREEN_COLORS, MAX_SCREEN_COLORS,
+            NULL,0);
+      }
+   }
+
+PNG files describe monochrome as black being zero and white being one.
+The following code will reverse this (make black be one and white be
+zero):
+
+   if (bit_depth == 1 && color_type == PNG_COLOR_GRAY)
+      png_set_invert_mono(png_ptr);
+
+PNG files store 16 bit pixels in network byte order (big-endian,
+ie. most significant bits first).  This code changes the storage to the
+other way (little-endian, i.e. least significant bits first, the
+way PCs store them):
+
+    if (bit_depth == 16)
+        png_set_swap(png_ptr);
+
+If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you
+need to change the order the pixels are packed into bytes, you can use:
+
+    if (bit_depth < 8)
+       png_set_packswap(png_ptr);
+
+The last thing to handle is interlacing; this is covered in detail below,
+but you must call the function here if you want libpng to handle expansion
+of the interlaced image.
+
+    number_of_passes = png_set_interlace_handling(png_ptr);
+
+After setting the transformations, libpng can update your png_info
+structure to reflect any transformations you've requested with this
+call.  This is most useful to update the info structure's rowbytes
+field so you can use it to allocate your image memory.  This function
+will also update your palette with the correct screen_gamma and
+background if these have been given with the calls above.
+
+    png_read_update_info(png_ptr, info_ptr);
+
+After you call png_read_update_info(), you can allocate any
+memory you need to hold the image.  The row data is simply
+raw byte data for all forms of images.  As the actual allocation
+varies among applications, no example will be given.  If you
+are allocating one large chunk, you will need to build an
+array of pointers to each row, as it will be needed for some
+of the functions below.
+
+After you've allocated memory, you can read the image data.
+The simplest way to do this is in one function call.  If you are
+allocating enough memory to hold the whole image, you can just
+call png_read_image() and libpng will read in all the image data
+and put it in the memory area supplied.  You will need to pass in
+an array of pointers to each row.
+
+This function automatically handles interlacing, so you don't need
+to call png_set_interlace_handling() or call this function multiple
+times, or any of that other stuff necessary with png_read_rows().
+
+   png_read_image(png_ptr, row_pointers);
+
+where row_pointers is:
+
+   png_bytep row_pointers[height];
+
+You can point to void or char or whatever you use for pixels.
+
+If you don't want to read in the whole image at once, you can
+use png_read_rows() instead.  If there is no interlacing (check
+interlace_type == PNG_INTERLACE_NONE), this is simple:
+
+    png_read_rows(png_ptr, row_pointers, NULL,
+       number_of_rows);
+
+where row_pointers is the same as in the png_read_image() call.
+
+If you are doing this just one row at a time, you can do this with
+row_pointers:
+
+    png_bytep row_pointers = row;
+    png_read_row(png_ptr, &row_pointers, NULL);
+
+If the file is interlaced (info_ptr->interlace_type != 0), things get
+somewhat harder.  The only current (PNG Specification version 1.0)
+interlacing type for PNG is (interlace_type == PNG_INTERLACE_ADAM7)
+is a somewhat complicated 2D interlace scheme, known as Adam7, that
+breaks down an image into seven smaller images of varying size, based
+on an 8x8 grid.
+
+libpng can fill out those images or it can give them to you "as is".
+If you want them filled out, there are two ways to do that.  The one
+mentioned in the PNG specification is to expand each pixel to cover
+those pixels that have not been read yet (the "rectangle" method).
+This results in a blocky image for the first pass, which gradually
+smooths out as more pixels are read.  The other method is the "sparkle"
+method, where pixels are drawn only in their final locations, with the
+rest of the image remaining whatever colors they were initialized to
+before the start of the read.  The first method usually looks better,
+but tends to be slower, as there are more pixels to put in the rows.
+
+If you don't want libpng to handle the interlacing details, just call
+png_read_rows() seven times to read in all seven images.  Each of the
+images is a valid image by itself, or they can all be combined on an
+8x8 grid to form a single image (although if you intend to combine them
+you would be far better off using the libpng interlace handling).
+
+The first pass will return an image 1/8 as wide as the entire image
+(every 8th column starting in column 0) and 1/8 as high as the original
+(every 8th row starting in row 0), the second will be 1/8 as wide
+(starting in column 4) and 1/8 as high (also starting in row 0).  The
+third pass will be 1/4 as wide (every 4th pixel starting in column 0) and
+1/8 as high (every 8th row starting in row 4), and the fourth pass will
+be 1/4 as wide and 1/4 as high (every 4th column starting in column 2,
+and every 4th row starting in row 0).  The fifth pass will return an
+image 1/2 as wide, and 1/4 as high (starting at column 0 and row 2),
+while the sixth pass will be 1/2 as wide and 1/2 as high as the original
+(starting in column 1 and row 0).  The seventh and final pass will be as
+wide as the original, and 1/2 as high, containing all of the odd
+numbered scanlines.  Phew!
+
+If you want libpng to expand the images, call this before calling
+png_start_read_image() or png_read_update_info():
+
+    if (interlace_type == PNG_INTERLACE_ADAM7)
+        number_of_passes
+           = png_set_interlace_handling(png_ptr);
+
+This will return the number of passes needed.  Currently, this
+is seven, but may change if another interlace type is added.
+This function can be called even if the file is not interlaced,
+where it will return one pass.
+
+If you are not going to display the image after each pass, but are
+going to wait until the entire image is read in, use the sparkle
+effect.  This effect is faster and the end result of either method
+is exactly the same.  If you are planning on displaying the image
+after each pass, the "rectangle" effect is generally considered the
+better looking one.
+
+If you only want the "sparkle" effect, just call png_read_rows() as
+normal, with the third parameter NULL.  Make sure you make pass over
+the image number_of_passes times, and you don't change the data in the
+rows between calls.  You can change the locations of the data, just
+not the data.  Each pass only writes the pixels appropriate for that
+pass, and assumes the data from previous passes is still valid.
+
+    png_read_rows(png_ptr, row_pointers, NULL,
+       number_of_rows);
+
+If you only want the first effect (the rectangles), do the same as
+before except pass the row buffer in the third parameter, and leave
+the second parameter NULL.
+
+    png_read_rows(png_ptr, NULL, row_pointers,
+       number_of_rows);
+
+After you are finished reading the image, you can finish reading
+the file.  If you are interested in comments or time, which may be
+stored either before or after the image data, you should pass the
+separate png_info struct if you want to keep the comments from
+before and after the image separate.  If you are not interested, you
+can pass NULL.
+
+   png_read_end(png_ptr, end_info);
+
+When you are done, you can free all memory allocated by libpng like this:
+
+   png_destroy_read_struct(&png_ptr, &info_ptr,
+       &end_info);
+
+For a more compact example of reading a PNG image, see the file example.c.
+
+
+Reading PNG files progressively:
+
+The progressive reader is slightly different then the non-progressive
+reader.  Instead of calling png_read_info(), png_read_rows(), and
+png_read_end(), you make one call to png_process_data(), which calls
+callbacks when it has the info, a row, or the end of the image.  You
+set up these callbacks with png_set_progressive_read_fn().  You don't
+have to worry about the input/output functions of libpng, as you are
+giving the library the data directly in png_process_data().  I will
+assume that you have read the section on reading PNG files above,
+so I will only highlight the differences (although I will show
+all of the code).
+
+png_structp png_ptr;
+png_infop info_ptr;
+
+ /*  An example code fragment of how you would
+     initialize the progressive reader in your
+     application. */
+ int
+ initialize_png_reader()
+ {
+    png_ptr = png_create_read_struct
+        (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+         user_error_fn, user_warning_fn);
+    if (!png_ptr)
+        return -1;
+    info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr)
+    {
+        png_destroy_read_struct(&png_ptr, (png_infopp)NULL,
+           (png_infopp)NULL);
+        return -1;
+    }
+
+    if (setjmp(png_ptr->jmpbuf))
+    {
+        png_destroy_read_struct(&png_ptr, &info_ptr,
+           (png_infopp)NULL);
+        return -1;
+    }
+
+    /* This one's new.  You can provide functions
+       to be called when the header info is valid,
+       when each row is completed, and when the image
+       is finished.  If you aren't using all functions,
+       you can specify NULL parameters.  Even when all
+       three functions are NULL, you need to call
+       png_set_progressive_read_fn().  You can use
+       any struct as the user_ptr (cast to a void pointer
+       for the function call), and retrieve the pointer
+       from inside the callbacks using the function
+
+          png_get_progressive_ptr(png_ptr);
+
+       which will return a void pointer, which you have
+       to cast appropriately.
+     */
+    png_set_progressive_read_fn(png_ptr, (void *)user_ptr,
+        info_callback, row_callback, end_callback);
+
+    return 0;
+ }
+
+ /* A code fragment that you call as you receive blocks
+   of data */
+ int
+ process_data(png_bytep buffer, png_uint_32 length)
+ {
+    if (setjmp(png_ptr->jmpbuf))
+    {
+        png_destroy_read_struct(&png_ptr, &info_ptr,
+           (png_infopp)NULL);
+        return -1;
+    }
+
+    /* This one's new also.  Simply give it a chunk
+       of data from the file stream (in order, of
+       course).  On machines with segmented memory
+       models machines, don't give it any more than
+       64K.  The library seems to run fine with sizes
+       of 4K. Although you can give it much less if
+       necessary (I assume you can give it chunks of
+       1 byte, I haven't tried less then 256 bytes
+       yet).  When this function returns, you may
+       want to display any rows that were generated
+       in the row callback if you don't already do
+       so there.
+     */
+    png_process_data(png_ptr, info_ptr, buffer, length);
+    return 0;
+ }
+
+ /* This function is called (as set by
+    png_set_progressive_read_fn() above) when enough data
+    has been supplied so all of the header has been
+    read.
+ */
+ void
+ info_callback(png_structp png_ptr, png_infop info)
+ {
+    /* Do any setup here, including setting any of
+       the transformations mentioned in the Reading
+       PNG files section.  For now, you _must_ call
+       either png_start_read_image() or
+       png_read_update_info() after all the
+       transformations are set (even if you don't set
+       any).  You may start getting rows before
+       png_process_data() returns, so this is your
+       last chance to prepare for that.
+     */
+ }
+
+ /* This function is called when each row of image
+    data is complete */
+ void
+ row_callback(png_structp png_ptr, png_bytep new_row,
+    png_uint_32 row_num, int pass)
+ {
+    /* If the image is interlaced, and you turned
+       on the interlace handler, this function will
+       be called for every row in every pass.  Some
+       of these rows will not be changed from the
+       previous pass.  When the row is not changed,
+       the new_row variable will be NULL.  The rows
+       and passes are called in order, so you don't
+       really need the row_num and pass, but I'm
+       supplying them because it may make your life
+       easier.
+
+       For the non-NULL rows of interlaced images,
+       you must call png_progressive_combine_row()
+       passing in the row and the old row.  You can
+       call this function for NULL rows (it will just
+       return) and for non-interlaced images (it just
+       does the memcpy for you) if it will make the
+       code easier.  Thus, you can just do this for
+       all cases:
+     */
+
+        png_progressive_combine_row(png_ptr, old_row,
+          new_row);
+
+    /* where old_row is what was displayed for
+       previously for the row.  Note that the first
+       pass (pass == 0, really) will completely cover
+       the old row, so the rows do not have to be
+       initialized.  After the first pass (and only
+       for interlaced images), you will have to pass
+       the current row, and the function will combine
+       the old row and the new row.
+    */
+ }
+
+ void
+ end_callback(png_structp png_ptr, png_infop info)
+ {
+    /* This function is called after the whole image
+       has been read, including any chunks after the
+       image (up to and including the IEND).  You
+       will usually have the same info chunk as you
+       had in the header, although some data may have
+       been added to the comments and time fields.
+
+       Most people won't do much here, perhaps setting
+       a flag that marks the image as finished.
+     */
+ }
+
+
+
+.SH IV. Writing
+
+Much of this is very similar to reading.  However, everything of
+importance is repeated here, so you won't have to constantly look
+back up in the reading section to understand writing.
+
+You will want to do the I/O initialization before you get into libpng,
+so if it doesn't work, you don't have anything to undo. If you are not
+using the standard I/O functions, you will need to replace them with
+custom writing functions.  See the discussion under Customizing libpng.
+
+    FILE *fp = fopen(file_name, "wb");
+    if (!fp)
+    {
+       return;
+    }
+
+Next, png_struct and png_info need to be allocated and initialized.
+As these can be both relatively large, you may not want to store these
+on the stack, unless you have stack space to spare.  Of course, you
+will want to check if they return NULL.  If you are also reading,
+you won't want to name your read structure and your write structure
+both "png_ptr"; you can call them anything you like, such as
+"read_ptr" and "write_ptr".  Look at pngtest.c, for example.
+
+    png_structp png_ptr = png_create_write_struct
+       (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+        user_error_fn, user_warning_fn);
+    if (!png_ptr)
+       return;
+
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr)
+    {
+       png_destroy_write_struct(&png_ptr,
+         (png_infopp)NULL);
+       return;
+    }
+
+If you want to use your own memory allocation routines,
+define PNG_USER_MEM_SUPPORTED and use
+png_create_write_struct_2() instead of png_create_read_struct():
+
+    png_structp png_ptr = png_create_write_struct_2
+       (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+        user_error_fn, user_warning_fn, (png_voidp)
+        user_mem_ptr, user_malloc_fn, user_free_fn);
+
+After you have these structures, you will need to set up the
+error handling.  When libpng encounters an error, it expects to
+longjmp() back to your routine.  Therefore, you will need to call
+setjmp() and pass the png_ptr->jmpbuf.  If you
+write the file from different routines, you will need to update
+the jmpbuf field every time you enter a new routine that will
+call a png_ function.  See your documentation of setjmp/longjmp
+for your compiler for more information on setjmp/longjmp.  See
+the discussion on libpng error handling in the Customizing Libpng
+section below for more information on the libpng error handling.
+
+    if (setjmp(png_ptr->jmpbuf))
+    {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        fclose(fp);
+        return;
+    }
+    ...
+    return;
+
+Now you need to set up the output code.  The default for libpng is to
+use the C function fwrite().  If you use this, you will need to pass a
+valid FILE * in the function png_init_io().  Be sure that the file is
+opened in binary mode.  Again, if you wish to handle writing data in
+another way, see the discussion on libpng I/O handling in the Customizing
+Libpng section below.
+
+    png_init_io(png_ptr, fp);
+
+At this point, you can set up a callback function that will be
+called after each row has been written, which you can use to control
+a progress meter or the like.  It's demonstrated in pngtest.c.
+You must supply a function
+
+    void write_row_callback(png_ptr, png_uint_32 row, int pass);
+    {
+      /* put your code here */
+    }
+
+(You can give it another name that you like instead of "write_row_callback")
+
+To inform libpng about your function, use
+
+    png_set_write_status_fn(png_ptr, write_row_callback);
+
+You now have the option of modifying how the compression library will
+run.  The following functions are mainly for testing, but may be useful
+in some cases, like if you need to write PNG files extremely fast and
+are willing to give up some compression, or if you want to get the
+maximum possible compression at the expense of slower writing.  If you
+have no special needs in this area, let the library do what it wants by
+not calling this function at all, as it has been tuned to deliver a good
+speed/compression ratio. The second parameter to png_set_filter() is
+the filter method, for which the only valid value is '0' (as of the
+October 1996 PNG specification, version 1.0).  The third parameter is a
+flag that indicates which filter type(s) are to be tested for each
+scanline.  See the Compression Library for details on the specific filter
+types.
+
+
+    /* turn on or off filtering, and/or choose
+       specific filters */
+    png_set_filter(png_ptr, 0,
+       PNG_FILTER_NONE | PNG_FILTER_SUB |
+       PNG_FILTER_PAETH);
+
+The png_set_compression_???() functions interface to the zlib compression
+library, and should mostly be ignored unless you really know what you are
+doing.  The only generally useful call is png_set_compression_level()
+which changes how much time zlib spends on trying to compress the image
+data.  See the Compression Library for details on the compression levels.
+
+    /* set the zlib compression level */
+    png_set_compression_level(png_ptr,
+        Z_BEST_COMPRESSION);
+
+    /* set other zlib parameters */
+    png_set_compression_mem_level(png_ptr, 8);
+    png_set_compression_strategy(png_ptr,
+        Z_DEFAULT_STRATEGY);
+    png_set_compression_window_bits(png_ptr, 15);
+    png_set_compression_method(png_ptr, 8);
+
+You now need to fill in the png_info structure with all the data you
+wish to write before the actual image.  Note that the only thing you
+are allowed to write after the image is the text chunks and the time
+chunk (as of PNG Specification 1.0, anyway).  See png_write_end() and
+the latest PNG specification for more information on that.  If you
+wish to write them before the image, fill them in now, and flag that
+data as being valid.  If you want to wait until after the data, don't
+fill them until png_write_end().  For all the fields in png_info and
+their data types, see png.h.  For explanations of what the fields
+contain, see the PNG specification.
+
+Some of the more important parts of the png_info are:
+
+    png_set_IHDR(png_ptr, info_ptr, width, height,
+       bit_depth, color_type, interlace_type,
+       compression_type, filter_type)
+    width          - holds the width of the image
+                     in pixels (up to 2^31).
+    height         - holds the height of the image
+                     in pixels (up to 2^31).
+    bit_depth      - holds the bit depth of one of the
+                     image channels.
+                     (valid values are 1, 2, 4, 8, 16
+                     and depend also on the
+                     color_type.  See also significant
+                     bits (sBIT) below).
+    color_type     - describes which color/alpha
+                     channels are present.
+                     PNG_COLOR_TYPE_GRAY
+                        (bit depths 1, 2, 4, 8, 16)
+                     PNG_COLOR_TYPE_GRAY_ALPHA
+                        (bit depths 8, 16)
+                     PNG_COLOR_TYPE_PALETTE
+                        (bit depths 1, 2, 4, 8)
+                     PNG_COLOR_TYPE_RGB
+                        (bit_depths 8, 16)
+                     PNG_COLOR_TYPE_RGB_ALPHA
+                        (bit_depths 8, 16)
+
+                     PNG_COLOR_MASK_PALETTE
+                     PNG_COLOR_MASK_COLOR
+                     PNG_COLOR_MASK_ALPHA
+
+    interlace_type - PNG_INTERLACE_NONE or
+                     PNG_INTERLACE_ADAM7
+    compression_type - (must be
+                     PNG_COMPRESSION_TYPE_DEFAULT)
+    filter_type    - (must be PNG_FILTER_TYPE_DEFAULT)
+
+    png_set_PLTE(png_ptr, info_ptr, palette,
+       num_palette);
+    palette        - the palette for the file
+                     (array of png_color)
+    num_palette    - number of entries in the palette
+
+    png_set_gAMA(png_ptr, info_ptr, gamma);
+    gamma          - the gamma the image was created
+                     at (PNG_INFO_gAMA)
+
+    png_set_sRGB(png_ptr, info_ptr, srgb_intent);
+    srgb_intent    - the rendering intent
+                     (PNG_INFO_sRGB) The presence of
+                     the sRGB chunk means that the pixel
+                     data is in the sRGB color space.
+                     This chunk also implies specific
+                     values of gAMA and cHRM.  Rendering
+                     intent is the CSS-1 property that
+                     has been defined by the International
+                     Color Consortium
+                     (http://www.color.org).
+                     It can be one of
+                     PNG_SRGB_INTENT_SATURATION,
+                     PNG_SRGB_INTENT_PERCEPTUAL,
+                     PNG_SRGB_INTENT_ABSOLUTE, or
+                     PNG_SRGB_INTENT_RELATIVE.
+
+
+    png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr,
+       srgb_intent);
+    srgb_intent    - the rendering intent
+                     (PNG_INFO_sRGB) The presence of the
+                     sRGB chunk means that the pixel
+                     data is in the sRGB color space.
+                     This function also causes gAMA and
+                     cHRM chunks with the specific values
+                     that are consistent with sRGB to be
+                     written.
+
+    png_set_sBIT(png_ptr, info_ptr, sig_bit);
+    sig_bit        - the number of significant bits for
+                     (PNG_INFO_sBIT) each of the gray, red,
+                     green, and blue channels, whichever are
+                     appropriate for the given color type
+                     (png_color_16)
+
+    png_set_tRNS(png_ptr, info_ptr, trans, num_trans,
+       trans_values);
+    trans          - array of transparent entries for
+                     palette (PNG_INFO_tRNS)
+    trans_values   - transparent pixel for non-paletted
+                     images (PNG_INFO_tRNS)
+    num_trans      - number of transparent entries
+                     (PNG_INFO_tRNS)
+
+    png_set_hIST(png_ptr, info_ptr, hist);
+                    (PNG_INFO_hIST)
+    hist           - histogram of palette (array of
+                     png_color_16)
+
+    png_set_tIME(png_ptr, info_ptr, mod_time);
+    mod_time       - time image was last modified
+                     (PNG_VALID_tIME)
+
+    png_set_bKGD(png_ptr, info_ptr, background);
+    background     - background color (PNG_VALID_bKGD)
+
+    png_set_text(png_ptr, info_ptr, text_ptr, num_text);
+    text_ptr       - array of png_text holding image
+                     comments
+    text_ptr[i]->key   - keyword for comment.
+    text_ptr[i]->text  - text comments for current
+                         keyword.
+    text_ptr[i]->compression - type of compression used
+         on "text" PNG_TEXT_COMPRESSION_NONE or
+         PNG_TEXT_COMPRESSION_zTXt
+    num_text    - number of comments in text_ptr
+
+    png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y,
+        unit_type);
+    offset_x  - positive offset from the left
+                     edge of the screen
+    offset_y  - positive offset from the top
+                     edge of the screen
+    unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER
+
+    png_set_pHYs(png_ptr, info_ptr, res_x, res_y,
+        unit_type);
+    res_x       - pixels/unit physical resolution
+                  in x direction
+    res_y       - pixels/unit physical resolution
+                  in y direction
+    unit_type   - PNG_RESOLUTION_UNKNOWN,
+                  PNG_RESOLUTION_METER
+
+In PNG files, the alpha channel in an image is the level of opacity.
+If your data is supplied as a level of transparency, you can invert the
+alpha channel before you write it, so that 0 is fully transparent and 255
+(in 8-bit or paletted images) or 65535 (in 16-bit images) is fully opaque,
+with
+
+    png_set_invert_alpha(png_ptr);
+
+This must appear here instead of later with the other transformations
+because in the case of paletted images the tRNS chunk data has to
+be inverted before the tRNS chunk is written.  If your image is not a
+paletted image, the tRNS data (which in such cases represents a single
+color to be rendered as transparent) won't be changed.
+
+A quick word about text and num_text.  text is an array of png_text
+structures.  num_text is the number of valid structures in the array.
+If you want, you can use max_text to hold the size of the array, but
+libpng ignores it for writing (it does use it for reading).  Each
+png_text structure holds a keyword-text value, and a compression type.
+The compression types have the same valid numbers as the compression
+types of the image data.  Currently, the only valid number is zero.
+However, you can store text either compressed or uncompressed, unlike
+images, which always have to be compressed.  So if you don't want the
+text compressed, set the compression type to PNG_TEXT_COMPRESSION_NONE.
+Until text gets around 1000 bytes, it is not worth compressing it.
+After the text has been written out to the file, the compression type
+is set to PNG_TEXT_COMPRESSION_NONE_WR or PNG_TEXT_COMPRESSION_zTXt_WR,
+so that it isn't written out again at the end (in case you are calling
+png_write_end() with the same struct.
+
+The keywords that are given in the PNG Specification are:
+
+    Title            Short (one line) title or
+                     caption for image
+    Author           Name of image's creator
+    Description      Description of image (possibly long)
+    Copyright        Copyright notice
+    Creation Time    Time of original image creation
+                     (usually RFC 1123 format, see below)
+    Software         Software used to create the image
+    Disclaimer       Legal disclaimer
+    Warning          Warning of nature of content
+    Source           Device used to create the image
+    Comment          Miscellaneous comment; conversion
+                     from other image format
+
+The keyword-text pairs work like this.  Keywords should be short
+simple descriptions of what the comment is about.  Some typical
+keywords are found in the PNG specification, as is some recommendations
+on keywords.  You can repeat keywords in a file.  You can even write
+some text before the image and some after.  For example, you may want
+to put a description of the image before the image, but leave the
+disclaimer until after, so viewers working over modem connections
+don't have to wait for the disclaimer to go over the modem before
+they start seeing the image.  Finally, keywords should be full
+words, not abbreviations.  Keywords and text are in the ISO 8859-1
+(Latin-1) character set (a superset of regular ASCII) and can not
+contain NUL characters, and should not contain control or other
+unprintable characters.  To make the comments widely readable, stick
+with basic ASCII, and avoid machine specific character set extensions
+like the IBM-PC character set.  The keyword must be present, but
+you can leave off the text string on non-compressed pairs.
+Compressed pairs must have a text string, as only the text string
+is compressed anyway, so the compression would be meaningless.
+
+PNG supports modification time via the png_time structure.  Two
+conversion routines are proved, png_convert_from_time_t() for
+time_t and png_convert_from_struct_tm() for struct tm.  The
+time_t routine uses gmtime().  You don't have to use either of
+these, but if you wish to fill in the png_time structure directly,
+you should provide the time in universal time (GMT) if possible
+instead of your local time.  Note that the year number is the full
+year (e.g. 1998, rather than 98 - PNG is year 2000 compliant!), and
+that months start with 1.
+
+If you want to store the time of the original image creation, you should
+use a plain tEXt chunk with the "Creation Time" keyword.  This is
+necessary because the "creation time" of a PNG image is somewhat vague,
+depending on whether you mean the PNG file, the time the image was
+created in a non-PNG format, a still photo from which the image was
+scanned, or possibly the subject matter itself.  In order to facilitate
+machine-readable dates, it is recommended that the "Creation Time"
+tEXt chunk use RFC 1123 format dates (e.g. "22 May 1997 18:07:10 GMT"),
+although this isn't a requirement.  Unlike the tIME chunk, the
+"Creation Time" tEXt chunk is not expected to be automatically changed
+by the software.  To facilitate the use of RFC 1123 dates, a function
+png_convert_to_rfc1123(png_timep) is provided to convert from PNG
+time to an RFC 1123 format string.
+
+You are now ready to write all the file information up to the actual
+image data.  You do this with a call to png_write_info().
+
+    png_write_info(png_ptr, info_ptr);
+
+After you've written the file information, you can set up the library
+to handle any special transformations of the image data.  The various
+ways to transform the data will be described in the order that they
+should occur.  This is important, as some of these change the color
+type and/or bit depth of the data, and some others only work on
+certain color types and bit depths.  Even though each transformation
+checks to see if it has data that it can do something with, you should
+make sure to only enable a transformation if it will be valid for the
+data.  For example, don't swap red and blue on grayscale data.
+
+PNG files store RGB pixels packed into 3 or 6 bytes.  This code tells
+the library to expand the input data to 4 or 8 bytes per pixel
+(or expand 1 or 2-byte grayscale data to 2 or 4 bytes per pixel).
+
+    png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
+
+where the 0 is the value that will be put in the 4th byte, and the
+location is either PNG_FILLER_BEFORE or PNG_FILLER_AFTER, depending
+upon whether the filler byte is stored XRGB or RGBX.
+
+PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as
+they can, resulting in, for example, 8 pixels per byte for 1 bit files.
+If the data is supplied at 1 pixel per byte, use this code, which will
+correctly pack the pixels into a single byte:
+
+    png_set_packing(png_ptr);
+
+PNG files reduce possible bit depths to 1, 2, 4, 8, and 16.  If your
+data is of another bit depth, you can write an sBIT chunk into the
+file so that decoders can get the original data if desired.
+
+    /* Set the true bit depth of the image data */
+    if (color_type & PNG_COLOR_MASK_COLOR)
+    {
+        sig_bit.red = true_bit_depth;
+        sig_bit.green = true_bit_depth;
+        sig_bit.blue = true_bit_depth;
+    }
+    else
+    {
+        sig_bit.gray = true_bit_depth;
+    }
+    if (color_type & PNG_COLOR_MASK_ALPHA)
+    {
+        sig_bit.alpha = true_bit_depth;
+    }
+
+    png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+
+If the data is stored in the row buffer in a bit depth other than
+one supported by PNG (e.g. 3 bit data in the range 0-7 for a 4-bit PNG),
+this will scale the values to appear to be the correct bit depth as
+is required by PNG.
+
+    png_set_shift(png_ptr, &sig_bit);
+
+PNG files store 16 bit pixels in network byte order (big-endian,
+ie. most significant bits first).  This code would be used if they are
+supplied the other way (little-endian, i.e. least significant bits
+first, the way PCs store them):
+
+    if (bit_depth > 8)
+       png_set_swap(png_ptr);
+
+If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you
+need to change the order the pixels are packed into bytes, you can use:
+
+    if (bit_depth < 8)
+       png_set_packswap(png_ptr);
+
+PNG files store 3 color pixels in red, green, blue order.  This code
+would be used if they are supplied as blue, green, red:
+
+    png_set_bgr(png_ptr);
+
+PNG files describe monochrome as black being zero and white being
+one. This code would be used if the pixels are supplied with this reversed
+(black being one and white being zero):
+
+    png_set_invert_mono(png_ptr);
+
+Finally, you can write your own transformation function if none of
+the existing ones meets your needs.  This is done by setting a callback
+with
+
+    png_set_write_user_transform_fn(png_ptr,
+       write_transform_fn);
+
+You must supply the function
+
+    void write_transform_fn(png_ptr ptr, row_info_ptr
+       row_info, png_bytep data)
+
+See pngtest.c for a working example.  Your function will be called
+before any of the other transformations have been processed.
+
+It is possible to have libpng flush any pending output, either manually,
+or automatically after a certain number of lines have been written.  To
+flush the output stream a single time call:
+
+    png_write_flush(png_ptr);
+
+and to have libpng flush the output stream periodically after a certain
+number of scanlines have been written, call:
+
+    png_set_flush(png_ptr, nrows);
+
+Note that the distance between rows is from the last time png_write_flush()
+was called, or the first row of the image if it has never been called.
+So if you write 50 lines, and then png_set_flush 25, it will flush the
+output on the next scanline, and every 25 lines thereafter, unless
+png_write_flush() is called before 25 more lines have been written.
+If nrows is too small (less than about 10 lines for a 640 pixel wide
+RGB image) the image compression may decrease noticeably (although this
+may be acceptable for real-time applications).  Infrequent flushing will
+only degrade the compression performance by a few percent over images
+that do not use flushing.
+
+That's it for the transformations.  Now you can write the image data.
+The simplest way to do this is in one function call.  If have the
+whole image in memory, you can just call png_write_image() and libpng
+will write the image.  You will need to pass in an array of pointers to
+each row.  This function automatically handles interlacing, so you don't
+need to call png_set_interlace_handling() or call this function multiple
+times, or any of that other stuff necessary with png_write_rows().
+
+    png_write_image(png_ptr, row_pointers);
+
+where row_pointers is:
+
+    png_byte *row_pointers[height];
+
+You can point to void or char or whatever you use for pixels.
+
+If you don't want to write the whole image at once, you can
+use png_write_rows() instead.  If the file is not interlaced,
+this is simple:
+
+    png_write_rows(png_ptr, row_pointers,
+       number_of_rows);
+
+row_pointers is the same as in the png_write_image() call.
+
+If you are just writing one row at a time, you can do this with
+row_pointers:
+
+    png_bytep row_pointer = row;
+
+    png_write_row(png_ptr, &row_pointer);
+
+When the file is interlaced, things can get a good deal more
+complicated.  The only currently (as of February 1998 -- PNG Specification
+version 1.0, dated October 1996) defined interlacing scheme for PNG files
+is the "Adam7" interlace scheme, that breaks down an
+image into seven smaller images of varying size.  libpng will build
+these images for you, or you can do them yourself.  If you want to
+build them yourself, see the PNG specification for details of which
+pixels to write when.
+
+If you don't want libpng to handle the interlacing details, just
+use png_set_interlace_handling() and call png_write_rows() the
+correct number of times to write all seven sub-images.
+
+If you want libpng to build the sub-images, call this before you start
+writing any rows:
+
+    number_of_passes =
+       png_set_interlace_handling(png_ptr);
+
+This will return the number of passes needed.  Currently, this
+is seven, but may change if another interlace type is added.
+
+Then write the complete image number_of_passes times.
+
+    png_write_rows(png_ptr, row_pointers,
+       number_of_rows);
+
+As some of these rows are not used, and thus return immediately,
+you may want to read about interlacing in the PNG specification,
+and only update the rows that are actually used.
+
+After you are finished writing the image, you should finish writing
+the file.  If you are interested in writing comments or time, you should
+pass an appropriately filled png_info pointer.  If you are not interested,
+you can pass NULL.
+
+    png_write_end(png_ptr, info_ptr);
+
+When you are done, you can free all memory used by libpng like this:
+
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+
+You must free any data you allocated for info_ptr, such as comments,
+palette, or histogram, before the call to png_destroy_write_struct();
+
+For a more compact example of writing a PNG image, see the file example.c.
+
+
+.SH V. Modifying/Customizing libpng:
+
+There are two issues here.  The first is changing how libpng does
+standard things like memory allocation, input/output, and error handling.
+The second deals with more complicated things like adding new chunks,
+adding new transformations, and generally changing how libpng works.
+
+All of the memory allocation, input/output, and error handling in libpng
+goes through callbacks that are user settable.  The default routines are
+in pngmem.c, pngrio.c, pngwio.c, and pngerror.c respectively.  To change
+these functions, call the appropriate png_set_???_fn() function.
+
+Memory allocation is done through the functions png_large_malloc(),
+png_malloc(), png_realloc(), png_large_free(), and png_free().  These
+currently just call the standard C functions.  The large functions must
+handle exactly 64K, but they don't have to handle more than that.  If
+your pointers can't access more then 64K at a time, you will want to set
+MAXSEG_64K in zlib.h.  Since it is unlikely that the method of handling
+memory allocation on a platform will change between applications, these
+functions must be modified in the library at compile time.
+
+Input/Output in libpng is done through png_read() and png_write(),
+which currently just call fread() and fwrite().  The FILE * is stored in
+png_struct and is initialized via png_init_io().  If you wish to change
+the method of I/O, the library supplies callbacks that you can set
+through the function png_set_read_fn() and png_set_write_fn() at run
+time, instead of calling the png_init_io() function.  These functions
+also provide a void pointer that can be retrieved via the function
+png_get_io_ptr().  For example:
+
+    png_set_read_fn(png_structp read_ptr,
+        voidp read_io_ptr, png_rw_ptr read_data_fn)
+
+    png_set_write_fn(png_structp write_ptr,
+        voidp write_io_ptr, png_rw_ptr write_data_fn,
+        png_flush_ptr output_flush_fn);
+
+    voidp read_io_ptr = png_get_io_ptr(read_ptr);
+    voidp write_io_ptr = png_get_io_ptr(write_ptr);
+
+The replacement I/O functions should have prototypes as follows:
+
+    void user_read_data(png_structp png_ptr,
+        png_bytep data, png_uint_32 length);
+    void user_write_data(png_structp png_ptr,
+        png_bytep data, png_uint_32 length);
+    void user_flush_data(png_structp png_ptr);
+
+Supplying NULL for the read, write, or flush functions sets them back
+to using the default C stream functions.  It is an error to read from
+a write stream, and vice versa.
+
+Error handling in libpng is done through png_error() and png_warning().
+Errors handled through png_error() are fatal, meaning that png_error()
+should never return to its caller.  Currently, this is handled via
+setjmp() and longjmp(), but you could change this to do things like
+exit() if you should wish.  On non-fatal errors, png_warning() is called
+to print a warning message, and then control returns to the calling code.
+By default png_error() and png_warning() print a message on stderr via
+fprintf() unless the library is compiled with PNG_NO_STDIO defined.  If
+you wish to change the behavior of the error functions, you will need to
+set up your own message callbacks.  These functions are normally supplied
+at the time that the png_struct is created.  It is also possible to change
+these functions after png_create_???_struct() has been called by calling:
+
+    png_set_error_fn(png_structp png_ptr,
+        png_voidp error_ptr, png_error_ptr error_fn,
+        png_error_ptr warning_fn);
+
+    png_voidp error_ptr = png_get_error_ptr(png_ptr);
+
+If NULL is supplied for either error_fn or warning_fn, then the libpng
+default function will be used, calling fprintf() and/or longjmp() if a
+problem is encountered.  The replacement error functions should have
+parameters as follows:
+
+    void user_error_fn(png_structp png_ptr,
+        png_const_charp error_msg);
+    void user_warning_fn(png_structp png_ptr,
+        png_const_charp warning_msg);
+
+The motivation behind using setjmp() and longjmp() is the C++ throw and
+catch exception handling methods.  This makes the code much easier to write,
+as there is no need to check every return code of every function call.
+However, there are some uncertainties about the status of local variables
+after a longjmp, so the user may want to be careful about doing anything after
+setjmp returns non-zero besides returning itself.  Consult your compiler
+documentation for more details.
+
+If you need to read or write custom chunks, you will need to get deeper
+into the libpng code, as a mechanism has not yet been supplied for user
+callbacks with custom chunks.  First, read the PNG specification, and have
+a first level of understanding of how it works.  Pay particular attention
+to the sections that describe chunk names, and look at how other chunks
+were designed, so you can do things similarly.  Second, check out the
+sections of libpng that read and write chunks.  Try to find a chunk that
+is similar to yours and copy off of it.  More details can be found in the
+comments inside the code.  A way of handling unknown chunks in a generic
+method, potentially via callback functions, would be best.
+
+If you wish to write your own transformation for the data, look through
+the part of the code that does the transformations, and check out some of
+the simpler ones to get an idea of how they work.  Try to find a similar
+transformation to the one you want to add and copy off of it.  More details
+can be found in the comments inside the code itself.
+
+Configuring for 16 bit platforms:
+
+You may need to change the png_large_malloc() and png_large_free()
+routines in pngmem.c, as these are required to allocate 64K, although
+there is already support for many of the common DOS compilers.  Also,
+you will want to look into zconf.h to tell zlib (and thus libpng) that
+it cannot allocate more then 64K at a time.  Even if you can, the memory
+won't be accessible.  So limit zlib and libpng to 64K by defining MAXSEG_64K.
+
+Configuring for DOS:
+
+For DOS users who only have access to the lower 640K, you will
+have to limit zlib's memory usage via a png_set_compression_mem_level()
+call.  See zlib.h or zconf.h in the zlib library for more information.
+
+Configuring for Medium Model:
+
+Libpng's support for medium model has been tested on most of the popular
+compilers.  Make sure MAXSEG_64K gets defined, USE_FAR_KEYWORD gets
+defined, and FAR gets defined to far in pngconf.h, and you should be
+all set.  Everything in the library (except for zlib's structure) is
+expecting far data.  You must use the typedefs with the p or pp on
+the end for pointers (or at least look at them and be careful).  Make
+note that the row's of data are defined as png_bytepp, which is an
+unsigned char far * far *.
+
+Configuring for gui/windowing platforms:
+
+You will need to write new error and warning functions that use the GUI
+interface, as described previously, and set them to be the error and
+warning functions at the time that png_create_???_struct() is called,
+in order to have them available during the structure initialization.
+They can be changed later via png_set_error_fn().  On some compilers,
+you may also have to change the memory allocators (png_malloc, etc.).
+
+Configuring for compiler xxx:
+
+All includes for libpng are in pngconf.h.  If you need to add/change/delete
+an include, this is the place to do it.  The includes that are not
+needed outside libpng are protected by the PNG_INTERNAL definition,
+which is only defined for those routines inside libpng itself.  The
+files in libpng proper only include png.h, which includes pngconf.h.
+
+Configuring zlib:
+
+There are special functions to configure the compression.  Perhaps the
+most useful one changes the compression level, which currently uses
+input compression values in the range 0 - 9.  The library normally
+uses the default compression level (Z_DEFAULT_COMPRESSION = 6).  Tests
+have shown that for a large majority of images, compression values in
+the range 3-6 compress nearly as well as higher levels, and do so much
+faster.  For online applications it may be desirable to have maximum speed
+(Z_BEST_SPEED = 1).  With versions of zlib after v0.99, you can also
+specify no compression (Z_NO_COMPRESSION = 0), but this would create
+files larger than just storing the raw bitmap.  You can specify the
+compression level by calling:
+
+    png_set_compression_level(png_ptr, level);
+
+Another useful one is to reduce the memory level used by the library.
+The memory level defaults to 8, but it can be lowered if you are
+short on memory (running DOS, for example, where you only have 640K).
+
+    png_set_compression_mem_level(png_ptr, level);
+
+The other functions are for configuring zlib.  They are not recommended
+for normal use and may result in writing an invalid PNG file.  See
+zlib.h for more information on what these mean.
+
+    png_set_compression_strategy(png_ptr,
+        strategy);
+    png_set_compression_window_bits(png_ptr,
+        window_bits);
+    png_set_compression_method(png_ptr, method);
+
+Controlling row filtering:
+
+If you want to control whether libpng uses filtering or not, which
+filters are used, and how it goes about picking row filters, you
+can call one of these functions.  The selection and configuration
+of row filters can have a significant impact on the size and
+encoding speed and a somewhat lesser impact on the decoding speed
+of an image.  Filtering is enabled by default for RGB and grayscale
+images (with and without alpha), but not for paletted images nor
+for any images with bit depths less than 8 bits/pixel.
+
+The 'method' parameter sets the main filtering method, which is
+currently only '0' in the PNG 1.0 specification.  The 'filters'
+parameter sets which filter(s), if any, should be used for each
+scanline.  Possible values are PNG_ALL_FILTERS and PNG_NO_FILTERS
+to turn filtering on and off, respectively.
+
+Individual filter types are PNG_FILTER_NONE, PNG_FILTER_SUB,
+PNG_FILTER_UP, PNG_FILTER_AVG, PNG_FILTER_PAETH, which can be bitwise
+ORed together '|' to specify one or more filters to use.  These
+filters are described in more detail in the PNG specification.  If
+you intend to change the filter type during the course of writing
+the image, you should start with flags set for all of the filters
+you intend to use so that libpng can initialize its internal
+structures appropriately for all of the filter types.
+
+    filters = PNG_FILTER_NONE | PNG_FILTER_SUB
+       | PNG_FILTER_UP;
+    png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE,
+       filters);
+
+It is also possible to influence how libpng chooses from among the
+available filters.  This is done in two ways - by telling it how
+important it is to keep the same filter for successive rows, and
+by telling it the relative computational costs of the filters.
+
+    double weights[3] = {1.5, 1.3, 1.1},
+       costs[PNG_FILTER_VALUE_LAST] =
+       {1.0, 1.3, 1.3, 1.5, 1.7};
+
+    png_set_filter_selection(png_ptr,
+       PNG_FILTER_SELECTION_WEIGHTED, 3,
+       weights, costs);
+
+The weights are multiplying factors that indicate to libpng that the
+row filter should be the same for successive rows unless another row filter
+is that many times better than the previous filter.  In the above example,
+if the previous 3 filters were SUB, SUB, NONE, the SUB filter could have a
+"sum of absolute differences" 1.5 x 1.3 times higher than other filters
+and still be chosen, while the NONE filter could have a sum 1.1 times
+higher than other filters and still be chosen.  Unspecified weights are
+taken to be 1.0, and the specified weights should probably be declining
+like those above in order to emphasize recent filters over older filters.
+
+The filter costs specify for each filter type a relative decoding cost
+to be considered when selecting row filters.  This means that filters
+with higher costs are less likely to be chosen over filters with lower
+costs, unless their "sum of absolute differences" is that much smaller.
+The costs do not necessarily reflect the exact computational speeds of
+the various filters, since this would unduly influence the final image
+size.
+
+Note that the numbers above were invented purely for this example and
+are given only to help explain the function usage.  Little testing has
+been done to find optimum values for either the costs or the weights.
+
+Removing unwanted object code:
+
+There are a bunch of #define's in pngconf.h that control what parts of
+libpng are compiled.  All the defines end in _SUPPORTED.  If you are
+never going to use a capability, you can change the #define to #undef
+before recompiling libpng and save yourself code and data space, or
+you can turn off individual capabilities with defines that begin with
+PNG_NO_.
+
+You can also turn all of the transforms and ancillary chunk capabilities
+off en masse with  compiler directives that define
+PNG_NO_READ[or WRITE]_TRANSFORMS, or PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS,
+or all four,
+along with directives to turn on any of the capabilities that you do
+want.  The PNG_NO_READ[or WRITE]_TRANSFORMS directives disable
+the extra transformations but still leave the library fully capable of reading
+and writing PNG files with all known public chunks [except for sPLT].
+Use of the PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS directive
+produces a library that is incapable of reading or writing ancillary chunks.
+If you are not using the progressive reading capability, you can
+turn that off with PNG_NO_PROGRESSIVE_READ (don't confuse
+this with the INTERLACING capability, which you'll still have).
+
+All the reading and writing specific code are in separate files, so the
+linker should only grab the files it needs.  However, if you want to
+make sure, or if you are building a stand alone library, all the
+reading files start with pngr and all the writing files start with
+pngw.  The files that don't match either (like png.c, pngtrans.c, etc.)
+are used for both reading and writing, and always need to be included.
+The progressive reader is in pngpread.c
+
+If you are creating or distributing a dynamically linked library (a .so
+or DLL file), you should not remove or disable any parts of the library,
+as this will cause applications linked with different versions of the
+library to fail if they call functions not available in your library.
+The size of the library itself should not be an issue, because only
+those sections that are actually used will be loaded into memory.
+
+Requesting debug printout:
+
+The macro definition PNG_DEBUG can be used to request debugging
+printout.  Set it to an integer value in the range 0 to 3.  Higher
+numbers result in increasing amounts of debugging information.  The
+information is printed to the "stderr" file, unless another file
+name is specified in the PNG_DEBUG_FILE macro definition.
+
+When PNG_DEBUG > 0, the following functions (macros) become available:
+
+   png_debug(level, message)
+   png_debug1(level, message, p1)
+   png_debug2(level, message, p1, p2)
+
+in which "level" is compared to PNG_DEBUG to decide whether to print
+the message, "message" is the formatted string to be printed,
+and p1 and p2 are parameters that are to be embedded in the string
+according to printf-style formatting directives.  For example,
+
+   png_debug1(2, "foo=%d\n", foo);
+
+is expanded to
+
+   if(PNG_DEBUG > 2)
+     fprintf(PNG_DEBUG_FILE, "foo=%d\n", foo);
+
+When PNG_DEBUG is defined but is zero, the macros aren't defined, but you
+can still use PNG_DEBUG to control your own debugging:
+
+   #ifdef PNG_DEBUG
+       fprintf(stderr, ...
+   #endif
+
+When PNG_DEBUG = 1, the macros are defined, but only png_debug statements
+having level = 0 will be printed.  There aren't any such statements in
+this version of libpng, but if you insert some they will be printed.
+
+.SH VI.  Changes to Libpng from version 0.88
+
+It should be noted that versions of libpng later than 0.96 are not
+distributed by the original libpng author, Guy Schalnat, nor by
+Andreas Dilger, who had taken over from Guy during 1996 and 1997, and
+distributed versions 0.89 through 0.96, but rather by another member
+of the original PNG Group, Glenn Randers-Pehrson.  Guy and Andreas are
+still alive and well, but they have moved on to other things.
+
+The old libpng functions png_read_init(), png_write_init(),
+png_info_init(), png_read_destroy(), and png_write_destory() have been
+moved to PNG_INTERNAL in version 0.95 to discourage their use.  The
+preferred method of creating and initializing the libpng structures is
+via the png_create_read_struct(), png_create_write_struct(), and
+png_create_info_struct() because they isolate the size of the structures
+from the application, allow version error checking, and also allow the
+use of custom error handling routines during the initialization, which
+the old functions do not.  The functions png_read_destroy() and
+png_write_destroy() do not actually free the memory that libpng
+allocated for these structs, but just reset the data structures, so they
+can be used instead of png_destroy_read_struct() and
+png_destroy_write_struct() if you feel there is too much system overhead
+allocating and freeing the png_struct for each image read.
+
+Setting the error callbacks via png_set_message_fn() before
+png_read_init() as was suggested in libpng-0.88 is no longer supported
+because this caused applications that do not use custom error functions
+to fail if the png_ptr was not initialized to zero.  It is still possible
+to set the error callbacks AFTER png_read_init(), or to change them with
+png_set_error_fn(), which is essentially the same function, but with a
+new name to force compilation errors with applications that try to use
+the old method.
+
+.SH VII. Y2K Compliance in libpng
+
+January 13, 1999
+
+Since the PNG Development group is an ad-hoc body, we can't make
+an official declaration.
+
+This is your unofficial assurance that libpng from version 0.81 and
+upward are Y2K compliant.  It is my belief that earlier versions were
+also Y2K compliant.
+
+Libpng only has three year fields.  One is a 2-byte unsigned integer that
+will hold years up to 65535.  The other two hold the date in text
+format, and will hold years up to 9999.
+
+The integer is
+    "png_uint_16 year" in png_time_struct.
+
+The strings are
+    "png_charp time_buffer" in png_struct and
+    "near_time_buffer", which is a local character string in png.c.
+
+There are seven time-related functions:
+
+    png_convert_to_rfc_1123() in png.c 
+      (formerly png_convert_to_rfc_1152() in error)
+    png_convert_from_struct_tm() in pngwrite.c, called in pngwrite.c
+    png_convert_from_time_t() in pngwrite.c
+    png_get_tIME() in pngget.c
+    png_handle_tIME() in pngrutil.c, called in pngread.c
+    png_set_tIME() in pngset.c
+    png_write_tIME() in pngwutil.c, called in pngwrite.c
+
+All appear to handle dates properly in a Y2K environment.  The 
+png_convert_from_time_t() function calls gmtime() to convert from system
+clock time, which returns (year - 1900), which we properly convert to
+the full 4-digit year.  There is a possibility that applications using
+libpng are not passing 4-digit years into the png_convert_to_rfc_1123()
+function, or incorrectly passing only a 2-digit year instead of
+"year - 1900" into the png_convert_from_struct_tm() function, but this
+is not under our control.  The libpng documentation has always stated
+that it works with 4-digit years, and the APIs have been documented as
+such.
+
+The tIME chunk itself is also Y2K compliant.  It uses a 2-byte unsigned
+integer to hold the year, and can hold years as large as 65535.
+
+
+   Glenn Randers-Pehrson
+   libpng maintainer
+   PNG Development Group
+
+.SH NOTE
+
+Note about libpng version numbers:
+
+Due to various miscommunications, unforeseen code incompatibilities
+and occasional factors outside the authors' control, version numbering
+on the library has not always been consistent and straightforward.
+The following table summarizes matters since version 0.89c, which was
+the first widely used release:
+
+   source     png.h   png.h   shared-lib
+   version    string    int   version
+   -------    ------  ------  ----------
+   0.89c      0.89        89  1.0.89
+   0.90       0.90        90  0.90  [should be 2.0.90]
+   0.95       0.95        95  0.95  [should be 2.0.95]
+   0.96       0.96        96  0.96  [should be 2.0.96]
+   0.97b      1.00.97     97  1.0.1 [should be 2.0.97]
+   0.97c      0.97        97  2.0.97
+   0.98       0.98        98  2.0.98
+   0.99       0.99        98  2.0.99
+   0.99a-m    0.99        99  2.0.99
+   1.00       1.00       100  2.1.0 [int should be 10000]
+   1.0.0      1.0.0      100  2.1.0 [int should be 10000]
+   1.0.1      1.0.1    10001  2.1.0
+
+Henceforth the source version will match the shared-library
+minor and patch numbers; the shared-library major version number will be
+used for changes in backward compatibility, as it is intended.
+The PNG_PNGLIB_VER macro, which is not used within libpng but
+is available for applications, is an unsigned integer of the form
+xyyzz corresponding to the source version x.y.z (leading zeros in y and z).
+.SH "SEE ALSO"
+libpngpf(3), png(5)
+.LP
+.IR libpng :
+.IP
+ftp://ftp.uu.net/graphics/png
+http://www.cdrom.com/pub/png
+
+.LP
+.IR zlib :
+.IP
+(generally) at the same location as
+.I libpng
+or at
+.br
+ftp://ftp.uu.net/pub/archiving/zip/zlib
+.br
+http://www.cdrom.com/pub/infozip/zlib
+
+.LP
+.IR PNG specification: RFC 2083
+.IP
+(generally) at the same location as
+.I libpng
+or at
+.br
+ftp://ds.internic.net/rfc/rfc2083.txt
+.br
+or (as a W3C Recommendation) at
+.br
+http://www.w3.org/TR/REC-png.html
+
+.LP
+In the case of any inconsistency between the PNG specification
+and this library, the specification takes precedence.
+
+.SH AUTHORS
+This man page: Glenn Randers-Pehrson
+<randeg@alumni.rpi.edu>
+  
+Contributing Authors: John Bowler, Kevin Bracey, Sam Bushell, Andreas Dilger,
+Magnus Holmgren, Tom Lane, Dave Martindale, Glenn Randers-Pehrson,
+Greg Roelofs, Guy Eric Schalnat, Paul Schmidt, Tom Tanner, Willem van
+Schaik, Tim Wegner.
+<png-implement@dworkin.wustl.edu>
+
+The contributing authors would like to thank all those who helped
+with testing, bug fixes, and patience.  This wouldn't have been
+possible without all of you.
+
+Thanks to Frank J. T. Wojcik for helping with the documentation.
+  
+Libpng version 1.0.3 - January 14, 1999:
+Initially created in 1995 by Guy Eric Schalnat, then of Group 42, Inc.
+Currently maintained by Glenn Randers-Pehrson (randeg@alumni.rpi.edu).
+
+Supported by the PNG development group
+.br
+(png-implement@dworkin.wustl.edu).
+
+.SH COPYRIGHT NOTICE:
+
+Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+Copyright (c) 1996, 1997 Andreas Dilger
+Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+
+The PNG Reference Library (libpng) is supplied "AS IS".  The Contributing
+Authors and Group 42, Inc. disclaim all warranties, expressed or implied,
+including, without limitation, the warranties of merchantability and of
+fitness for any purpose.  The Contributing Authors and Group 42, Inc.
+assume no liability for direct, indirect, incidental, special, exemplary,
+or consequential damages, which may result from the use of the PNG
+Reference Library, even if advised of the possibility of such damage.
+
+Permission is hereby granted to use, copy, modify, and distribute this
+source code, or portions hereof, for any purpose, without fee, subject
+to the following restrictions:
+
+ 1. The origin of this source code must not be
+    misrepresented.
+
+ 2. Altered versions must be plainly marked as such
+    and must not be misrepresented as being the
+    original source.
+
+ 3. This Copyright notice may not be removed or
+    altered from any source or altered source
+    distribution.
+
+The Contributing Authors and Group 42, Inc. specifically permit, without
+fee, and encourage the use of this source code as a component to
+supporting the PNG file format in commercial products.  If you use this
+source code in a product, acknowledgment is not required but would be
+appreciated.
+
+.\" end of man page
+
diff --git a/src/png/libpng.txt b/src/png/libpng.txt
new file mode 100644 (file)
index 0000000..1a30a9f
--- /dev/null
@@ -0,0 +1,2067 @@
+libpng.txt - A description on how to use and modify libpng
+
+ libpng version 1.0.3 - January 14, 1999
+ Updated and distributed by Glenn Randers-Pehrson
+ <randeg@alumni.rpi.edu>
+ Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ For conditions of distribution and use, see copyright
+ notice in png.h.
+
+ based on:
+
+ libpng 1.0 beta 6  version 0.96 May 28, 1997
+ Updated and distributed by Andreas Dilger
+ Copyright (c) 1996, 1997 Andreas Dilger
+
+ libpng 1.0 beta 2 - version 0.88  January 26, 1996
+ For conditions of distribution and use, see copyright
+ notice in png.h. Copyright (c) 1995, 1996 Guy Eric
+ Schalnat, Group 42, Inc.
+
+ Updated/rewritten per request in the libpng FAQ
+ Copyright (c) 1995 Frank J. T. Wojcik
+ December 18, 1995 && January 20, 1996
+
+I. Introduction
+
+This file describes how to use and modify the PNG reference library
+(known as libpng) for your own use.  There are five sections to this
+file: introduction, structures, reading, writing, and modification and
+configuration notes for various special platforms.  In addition to this
+file, example.c is a good starting point for using the library, as
+it is heavily commented and should include everything most people
+will need.  We assume that libpng is already installed; see the
+INSTALL file for instructions on how to install libpng.
+
+Libpng was written as a companion to the PNG specification, as a way
+of reducing the amount of time and effort it takes to support the PNG
+file format in application programs.  The PNG specification is available
+as RFC 2083 <ftp://ftp.uu.net/graphics/png/documents/> and as a
+W3C Recommendation <http://www.w3.org/TR/REC.png.html>. Some
+additional chunks are described in the special-purpose public chunks
+documents at <ftp://ftp.uu.net/graphics/png/documents/>.  Other information
+about PNG, and the latest version of libpng, can be found at the PNG home
+page, <http://www.cdrom.com/pub/png/>.
+
+Most users will not have to modify the library significantly; advanced
+users may want to modify it more.  All attempts were made to make it as
+complete as possible, while keeping the code easy to understand.
+Currently, this library only supports C.  Support for other languages
+is being considered.
+
+Libpng has been designed to handle multiple sessions at one time,
+to be easily modifiable, to be portable to the vast majority of
+machines (ANSI, K&R, 16-, 32-, and 64-bit) available, and to be easy
+to use.  The ultimate goal of libpng is to promote the acceptance of
+the PNG file format in whatever way possible.  While there is still
+work to be done (see the TODO file), libpng should cover the
+majority of the needs of its users.
+
+Libpng uses zlib for its compression and decompression of PNG files.
+Further information about zlib, and the latest version of zlib, can
+be found at the zlib home page, <http://www.cdrom.com/pub/infozip/zlib/>.
+The zlib compression utility is a general purpose utility that is
+useful for more than PNG files, and can be used without libpng.
+See the documentation delivered with zlib for more details.
+You can usually find the source files for the zlib utility wherever you
+find the libpng source files.
+
+Libpng is thread safe, provided the threads are using different
+instances of the structures.  Each thread should have its own
+png_struct and png_info instances, and thus its own image.
+Libpng does not protect itself against two threads using the
+same instance of a structure.
+
+
+II. Structures
+
+There are two main structures that are important to libpng, png_struct
+and png_info.  The first, png_struct, is an internal structure that
+will not, for the most part, be used by a user except as the first
+variable passed to every libpng function call.
+
+The png_info structure is designed to provide information about the
+PNG file.  At one time, the fields of png_info were intended to be
+directly accessible to the user.  However, this tended to cause problems
+with applications using dynamically loaded libraries, and as a result
+a set of interface functions for png_info was developed.  The fields
+of png_info are still available for older applications, but it is
+suggested that applications use the new interfaces if at all possible.
+
+The png.h header file is an invaluable reference for programming with libpng.
+And while I'm on the topic, make sure you include the libpng header file:
+
+#include <png.h>
+
+III. Reading
+
+Reading PNG files:
+
+We'll now walk you through the possible functions to call when reading
+in a PNG file, briefly explaining the syntax and purpose of each one.
+See example.c and png.h for more detail.  While Progressive reading
+is covered in the next section, you will still need some of the
+functions discussed in this section to read a PNG file.
+
+You will want to do the I/O initialization(*) before you get into libpng,
+so if it doesn't work, you don't have much to undo.  Of course, you
+will also want to insure that you are, in fact, dealing with a PNG
+file.  Libpng provides a simple check to see if a file is a PNG file.
+To use it, pass in the first 1 to 8 bytes of the file, and it will
+return true or false (1 or 0) depending on whether the bytes could be
+part of a PNG file.  Of course, the more bytes you pass in, the
+greater the accuracy of the prediction.
+
+If you are intending to keep the file pointer open for use in libpng,
+you must ensure you don't read more than 8 bytes from the beginning
+of the file, and you also have to make a call to png_set_sig_bytes_read()
+with the number of bytes you read from the beginning.  Libpng will
+then only check the bytes (if any) that your program didn't read.
+
+(*): If you are not using the standard I/O functions, you will need
+to replace them with custom functions.  See the discussion under
+Customizing libpng.
+
+
+    FILE *fp = fopen(file_name, "rb");
+    if (!fp)
+    {
+        return;
+    }
+    fread(header, 1, number, fp);
+    is_png = !png_sig_cmp(header, 0, number);
+    if (!is_png)
+    {
+        return;
+    }
+
+
+Next, png_struct and png_info need to be allocated and initialized.  In
+order to ensure that the size of these structures is correct even with a
+dynamically linked libpng, there are functions to initialize and
+allocate the structures.  We also pass the library version, optional
+pointers to error handling functions, and a pointer to a data struct for
+use by the error functions, if necessary (the pointer and functions can
+be NULL if the default error handlers are to be used).  See the section
+on Changes to Libpng below regarding the old initialization functions.
+
+    png_structp png_ptr = png_create_read_struct
+       (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+        user_error_fn, user_warning_fn);
+    if (!png_ptr)
+        return;
+
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr)
+    {
+        png_destroy_read_struct(&png_ptr,
+           (png_infopp)NULL, (png_infopp)NULL);
+        return;
+    }
+
+    png_infop end_info = png_create_info_struct(png_ptr);
+    if (!end_info)
+    {
+        png_destroy_read_struct(&png_ptr, &info_ptr,
+          (png_infopp)NULL);
+        return;
+    }
+
+If you want to use your own memory allocation routines,
+define PNG_USER_MEM_SUPPORTED and use
+png_create_read_struct_2() instead of png_create_read_struct():
+
+    png_structp png_ptr = png_create_read_struct_2
+       (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+        user_error_fn, user_warning_fn, (png_voidp)
+        user_mem_ptr, user_malloc_fn, user_free_fn);
+
+The error handling routines passed to png_create_read_struct()
+and the memory alloc/free routines passed to png_create_struct_2()
+are only necessary if you are not using the libpng supplied error
+handling and memory alloc/free functions.
+
+When libpng encounters an error, it expects to longjmp back
+to your routine.  Therefore, you will need to call setjmp and pass
+your png_ptr->jmpbuf.  If you read the file from different
+routines, you will need to update the jmpbuf field every time you enter
+a new routine that will call a png_ function.
+
+See your documentation of setjmp/longjmp for your compiler for more
+handling in the Customizing Libpng section below for more information on
+the libpng error handling.  If an error occurs, and libpng longjmp's
+back to your setjmp, you will want to call png_destroy_read_struct() to
+free any memory.
+
+    if (setjmp(png_ptr->jmpbuf))
+    {
+        png_destroy_read_struct(&png_ptr, &info_ptr,
+           &end_info);
+        fclose(fp);
+        return;
+    }
+
+Now you need to set up the input code.  The default for libpng is to
+use the C function fread().  If you use this, you will need to pass a
+valid FILE * in the function png_init_io().  Be sure that the file is
+opened in binary mode.  If you wish to handle reading data in another
+way, you need not call the png_init_io() function, but you must then
+implement the libpng I/O methods discussed in the Customizing Libpng
+section below.
+
+    png_init_io(png_ptr, fp);
+
+If you had previously opened the file and read any of the signature from
+the beginning in order to see if this was a PNG file, you need to let
+libpng know that there are some bytes missing from the start of the file.
+
+    png_set_sig_bytes(png_ptr, number);
+
+At this point, you can set up a callback function that will be
+called after each row has been read, which you can use to control
+a progress meter or the like.  It's demonstrated in pngtest.c.
+You must supply a function
+
+    void read_row_callback(png_ptr, png_uint_32 row, int pass);
+    {
+      /* put your code here */
+    }
+
+(You can give it another name that you like instead of "read_row_callback")
+
+To inform libpng about your function, use
+
+    png_set_read_status_fn(png_ptr, read_row_callback);
+
+In PNG files, the alpha channel in an image is the level of opacity.
+If you need the alpha channel in an image to be the level of transparency
+instead of opacity, you can invert the alpha channel (or the tRNS chunk
+data) after it's read, so that 0 is fully opaque and 255 (in 8-bit or
+paletted images) or 65535 (in 16-bit images) is fully transparent, with
+
+    png_set_invert_alpha(png_ptr);
+
+This has to appear here rather than later with the other transformations
+because the tRNS chunk data must be modified in the case of paletted images.
+If your image is not a paletted image, the tRNS data (which in such cases
+represents a single color to be rendered as transparent) won't be changed.
+
+Finally, you can write your own transformation function if none of
+the existing ones meets your needs.  This is done by setting a callback
+with
+
+    png_set_read_user_transform_fn(png_ptr,
+       read_transform_fn);
+
+You must supply the function
+
+    void read_transform_fn(png_ptr ptr, row_info_ptr
+       row_info, png_bytep data)
+
+See pngtest.c for a working example.  Your function will be called
+after all of the other transformations have been processed.
+
+You are now ready to read all the file information up to the actual
+image data.  You do this with a call to png_read_info().
+
+    png_read_info(png_ptr, info_ptr);
+
+Functions are used to get the information from the info_ptr:
+
+    png_get_IHDR(png_ptr, info_ptr, &width, &height,
+       &bit_depth, &color_type, &interlace_type,
+       &compression_type, &filter_type);
+
+    width          - holds the width of the image
+                     in pixels (up to 2^31).
+    height         - holds the height of the image
+                     in pixels (up to 2^31).
+    bit_depth      - holds the bit depth of one of the
+                     image channels.  (valid values are
+                     1, 2, 4, 8, 16 and depend also on
+                     the color_type.  See also
+                     significant bits (sBIT) below).
+    color_type     - describes which color/alpha channels
+                         are present.
+                     PNG_COLOR_TYPE_GRAY
+                        (bit depths 1, 2, 4, 8, 16)
+                     PNG_COLOR_TYPE_GRAY_ALPHA
+                        (bit depths 8, 16)
+                     PNG_COLOR_TYPE_PALETTE
+                        (bit depths 1, 2, 4, 8)
+                     PNG_COLOR_TYPE_RGB
+                        (bit_depths 8, 16)
+                     PNG_COLOR_TYPE_RGB_ALPHA
+                        (bit_depths 8, 16)
+
+                     PNG_COLOR_MASK_PALETTE
+                     PNG_COLOR_MASK_COLOR
+                     PNG_COLOR_MASK_ALPHA
+
+    filter_type    - (must be PNG_FILTER_TYPE_BASE
+                     for PNG 1.0)
+    compression_type - (must be PNG_COMPRESSION_TYPE_BASE
+                     for PNG 1.0)
+    interlace_type - (PNG_INTERLACE_NONE or
+                     PNG_INTERLACE_ADAM7)
+    Any or all of interlace_type, compression_type, of
+                     filter_type can be
+    NULL if you are not interested in their values.
+
+    channels = png_get_channels(png_ptr, info_ptr);
+    channels       - number of channels of info for the
+                     color type (valid values are 1 (GRAY,
+                     PALETTE), 2 (GRAY_ALPHA), 3 (RGB),
+                     4 (RGB_ALPHA or RGB + filler byte))
+    rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+    rowbytes       - number of bytes needed to hold a row
+
+    signature = png_get_signature(png_ptr, info_ptr);
+    signature      - holds the signature read from the
+                     file (if any).  The data is kept in
+                     the same offset it would be if the
+                     whole signature were read (i.e. if an
+                     application had already read in 4
+                     bytes of signature before starting
+                     libpng, the remaining 4 bytes would
+                     be in signature[4] through signature[7]
+                     (see png_set_sig_bytes())).
+
+
+    width            = png_get_image_width(png_ptr,
+                         info_ptr);
+    height           = png_get_image_height(png_ptr,
+                         info_ptr);
+    bit_depth        = png_get_bit_depth(png_ptr,
+                         info_ptr);
+    color_type       = png_get_color_type(png_ptr,
+                         info_ptr);
+    filter_type      = png_get_filter_type(png_ptr,
+                         info_ptr);
+    compression_type = png_get_compression_type(png_ptr,
+                         info_ptr);
+    interlace_type   = png_get_interlace_type(png_ptr,
+                         info_ptr);
+
+
+These are also important, but their validity depends on whether the chunk
+has been read.  The png_get_valid(png_ptr, info_ptr, PNG_INFO_<chunk>) and
+png_get_<chunk>(png_ptr, info_ptr, ...) functions return non-zero if the
+data has been read, or zero if it is missing.  The parameters to the
+png_get_<chunk> are set directly if they are simple data types, or a pointer
+into the info_ptr is returned for any complex types.
+
+    png_get_PLTE(png_ptr, info_ptr, &palette,
+                     &num_palette);
+    palette        - the palette for the file
+                     (array of png_color)
+    num_palette    - number of entries in the palette
+
+    png_get_gAMA(png_ptr, info_ptr, &gamma);
+    gamma          - the gamma the file is written
+                     at (PNG_INFO_gAMA)
+
+    png_get_sRGB(png_ptr, info_ptr, &srgb_intent);
+    srgb_intent    - the rendering intent (PNG_INFO_sRGB)
+                     The presence of the sRGB chunk
+                     means that the pixel data is in the
+                     sRGB color space.  This chunk also
+                     implies specific values of gAMA and
+                     cHRM.
+
+    png_get_sBIT(png_ptr, info_ptr, &sig_bit);
+    sig_bit        - the number of significant bits for
+                     (PNG_INFO_sBIT) each of the gray,
+                     red, green, and blue channels,
+                     whichever are appropriate for the
+                     given color type (png_color_16)
+
+    png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans,
+                     &trans_values);
+    trans          - array of transparent entries for
+                     palette (PNG_INFO_tRNS)
+    trans_values   - transparent pixel for non-paletted
+                     images (PNG_INFO_tRNS)
+    num_trans      - number of transparent entries
+                     (PNG_INFO_tRNS)
+
+    png_get_hIST(png_ptr, info_ptr, &hist);
+                     (PNG_INFO_hIST)
+    hist           - histogram of palette (array of
+                     png_color_16)
+
+    png_get_tIME(png_ptr, info_ptr, &mod_time);
+    mod_time       - time image was last modified
+                    (PNG_VALID_tIME)
+
+    png_get_bKGD(png_ptr, info_ptr, &background);
+    background     - background color (PNG_VALID_bKGD)
+
+    num_text = png_get_text(png_ptr, info_ptr, &text_ptr);
+    text_ptr       - array of png_text holding image
+                     comments
+    text_ptr[i]->key   - keyword for comment.
+    text_ptr[i]->text  - text comments for current
+                         keyword.
+    text_ptr[i]->compression - type of compression used
+                     on "text" PNG_TEXT_COMPRESSION_NONE
+                     or PNG_TEXT_COMPRESSION_zTXt
+    num_text       - number of comments
+
+    png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y,
+                     &unit_type);
+    offset_x       - positive offset from the left edge
+                     of the screen
+    offset_y       - positive offset from the top edge
+                     of the screen
+    unit_type      - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER
+
+    png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y,
+                     &unit_type);
+    res_x          - pixels/unit physical resolution in
+                     x direction
+    res_y          - pixels/unit physical resolution in
+                     x direction
+    unit_type      - PNG_RESOLUTION_UNKNOWN,
+                     PNG_RESOLUTION_METER
+
+The data from the pHYs chunk can be retrieved in several convenient
+forms:
+
+    res_x = png_get_x_pixels_per_meter(png_ptr,
+                  info_ptr)
+    res_y = png_get_y_pixels_per_meter(png_ptr,
+                  info_ptr)
+    res_x_and_y = png_get_pixels_per_meter(png_ptr,
+                  info_ptr)
+    aspect_ratio = png_get_pixel_aspect_ratio(png_ptr,
+                  info_ptr)
+
+   (Each of these returns 0 [signifying "unknown"] if
+       the data is not present or if res_x is 0;
+       res_x_and_y is 0 if res_x != res_y)
+
+For more information, see the png_info definition in png.h and the
+PNG specification for chunk contents.  Be careful with trusting
+rowbytes, as some of the transformations could increase the space
+needed to hold a row (expand, filler, gray_to_rgb, etc.).
+See png_read_update_info(), below.
+
+A quick word about text_ptr and num_text.  PNG stores comments in
+keyword/text pairs, one pair per chunk, with no limit on the number
+of text chunks, and a 2^31 byte limit on their size.  While there are
+suggested keywords, there is no requirement to restrict the use to these
+strings.  It is strongly suggested that keywords and text be sensible
+to humans (that's the point), so don't use abbreviations.  Non-printing
+symbols are not allowed.  See the PNG specification for more details.
+There is also no requirement to have text after the keyword.
+
+Keywords should be limited to 79 Latin-1 characters without leading or
+trailing spaces, but non-consecutive spaces are allowed within the
+keyword.  It is possible to have the same keyword any number of times.
+The text_ptr is an array of png_text structures, each holding pointer
+to a keyword and a pointer to a text string.  Only the text string may
+be null.  The keyword/text pairs are put into the array in the order
+that they are received.  However, some or all of the text chunks may be
+after the image, so, to make sure you have read all the text chunks,
+don't mess with these until after you read the stuff after the image.
+This will be mentioned again below in the discussion that goes with
+png_read_end().
+
+After you've read the header information, you can set up the library
+to handle any special transformations of the image data.  The various
+ways to transform the data will be described in the order that they
+should occur.  This is important, as some of these change the color
+type and/or bit depth of the data, and some others only work on
+certain color types and bit depths.  Even though each transformation
+checks to see if it has data that it can do something with, you should
+make sure to only enable a transformation if it will be valid for the
+data.  For example, don't swap red and blue on grayscale data.
+
+The colors used for the background and transparency values should be
+supplied in the same format/depth as the current image data.  They
+are stored in the same format/depth as the image data in a bKGD or tRNS
+chunk, so this is what libpng expects for this data.  The colors are
+transformed to keep in sync with the image data when an application
+calls the png_read_update_info() routine (see below).
+
+Data will be decoded into the supplied row buffers packed into bytes
+unless the library has been told to transform it into another format.
+For example, 4 bit/pixel paletted or grayscale data will be returned
+2 pixels/byte with the leftmost pixel in the high-order bits of the
+byte, unless png_set_packing() is called.  8-bit RGB data will be stored
+in RGB RGB RGB format unless png_set_filler() is called to insert filler
+bytes, either before or after each RGB triplet.  16-bit RGB data will
+be returned RRGGBB RRGGBB, with the most significant byte of the color
+value first, unless png_set_strip_16() is called to transform it to
+regular RGB RGB triplets, or png_set_filler() is called to insert
+filler bytes, either before or after each RRGGBB triplet.  Similarly,
+8-bit or 16-bit grayscale data can be modified with png_set_filler()
+or png_set_strip_16().
+
+The following code transforms grayscale images of less than 8 to 8 bits,
+changes paletted images to RGB, and adds a full alpha channel if there is
+transparency information in a tRNS chunk.  This is most useful on
+grayscale images with bit depths of 2 or 4 or if there is a multiple-image
+viewing application that wishes to treat all images in the same way.
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE &&
+        bit_depth <= 8) png_set_expand(png_ptr);
+
+    if (color_type == PNG_COLOR_TYPE_GRAY &&
+        bit_depth < 8) png_set_expand(png_ptr);
+
+    if (png_get_valid(png_ptr, info_ptr,
+        PNG_INFO_tRNS)) png_set_expand(png_ptr);
+
+PNG can have files with 16 bits per channel.  If you only can handle
+8 bits per channel, this will strip the pixels down to 8 bit.
+
+    if (bit_depth == 16)
+        png_set_strip_16(png_ptr);
+
+The png_set_background() function tells libpng to composite images
+with alpha or simple transparency against the supplied background
+color.  If the PNG file contains a bKGD chunk (PNG_INFO_bKGD valid),
+you may use this color, or supply another color more suitable for
+the current display (e.g., the background color from a web page).  You
+need to tell libpng whether the color is in the gamma space of the
+display (PNG_BACKGROUND_GAMMA_SCREEN for colors you supply), the file
+(PNG_BACKGROUND_GAMMA_FILE for colors from the bKGD chunk), or one
+that is neither of these gammas (PNG_BACKGROUND_GAMMA_UNIQUE - I don't
+know why anyone would use this, but it's here).
+
+If, for some reason, you don't need the alpha channel on an image,
+and you want to remove it rather than combining it with the background
+(but the image author certainly had in mind that you *would* combine
+it with the background, so that's what you should probably do):
+
+    if (color_type & PNG_COLOR_MASK_ALPHA)
+        png_set_strip_alpha(png_ptr);
+
+PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as
+they can, resulting in, for example, 8 pixels per byte for 1 bit
+files.  This code expands to 1 pixel per byte without changing the
+values of the pixels:
+
+    if (bit_depth < 8)
+        png_set_packing(png_ptr);
+
+PNG files have possible bit depths of 1, 2, 4, 8, and 16.  All pixels
+stored in a PNG image have been "scaled" or "shifted" up to the next
+higher possible bit depth (e.g. from 5 bits/sample in the range [0,31] to
+8 bits/sample in the range [0, 255]).  However, it is also possible to
+convert the PNG pixel data back to the original bit depth of the image.
+This call reduces the pixels back down to the original bit depth:
+
+    png_color_16p sig_bit;
+
+    if (png_get_sBIT(png_ptr, info_ptr, &sig_bit))
+        png_set_shift(png_ptr, sig_bit);
+
+PNG files store 3-color pixels in red, green, blue order.  This code
+changes the storage of the pixels to blue, green, red:
+
+    if (color_type == PNG_COLOR_TYPE_RGB ||
+        color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+        png_set_bgr(png_ptr);
+
+PNG files store RGB pixels packed into 3 bytes. This code expands them
+into 4 bytes for windowing systems that need them in this format:
+
+    if (bit_depth == 8 && color_type ==
+        PNG_COLOR_TYPE_RGB) png_set_filler(png_ptr,
+        filler, PNG_FILLER_BEFORE);
+
+where "filler" is the 8 or 16-bit number to fill with, and the location is
+either PNG_FILLER_BEFORE or PNG_FILLER_AFTER, depending upon whether
+you want the filler before the RGB or after.  This transformation
+does not affect images that already have full alpha channels.
+
+If you are reading an image with an alpha channel, and you need the
+data as ARGB instead of the normal PNG format RGBA:
+
+    if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+        png_set_swap_alpha(png_ptr);
+
+For some uses, you may want a grayscale image to be represented as
+RGB.  This code will do that conversion:
+
+    if (color_type == PNG_COLOR_TYPE_GRAY ||
+        color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+          png_set_gray_to_rgb(png_ptr);
+
+Conversely, you can convert an RGB or RGBA image to grayscale or grayscale
+with alpha.  This is intended for conversion of images that really are
+gray (red == green == blue), so the function simply strips out the red
+and blue channels, leaving the green channel in the gray position.
+
+    if (color_type == PNG_COLOR_TYPE_RGB ||
+        color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+          png_set_rgb_to_gray(png_ptr, error_action,
+             float red_weight, float green_weight);
+
+    error_action = 1: silently do the conversion
+    error_action = 2: issue a warning if the original
+                      image has any pixel where
+                      red != green or red != blue
+    error_action = 3: issue an error and abort the
+                      conversion if the original
+                      image has any pixel where
+                      red != green or red != blue
+
+    red_weight:       weight of red component
+                      (NULL -> default 54/256)
+    green_weight:     weight of green component
+                      (NULL -> default 183/256)
+
+If you have set error_action = 1 or 2, you can
+later check whether the image really was gray, after processing
+the image rows, with the png_get_rgb_to_gray_status(png_ptr) function.
+It will return a png_byte that is zero if the image was gray or
+1 if there were any non-gray pixels.  bKGD and sBIT data
+will be silently converted to grayscale, using the green channel
+data, regardless of the error_action setting.
+
+With 0.0<=red_weight+green_weight<=1.0,
+the normalized graylevel is computed:
+
+    int rw = red_weight * 256;
+    int gw = green_weight * 256;
+    int bw = 256 - (rw + gw);
+    gray = (rw*red + gw*green + bw*blue)/256;
+
+The default values approximate those recommended in the Charles
+Poynton's Color FAQ, <http://www.inforamp.net/~poynton/>
+Copyright (c) 1998-01-04 Charles Poynton poynton@inforamp.net
+
+    Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
+
+Libpng approximates this with
+
+    Y = 0.211 * R    + 0.715 * G    + 0.074 * B
+
+which can be expressed with integers as
+
+    Y = (54 * R + 183 * G + 19 * B)/256
+
+The calculation is done in a linear colorspace, if the image gamma
+is known.
+
+If you have a grayscale and you are using png_set_expand() to change to
+a higher bit-depth, you must either supply the background color as a gray
+value at the original file bit-depth (need_expand = 1) or else supply the
+background color as an RGB triplet at the final, expanded bit depth
+(need_expand = 0).  Similarly, if you are reading a paletted image, you
+must either supply the background color as a palette index (need_expand = 1)
+or as an RGB triplet that may or may not be in the palette (need_expand = 0).
+
+    png_color_16 my_background;
+    png_color_16p image_background;
+
+    if (png_get_bKGD(png_ptr, info_ptr, &image_background))
+        png_set_background(png_ptr, image_background,
+          PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
+    else
+        png_set_background(png_ptr, &my_background,
+          PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
+
+To properly display PNG images on any kind of system, the application needs
+to know what the display gamma is.  Ideally, the user will know this, and
+the application will allow them to set it.  One method of allowing the user
+to set the display gamma separately for each system is to check for the
+DISPLAY_GAMMA and VIEWING_GAMMA environment variables or for a SCREEN_GAMMA
+environment variable, which will hopefully be correctly set.
+
+Note that display_gamma is the gamma of your display, while screen_gamma is
+the overall gamma correction required to produce pleasing results,
+which depends on the lighting conditions in the surrounding environment.
+Screen_gamma is display_gamma/viewing_gamma, where viewing_gamma is
+the amount of additional gamma correction needed to compensate for
+a (viewing_gamma=1.25) environment.  In a dim or brightly lit room, no
+compensation other than the display_gamma is needed (viewing_gamma=1.0).
+
+   if (/* We have a user-defined screen
+       gamma value */)
+   {
+      screen_gamma = user_defined_screen_gamma;
+   }
+   /* One way that applications can share the same
+      screen gamma value */
+   else if ((gamma_str = getenv("SCREEN_GAMMA"))
+      != NULL)
+   {
+      screen_gamma = atof(gamma_str);
+   }
+   /* If we don't have another value */
+   else
+   {
+      screen_gamma = 2.2; /* A good guess for a
+           PC monitor in a bright office or a dim room */
+      screen_gamma = 2.0; /* A good guess for a
+           PC monitor in a dark room */
+      screen_gamma = 1.7 or 1.0;  /* A good
+           guess for Mac systems */
+   }
+
+The png_set_gamma() function handles gamma transformations of the data.
+Pass both the file gamma and the current screen_gamma.  If the file does
+not have a gamma value, you can pass one anyway if you have an idea what
+it is (usually 0.45455 is a good guess for GIF images on PCs).  Note
+that file gammas are inverted from screen gammas.  See the discussions
+on gamma in the PNG specification for an excellent description of what
+gamma is, and why all applications should support it.  It is strongly
+recommended that PNG viewers support gamma correction.
+
+   if (png_get_gAMA(png_ptr, info_ptr, &gamma))
+      png_set_gamma(png_ptr, screen_gamma, gamma);
+   else
+      png_set_gamma(png_ptr, screen_gamma, 0.45455);
+
+If you need to reduce an RGB file to a paletted file, or if a paletted
+file has more entries then will fit on your screen, png_set_dither()
+will do that.  Note that this is a simple match dither that merely
+finds the closest color available.  This should work fairly well with
+optimized palettes, and fairly badly with linear color cubes.  If you
+pass a palette that is larger then maximum_colors, the file will
+reduce the number of colors in the palette so it will fit into
+maximum_colors.  If there is a histogram, it will use it to make
+more intelligent choices when reducing the palette.  If there is no
+histogram, it may not do as good a job.
+
+   if (color_type & PNG_COLOR_MASK_COLOR)
+   {
+      if (png_get_valid(png_ptr, info_ptr,
+         PNG_INFO_PLTE))
+      {
+         png_color_16p histogram;
+
+         png_get_hIST(png_ptr, info_ptr,
+            &histogram);
+         png_set_dither(png_ptr, palette, num_palette,
+            max_screen_colors, histogram, 1);
+      }
+      else
+      {
+         png_color std_color_cube[MAX_SCREEN_COLORS] =
+            { ... colors ... };
+
+         png_set_dither(png_ptr, std_color_cube,
+            MAX_SCREEN_COLORS, MAX_SCREEN_COLORS,
+            NULL,0);
+      }
+   }
+
+PNG files describe monochrome as black being zero and white being one.
+The following code will reverse this (make black be one and white be
+zero):
+
+   if (bit_depth == 1 && color_type == PNG_COLOR_GRAY)
+      png_set_invert_mono(png_ptr);
+
+PNG files store 16 bit pixels in network byte order (big-endian,
+ie. most significant bits first).  This code changes the storage to the
+other way (little-endian, i.e. least significant bits first, the
+way PCs store them):
+
+    if (bit_depth == 16)
+        png_set_swap(png_ptr);
+
+If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you
+need to change the order the pixels are packed into bytes, you can use:
+
+    if (bit_depth < 8)
+       png_set_packswap(png_ptr);
+
+The last thing to handle is interlacing; this is covered in detail below,
+but you must call the function here if you want libpng to handle expansion
+of the interlaced image.
+
+    number_of_passes = png_set_interlace_handling(png_ptr);
+
+After setting the transformations, libpng can update your png_info
+structure to reflect any transformations you've requested with this
+call.  This is most useful to update the info structure's rowbytes
+field so you can use it to allocate your image memory.  This function
+will also update your palette with the correct screen_gamma and
+background if these have been given with the calls above.
+
+    png_read_update_info(png_ptr, info_ptr);
+
+After you call png_read_update_info(), you can allocate any
+memory you need to hold the image.  The row data is simply
+raw byte data for all forms of images.  As the actual allocation
+varies among applications, no example will be given.  If you
+are allocating one large chunk, you will need to build an
+array of pointers to each row, as it will be needed for some
+of the functions below.
+
+After you've allocated memory, you can read the image data.
+The simplest way to do this is in one function call.  If you are
+allocating enough memory to hold the whole image, you can just
+call png_read_image() and libpng will read in all the image data
+and put it in the memory area supplied.  You will need to pass in
+an array of pointers to each row.
+
+This function automatically handles interlacing, so you don't need
+to call png_set_interlace_handling() or call this function multiple
+times, or any of that other stuff necessary with png_read_rows().
+
+   png_read_image(png_ptr, row_pointers);
+
+where row_pointers is:
+
+   png_bytep row_pointers[height];
+
+You can point to void or char or whatever you use for pixels.
+
+If you don't want to read in the whole image at once, you can
+use png_read_rows() instead.  If there is no interlacing (check
+interlace_type == PNG_INTERLACE_NONE), this is simple:
+
+    png_read_rows(png_ptr, row_pointers, NULL,
+       number_of_rows);
+
+where row_pointers is the same as in the png_read_image() call.
+
+If you are doing this just one row at a time, you can do this with
+row_pointers:
+
+    png_bytep row_pointers = row;
+    png_read_row(png_ptr, &row_pointers, NULL);
+
+If the file is interlaced (info_ptr->interlace_type != 0), things get
+somewhat harder.  The only current (PNG Specification version 1.0)
+interlacing type for PNG is (interlace_type == PNG_INTERLACE_ADAM7)
+is a somewhat complicated 2D interlace scheme, known as Adam7, that
+breaks down an image into seven smaller images of varying size, based
+on an 8x8 grid.
+
+libpng can fill out those images or it can give them to you "as is".
+If you want them filled out, there are two ways to do that.  The one
+mentioned in the PNG specification is to expand each pixel to cover
+those pixels that have not been read yet (the "rectangle" method).
+This results in a blocky image for the first pass, which gradually
+smooths out as more pixels are read.  The other method is the "sparkle"
+method, where pixels are drawn only in their final locations, with the
+rest of the image remaining whatever colors they were initialized to
+before the start of the read.  The first method usually looks better,
+but tends to be slower, as there are more pixels to put in the rows.
+
+If you don't want libpng to handle the interlacing details, just call
+png_read_rows() seven times to read in all seven images.  Each of the
+images is a valid image by itself, or they can all be combined on an
+8x8 grid to form a single image (although if you intend to combine them
+you would be far better off using the libpng interlace handling).
+
+The first pass will return an image 1/8 as wide as the entire image
+(every 8th column starting in column 0) and 1/8 as high as the original
+(every 8th row starting in row 0), the second will be 1/8 as wide
+(starting in column 4) and 1/8 as high (also starting in row 0).  The
+third pass will be 1/4 as wide (every 4th pixel starting in column 0) and
+1/8 as high (every 8th row starting in row 4), and the fourth pass will
+be 1/4 as wide and 1/4 as high (every 4th column starting in column 2,
+and every 4th row starting in row 0).  The fifth pass will return an
+image 1/2 as wide, and 1/4 as high (starting at column 0 and row 2),
+while the sixth pass will be 1/2 as wide and 1/2 as high as the original
+(starting in column 1 and row 0).  The seventh and final pass will be as
+wide as the original, and 1/2 as high, containing all of the odd
+numbered scanlines.  Phew!
+
+If you want libpng to expand the images, call this before calling
+png_start_read_image() or png_read_update_info():
+
+    if (interlace_type == PNG_INTERLACE_ADAM7)
+        number_of_passes
+           = png_set_interlace_handling(png_ptr);
+
+This will return the number of passes needed.  Currently, this
+is seven, but may change if another interlace type is added.
+This function can be called even if the file is not interlaced,
+where it will return one pass.
+
+If you are not going to display the image after each pass, but are
+going to wait until the entire image is read in, use the sparkle
+effect.  This effect is faster and the end result of either method
+is exactly the same.  If you are planning on displaying the image
+after each pass, the "rectangle" effect is generally considered the
+better looking one.
+
+If you only want the "sparkle" effect, just call png_read_rows() as
+normal, with the third parameter NULL.  Make sure you make pass over
+the image number_of_passes times, and you don't change the data in the
+rows between calls.  You can change the locations of the data, just
+not the data.  Each pass only writes the pixels appropriate for that
+pass, and assumes the data from previous passes is still valid.
+
+    png_read_rows(png_ptr, row_pointers, NULL,
+       number_of_rows);
+
+If you only want the first effect (the rectangles), do the same as
+before except pass the row buffer in the third parameter, and leave
+the second parameter NULL.
+
+    png_read_rows(png_ptr, NULL, row_pointers,
+       number_of_rows);
+
+After you are finished reading the image, you can finish reading
+the file.  If you are interested in comments or time, which may be
+stored either before or after the image data, you should pass the
+separate png_info struct if you want to keep the comments from
+before and after the image separate.  If you are not interested, you
+can pass NULL.
+
+   png_read_end(png_ptr, end_info);
+
+When you are done, you can free all memory allocated by libpng like this:
+
+   png_destroy_read_struct(&png_ptr, &info_ptr,
+       &end_info);
+
+For a more compact example of reading a PNG image, see the file example.c.
+
+
+Reading PNG files progressively:
+
+The progressive reader is slightly different then the non-progressive
+reader.  Instead of calling png_read_info(), png_read_rows(), and
+png_read_end(), you make one call to png_process_data(), which calls
+callbacks when it has the info, a row, or the end of the image.  You
+set up these callbacks with png_set_progressive_read_fn().  You don't
+have to worry about the input/output functions of libpng, as you are
+giving the library the data directly in png_process_data().  I will
+assume that you have read the section on reading PNG files above,
+so I will only highlight the differences (although I will show
+all of the code).
+
+png_structp png_ptr;
+png_infop info_ptr;
+
+ /*  An example code fragment of how you would
+     initialize the progressive reader in your
+     application. */
+ int
+ initialize_png_reader()
+ {
+    png_ptr = png_create_read_struct
+        (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+         user_error_fn, user_warning_fn);
+    if (!png_ptr)
+        return -1;
+    info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr)
+    {
+        png_destroy_read_struct(&png_ptr, (png_infopp)NULL,
+           (png_infopp)NULL);
+        return -1;
+    }
+
+    if (setjmp(png_ptr->jmpbuf))
+    {
+        png_destroy_read_struct(&png_ptr, &info_ptr,
+           (png_infopp)NULL);
+        return -1;
+    }
+
+    /* This one's new.  You can provide functions
+       to be called when the header info is valid,
+       when each row is completed, and when the image
+       is finished.  If you aren't using all functions,
+       you can specify NULL parameters.  Even when all
+       three functions are NULL, you need to call
+       png_set_progressive_read_fn().  You can use
+       any struct as the user_ptr (cast to a void pointer
+       for the function call), and retrieve the pointer
+       from inside the callbacks using the function
+
+          png_get_progressive_ptr(png_ptr);
+
+       which will return a void pointer, which you have
+       to cast appropriately.
+     */
+    png_set_progressive_read_fn(png_ptr, (void *)user_ptr,
+        info_callback, row_callback, end_callback);
+
+    return 0;
+ }
+
+ /* A code fragment that you call as you receive blocks
+   of data */
+ int
+ process_data(png_bytep buffer, png_uint_32 length)
+ {
+    if (setjmp(png_ptr->jmpbuf))
+    {
+        png_destroy_read_struct(&png_ptr, &info_ptr,
+           (png_infopp)NULL);
+        return -1;
+    }
+
+    /* This one's new also.  Simply give it a chunk
+       of data from the file stream (in order, of
+       course).  On machines with segmented memory
+       models machines, don't give it any more than
+       64K.  The library seems to run fine with sizes
+       of 4K. Although you can give it much less if
+       necessary (I assume you can give it chunks of
+       1 byte, I haven't tried less then 256 bytes
+       yet).  When this function returns, you may
+       want to display any rows that were generated
+       in the row callback if you don't already do
+       so there.
+     */
+    png_process_data(png_ptr, info_ptr, buffer, length);
+    return 0;
+ }
+
+ /* This function is called (as set by
+    png_set_progressive_read_fn() above) when enough data
+    has been supplied so all of the header has been
+    read.
+ */
+ void
+ info_callback(png_structp png_ptr, png_infop info)
+ {
+    /* Do any setup here, including setting any of
+       the transformations mentioned in the Reading
+       PNG files section.  For now, you _must_ call
+       either png_start_read_image() or
+       png_read_update_info() after all the
+       transformations are set (even if you don't set
+       any).  You may start getting rows before
+       png_process_data() returns, so this is your
+       last chance to prepare for that.
+     */
+ }
+
+ /* This function is called when each row of image
+    data is complete */
+ void
+ row_callback(png_structp png_ptr, png_bytep new_row,
+    png_uint_32 row_num, int pass)
+ {
+    /* If the image is interlaced, and you turned
+       on the interlace handler, this function will
+       be called for every row in every pass.  Some
+       of these rows will not be changed from the
+       previous pass.  When the row is not changed,
+       the new_row variable will be NULL.  The rows
+       and passes are called in order, so you don't
+       really need the row_num and pass, but I'm
+       supplying them because it may make your life
+       easier.
+
+       For the non-NULL rows of interlaced images,
+       you must call png_progressive_combine_row()
+       passing in the row and the old row.  You can
+       call this function for NULL rows (it will just
+       return) and for non-interlaced images (it just
+       does the memcpy for you) if it will make the
+       code easier.  Thus, you can just do this for
+       all cases:
+     */
+
+        png_progressive_combine_row(png_ptr, old_row,
+          new_row);
+
+    /* where old_row is what was displayed for
+       previously for the row.  Note that the first
+       pass (pass == 0, really) will completely cover
+       the old row, so the rows do not have to be
+       initialized.  After the first pass (and only
+       for interlaced images), you will have to pass
+       the current row, and the function will combine
+       the old row and the new row.
+    */
+ }
+
+ void
+ end_callback(png_structp png_ptr, png_infop info)
+ {
+    /* This function is called after the whole image
+       has been read, including any chunks after the
+       image (up to and including the IEND).  You
+       will usually have the same info chunk as you
+       had in the header, although some data may have
+       been added to the comments and time fields.
+
+       Most people won't do much here, perhaps setting
+       a flag that marks the image as finished.
+     */
+ }
+
+
+
+IV. Writing
+
+Much of this is very similar to reading.  However, everything of
+importance is repeated here, so you won't have to constantly look
+back up in the reading section to understand writing.
+
+You will want to do the I/O initialization before you get into libpng,
+so if it doesn't work, you don't have anything to undo. If you are not
+using the standard I/O functions, you will need to replace them with
+custom writing functions.  See the discussion under Customizing libpng.
+
+    FILE *fp = fopen(file_name, "wb");
+    if (!fp)
+    {
+       return;
+    }
+
+Next, png_struct and png_info need to be allocated and initialized.
+As these can be both relatively large, you may not want to store these
+on the stack, unless you have stack space to spare.  Of course, you
+will want to check if they return NULL.  If you are also reading,
+you won't want to name your read structure and your write structure
+both "png_ptr"; you can call them anything you like, such as
+"read_ptr" and "write_ptr".  Look at pngtest.c, for example.
+
+    png_structp png_ptr = png_create_write_struct
+       (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+        user_error_fn, user_warning_fn);
+    if (!png_ptr)
+       return;
+
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr)
+    {
+       png_destroy_write_struct(&png_ptr,
+         (png_infopp)NULL);
+       return;
+    }
+
+If you want to use your own memory allocation routines,
+define PNG_USER_MEM_SUPPORTED and use
+png_create_write_struct_2() instead of png_create_read_struct():
+
+    png_structp png_ptr = png_create_write_struct_2
+       (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+        user_error_fn, user_warning_fn, (png_voidp)
+        user_mem_ptr, user_malloc_fn, user_free_fn);
+
+After you have these structures, you will need to set up the
+error handling.  When libpng encounters an error, it expects to
+longjmp() back to your routine.  Therefore, you will need to call
+setjmp() and pass the png_ptr->jmpbuf.  If you
+write the file from different routines, you will need to update
+the jmpbuf field every time you enter a new routine that will
+call a png_ function.  See your documentation of setjmp/longjmp
+for your compiler for more information on setjmp/longjmp.  See
+the discussion on libpng error handling in the Customizing Libpng
+section below for more information on the libpng error handling.
+
+    if (setjmp(png_ptr->jmpbuf))
+    {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        fclose(fp);
+        return;
+    }
+    ...
+    return;
+
+Now you need to set up the output code.  The default for libpng is to
+use the C function fwrite().  If you use this, you will need to pass a
+valid FILE * in the function png_init_io().  Be sure that the file is
+opened in binary mode.  Again, if you wish to handle writing data in
+another way, see the discussion on libpng I/O handling in the Customizing
+Libpng section below.
+
+    png_init_io(png_ptr, fp);
+
+At this point, you can set up a callback function that will be
+called after each row has been written, which you can use to control
+a progress meter or the like.  It's demonstrated in pngtest.c.
+You must supply a function
+
+    void write_row_callback(png_ptr, png_uint_32 row, int pass);
+    {
+      /* put your code here */
+    }
+
+(You can give it another name that you like instead of "write_row_callback")
+
+To inform libpng about your function, use
+
+    png_set_write_status_fn(png_ptr, write_row_callback);
+
+You now have the option of modifying how the compression library will
+run.  The following functions are mainly for testing, but may be useful
+in some cases, like if you need to write PNG files extremely fast and
+are willing to give up some compression, or if you want to get the
+maximum possible compression at the expense of slower writing.  If you
+have no special needs in this area, let the library do what it wants by
+not calling this function at all, as it has been tuned to deliver a good
+speed/compression ratio. The second parameter to png_set_filter() is
+the filter method, for which the only valid value is '0' (as of the
+October 1996 PNG specification, version 1.0).  The third parameter is a
+flag that indicates which filter type(s) are to be tested for each
+scanline.  See the Compression Library for details on the specific filter
+types.
+
+
+    /* turn on or off filtering, and/or choose
+       specific filters */
+    png_set_filter(png_ptr, 0,
+       PNG_FILTER_NONE | PNG_FILTER_SUB |
+       PNG_FILTER_PAETH);
+
+The png_set_compression_???() functions interface to the zlib compression
+library, and should mostly be ignored unless you really know what you are
+doing.  The only generally useful call is png_set_compression_level()
+which changes how much time zlib spends on trying to compress the image
+data.  See the Compression Library for details on the compression levels.
+
+    /* set the zlib compression level */
+    png_set_compression_level(png_ptr,
+        Z_BEST_COMPRESSION);
+
+    /* set other zlib parameters */
+    png_set_compression_mem_level(png_ptr, 8);
+    png_set_compression_strategy(png_ptr,
+        Z_DEFAULT_STRATEGY);
+    png_set_compression_window_bits(png_ptr, 15);
+    png_set_compression_method(png_ptr, 8);
+
+You now need to fill in the png_info structure with all the data you
+wish to write before the actual image.  Note that the only thing you
+are allowed to write after the image is the text chunks and the time
+chunk (as of PNG Specification 1.0, anyway).  See png_write_end() and
+the latest PNG specification for more information on that.  If you
+wish to write them before the image, fill them in now, and flag that
+data as being valid.  If you want to wait until after the data, don't
+fill them until png_write_end().  For all the fields in png_info and
+their data types, see png.h.  For explanations of what the fields
+contain, see the PNG specification.
+
+Some of the more important parts of the png_info are:
+
+    png_set_IHDR(png_ptr, info_ptr, width, height,
+       bit_depth, color_type, interlace_type,
+       compression_type, filter_type)
+    width          - holds the width of the image
+                     in pixels (up to 2^31).
+    height         - holds the height of the image
+                     in pixels (up to 2^31).
+    bit_depth      - holds the bit depth of one of the
+                     image channels.
+                     (valid values are 1, 2, 4, 8, 16
+                     and depend also on the
+                     color_type.  See also significant
+                     bits (sBIT) below).
+    color_type     - describes which color/alpha
+                     channels are present.
+                     PNG_COLOR_TYPE_GRAY
+                        (bit depths 1, 2, 4, 8, 16)
+                     PNG_COLOR_TYPE_GRAY_ALPHA
+                        (bit depths 8, 16)
+                     PNG_COLOR_TYPE_PALETTE
+                        (bit depths 1, 2, 4, 8)
+                     PNG_COLOR_TYPE_RGB
+                        (bit_depths 8, 16)
+                     PNG_COLOR_TYPE_RGB_ALPHA
+                        (bit_depths 8, 16)
+
+                     PNG_COLOR_MASK_PALETTE
+                     PNG_COLOR_MASK_COLOR
+                     PNG_COLOR_MASK_ALPHA
+
+    interlace_type - PNG_INTERLACE_NONE or
+                     PNG_INTERLACE_ADAM7
+    compression_type - (must be
+                     PNG_COMPRESSION_TYPE_DEFAULT)
+    filter_type    - (must be PNG_FILTER_TYPE_DEFAULT)
+
+    png_set_PLTE(png_ptr, info_ptr, palette,
+       num_palette);
+    palette        - the palette for the file
+                     (array of png_color)
+    num_palette    - number of entries in the palette
+
+    png_set_gAMA(png_ptr, info_ptr, gamma);
+    gamma          - the gamma the image was created
+                     at (PNG_INFO_gAMA)
+
+    png_set_sRGB(png_ptr, info_ptr, srgb_intent);
+    srgb_intent    - the rendering intent
+                     (PNG_INFO_sRGB) The presence of
+                     the sRGB chunk means that the pixel
+                     data is in the sRGB color space.
+                     This chunk also implies specific
+                     values of gAMA and cHRM.  Rendering
+                     intent is the CSS-1 property that
+                     has been defined by the International
+                     Color Consortium
+                     (http://www.color.org).
+                     It can be one of
+                     PNG_SRGB_INTENT_SATURATION,
+                     PNG_SRGB_INTENT_PERCEPTUAL,
+                     PNG_SRGB_INTENT_ABSOLUTE, or
+                     PNG_SRGB_INTENT_RELATIVE.
+
+
+    png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr,
+       srgb_intent);
+    srgb_intent    - the rendering intent
+                     (PNG_INFO_sRGB) The presence of the
+                     sRGB chunk means that the pixel
+                     data is in the sRGB color space.
+                     This function also causes gAMA and
+                     cHRM chunks with the specific values
+                     that are consistent with sRGB to be
+                     written.
+
+    png_set_sBIT(png_ptr, info_ptr, sig_bit);
+    sig_bit        - the number of significant bits for
+                     (PNG_INFO_sBIT) each of the gray, red,
+                     green, and blue channels, whichever are
+                     appropriate for the given color type
+                     (png_color_16)
+
+    png_set_tRNS(png_ptr, info_ptr, trans, num_trans,
+       trans_values);
+    trans          - array of transparent entries for
+                     palette (PNG_INFO_tRNS)
+    trans_values   - transparent pixel for non-paletted
+                     images (PNG_INFO_tRNS)
+    num_trans      - number of transparent entries
+                     (PNG_INFO_tRNS)
+
+    png_set_hIST(png_ptr, info_ptr, hist);
+                    (PNG_INFO_hIST)
+    hist           - histogram of palette (array of
+                     png_color_16)
+
+    png_set_tIME(png_ptr, info_ptr, mod_time);
+    mod_time       - time image was last modified
+                     (PNG_VALID_tIME)
+
+    png_set_bKGD(png_ptr, info_ptr, background);
+    background     - background color (PNG_VALID_bKGD)
+
+    png_set_text(png_ptr, info_ptr, text_ptr, num_text);
+    text_ptr       - array of png_text holding image
+                     comments
+    text_ptr[i]->key   - keyword for comment.
+    text_ptr[i]->text  - text comments for current
+                         keyword.
+    text_ptr[i]->compression - type of compression used
+         on "text" PNG_TEXT_COMPRESSION_NONE or
+         PNG_TEXT_COMPRESSION_zTXt
+    num_text    - number of comments in text_ptr
+
+    png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y,
+        unit_type);
+    offset_x  - positive offset from the left
+                     edge of the screen
+    offset_y  - positive offset from the top
+                     edge of the screen
+    unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER
+
+    png_set_pHYs(png_ptr, info_ptr, res_x, res_y,
+        unit_type);
+    res_x       - pixels/unit physical resolution
+                  in x direction
+    res_y       - pixels/unit physical resolution
+                  in y direction
+    unit_type   - PNG_RESOLUTION_UNKNOWN,
+                  PNG_RESOLUTION_METER
+
+In PNG files, the alpha channel in an image is the level of opacity.
+If your data is supplied as a level of transparency, you can invert the
+alpha channel before you write it, so that 0 is fully transparent and 255
+(in 8-bit or paletted images) or 65535 (in 16-bit images) is fully opaque,
+with
+
+    png_set_invert_alpha(png_ptr);
+
+This must appear here instead of later with the other transformations
+because in the case of paletted images the tRNS chunk data has to
+be inverted before the tRNS chunk is written.  If your image is not a
+paletted image, the tRNS data (which in such cases represents a single
+color to be rendered as transparent) won't be changed.
+
+A quick word about text and num_text.  text is an array of png_text
+structures.  num_text is the number of valid structures in the array.
+If you want, you can use max_text to hold the size of the array, but
+libpng ignores it for writing (it does use it for reading).  Each
+png_text structure holds a keyword-text value, and a compression type.
+The compression types have the same valid numbers as the compression
+types of the image data.  Currently, the only valid number is zero.
+However, you can store text either compressed or uncompressed, unlike
+images, which always have to be compressed.  So if you don't want the
+text compressed, set the compression type to PNG_TEXT_COMPRESSION_NONE.
+Until text gets around 1000 bytes, it is not worth compressing it.
+After the text has been written out to the file, the compression type
+is set to PNG_TEXT_COMPRESSION_NONE_WR or PNG_TEXT_COMPRESSION_zTXt_WR,
+so that it isn't written out again at the end (in case you are calling
+png_write_end() with the same struct.
+
+The keywords that are given in the PNG Specification are:
+
+    Title            Short (one line) title or
+                     caption for image
+    Author           Name of image's creator
+    Description      Description of image (possibly long)
+    Copyright        Copyright notice
+    Creation Time    Time of original image creation
+                     (usually RFC 1123 format, see below)
+    Software         Software used to create the image
+    Disclaimer       Legal disclaimer
+    Warning          Warning of nature of content
+    Source           Device used to create the image
+    Comment          Miscellaneous comment; conversion
+                     from other image format
+
+The keyword-text pairs work like this.  Keywords should be short
+simple descriptions of what the comment is about.  Some typical
+keywords are found in the PNG specification, as is some recommendations
+on keywords.  You can repeat keywords in a file.  You can even write
+some text before the image and some after.  For example, you may want
+to put a description of the image before the image, but leave the
+disclaimer until after, so viewers working over modem connections
+don't have to wait for the disclaimer to go over the modem before
+they start seeing the image.  Finally, keywords should be full
+words, not abbreviations.  Keywords and text are in the ISO 8859-1
+(Latin-1) character set (a superset of regular ASCII) and can not
+contain NUL characters, and should not contain control or other
+unprintable characters.  To make the comments widely readable, stick
+with basic ASCII, and avoid machine specific character set extensions
+like the IBM-PC character set.  The keyword must be present, but
+you can leave off the text string on non-compressed pairs.
+Compressed pairs must have a text string, as only the text string
+is compressed anyway, so the compression would be meaningless.
+
+PNG supports modification time via the png_time structure.  Two
+conversion routines are proved, png_convert_from_time_t() for
+time_t and png_convert_from_struct_tm() for struct tm.  The
+time_t routine uses gmtime().  You don't have to use either of
+these, but if you wish to fill in the png_time structure directly,
+you should provide the time in universal time (GMT) if possible
+instead of your local time.  Note that the year number is the full
+year (e.g. 1998, rather than 98 - PNG is year 2000 compliant!), and
+that months start with 1.
+
+If you want to store the time of the original image creation, you should
+use a plain tEXt chunk with the "Creation Time" keyword.  This is
+necessary because the "creation time" of a PNG image is somewhat vague,
+depending on whether you mean the PNG file, the time the image was
+created in a non-PNG format, a still photo from which the image was
+scanned, or possibly the subject matter itself.  In order to facilitate
+machine-readable dates, it is recommended that the "Creation Time"
+tEXt chunk use RFC 1123 format dates (e.g. "22 May 1997 18:07:10 GMT"),
+although this isn't a requirement.  Unlike the tIME chunk, the
+"Creation Time" tEXt chunk is not expected to be automatically changed
+by the software.  To facilitate the use of RFC 1123 dates, a function
+png_convert_to_rfc1123(png_timep) is provided to convert from PNG
+time to an RFC 1123 format string.
+
+You are now ready to write all the file information up to the actual
+image data.  You do this with a call to png_write_info().
+
+    png_write_info(png_ptr, info_ptr);
+
+After you've written the file information, you can set up the library
+to handle any special transformations of the image data.  The various
+ways to transform the data will be described in the order that they
+should occur.  This is important, as some of these change the color
+type and/or bit depth of the data, and some others only work on
+certain color types and bit depths.  Even though each transformation
+checks to see if it has data that it can do something with, you should
+make sure to only enable a transformation if it will be valid for the
+data.  For example, don't swap red and blue on grayscale data.
+
+PNG files store RGB pixels packed into 3 or 6 bytes.  This code tells
+the library to expand the input data to 4 or 8 bytes per pixel
+(or expand 1 or 2-byte grayscale data to 2 or 4 bytes per pixel).
+
+    png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
+
+where the 0 is the value that will be put in the 4th byte, and the
+location is either PNG_FILLER_BEFORE or PNG_FILLER_AFTER, depending
+upon whether the filler byte is stored XRGB or RGBX.
+
+PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as
+they can, resulting in, for example, 8 pixels per byte for 1 bit files.
+If the data is supplied at 1 pixel per byte, use this code, which will
+correctly pack the pixels into a single byte:
+
+    png_set_packing(png_ptr);
+
+PNG files reduce possible bit depths to 1, 2, 4, 8, and 16.  If your
+data is of another bit depth, you can write an sBIT chunk into the
+file so that decoders can get the original data if desired.
+
+    /* Set the true bit depth of the image data */
+    if (color_type & PNG_COLOR_MASK_COLOR)
+    {
+        sig_bit.red = true_bit_depth;
+        sig_bit.green = true_bit_depth;
+        sig_bit.blue = true_bit_depth;
+    }
+    else
+    {
+        sig_bit.gray = true_bit_depth;
+    }
+    if (color_type & PNG_COLOR_MASK_ALPHA)
+    {
+        sig_bit.alpha = true_bit_depth;
+    }
+
+    png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+
+If the data is stored in the row buffer in a bit depth other than
+one supported by PNG (e.g. 3 bit data in the range 0-7 for a 4-bit PNG),
+this will scale the values to appear to be the correct bit depth as
+is required by PNG.
+
+    png_set_shift(png_ptr, &sig_bit);
+
+PNG files store 16 bit pixels in network byte order (big-endian,
+ie. most significant bits first).  This code would be used if they are
+supplied the other way (little-endian, i.e. least significant bits
+first, the way PCs store them):
+
+    if (bit_depth > 8)
+       png_set_swap(png_ptr);
+
+If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you
+need to change the order the pixels are packed into bytes, you can use:
+
+    if (bit_depth < 8)
+       png_set_packswap(png_ptr);
+
+PNG files store 3 color pixels in red, green, blue order.  This code
+would be used if they are supplied as blue, green, red:
+
+    png_set_bgr(png_ptr);
+
+PNG files describe monochrome as black being zero and white being
+one. This code would be used if the pixels are supplied with this reversed
+(black being one and white being zero):
+
+    png_set_invert_mono(png_ptr);
+
+Finally, you can write your own transformation function if none of
+the existing ones meets your needs.  This is done by setting a callback
+with
+
+    png_set_write_user_transform_fn(png_ptr,
+       write_transform_fn);
+
+You must supply the function
+
+    void write_transform_fn(png_ptr ptr, row_info_ptr
+       row_info, png_bytep data)
+
+See pngtest.c for a working example.  Your function will be called
+before any of the other transformations have been processed.
+
+It is possible to have libpng flush any pending output, either manually,
+or automatically after a certain number of lines have been written.  To
+flush the output stream a single time call:
+
+    png_write_flush(png_ptr);
+
+and to have libpng flush the output stream periodically after a certain
+number of scanlines have been written, call:
+
+    png_set_flush(png_ptr, nrows);
+
+Note that the distance between rows is from the last time png_write_flush()
+was called, or the first row of the image if it has never been called.
+So if you write 50 lines, and then png_set_flush 25, it will flush the
+output on the next scanline, and every 25 lines thereafter, unless
+png_write_flush() is called before 25 more lines have been written.
+If nrows is too small (less than about 10 lines for a 640 pixel wide
+RGB image) the image compression may decrease noticeably (although this
+may be acceptable for real-time applications).  Infrequent flushing will
+only degrade the compression performance by a few percent over images
+that do not use flushing.
+
+That's it for the transformations.  Now you can write the image data.
+The simplest way to do this is in one function call.  If have the
+whole image in memory, you can just call png_write_image() and libpng
+will write the image.  You will need to pass in an array of pointers to
+each row.  This function automatically handles interlacing, so you don't
+need to call png_set_interlace_handling() or call this function multiple
+times, or any of that other stuff necessary with png_write_rows().
+
+    png_write_image(png_ptr, row_pointers);
+
+where row_pointers is:
+
+    png_byte *row_pointers[height];
+
+You can point to void or char or whatever you use for pixels.
+
+If you don't want to write the whole image at once, you can
+use png_write_rows() instead.  If the file is not interlaced,
+this is simple:
+
+    png_write_rows(png_ptr, row_pointers,
+       number_of_rows);
+
+row_pointers is the same as in the png_write_image() call.
+
+If you are just writing one row at a time, you can do this with
+row_pointers:
+
+    png_bytep row_pointer = row;
+
+    png_write_row(png_ptr, &row_pointer);
+
+When the file is interlaced, things can get a good deal more
+complicated.  The only currently (as of February 1998 -- PNG Specification
+version 1.0, dated October 1996) defined interlacing scheme for PNG files
+is the "Adam7" interlace scheme, that breaks down an
+image into seven smaller images of varying size.  libpng will build
+these images for you, or you can do them yourself.  If you want to
+build them yourself, see the PNG specification for details of which
+pixels to write when.
+
+If you don't want libpng to handle the interlacing details, just
+use png_set_interlace_handling() and call png_write_rows() the
+correct number of times to write all seven sub-images.
+
+If you want libpng to build the sub-images, call this before you start
+writing any rows:
+
+    number_of_passes =
+       png_set_interlace_handling(png_ptr);
+
+This will return the number of passes needed.  Currently, this
+is seven, but may change if another interlace type is added.
+
+Then write the complete image number_of_passes times.
+
+    png_write_rows(png_ptr, row_pointers,
+       number_of_rows);
+
+As some of these rows are not used, and thus return immediately,
+you may want to read about interlacing in the PNG specification,
+and only update the rows that are actually used.
+
+After you are finished writing the image, you should finish writing
+the file.  If you are interested in writing comments or time, you should
+pass an appropriately filled png_info pointer.  If you are not interested,
+you can pass NULL.
+
+    png_write_end(png_ptr, info_ptr);
+
+When you are done, you can free all memory used by libpng like this:
+
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+
+You must free any data you allocated for info_ptr, such as comments,
+palette, or histogram, before the call to png_destroy_write_struct();
+
+For a more compact example of writing a PNG image, see the file example.c.
+
+
+V. Modifying/Customizing libpng:
+
+There are two issues here.  The first is changing how libpng does
+standard things like memory allocation, input/output, and error handling.
+The second deals with more complicated things like adding new chunks,
+adding new transformations, and generally changing how libpng works.
+
+All of the memory allocation, input/output, and error handling in libpng
+goes through callbacks that are user settable.  The default routines are
+in pngmem.c, pngrio.c, pngwio.c, and pngerror.c respectively.  To change
+these functions, call the appropriate png_set_???_fn() function.
+
+Memory allocation is done through the functions png_large_malloc(),
+png_malloc(), png_realloc(), png_large_free(), and png_free().  These
+currently just call the standard C functions.  The large functions must
+handle exactly 64K, but they don't have to handle more than that.  If
+your pointers can't access more then 64K at a time, you will want to set
+MAXSEG_64K in zlib.h.  Since it is unlikely that the method of handling
+memory allocation on a platform will change between applications, these
+functions must be modified in the library at compile time.
+
+Input/Output in libpng is done through png_read() and png_write(),
+which currently just call fread() and fwrite().  The FILE * is stored in
+png_struct and is initialized via png_init_io().  If you wish to change
+the method of I/O, the library supplies callbacks that you can set
+through the function png_set_read_fn() and png_set_write_fn() at run
+time, instead of calling the png_init_io() function.  These functions
+also provide a void pointer that can be retrieved via the function
+png_get_io_ptr().  For example:
+
+    png_set_read_fn(png_structp read_ptr,
+        voidp read_io_ptr, png_rw_ptr read_data_fn)
+
+    png_set_write_fn(png_structp write_ptr,
+        voidp write_io_ptr, png_rw_ptr write_data_fn,
+        png_flush_ptr output_flush_fn);
+
+    voidp read_io_ptr = png_get_io_ptr(read_ptr);
+    voidp write_io_ptr = png_get_io_ptr(write_ptr);
+
+The replacement I/O functions should have prototypes as follows:
+
+    void user_read_data(png_structp png_ptr,
+        png_bytep data, png_uint_32 length);
+    void user_write_data(png_structp png_ptr,
+        png_bytep data, png_uint_32 length);
+    void user_flush_data(png_structp png_ptr);
+
+Supplying NULL for the read, write, or flush functions sets them back
+to using the default C stream functions.  It is an error to read from
+a write stream, and vice versa.
+
+Error handling in libpng is done through png_error() and png_warning().
+Errors handled through png_error() are fatal, meaning that png_error()
+should never return to its caller.  Currently, this is handled via
+setjmp() and longjmp(), but you could change this to do things like
+exit() if you should wish.  On non-fatal errors, png_warning() is called
+to print a warning message, and then control returns to the calling code.
+By default png_error() and png_warning() print a message on stderr via
+fprintf() unless the library is compiled with PNG_NO_STDIO defined.  If
+you wish to change the behavior of the error functions, you will need to
+set up your own message callbacks.  These functions are normally supplied
+at the time that the png_struct is created.  It is also possible to change
+these functions after png_create_???_struct() has been called by calling:
+
+    png_set_error_fn(png_structp png_ptr,
+        png_voidp error_ptr, png_error_ptr error_fn,
+        png_error_ptr warning_fn);
+
+    png_voidp error_ptr = png_get_error_ptr(png_ptr);
+
+If NULL is supplied for either error_fn or warning_fn, then the libpng
+default function will be used, calling fprintf() and/or longjmp() if a
+problem is encountered.  The replacement error functions should have
+parameters as follows:
+
+    void user_error_fn(png_structp png_ptr,
+        png_const_charp error_msg);
+    void user_warning_fn(png_structp png_ptr,
+        png_const_charp warning_msg);
+
+The motivation behind using setjmp() and longjmp() is the C++ throw and
+catch exception handling methods.  This makes the code much easier to write,
+as there is no need to check every return code of every function call.
+However, there are some uncertainties about the status of local variables
+after a longjmp, so the user may want to be careful about doing anything after
+setjmp returns non-zero besides returning itself.  Consult your compiler
+documentation for more details.
+
+If you need to read or write custom chunks, you will need to get deeper
+into the libpng code, as a mechanism has not yet been supplied for user
+callbacks with custom chunks.  First, read the PNG specification, and have
+a first level of understanding of how it works.  Pay particular attention
+to the sections that describe chunk names, and look at how other chunks
+were designed, so you can do things similarly.  Second, check out the
+sections of libpng that read and write chunks.  Try to find a chunk that
+is similar to yours and copy off of it.  More details can be found in the
+comments inside the code.  A way of handling unknown chunks in a generic
+method, potentially via callback functions, would be best.
+
+If you wish to write your own transformation for the data, look through
+the part of the code that does the transformations, and check out some of
+the simpler ones to get an idea of how they work.  Try to find a similar
+transformation to the one you want to add and copy off of it.  More details
+can be found in the comments inside the code itself.
+
+Configuring for 16 bit platforms:
+
+You may need to change the png_large_malloc() and png_large_free()
+routines in pngmem.c, as these are required to allocate 64K, although
+there is already support for many of the common DOS compilers.  Also,
+you will want to look into zconf.h to tell zlib (and thus libpng) that
+it cannot allocate more then 64K at a time.  Even if you can, the memory
+won't be accessible.  So limit zlib and libpng to 64K by defining MAXSEG_64K.
+
+Configuring for DOS:
+
+For DOS users who only have access to the lower 640K, you will
+have to limit zlib's memory usage via a png_set_compression_mem_level()
+call.  See zlib.h or zconf.h in the zlib library for more information.
+
+Configuring for Medium Model:
+
+Libpng's support for medium model has been tested on most of the popular
+compilers.  Make sure MAXSEG_64K gets defined, USE_FAR_KEYWORD gets
+defined, and FAR gets defined to far in pngconf.h, and you should be
+all set.  Everything in the library (except for zlib's structure) is
+expecting far data.  You must use the typedefs with the p or pp on
+the end for pointers (or at least look at them and be careful).  Make
+note that the row's of data are defined as png_bytepp, which is an
+unsigned char far * far *.
+
+Configuring for gui/windowing platforms:
+
+You will need to write new error and warning functions that use the GUI
+interface, as described previously, and set them to be the error and
+warning functions at the time that png_create_???_struct() is called,
+in order to have them available during the structure initialization.
+They can be changed later via png_set_error_fn().  On some compilers,
+you may also have to change the memory allocators (png_malloc, etc.).
+
+Configuring for compiler xxx:
+
+All includes for libpng are in pngconf.h.  If you need to add/change/delete
+an include, this is the place to do it.  The includes that are not
+needed outside libpng are protected by the PNG_INTERNAL definition,
+which is only defined for those routines inside libpng itself.  The
+files in libpng proper only include png.h, which includes pngconf.h.
+
+Configuring zlib:
+
+There are special functions to configure the compression.  Perhaps the
+most useful one changes the compression level, which currently uses
+input compression values in the range 0 - 9.  The library normally
+uses the default compression level (Z_DEFAULT_COMPRESSION = 6).  Tests
+have shown that for a large majority of images, compression values in
+the range 3-6 compress nearly as well as higher levels, and do so much
+faster.  For online applications it may be desirable to have maximum speed
+(Z_BEST_SPEED = 1).  With versions of zlib after v0.99, you can also
+specify no compression (Z_NO_COMPRESSION = 0), but this would create
+files larger than just storing the raw bitmap.  You can specify the
+compression level by calling:
+
+    png_set_compression_level(png_ptr, level);
+
+Another useful one is to reduce the memory level used by the library.
+The memory level defaults to 8, but it can be lowered if you are
+short on memory (running DOS, for example, where you only have 640K).
+
+    png_set_compression_mem_level(png_ptr, level);
+
+The other functions are for configuring zlib.  They are not recommended
+for normal use and may result in writing an invalid PNG file.  See
+zlib.h for more information on what these mean.
+
+    png_set_compression_strategy(png_ptr,
+        strategy);
+    png_set_compression_window_bits(png_ptr,
+        window_bits);
+    png_set_compression_method(png_ptr, method);
+
+Controlling row filtering:
+
+If you want to control whether libpng uses filtering or not, which
+filters are used, and how it goes about picking row filters, you
+can call one of these functions.  The selection and configuration
+of row filters can have a significant impact on the size and
+encoding speed and a somewhat lesser impact on the decoding speed
+of an image.  Filtering is enabled by default for RGB and grayscale
+images (with and without alpha), but not for paletted images nor
+for any images with bit depths less than 8 bits/pixel.
+
+The 'method' parameter sets the main filtering method, which is
+currently only '0' in the PNG 1.0 specification.  The 'filters'
+parameter sets which filter(s), if any, should be used for each
+scanline.  Possible values are PNG_ALL_FILTERS and PNG_NO_FILTERS
+to turn filtering on and off, respectively.
+
+Individual filter types are PNG_FILTER_NONE, PNG_FILTER_SUB,
+PNG_FILTER_UP, PNG_FILTER_AVG, PNG_FILTER_PAETH, which can be bitwise
+ORed together '|' to specify one or more filters to use.  These
+filters are described in more detail in the PNG specification.  If
+you intend to change the filter type during the course of writing
+the image, you should start with flags set for all of the filters
+you intend to use so that libpng can initialize its internal
+structures appropriately for all of the filter types.
+
+    filters = PNG_FILTER_NONE | PNG_FILTER_SUB
+       | PNG_FILTER_UP;
+    png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE,
+       filters);
+
+It is also possible to influence how libpng chooses from among the
+available filters.  This is done in two ways - by telling it how
+important it is to keep the same filter for successive rows, and
+by telling it the relative computational costs of the filters.
+
+    double weights[3] = {1.5, 1.3, 1.1},
+       costs[PNG_FILTER_VALUE_LAST] =
+       {1.0, 1.3, 1.3, 1.5, 1.7};
+
+    png_set_filter_selection(png_ptr,
+       PNG_FILTER_SELECTION_WEIGHTED, 3,
+       weights, costs);
+
+The weights are multiplying factors that indicate to libpng that the
+row filter should be the same for successive rows unless another row filter
+is that many times better than the previous filter.  In the above example,
+if the previous 3 filters were SUB, SUB, NONE, the SUB filter could have a
+"sum of absolute differences" 1.5 x 1.3 times higher than other filters
+and still be chosen, while the NONE filter could have a sum 1.1 times
+higher than other filters and still be chosen.  Unspecified weights are
+taken to be 1.0, and the specified weights should probably be declining
+like those above in order to emphasize recent filters over older filters.
+
+The filter costs specify for each filter type a relative decoding cost
+to be considered when selecting row filters.  This means that filters
+with higher costs are less likely to be chosen over filters with lower
+costs, unless their "sum of absolute differences" is that much smaller.
+The costs do not necessarily reflect the exact computational speeds of
+the various filters, since this would unduly influence the final image
+size.
+
+Note that the numbers above were invented purely for this example and
+are given only to help explain the function usage.  Little testing has
+been done to find optimum values for either the costs or the weights.
+
+Removing unwanted object code:
+
+There are a bunch of #define's in pngconf.h that control what parts of
+libpng are compiled.  All the defines end in _SUPPORTED.  If you are
+never going to use a capability, you can change the #define to #undef
+before recompiling libpng and save yourself code and data space, or
+you can turn off individual capabilities with defines that begin with
+PNG_NO_.
+
+You can also turn all of the transforms and ancillary chunk capabilities
+off en masse with  compiler directives that define
+PNG_NO_READ[or WRITE]_TRANSFORMS, or PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS,
+or all four,
+along with directives to turn on any of the capabilities that you do
+want.  The PNG_NO_READ[or WRITE]_TRANSFORMS directives disable
+the extra transformations but still leave the library fully capable of reading
+and writing PNG files with all known public chunks [except for sPLT].
+Use of the PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS directive
+produces a library that is incapable of reading or writing ancillary chunks.
+If you are not using the progressive reading capability, you can
+turn that off with PNG_NO_PROGRESSIVE_READ (don't confuse
+this with the INTERLACING capability, which you'll still have).
+
+All the reading and writing specific code are in separate files, so the
+linker should only grab the files it needs.  However, if you want to
+make sure, or if you are building a stand alone library, all the
+reading files start with pngr and all the writing files start with
+pngw.  The files that don't match either (like png.c, pngtrans.c, etc.)
+are used for both reading and writing, and always need to be included.
+The progressive reader is in pngpread.c
+
+If you are creating or distributing a dynamically linked library (a .so
+or DLL file), you should not remove or disable any parts of the library,
+as this will cause applications linked with different versions of the
+library to fail if they call functions not available in your library.
+The size of the library itself should not be an issue, because only
+those sections that are actually used will be loaded into memory.
+
+Requesting debug printout:
+
+The macro definition PNG_DEBUG can be used to request debugging
+printout.  Set it to an integer value in the range 0 to 3.  Higher
+numbers result in increasing amounts of debugging information.  The
+information is printed to the "stderr" file, unless another file
+name is specified in the PNG_DEBUG_FILE macro definition.
+
+When PNG_DEBUG > 0, the following functions (macros) become available:
+
+   png_debug(level, message)
+   png_debug1(level, message, p1)
+   png_debug2(level, message, p1, p2)
+
+in which "level" is compared to PNG_DEBUG to decide whether to print
+the message, "message" is the formatted string to be printed,
+and p1 and p2 are parameters that are to be embedded in the string
+according to printf-style formatting directives.  For example,
+
+   png_debug1(2, "foo=%d\n", foo);
+
+is expanded to
+
+   if(PNG_DEBUG > 2)
+     fprintf(PNG_DEBUG_FILE, "foo=%d\n", foo);
+
+When PNG_DEBUG is defined but is zero, the macros aren't defined, but you
+can still use PNG_DEBUG to control your own debugging:
+
+   #ifdef PNG_DEBUG
+       fprintf(stderr, ...
+   #endif
+
+When PNG_DEBUG = 1, the macros are defined, but only png_debug statements
+having level = 0 will be printed.  There aren't any such statements in
+this version of libpng, but if you insert some they will be printed.
+
+VI.  Changes to Libpng from version 0.88
+
+It should be noted that versions of libpng later than 0.96 are not
+distributed by the original libpng author, Guy Schalnat, nor by
+Andreas Dilger, who had taken over from Guy during 1996 and 1997, and
+distributed versions 0.89 through 0.96, but rather by another member
+of the original PNG Group, Glenn Randers-Pehrson.  Guy and Andreas are
+still alive and well, but they have moved on to other things.
+
+The old libpng functions png_read_init(), png_write_init(),
+png_info_init(), png_read_destroy(), and png_write_destory() have been
+moved to PNG_INTERNAL in version 0.95 to discourage their use.  The
+preferred method of creating and initializing the libpng structures is
+via the png_create_read_struct(), png_create_write_struct(), and
+png_create_info_struct() because they isolate the size of the structures
+from the application, allow version error checking, and also allow the
+use of custom error handling routines during the initialization, which
+the old functions do not.  The functions png_read_destroy() and
+png_write_destroy() do not actually free the memory that libpng
+allocated for these structs, but just reset the data structures, so they
+can be used instead of png_destroy_read_struct() and
+png_destroy_write_struct() if you feel there is too much system overhead
+allocating and freeing the png_struct for each image read.
+
+Setting the error callbacks via png_set_message_fn() before
+png_read_init() as was suggested in libpng-0.88 is no longer supported
+because this caused applications that do not use custom error functions
+to fail if the png_ptr was not initialized to zero.  It is still possible
+to set the error callbacks AFTER png_read_init(), or to change them with
+png_set_error_fn(), which is essentially the same function, but with a
+new name to force compilation errors with applications that try to use
+the old method.
+
+VII. Y2K Compliance in libpng
+
+January 13, 1999
+
+Since the PNG Development group is an ad-hoc body, we can't make
+an official declaration.
+
+This is your unofficial assurance that libpng from version 0.81 and
+upward are Y2K compliant.  It is my belief that earlier versions were
+also Y2K compliant.
+
+Libpng only has three year fields.  One is a 2-byte unsigned integer that
+will hold years up to 65535.  The other two hold the date in text
+format, and will hold years up to 9999.
+
+The integer is
+    "png_uint_16 year" in png_time_struct.
+
+The strings are
+    "png_charp time_buffer" in png_struct and
+    "near_time_buffer", which is a local character string in png.c.
+
+There are seven time-related functions:
+
+    png_convert_to_rfc_1123() in png.c 
+      (formerly png_convert_to_rfc_1152() in error)
+    png_convert_from_struct_tm() in pngwrite.c, called in pngwrite.c
+    png_convert_from_time_t() in pngwrite.c
+    png_get_tIME() in pngget.c
+    png_handle_tIME() in pngrutil.c, called in pngread.c
+    png_set_tIME() in pngset.c
+    png_write_tIME() in pngwutil.c, called in pngwrite.c
+
+All appear to handle dates properly in a Y2K environment.  The 
+png_convert_from_time_t() function calls gmtime() to convert from system
+clock time, which returns (year - 1900), which we properly convert to
+the full 4-digit year.  There is a possibility that applications using
+libpng are not passing 4-digit years into the png_convert_to_rfc_1123()
+function, or incorrectly passing only a 2-digit year instead of
+"year - 1900" into the png_convert_from_struct_tm() function, but this
+is not under our control.  The libpng documentation has always stated
+that it works with 4-digit years, and the APIs have been documented as
+such.
+
+The tIME chunk itself is also Y2K compliant.  It uses a 2-byte unsigned
+integer to hold the year, and can hold years as large as 65535.
+
+
+   Glenn Randers-Pehrson
+   libpng maintainer
+   PNG Development Group
diff --git a/src/png/libpngpf.3 b/src/png/libpngpf.3
new file mode 100644 (file)
index 0000000..c2654e7
--- /dev/null
@@ -0,0 +1,347 @@
+.TH LIBPNGPF 3 "January 14, 1999"
+.SH NAME
+libpng \- Portable Network Graphics (PNG) Reference Library 1.0.3 - January 14, 1999
+(private functions)
+.SH SYNOPSIS
+#include <png.h>
+
+void png_build_gamma_table (png_structp png_ptr);
+
+void png_build_grayscale_palette (int bit_depth, png_colorp
+palette);
+
+void png_calculate_crc (png_structp png_ptr, png_bytep ptr,
+png_size_t length);
+void png_check_chunk_name (png_structp png_ptr, png_bytep
+chunk_name);
+
+png_size_t png_check_keyword (png_structp png_ptr, png_charp
+key, png_charpp new_key);
+
+void png_combine_row (png_structp png_ptr, png_bytep row, int
+mask);
+
+void png_correct_palette (png_structp png_ptr, png_colorp
+palette, int num_palette);
+
+int png_crc_error (png_structp png_ptr);
+
+int png_crc_finish (png_structp png_ptr, png_uint_32 skip);
+
+void png_crc_read (png_structp png_ptr, png_bytep buf,
+png_size_t length);
+
+png_voidp png_create_struct (int type, png_malloc_ptr malloc_fn);
+
+png_voidp png_create_struct_2 (int type);
+
+void png_destroy_struct (png_voidp struct_ptr);
+
+void png_destroy_struct_2 (png_voidp struct_ptr, png_free_ptr
+free_fn);
+
+void png_do_background (png_row_infop row_info, png_bytep row,
+png_color_16p trans_values, png_color_16p background,
+png_color_16p background_1, png_bytep gamma_table, png_bytep
+gamma_from_1, png_bytep gamma_to_1, png_uint_16pp gamma_16,
+png_uint_16pp gamma_16_from_1, png_uint_16pp gamma_16_to_1, int
+gamma_shift);
+
+void png_do_bgr (png_row_infop row_info, png_bytep row);
+
+void png_do_chop (png_row_infop row_info, png_bytep row);
+
+void png_do_dither (png_row_infop row_info, png_bytep row,
+png_bytep palette_lookup, png_bytep dither_lookup);
+
+void png_do_expand (png_row_infop row_info, png_bytep row,
+png_color_16p trans_value);
+
+void png_do_expand_palette (png_row_infop row_info, png_bytep
+row, png_colorp palette, png_bytep trans, int num_trans);
+
+void png_do_gamma (png_row_infop row_info, png_bytep row,
+png_bytep gamma_table, png_uint_16pp gamma_16_table, int
+gamma_shift);
+
+void png_do_gray_to_rgb (png_row_infop row_info, png_bytep
+row);
+
+void png_do_invert (png_row_infop row_info, png_bytep row);
+
+void png_do_pack (png_row_infop row_info, png_bytep row,
+png_uint_32 bit_depth);
+
+void png_do_packswap (png_row_infop row_info, png_bytep row);
+
+void png_do_read_filler (png_row_infop row_info, png_bytep row,
+png_uint_32 filler, png_uint_32 flags);
+
+void png_do_read_interlace (png_row_infop row_info, png_bytep
+row, int pass, png_uint_32 transformations);
+
+void png_do_read_invert_alpha (png_row_infop row_info,
+png_bytep row);
+
+void png_do_read_swap_alpha (png_row_infop row_info, png_bytep
+row);
+
+void png_do_read_transformations (png_structp png_ptr);
+
+int png_do_rgb_to_gray (png_row_infop row_info, png_bytep
+row);
+
+void png_do_shift (png_row_infop row_info, png_bytep row,
+png_color_8p bit_depth);
+
+void png_do_strip_filler (png_row_infop row_info, png_bytep
+row, png_uint_32 flags);
+
+void png_do_swap (png_row_infop row_info, png_bytep row);
+
+void png_do_unpack (png_row_infop row_info, png_bytep row);
+
+void png_do_unshift (png_row_infop row_info, png_bytep row,
+png_color_8p sig_bits);
+
+void png_do_write_interlace (png_row_infop row_info, png_bytep
+row, int pass);
+
+void png_do_write_invert_alpha (png_row_infop row_info,
+png_bytep row);
+
+void png_do_write_swap_alpha (png_row_infop row_info, png_bytep
+row);
+
+void png_do_write_transformations (png_structp png_ptr);
+
+void *png_far_to_near (png_structp png_ptr,png_voidp ptr,
+int check);
+
+void png_flush (png_structp png_ptr);
+
+png_int_32 png_get_int_32 (png_bytep buf);
+
+png_uint_16 png_get_uint_16 (png_bytep buf);
+
+png_uint_32 png_get_uint_32 (png_bytep buf);
+
+void png_handle_bKGD (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_cHRM (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_gAMA (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_hIST (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_IEND (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_IHDR (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_oFFs (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_pCAL (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_pHYs (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_PLTE (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_sBIT (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_sRGB (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_tEXt (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_tIME (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_tRNS (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_handle_unknown (png_structp png_ptr, png_infop
+info_ptr, png_uint_32 length);
+
+void png_handle_zTXt (png_structp png_ptr, png_infop info_ptr,
+png_uint_32 length);
+
+void png_info_destroy (png_structp png_ptr, png_infop
+info_ptr);
+
+void png_init_read_transformations (png_structp png_ptr);
+
+void png_process_IDAT_data (png_structp png_ptr, png_bytep
+buffer, png_size_t buffer_length);
+
+void png_process_some_data (png_structp png_ptr, png_infop
+info_ptr);
+
+void png_push_check_crc (png_structp png_ptr);
+
+void png_push_crc_finish (png_structp png_ptr);
+
+void png_push_crc_skip (png_structp png_ptr, png_uint_32
+length);
+
+void png_push_fill_buffer (png_structp png_ptr, png_bytep
+buffer, png_size_t length);
+
+void png_push_handle_tEXt (png_structp png_ptr, png_infop
+info_ptr, png_uint_32 length);
+
+void png_push_handle_unknown (png_structp png_ptr, png_infop
+info_ptr, png_uint_32 length);
+
+void png_push_handle_zTXt (png_structp png_ptr, png_infop
+info_ptr, png_uint_32 length);
+
+void png_push_have_end (png_structp png_ptr, png_infop
+info_ptr);
+
+void png_push_have_info (png_structp png_ptr, png_infop
+info_ptr);
+
+void png_push_have_row (png_structp png_ptr, png_bytep row);
+
+void png_push_process_row (png_structp png_ptr);
+
+void png_push_read_chunk (png_structp png_ptr, png_infop
+info_ptr);
+
+void png_push_read_end (png_structp png_ptr, png_infop
+info_ptr);
+
+void png_push_read_IDAT (png_structp png_ptr);
+
+void png_push_read_sig (png_structp png_ptr, png_infop
+info_ptr);
+
+void png_push_read_tEXt (png_structp png_ptr, png_infop
+info_ptr);
+
+void png_push_read_zTXt (png_structp png_ptr, png_infop
+info_ptr);
+
+void png_push_restore_buffer (png_structp png_ptr, png_bytep
+buffer, png_size_t buffer_length);
+
+void png_push_save_buffer (png_structp png_ptr);
+
+void png_read_data (png_structp png_ptr, png_bytep data,
+png_size_t length);
+
+void png_read_filter_row (png_structp png_ptr, png_row_infop
+row_info, png_bytep row, png_bytep prev_row, int filter);
+
+void png_read_finish_row (png_structp png_ptr);
+
+void png_read_init (png_structp png_ptr);
+
+void png_read_push_finish_row (png_structp png_ptr);
+
+void png_read_start_row (png_structp png_ptr);
+
+void png_read_transform_info (png_structp png_ptr, png_infop
+info_ptr);
+
+void png_reset_crc (png_structp png_ptr);
+
+void png_save_int_32 (png_bytep buf, png_int_32 i);
+
+void png_save_uint_16 (png_bytep buf, unsigned int i);
+
+void png_save_uint_32 (png_bytep buf, png_uint_32 i);
+
+void png_write_bKGD (png_structp png_ptr, png_color_16p values,
+int color_type);
+
+void png_write_cHRM (png_structp png_ptr, double white_x,
+double white_y, double red_x, double red_y, double green_x,
+double green_y, double blue_x, double blue_y);
+
+void png_write_data (png_structp png_ptr, png_bytep data,
+png_size_t length);
+void png_write_filtered_row (png_structp png_ptr, png_bytep
+filtered_row);
+
+void png_write_find_filter (png_structp png_ptr, png_row_infop
+row_info);
+
+void png_write_finish_row (png_structp png_ptr);
+
+void png_write_gAMA (png_structp png_ptr, double file_gamma);
+
+void png_write_hIST (png_structp png_ptr, png_uint_16p hist,
+int num_hist);
+
+void png_write_init (png_structp png_ptr);
+
+void png_write_IDAT (png_structp png_ptr, png_bytep data,
+png_size_t length);
+
+void png_write_IEND (png_structp png_ptr);
+
+void png_write_IHDR (png_structp png_ptr, png_uint_32 width,
+png_uint_32 height, int bit_depth, int color_type, int
+compression_type, int filter_type, int interlace_type);
+
+void png_write_oFFs (png_structp png_ptr, png_uint_32 x_offset,
+png_uint_32 y_offset, int unit_type);
+
+void png_write_pCAL (png_structp png_ptr, png_charp purpose,
+png_int_32 X0, png_int_32 X1, int type, int nparams, png_charp
+units, png_charpp params);
+
+void png_write_pHYs (png_structp png_ptr, png_uint_32
+x_pixels_per_unit, png_uint_32 y_pixels_per_unit, int
+unit_type);
+
+void png_write_PLTE (png_structp png_ptr, png_colorp palette,
+png_uint_32 num_pal);
+
+void png_write_sBIT (png_structp png_ptr, png_color_8p sbit,
+int color_type);
+
+void png_write_sig (png_structp png_ptr);
+
+void png_write_sRGB (png_structp png_ptr, int intent);
+
+void png_write_start_row (png_structp png_ptr);
+
+void png_write_tEXt (png_structp png_ptr, png_charp key,
+png_charp text, png_size_t text_len);
+
+void png_write_tIME (png_structp png_ptr, png_timep mod_time);
+
+void png_write_tRNS (png_structp png_ptr, png_bytep trans,
+png_color_16p values, int number, int color_type);
+
+void png_write_zTXt (png_structp png_ptr, png_charp key,
+png_charp text, png_size_t text_len, int compression);
+
+voidpf png_zalloc (voidpf png_ptr, uInt items, uInt size);
+
+void png_zfree (voidpf png_ptr, voidpf ptr);
+
+.SH DESCRIPTION
+The functions listed above are used privately by libpng
+and are not recommended for use by applications.  They
+are listed alphabetically here as an aid to libpng maintainers.
+See png.h for more information on these functions.
+
+.SH SEE ALSO
+libpng(3), png(5)
+.SH AUTHOR
+Glenn Randers-Pehrson
diff --git a/src/png/png.5 b/src/png/png.5
new file mode 100644 (file)
index 0000000..2c0fcde
--- /dev/null
@@ -0,0 +1,44 @@
+.TH PNG 5 "January 14, 1999"
+.SH NAME
+png \- Portable Network Graphics (PNG) format
+.SH DESCRIPTION
+PNG (Portable Network Graphics) is an extensible file format for the
+lossless, portable, well-compressed storage of raster images. PNG provides
+a patent-free replacement for GIF and can also replace many
+common uses of TIFF. Indexed-color, grayscale, and truecolor images are
+supported, plus an optional alpha channel. Sample depths range from
+1 to 16 bits.
+.br
+
+PNG is designed to work well in online viewing applications, such as the
+World Wide Web, so it is fully streamable with a progressive display
+option. PNG is robust, providing both full file integrity checking and
+fast, simple detection of common transmission errors. Also, PNG can store
+gamma and chromaticity data for improved color matching on heterogeneous
+platforms.
+
+.SH "SEE ALSO"
+.IR libpng(3), zlib(3), deflate(5), and zlib(5)
+.LP
+PNG specification:
+RFC 2083
+.IP
+.br
+ftp://ds.internic.net/rfc/rfc2083.txt
+.br
+or (as a W3C Recommendation) at
+.br
+http://www.w3.org/TR/REC-png.html
+.SH AUTHORS
+This man page: Glenn Randers-Pehrson
+.LP
+Portable Network Graphics (PNG) Specification Version 1.0 (October 1, 1996):
+Thomas Boutell and others (png-list@dworkin.wustl.edu).
+.LP
+
+.SH COPYRIGHT NOTICE
+The PNG specification is copyright (c) 1996 Massachussets Institute of
+Technology.  See the specification for conditions of use and distribution.
+.LP
+.\" end of man page
+
diff --git a/src/png/png.c b/src/png/png.c
new file mode 100644 (file)
index 0000000..e422332
--- /dev/null
@@ -0,0 +1,359 @@
+
+/* png.c - location for general purpose libpng functions
+ *
+ * libpng version 1.0.3 - January 14, 1999
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ * 
+ */
+
+#define PNG_INTERNAL
+#define PNG_NO_EXTERN
+#include "png.h"
+
+/* Version information for C files.  This had better match the version
+ * string defined in png.h.
+ */
+
+char png_libpng_ver[12] = "1.0.3";
+
+/* Place to hold the signature string for a PNG file. */
+png_byte FARDATA png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+
+/* Constant strings for known chunk types.  If you need to add a chunk,
+ * add a string holding the name here.  If you want to make the code
+ * portable to EBCDIC machines, use ASCII numbers, not characters.
+ */
+png_byte FARDATA png_IHDR[5] = { 73,  72,  68,  82, '\0'};
+png_byte FARDATA png_IDAT[5] = { 73,  68,  65,  84, '\0'};
+png_byte FARDATA png_IEND[5] = { 73,  69,  78,  68, '\0'};
+png_byte FARDATA png_PLTE[5] = { 80,  76,  84,  69, '\0'};
+png_byte FARDATA png_bKGD[5] = { 98,  75,  71,  68, '\0'};
+png_byte FARDATA png_cHRM[5] = { 99,  72,  82,  77, '\0'};
+png_byte FARDATA png_gAMA[5] = {103,  65,  77,  65, '\0'};
+png_byte FARDATA png_hIST[5] = {104,  73,  83,  84, '\0'};
+png_byte FARDATA png_oFFs[5] = {111,  70,  70, 115, '\0'};
+png_byte FARDATA png_pCAL[5] = {112,  67,  65,  76, '\0'};
+png_byte FARDATA png_pHYs[5] = {112,  72,  89, 115, '\0'};
+png_byte FARDATA png_sBIT[5] = {115,  66,  73,  84, '\0'};
+png_byte FARDATA png_sRGB[5] = {115,  82,  71,  66, '\0'};
+png_byte FARDATA png_tEXt[5] = {116,  69,  88, 116, '\0'};
+png_byte FARDATA png_tIME[5] = {116,  73,  77,  69, '\0'};
+png_byte FARDATA png_tRNS[5] = {116,  82,  78,  83, '\0'};
+png_byte FARDATA png_zTXt[5] = {122,  84,  88, 116, '\0'};
+
+/* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+/* start of interlace block */
+int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
+
+/* offset to next interlace block */
+int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
+
+/* start of interlace block in the y direction */
+int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
+
+/* offset to next interlace block in the y direction */
+int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
+
+/* Width of interlace block.  This is not currently used - if you need
+ * it, uncomment it here and in png.h
+int FARDATA png_pass_width[] = {8, 4, 4, 2, 2, 1, 1};
+*/
+
+/* Height of interlace block.  This is not currently used - if you need
+ * it, uncomment it here and in png.h
+int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1};
+*/
+
+/* Mask to determine which pixels are valid in a pass */
+int FARDATA png_pass_mask[] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff};
+
+/* Mask to determine which pixels to overwrite while displaying */
+int FARDATA png_pass_dsp_mask[] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff};
+
+
+/* Tells libpng that we have already handled the first "num_bytes" bytes
+ * of the PNG file signature.  If the PNG data is embedded into another
+ * stream we can set num_bytes = 8 so that libpng will not attempt to read
+ * or write any of the magic bytes before it starts on the IHDR.
+ */
+void
+png_set_sig_bytes(png_structp png_ptr, int num_bytes)
+{
+   png_debug(1, "in png_set_sig_bytes\n");
+   if (num_bytes > 8)
+      png_error(png_ptr, "Too many bytes for PNG signature.");
+
+   png_ptr->sig_bytes = num_bytes < 0 ? 0 : num_bytes;
+}
+
+/* Checks whether the supplied bytes match the PNG signature.  We allow
+ * checking less than the full 8-byte signature so that those apps that
+ * already read the first few bytes of a file to determine the file type
+ * can simply check the remaining bytes for extra assurance.  Returns
+ * an integer less than, equal to, or greater than zero if sig is found,
+ * respectively, to be less than, to match, or be greater than the correct
+ * PNG signature (this is the same behaviour as strcmp, memcmp, etc).
+ */
+int
+png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check)
+{
+   if (num_to_check > 8)
+      num_to_check = 8;
+   else if (num_to_check < 1)
+      return (0);
+
+   if (start > 7)
+      return (0);
+
+   if (start + num_to_check > 8)
+      num_to_check = 8 - start;
+
+   return ((int)(png_memcmp(&sig[start], &png_sig[start], num_to_check)));
+}
+
+/* (Obsolete) function to check signature bytes.  It does not allow one
+ * to check a partial signature.  This function might be removed in the
+ * future - use png_sig_cmp().  Returns true (nonzero) if the file is a PNG.
+ */
+int
+png_check_sig(png_bytep sig, int num)
+{
+  return ((int)!png_sig_cmp(sig, (png_size_t)0, (png_size_t)num));
+}
+
+/* Function to allocate memory for zlib. */
+voidpf
+png_zalloc(voidpf png_ptr, uInt items, uInt size)
+{
+   png_uint_32 num_bytes = (png_uint_32)items * size;
+   png_voidp ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes);
+
+   if (num_bytes > (png_uint_32)0x8000L)
+   {
+      png_memset(ptr, 0, (png_size_t)0x8000L);
+      png_memset((png_bytep)ptr + (png_size_t)0x8000L, 0,
+         (png_size_t)(num_bytes - (png_uint_32)0x8000L));
+   }
+   else
+   {
+      png_memset(ptr, 0, (png_size_t)num_bytes);
+   }
+   return ((voidpf)ptr);
+}
+
+/* function to free memory for zlib */
+void
+png_zfree(voidpf png_ptr, voidpf ptr)
+{
+   png_free((png_structp)png_ptr, (png_voidp)ptr);
+}
+
+/* Reset the CRC variable to 32 bits of 1's.  Care must be taken
+ * in case CRC is > 32 bits to leave the top bits 0.
+ */
+void
+png_reset_crc(png_structp png_ptr)
+{
+   png_ptr->crc = crc32(0, Z_NULL, 0);
+}
+
+/* Calculate the CRC over a section of data.  We can only pass as
+ * much data to this routine as the largest single buffer size.  We
+ * also check that this data will actually be used before going to the
+ * trouble of calculating it.
+ */
+void
+png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length)
+{
+   int need_crc = 1;
+
+   if (png_ptr->chunk_name[0] & 0x20)                     /* ancillary */
+   {
+      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
+          (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
+         need_crc = 0;
+   }
+   else                                                    /* critical */
+   {
+      if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE)
+         need_crc = 0;
+   }
+
+   if (need_crc)
+      png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length);
+}
+
+/* Allocate the memory for an info_struct for the application.  We don't
+ * really need the png_ptr, but it could potentially be useful in the
+ * future.  This should be used in favour of malloc(sizeof(png_info))
+ * and png_info_init() so that applications that want to use a shared
+ * libpng don't have to be recompiled if png_info changes size.
+ */
+png_infop
+png_create_info_struct(png_structp png_ptr)
+{
+   png_infop info_ptr;
+
+   png_debug(1, "in png_create_info_struct\n");
+   if(png_ptr == NULL) return (NULL);
+#ifdef PNG_USER_MEM_SUPPORTED
+   if ((info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO,
+      png_ptr->malloc_fn)) != NULL)
+#else
+   if ((info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO)) != NULL)
+#endif
+   {
+      png_info_init(info_ptr);
+   }
+
+   return (info_ptr);
+}
+
+/* This function frees the memory associated with a single info struct.
+ * Normally, one would use either png_destroy_read_struct() or
+ * png_destroy_write_struct() to free an info struct, but this may be
+ * useful for some applications.
+ */
+void
+png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr)
+{
+   png_infop info_ptr = NULL;
+
+   png_debug(1, "in png_destroy_info_struct\n");
+   if (info_ptr_ptr != NULL)
+      info_ptr = *info_ptr_ptr;
+
+   if (info_ptr != NULL)
+   {
+      png_info_destroy(png_ptr, info_ptr);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn);
+#else
+      png_destroy_struct((png_voidp)info_ptr);
+#endif
+      *info_ptr_ptr = (png_infop)NULL;
+   }
+}
+
+/* Initialize the info structure.  This is now an internal function (0.89)
+ * and applications using it are urged to use png_create_info_struct()
+ * instead.
+ */
+void
+png_info_init(png_infop info_ptr)
+{
+   png_debug(1, "in png_info_init\n");
+   /* set everything to 0 */
+   png_memset(info_ptr, 0, sizeof (png_info));
+}
+
+/* This is an internal routine to free any memory that the info struct is
+ * pointing to before re-using it or freeing the struct itself.  Recall
+ * that png_free() checks for NULL pointers for us.
+ */
+void
+png_info_destroy(png_structp png_ptr, png_infop info_ptr)
+{
+#if defined(PNG_READ_tEXt_SUPPORTED) || defined(PNG_READ_zTXt_SUPPORTED)
+   png_debug(1, "in png_info_destroy\n");
+   if (info_ptr->text != NULL)
+   {
+      int i;
+      for (i = 0; i < info_ptr->num_text; i++)
+      {
+         png_free(png_ptr, info_ptr->text[i].key);
+      }
+      png_free(png_ptr, info_ptr->text);
+   }
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+   png_free(png_ptr, info_ptr->pcal_purpose);
+   png_free(png_ptr, info_ptr->pcal_units);
+   if (info_ptr->pcal_params != NULL)
+   {
+      int i;
+      for (i = 0; i < (int)info_ptr->pcal_nparams; i++)
+      {
+         png_free(png_ptr, info_ptr->pcal_params[i]);
+      }
+      png_free(png_ptr, info_ptr->pcal_params);
+   }
+#endif
+
+   png_info_init(info_ptr);
+}
+
+/* This function returns a pointer to the io_ptr associated with the user
+ * functions.  The application should free any memory associated with this
+ * pointer before png_write_destroy() or png_read_destroy() are called.
+ */
+png_voidp
+png_get_io_ptr(png_structp png_ptr)
+{
+   return (png_ptr->io_ptr);
+}
+
+#if !defined(PNG_NO_STDIO)
+/* Initialize the default input/output functions for the PNG file.  If you
+ * use your own read or write routines, you can call either png_set_read_fn()
+ * or png_set_write_fn() instead of png_init_io().
+ */
+void
+png_init_io(png_structp png_ptr, FILE *fp)
+{
+   png_debug(1, "in png_init_io\n");
+   png_ptr->io_ptr = (png_voidp)fp;
+}
+#endif
+
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+/* Convert the supplied time into an RFC 1123 string suitable for use in
+ * a "Creation Time" or other text-based time string.
+ */
+png_charp
+png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime)
+{
+   static PNG_CONST char short_months[12][4] =
+        {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+   if (png_ptr->time_buffer == NULL)
+   {
+      png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29*
+         sizeof(char)));
+   }
+
+#ifdef USE_FAR_KEYWORD
+   {
+      char near_time_buf[29];
+      sprintf(near_time_buf, "%d %s %d %02d:%02d:%02d +0000",
+               ptime->day % 32, short_months[(ptime->month - 1) % 12],
+               ptime->year, ptime->hour % 24, ptime->minute % 60,
+               ptime->second % 61);
+      png_memcpy(png_ptr->time_buffer, near_time_buf,
+      29*sizeof(char));
+   }
+#else
+   sprintf(png_ptr->time_buffer, "%d %s %d %02d:%02d:%02d +0000",
+               ptime->day % 32, short_months[(ptime->month - 1) % 12],
+               ptime->year, ptime->hour % 24, ptime->minute % 60,
+               ptime->second % 61);
+#endif
+   return ((png_charp)png_ptr->time_buffer);
+}
+#endif /* PNG_TIME_RFC1123_SUPPORTED */
+
+png_charp
+png_get_copyright(png_structp png_ptr)
+{
+   if(png_ptr == NULL)
+     /* silence compiler warning about unused png_ptr */ ;
+   return("\n libpng version 1.0.3 - January 14, 1999\n\
+   Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.\n\
+   Copyright (c) 1996, 1997 Andreas Dilger\n\
+   Copyright (c) 1998, 1999, Glenn Randers-Pehrson\n");
+}
diff --git a/src/png/png.h b/src/png/png.h
new file mode 100644 (file)
index 0000000..4314ac7
--- /dev/null
@@ -0,0 +1,2227 @@
+
+/* png.h - header file for PNG reference library
+ *
+ * libpng version 1.0.3 - January 14, 1999
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ *
+ * Y2K compliance in libpng:
+ * =========================
+ *
+ *    January 13, 1999
+ *
+ *    Since the PNG Development group is an ad-hoc body, we can't make
+ *    an official declaration.
+ *
+ *    This is your unofficial assurance that libpng from version 0.81 and
+ *    upward are Y2K compliant.  It is my belief that earlier versions were
+ *    also Y2K compliant.
+ *
+ *    Libpng only has three year fields.  One is a 2-byte unsigned integer
+ *    that will hold years up to 65535.  The other two hold the date in text
+ *    format, and will hold years up to 9999.
+ *
+ *    The integer is
+ *        "png_uint_16 year" in png_time_struct.
+ *
+ *    The strings are
+ *        "png_charp time_buffer" in png_struct and
+ *        "near_time_buffer", which is a local character string in png.c.
+ *
+ *    There are seven time-related functions:
+ *        png.c: png_convert_to_rfc_1123() in png.c
+ *          (formerly png_convert_to_rfc_1152() in error)
+ *        png_convert_from_struct_tm() in pngwrite.c, called in pngwrite.c
+ *        png_convert_from_time_t() in pngwrite.c
+ *        png_get_tIME() in pngget.c
+ *        png_handle_tIME() in pngrutil.c, called in pngread.c
+ *        png_set_tIME() in pngset.c
+ *        png_write_tIME() in pngwutil.c, called in pngwrite.c
+ *
+ *    All handle dates properly in a Y2K environment.  The
+ *    png_convert_from_time_t() function calls gmtime() to convert from system
+ *    clock time, which returns (year - 1900), which we properly convert to
+ *    the full 4-digit year.  There is a possibility that applications using
+ *    libpng are not passing 4-digit years into the png_convert_to_rfc_1123()
+ *    function, or incorrectly passing only a 2-digit year instead of
+ *    "year - 1900" into the png_convert_from_struct_tm() function, but this
+ *    is not under our control.  The libpng documentation has always stated
+ *    that it works with 4-digit years, and the APIs have been documented as
+ *    such.
+ *
+ *    The tIME chunk itself is also Y2K compliant.  It uses a 2-byte unsigned
+ *    integer to hold the year, and can hold years as large as 65535.
+ *
+ *
+ *       Glenn Randers-Pehrson
+ *       libpng maintainer
+ *       PNG Development Group
+ *
+ * Note about libpng version numbers:
+ *
+ *    Due to various miscommunications, unforeseen code incompatibilities
+ *    and occasional factors outside the authors' control, version numbering
+ *    on the library has not always been consistent and straightforward.
+ *    The following table summarizes matters since version 0.89c, which was
+ *    the first widely used release:
+ *
+ *    source                    png.h    png.h   shared-lib
+ *    version                   string     int   version
+ *    -------                   ------   -----  ----------
+ *    0.89c ("1.0 beta 3")      0.89        89  1.0.89
+ *    0.90  ("1.0 beta 4")      0.90        90  0.90  [should have been 2.0.90]
+ *    0.95  ("1.0 beta 5")      0.95        95  0.95  [should have been 2.0.95]
+ *    0.96  ("1.0 beta 6")      0.96        96  0.96  [should have been 2.0.96]
+ *    0.97b ("1.00.97 beta 7")  1.00.97     97  1.0.1 [should have been 2.0.97]
+ *    0.97c                     0.97        97  2.0.97
+ *    0.98                      0.98        98  2.0.98
+ *    0.99                      0.99        98  2.0.99
+ *    0.99a-m                   0.99        99  2.0.99
+ *    1.00                      1.00       100  2.1.0 [int should be 10000]
+ *    1.0.0                     1.0.0      100  2.1.0 [int should be 10000]
+ *    1.0.1                     1.0.1    10001  2.1.0
+ *    1.0.1a-e                  1.0.1a-e 10002  2.1.0.1a-e
+ *    1.0.2                     1.0.2    10002  2.1.0.2
+ *    1.0.2a-c                  1.0.2a   10003  2.1.0.2a-c
+ *    1.0.3                     1.0.3    10003  2.1.0.3
+ *
+ *    Henceforth the source version will match the shared-library minor
+ *    and patch numbers; the shared-library major version number will be
+ *    used for changes in backward compatibility, as it is intended.  The
+ *    PNG_PNGLIB_VER macro, which is not used within libpng but is available
+ *    for applications, is an unsigned integer of the form xyyzz corresponding
+ *    to the source version x.y.z (leading zeros in y and z).
+ *
+ * See libpng.txt or libpng.3 for more information.  The PNG specification
+ * is available as RFC 2083 <ftp://ftp.uu.net/graphics/png/documents/>
+ * and as a W3C Recommendation <http://www.w3.org/TR/REC.png.html>
+ *
+ * Contributing Authors:
+ *    John Bowler
+ *    Kevin Bracey
+ *    Sam Bushell
+ *    Andreas Dilger
+ *    Magnus Holmgren
+ *    Tom Lane
+ *    Dave Martindale
+ *    Glenn Randers-Pehrson
+ *    Greg Roelofs
+ *    Guy Eric Schalnat
+ *    Paul Schmidt
+ *    Tom Tanner
+ *    Willem van Schaik
+ *    Tim Wegner
+ *
+ * The contributing authors would like to thank all those who helped
+ * with testing, bug fixes, and patience.  This wouldn't have been
+ * possible without all of you.
+ *
+ * Thanks to Frank J. T. Wojcik for helping with the documentation.
+ *
+ * COPYRIGHT NOTICE:
+ *
+ * The PNG Reference Library is supplied "AS IS".  The Contributing Authors
+ * and Group 42, Inc. disclaim all warranties, expressed or implied,
+ * including, without limitation, the warranties of merchantability and of
+ * fitness for any purpose.  The Contributing Authors and Group 42, Inc.
+ * assume no liability for direct, indirect, incidental, special, exemplary,
+ * or consequential damages, which may result from the use of the PNG
+ * Reference Library, even if advised of the possibility of such damage.
+ *
+ * Permission is hereby granted to use, copy, modify, and distribute this
+ * source code, or portions hereof, for any purpose, without fee, subject
+ * to the following restrictions:
+ * 1. The origin of this source code must not be misrepresented.
+ * 2. Altered versions must be plainly marked as such and must not be
+ *    misrepresented as being the original source.
+ * 3. This Copyright notice may not be removed or altered from any source or
+ *    altered source distribution.
+ *
+ * The Contributing Authors and Group 42, Inc. specifically permit, without
+ * fee, and encourage the use of this source code as a component to
+ * supporting the PNG file format in commercial products.  If you use this
+ * source code in a product, acknowledgment is not required but would be
+ * appreciated.
+ */
+
+
+#ifndef _PNG_H
+#define _PNG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* This is not the place to learn how to use libpng.  The file libpng.txt
+ * describes how to use libpng, and the file example.c summarizes it
+ * with some code on which to build.  This file is useful for looking
+ * at the actual function definitions and structure components.
+ */
+
+/* include the compression library's header */
+#include "zlib.h"
+
+/* include all user configurable info */
+#include "pngconf.h"
+
+/* This file is arranged in several sections.  The first section contains
+ * structure and type definitions.  The second section contains the external
+ * library functions, while the third has the internal library functions,
+ * which applications aren't expected to use directly.
+ */
+
+/* Version information for png.h - this should match the version in png.c */
+#define PNG_LIBPNG_VER_STRING "1.0.3"
+
+/* Careful here.  At one time, Guy wanted to use 082, but that would be octal.
+ * We must not include leading zeros.
+ * Versions 0.7 through 1.0.0 were in the range 0 to 100 here (only
+ * version 1.0.0 was mis-numbered 100 instead of 10000).  From
+ * version 1.0.1 it's    xxyyzz, where x=major, y=minor, z=bugfix */
+#define PNG_LIBPNG_VER    10003  /* 1.0.3 */
+
+/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */
+#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN)
+/* Version information for C files, stored in png.c.  This had better match
+ * the version above.
+ */
+extern char png_libpng_ver[12];   /* need room for 99.99.99aa */
+
+/* Structures to facilitate easy interlacing.  See png.c for more details */
+extern int FARDATA png_pass_start[7];
+extern int FARDATA png_pass_inc[7];
+extern int FARDATA png_pass_ystart[7];
+extern int FARDATA png_pass_yinc[7];
+extern int FARDATA png_pass_mask[7];
+extern int FARDATA png_pass_dsp_mask[7];
+/* These aren't currently used.  If you need them, see png.c for more details
+extern int FARDATA png_pass_width[7];
+extern int FARDATA png_pass_height[7];
+*/
+
+#endif /* PNG_NO_EXTERN */
+
+/* Three color definitions.  The order of the red, green, and blue, (and the
+ * exact size) is not important, although the size of the fields need to
+ * be png_byte or png_uint_16 (as defined below).
+ */
+typedef struct png_color_struct
+{
+   png_byte red;
+   png_byte green;
+   png_byte blue;
+} png_color;
+typedef png_color FAR * png_colorp;
+typedef png_color FAR * FAR * png_colorpp;
+
+typedef struct png_color_16_struct
+{
+   png_byte index;    /* used for palette files */
+   png_uint_16 red;   /* for use in red green blue files */
+   png_uint_16 green;
+   png_uint_16 blue;
+   png_uint_16 gray;  /* for use in grayscale files */
+} png_color_16;
+typedef png_color_16 FAR * png_color_16p;
+typedef png_color_16 FAR * FAR * png_color_16pp;
+
+typedef struct png_color_8_struct
+{
+   png_byte red;   /* for use in red green blue files */
+   png_byte green;
+   png_byte blue;
+   png_byte gray;  /* for use in grayscale files */
+   png_byte alpha; /* for alpha channel files */
+} png_color_8;
+typedef png_color_8 FAR * png_color_8p;
+typedef png_color_8 FAR * FAR * png_color_8pp;
+
+/* png_text holds the text in a PNG file, and whether they are compressed
+   in the PNG file or not.  The "text" field points to a regular C string. */
+typedef struct png_text_struct
+{
+   int compression;        /* compression value, see PNG_TEXT_COMPRESSION_ */
+   png_charp key;          /* keyword, 1-79 character description of "text" */
+   png_charp text;         /* comment, may be an empty string (ie "") */
+   png_size_t text_length; /* length of "text" field */
+} png_text;
+typedef png_text FAR * png_textp;
+typedef png_text FAR * FAR * png_textpp;
+
+/* Supported compression types for text in PNG files (tEXt, and zTXt).
+ * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */
+#define PNG_TEXT_COMPRESSION_NONE_WR -3
+#define PNG_TEXT_COMPRESSION_zTXt_WR -2
+#define PNG_TEXT_COMPRESSION_NONE    -1
+#define PNG_TEXT_COMPRESSION_zTXt     0
+#define PNG_TEXT_COMPRESSION_LAST     1  /* Not a valid value */
+
+/* png_time is a way to hold the time in an machine independent way.
+ * Two conversions are provided, both from time_t and struct tm.  There
+ * is no portable way to convert to either of these structures, as far
+ * as I know.  If you know of a portable way, send it to me.  As a side
+ * note - PNG is Year 2000 compliant!
+ */
+typedef struct png_time_struct
+{
+   png_uint_16 year; /* full year, as in, 1995 */
+   png_byte month;   /* month of year, 1 - 12 */
+   png_byte day;     /* day of month, 1 - 31 */
+   png_byte hour;    /* hour of day, 0 - 23 */
+   png_byte minute;  /* minute of hour, 0 - 59 */
+   png_byte second;  /* second of minute, 0 - 60 (for leap seconds) */
+} png_time;
+typedef png_time FAR * png_timep;
+typedef png_time FAR * FAR * png_timepp;
+
+/* png_info is a structure that holds the information in a PNG file so
+ * that the application can find out the characteristics of the image.
+ * If you are reading the file, this structure will tell you what is
+ * in the PNG file.  If you are writing the file, fill in the information
+ * you want to put into the PNG file, then call png_write_info().
+ * The names chosen should be very close to the PNG specification, so
+ * consult that document for information about the meaning of each field.
+ *
+ * With libpng < 0.95, it was only possible to directly set and read the
+ * the values in the png_info_struct, which meant that the contents and
+ * order of the values had to remain fixed.  With libpng 0.95 and later,
+ * however, there are now functions that abstract the contents of
+ * png_info_struct from the application, so this makes it easier to use
+ * libpng with dynamic libraries, and even makes it possible to use
+ * libraries that don't have all of the libpng ancillary chunk-handing
+ * functionality.
+ *
+ * In any case, the order of the parameters in png_info_struct should NOT
+ * be changed for as long as possible to keep compatibility with applications
+ * that use the old direct-access method with png_info_struct.
+ */
+typedef struct png_info_struct
+{
+   /* the following are necessary for every PNG file */
+   png_uint_32 width;       /* width of image in pixels (from IHDR) */
+   png_uint_32 height;      /* height of image in pixels (from IHDR) */
+   png_uint_32 valid;       /* valid chunk data (see PNG_INFO_ below) */
+   png_uint_32 rowbytes;    /* bytes needed to hold an untransformed row */
+   png_colorp palette;      /* array of color values (valid & PNG_INFO_PLTE) */
+   png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
+   png_uint_16 num_trans;   /* number of transparent palette color (tRNS) */
+   png_byte bit_depth;      /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */
+   png_byte color_type;     /* see PNG_COLOR_TYPE_ below (from IHDR) */
+   png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */
+   png_byte filter_type;    /* must be PNG_FILTER_TYPE_BASE (from IHDR) */
+   png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
+
+   /* The following is informational only on read, and not used on writes. */
+   png_byte channels;       /* number of data channels per pixel (1, 3, 4)*/
+   png_byte pixel_depth;    /* number of bits per pixel */
+   png_byte spare_byte;     /* to align the data, and for future use */
+   png_byte signature[8];   /* magic bytes read by libpng from start of file */
+
+   /* The rest of the data is optional.  If you are reading, check the
+    * valid field to see if the information in these are valid.  If you
+    * are writing, set the valid field to those chunks you want written,
+    * and initialize the appropriate fields below.
+    */
+
+#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_WRITE_gAMA_SUPPORTED) || \
+    defined(PNG_READ_GAMMA_SUPPORTED)
+   /* The gAMA chunk describes the gamma characteristics of the system
+    * on which the image was created, normally in the range [1.0, 2.5].
+    * Data is valid if (valid & PNG_INFO_gAMA) is non-zero.
+    */
+   float gamma; /* gamma value of image, if (valid & PNG_INFO_gAMA) */
+#endif /* PNG_READ_gAMA_SUPPORTED || PNG_WRITE_gAMA_SUPPORTED */
+
+#if defined(PNG_READ_sRGB_SUPPORTED) || defined(PNG_WRITE_sRGB_SUPPORTED)
+    /* GR-P, 0.96a */
+    /* Data valid if (valid & PNG_INFO_sRGB) non-zero. */
+   png_byte srgb_intent; /* sRGB rendering intent [0, 1, 2, or 3] */
+#endif /* PNG_READ_sRGB_SUPPORTED || PNG_WRITE_sRGB_SUPPORTED */
+
+#if defined(PNG_READ_tEXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \
+    defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
+   /* The tEXt and zTXt chunks contain human-readable textual data in
+    * uncompressed and compressed forms, respectively.  The data in "text"
+    * is an array of pointers to uncompressed, null-terminated C strings.
+    * Each chunk has a keyword that describes the textual data contained
+    * in that chunk.  Keywords are not required to be unique, and the text
+    * string may be empty.  Any number of text chunks may be in an image.
+    */
+   int num_text; /* number of comments read/to write */
+   int max_text; /* current size of text array */
+   png_textp text; /* array of comments read/to write */
+#endif /* PNG_READ_OR_WRITE_tEXt_OR_zTXt_SUPPORTED */
+#if defined(PNG_READ_tIME_SUPPORTED) || defined(PNG_WRITE_tIME_SUPPORTED)
+   /* The tIME chunk holds the last time the displayed image data was
+    * modified.  See the png_time struct for the contents of this struct.
+    */
+   png_time mod_time;
+#endif /* PNG_READ_tIME_SUPPORTED || PNG_WRITE_tIME_SUPPORTED */
+#if defined(PNG_READ_sBIT_SUPPORTED) || defined(PNG_WRITE_sBIT_SUPPORTED)
+   /* The sBIT chunk specifies the number of significant high-order bits
+    * in the pixel data.  Values are in the range [1, bit_depth], and are
+    * only specified for the channels in the pixel data.  The contents of
+    * the low-order bits is not specified.  Data is valid if
+    * (valid & PNG_INFO_sBIT) is non-zero.
+    */
+   png_color_8 sig_bit; /* significant bits in color channels */
+#endif /* PNG_READ_sBIT_SUPPORTED || PNG_WRITE_sBIT_SUPPORTED */
+#if defined(PNG_READ_tRNS_SUPPORTED) || defined(PNG_WRITE_tRNS_SUPPORTED) || \
+    defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* The tRNS chunk supplies transparency data for paletted images and
+    * other image types that don't need a full alpha channel.  There are
+    * "num_trans" transparency values for a paletted image, stored in the
+    * same order as the palette colors, starting from index 0.  Values
+    * for the data are in the range [0, 255], ranging from fully transparent
+    * to fully opaque, respectively.  For non-paletted images, there is a
+    * single color specified that should be treated as fully transparent.
+    * Data is valid if (valid & PNG_INFO_tRNS) is non-zero.
+    */
+   png_bytep trans; /* transparent values for paletted image */
+   png_color_16 trans_values; /* transparent color for non-palette image */
+#endif /* PNG_READ_tRNS_SUPPORTED || PNG_WRITE_tRNS_SUPPORTED */
+#if defined(PNG_READ_bKGD_SUPPORTED) || defined(PNG_WRITE_bKGD_SUPPORTED) || \
+    defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* The bKGD chunk gives the suggested image background color if the
+    * display program does not have its own background color and the image
+    * is needs to composited onto a background before display.  The colors
+    * in "background" are normally in the same color space/depth as the
+    * pixel data.  Data is valid if (valid & PNG_INFO_bKGD) is non-zero.
+    */
+   png_color_16 background;
+#endif /* PNG_READ_bKGD_SUPPORTED || PNG_WRITE_bKGD_SUPPORTED */
+#if defined(PNG_READ_oFFs_SUPPORTED) || defined(PNG_WRITE_oFFs_SUPPORTED)
+   /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards
+    * and downwards from the top-left corner of the display, page, or other
+    * application-specific co-ordinate space.  See the PNG_OFFSET_ defines
+    * below for the unit types.  Valid if (valid & PNG_INFO_oFFs) non-zero.
+    */
+   png_uint_32 x_offset; /* x offset on page */
+   png_uint_32 y_offset; /* y offset on page */
+   png_byte offset_unit_type; /* offset units type */
+#endif /* PNG_READ_oFFs_SUPPORTED || PNG_WRITE_oFFs_SUPPORTED */
+#if defined(PNG_READ_pHYs_SUPPORTED) || defined(PNG_WRITE_pHYs_SUPPORTED)
+   /* The pHYs chunk gives the physical pixel density of the image for
+    * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_
+    * defines below).  Data is valid if (valid & PNG_INFO_pHYs) is non-zero.
+    */
+   png_uint_32 x_pixels_per_unit; /* horizontal pixel density */
+   png_uint_32 y_pixels_per_unit; /* vertical pixel density */
+   png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */
+#endif /* PNG_READ_pHYs_SUPPORTED || PNG_WRITE_pHYs_SUPPORTED */
+#if defined(PNG_READ_hIST_SUPPORTED) || defined(PNG_WRITE_hIST_SUPPORTED)
+   /* The hIST chunk contains the relative frequency or importance of the
+    * various palette entries, so that a viewer can intelligently select a
+    * reduced-color palette, if required.  Data is an array of "num_palette"
+    * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST)
+    * is non-zero.
+    */
+   png_uint_16p hist;
+#endif /* PNG_READ_hIST_SUPPORTED || PNG_WRITE_hIST_SUPPORTED */
+#if defined(PNG_READ_cHRM_SUPPORTED) || defined(PNG_WRITE_cHRM_SUPPORTED)
+   /* The cHRM chunk describes the CIE color characteristics of the monitor
+    * on which the PNG was created.  This data allows the viewer to do gamut
+    * mapping of the input image to ensure that the viewer sees the same
+    * colors in the image as the creator.  Values are in the range
+    * [0.0, 0.8].  Data valid if (valid & PNG_INFO_cHRM) non-zero.
+    */
+   float x_white;
+   float y_white;
+   float x_red;
+   float y_red;
+   float x_green;
+   float y_green;
+   float x_blue;
+   float y_blue;
+#endif /* PNG_READ_cHRM_SUPPORTED || PNG_WRITE_cHRM_SUPPORTED */
+#if defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED)
+   /* The pCAL chunk describes a transformation between the stored pixel
+    * values and original physcical data values used to create the image.
+    * The integer range [0, 2^bit_depth - 1] maps to the floating-point
+    * range given by [pcal_X0, pcal_X1], and are further transformed by a
+    * (possibly non-linear) transformation function given by "pcal_type"
+    * and "pcal_params" into "pcal_units".  Please see the PNG_EQUATION_
+    * defines below, and the PNG-Group's Scientific Visualization extension
+    * chunks document png-scivis-19970203 for a complete description of the
+    * transformations and how they should be implemented, as well as the
+    * png-extensions document for a description of the ASCII parameter
+    * strings.  Data values are valid if (valid & PNG_INFO_pCAL) non-zero.
+    */
+   png_charp pcal_purpose;  /* pCAL chunk description string */
+   png_int_32 pcal_X0;      /* minimum value */
+   png_int_32 pcal_X1;      /* maximum value */
+   png_charp pcal_units;    /* Latin-1 string giving physical units */
+   png_charpp pcal_params;  /* ASCII strings containing parameter values */
+   png_byte pcal_type;      /* equation type (see PNG_EQUATION_ below) */
+   png_byte pcal_nparams;   /* number of parameters given in pcal_params */
+#endif /* PNG_READ_pCAL_SUPPORTED || PNG_WRITE_pCAL_SUPPORTED */
+} png_info;
+typedef png_info FAR * png_infop;
+typedef png_info FAR * FAR * png_infopp;
+
+/* These describe the color_type field in png_info. */
+/* color type masks */
+#define PNG_COLOR_MASK_PALETTE    1
+#define PNG_COLOR_MASK_COLOR      2
+#define PNG_COLOR_MASK_ALPHA      4
+
+/* color types.  Note that not all combinations are legal */
+#define PNG_COLOR_TYPE_GRAY 0
+#define PNG_COLOR_TYPE_PALETTE  (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
+#define PNG_COLOR_TYPE_RGB        (PNG_COLOR_MASK_COLOR)
+#define PNG_COLOR_TYPE_RGB_ALPHA  (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
+#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA)
+
+/* This is for compression type. PNG 1.0 only defines the single type. */
+#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */
+#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE
+
+/* This is for filter type. PNG 1.0 only defines the single type. */
+#define PNG_FILTER_TYPE_BASE      0 /* Single row per-byte filtering */
+#define PNG_FILTER_TYPE_DEFAULT   PNG_FILTER_TYPE_BASE
+
+/* These are for the interlacing type.  These values should NOT be changed. */
+#define PNG_INTERLACE_NONE        0 /* Non-interlaced image */
+#define PNG_INTERLACE_ADAM7       1 /* Adam7 interlacing */
+#define PNG_INTERLACE_LAST        2 /* Not a valid value */
+
+/* These are for the oFFs chunk.  These values should NOT be changed. */
+#define PNG_OFFSET_PIXEL          0 /* Offset in pixels */
+#define PNG_OFFSET_MICROMETER     1 /* Offset in micrometers (1/10^6 meter) */
+#define PNG_OFFSET_LAST           2 /* Not a valid value */
+
+/* These are for the pCAL chunk.  These values should NOT be changed. */
+#define PNG_EQUATION_LINEAR       0 /* Linear transformation */
+#define PNG_EQUATION_BASE_E       1 /* Exponential base e transform */
+#define PNG_EQUATION_ARBITRARY    2 /* Arbitrary base exponential transform */
+#define PNG_EQUATION_HYPERBOLIC   3 /* Hyperbolic sine transformation */
+#define PNG_EQUATION_LAST         4 /* Not a valid value */
+
+/* These are for the pHYs chunk.  These values should NOT be changed. */
+#define PNG_RESOLUTION_UNKNOWN    0 /* pixels/unknown unit (aspect ratio) */
+#define PNG_RESOLUTION_METER      1 /* pixels/meter */
+#define PNG_RESOLUTION_LAST       2 /* Not a valid value */
+
+/* These are for the sRGB chunk.  These values should NOT be changed. */
+#define PNG_sRGB_INTENT_SATURATION 0
+#define PNG_sRGB_INTENT_PERCEPTUAL 1
+#define PNG_sRGB_INTENT_ABSOLUTE   2
+#define PNG_sRGB_INTENT_RELATIVE   3
+#define PNG_sRGB_INTENT_LAST       4 /* Not a valid value */
+
+
+
+/* These determine if an ancillary chunk's data has been successfully read
+ * from the PNG header, or if the application has filled in the corresponding
+ * data in the info_struct to be written into the output file.  The values
+ * of the PNG_INFO_<chunk> defines should NOT be changed.
+ */
+#define PNG_INFO_gAMA 0x0001
+#define PNG_INFO_sBIT 0x0002
+#define PNG_INFO_cHRM 0x0004
+#define PNG_INFO_PLTE 0x0008
+#define PNG_INFO_tRNS 0x0010
+#define PNG_INFO_bKGD 0x0020
+#define PNG_INFO_hIST 0x0040
+#define PNG_INFO_pHYs 0x0080
+#define PNG_INFO_oFFs 0x0100
+#define PNG_INFO_tIME 0x0200
+#define PNG_INFO_pCAL 0x0400
+#define PNG_INFO_sRGB 0x0800   /* GR-P, 0.96a */
+
+/* This is used for the transformation routines, as some of them
+ * change these values for the row.  It also should enable using
+ * the routines for other purposes.
+ */
+typedef struct png_row_info_struct
+{
+   png_uint_32 width; /* width of row */
+   png_uint_32 rowbytes; /* number of bytes in row */
+   png_byte color_type; /* color type of row */
+   png_byte bit_depth; /* bit depth of row */
+   png_byte channels; /* number of channels (1, 2, 3, or 4) */
+   png_byte pixel_depth; /* bits per pixel (depth * channels) */
+} png_row_info;
+
+typedef png_row_info FAR * png_row_infop;
+typedef png_row_info FAR * FAR * png_row_infopp;
+
+/* These are the function types for the I/O functions and for the functions
+ * that allow the user to override the default I/O functions with his or her
+ * own.  The png_error_ptr type should match that of user-supplied warning
+ * and error functions, while the png_rw_ptr type should match that of the
+ * user read/write data functions.
+ */
+typedef struct png_struct_def png_struct;
+typedef png_struct FAR * png_structp;
+
+typedef void (*png_error_ptr) PNGARG((png_structp, png_const_charp));
+typedef void (*png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t));
+typedef void (*png_flush_ptr) PNGARG((png_structp));
+typedef void (*png_read_status_ptr) PNGARG((png_structp, png_uint_32, int));
+typedef void (*png_write_status_ptr) PNGARG((png_structp, png_uint_32, int));
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+typedef void (*png_progressive_info_ptr) PNGARG((png_structp, png_infop));
+typedef void (*png_progressive_end_ptr) PNGARG((png_structp, png_infop));
+typedef void (*png_progressive_row_ptr) PNGARG((png_structp, png_bytep,
+   png_uint_32, int));
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+typedef void (*png_user_transform_ptr) PNGARG((png_structp,
+    png_row_infop, png_bytep));
+#endif /* PNG_READ|WRITE_USER_TRANSFORM_SUPPORTED */
+
+typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t));
+typedef void (*png_free_ptr) PNGARG((png_structp, png_structp));
+
+/* The structure that holds the information to read and write PNG files.
+ * The only people who need to care about what is inside of this are the
+ * people who will be modifying the library for their own special needs.
+ * It should NOT be accessed directly by an application, except to store
+ * the jmp_buf.
+ */
+
+struct png_struct_def
+{
+   jmp_buf jmpbuf;            /* used in png_error */
+
+   png_error_ptr error_fn;    /* function for printing errors and aborting */
+   png_error_ptr warning_fn;  /* function for printing warnings */
+   png_voidp error_ptr;       /* user supplied struct for error functions */
+   png_rw_ptr write_data_fn;  /* function for writing output data */
+   png_rw_ptr read_data_fn;   /* function for reading input data */
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+   png_user_transform_ptr read_user_transform_fn; /* user read transform */
+   png_user_transform_ptr write_user_transform_fn; /* user write transform */
+#endif
+   png_voidp io_ptr;          /* ptr to application struct for I/O functions*/
+
+   png_uint_32 mode;          /* tells us where we are in the PNG file */
+   png_uint_32 flags;         /* flags indicating various things to libpng */
+   png_uint_32 transformations; /* which transformations to perform */
+
+   z_stream zstream;          /* pointer to decompression structure (below) */
+   png_bytep zbuf;            /* buffer for zlib */
+   png_size_t zbuf_size;      /* size of zbuf */
+   int zlib_level;            /* holds zlib compression level */
+   int zlib_method;           /* holds zlib compression method */
+   int zlib_window_bits;      /* holds zlib compression window bits */
+   int zlib_mem_level;        /* holds zlib compression memory level */
+   int zlib_strategy;         /* holds zlib compression strategy */
+
+   png_uint_32 width;         /* width of image in pixels */
+   png_uint_32 height;        /* height of image in pixels */
+   png_uint_32 num_rows;      /* number of rows in current pass */
+   png_uint_32 usr_width;     /* width of row at start of write */
+   png_uint_32 rowbytes;      /* size of row in bytes */
+   png_uint_32 irowbytes;     /* size of current interlaced row in bytes */
+   png_uint_32 iwidth;        /* width of current interlaced row in pixels */
+   png_uint_32 row_number;    /* current row in interlace pass */
+   png_bytep prev_row;        /* buffer to save previous (unfiltered) row */
+   png_bytep row_buf;         /* buffer to save current (unfiltered) row */
+   png_bytep sub_row;         /* buffer to save "sub" row when filtering */
+   png_bytep up_row;          /* buffer to save "up" row when filtering */
+   png_bytep avg_row;         /* buffer to save "avg" row when filtering */
+   png_bytep paeth_row;       /* buffer to save "Paeth" row when filtering */
+   png_row_info row_info;     /* used for transformation routines */
+
+   png_uint_32 idat_size;     /* current IDAT size for read */
+   png_uint_32 crc;           /* current chunk CRC value */
+   png_colorp palette;        /* palette from the input file */
+   png_uint_16 num_palette;   /* number of color entries in palette */
+   png_uint_16 num_trans;     /* number of transparency values */
+   png_byte chunk_name[5];    /* null-terminated name of current chunk */
+   png_byte compression;      /* file compression type (always 0) */
+   png_byte filter;           /* file filter type (always 0) */
+   png_byte interlaced;       /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
+   png_byte pass;             /* current interlace pass (0 - 6) */
+   png_byte do_filter;        /* row filter flags (see PNG_FILTER_ below ) */
+   png_byte color_type;       /* color type of file */
+   png_byte bit_depth;        /* bit depth of file */
+   png_byte usr_bit_depth;    /* bit depth of users row */
+   png_byte pixel_depth;      /* number of bits per pixel */
+   png_byte channels;         /* number of channels in file */
+   png_byte usr_channels;     /* channels at start of write */
+   png_byte sig_bytes;        /* magic bytes read/written from start of file */
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+   png_uint_16 filler;           /* filler bytes for pixel expansion */
+#endif /* PNG_READ_FILLER_SUPPORTED */
+#if defined(PNG_READ_bKGD_SUPPORTED)
+   png_byte background_gamma_type;
+   float background_gamma;
+   png_color_16 background;   /* background color in screen gamma space */
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   png_color_16 background_1; /* background normalized to gamma 1.0 */
+#endif /* PNG_READ_GAMMA && PNG_READ_bKGD_SUPPORTED */
+#endif /* PNG_READ_bKGD_SUPPORTED */
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+   png_flush_ptr output_flush_fn;/* Function for flushing output */
+   png_uint_32 flush_dist;    /* how many rows apart to flush, 0 - no flush */
+   png_uint_32 flush_rows;    /* number of rows written since last flush */
+#endif /* PNG_WRITE_FLUSH_SUPPORTED */
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   int gamma_shift;      /* number of "insignificant" bits 16-bit gamma */
+   float gamma;          /* file gamma value */
+   float screen_gamma;   /* screen gamma value (display_gamma/viewing_gamma */
+#endif /* PNG_READ_GAMMA_SUPPORTED */
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_bytep gamma_table;     /* gamma table for 8 bit depth files */
+   png_bytep gamma_from_1;    /* converts from 1.0 to screen */
+   png_bytep gamma_to_1;      /* converts from file to 1.0 */
+   png_uint_16pp gamma_16_table; /* gamma table for 16 bit depth files */
+   png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */
+   png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */
+#endif /* PNG_READ_GAMMA_SUPPORTED || PNG_WRITE_GAMMA_SUPPORTED */
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined (PNG_READ_sBIT_SUPPORTED)
+   png_color_8 sig_bit;       /* significant bits in each available channel */
+#endif /* PNG_READ_GAMMA_SUPPORTED || PNG_READ_sBIT_SUPPORTED */
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+   png_color_8 shift;         /* shift for significant bit tranformation */
+#endif /* PNG_READ_SHIFT_SUPPORTED || PNG_WRITE_SHIFT_SUPPORTED */
+#if defined(PNG_READ_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \
+ || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_bytep trans;           /* transparency values for paletted files */
+   png_color_16 trans_values; /* transparency values for non-paletted files */
+#endif /* PNG_READ|WRITE_tRNS_SUPPORTED||PNG_READ_EXPAND|BACKGROUND_SUPPORTED */
+   png_read_status_ptr read_row_fn;   /* called after each row is decoded */
+   png_write_status_ptr write_row_fn; /* called after each row is encoded */
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+   png_progressive_info_ptr info_fn; /* called after header data fully read */
+   png_progressive_row_ptr row_fn;   /* called after each prog. row is decoded */
+   png_progressive_end_ptr end_fn;   /* called after image is complete */
+   png_bytep save_buffer_ptr;        /* current location in save_buffer */
+   png_bytep save_buffer;            /* buffer for previously read data */
+   png_bytep current_buffer_ptr;     /* current location in current_buffer */
+   png_bytep current_buffer;         /* buffer for recently used data */
+   png_uint_32 push_length;          /* size of current input chunk */
+   png_uint_32 skip_length;          /* bytes to skip in input data */
+   png_size_t save_buffer_size;      /* amount of data now in save_buffer */
+   png_size_t save_buffer_max;       /* total size of save_buffer */
+   png_size_t buffer_size;           /* total amount of available input data */
+   png_size_t current_buffer_size;   /* amount of data now in current_buffer */
+   int process_mode;                 /* what push library is currently doing */
+   int cur_palette;                  /* current push library palette index */
+#if defined(PNG_READ_tEXt_SUPPORTED) || defined(PNG_READ_zTXt_SUPPORTED)
+   png_size_t current_text_size;     /* current size of text input data */
+   png_size_t current_text_left;     /* how much text left to read in input */
+   png_charp current_text;           /* current text chunk buffer */
+   png_charp current_text_ptr;       /* current location in current_text */
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED && PNG_READ_tEXt/zTXt_SUPPORTED */
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
+/* for the Borland special 64K segment handler */
+   png_bytepp offset_table_ptr;
+   png_bytep offset_table;
+   png_uint_16 offset_table_number;
+   png_uint_16 offset_table_count;
+   png_uint_16 offset_table_count_free;
+#endif /* __TURBOC__&&!_Windows&&!__FLAT__ */
+#if defined(PNG_READ_DITHER_SUPPORTED)
+   png_bytep palette_lookup;         /* lookup table for dithering */
+   png_bytep dither_index;           /* index translation for palette files */
+#endif /* PNG_READ_DITHER_SUPPORTED */
+#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_READ_hIST_SUPPORTED)
+   png_uint_16p hist;                /* histogram */
+#endif
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   png_byte heuristic_method;        /* heuristic for row filter selection */
+   png_byte num_prev_filters;        /* number of weights for previous rows */
+   png_bytep prev_filters;           /* filter type(s) of previous row(s) */
+   png_uint_16p filter_weights;      /* weight(s) for previous line(s) */
+   png_uint_16p inv_filter_weights;  /* 1/weight(s) for previous line(s) */
+   png_uint_16p filter_costs;        /* relative filter calculation cost */
+   png_uint_16p inv_filter_costs;    /* 1/relative filter calculation cost */
+#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+   png_charp time_buffer;            /* String to hold RFC 1123 time text */
+#endif /* PNG_TIME_RFC1123_SUPPORTED */
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_voidp mem_ptr;                /* user supplied struct for mem functions */
+   png_malloc_ptr malloc_fn;         /* function for allocating memory */
+   png_free_ptr free_fn;             /* function for freeing memory */
+#endif /* PNG_USER_MEM_SUPPORTED */
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+   png_byte rgb_to_gray_status;
+   png_byte rgb_to_gray_red_coeff;
+   png_byte rgb_to_gray_green_coeff;
+   png_byte rgb_to_gray_blue_coeff;
+#endif
+};
+
+typedef png_struct FAR * FAR * png_structpp;
+
+/* Here are the function definitions most commonly used.  This is not
+ * the place to find out how to use libpng.  See libpng.txt for the
+ * full explanation, see example.c for the summary.  This just provides
+ * a simple one line of the use of each function.
+ */
+
+/* Tell lib we have already handled the first <num_bytes> magic bytes.
+ * Handling more than 8 bytes from the beginning of the file is an error.
+ */
+extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr,
+   int num_bytes));
+
+/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a
+ * PNG file.  Returns zero if the supplied bytes match the 8-byte PNG
+ * signature, and non-zero otherwise.  Having num_to_check == 0 or
+ * start > 7 will always fail (ie return non-zero).
+ */
+extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start,
+   png_size_t num_to_check));
+
+/* Simple signature checking function.  This is the same as calling
+ * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n).
+ */
+extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num));
+
+/* Allocate and initialize png_ptr struct for reading, and any other memory. */
+extern PNG_EXPORT(png_structp,png_create_read_struct)
+   PNGARG((png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn));
+
+/* Allocate and initialize png_ptr struct for writing, and any other memory */
+extern PNG_EXPORT(png_structp,png_create_write_struct)
+   PNGARG((png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn));
+
+#ifdef PNG_USER_MEM_SUPPORTED
+extern PNG_EXPORT(png_structp,png_create_read_struct_2)
+   PNGARG((png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+   png_malloc_ptr malloc_fn, png_free_ptr free_fn));
+extern PNG_EXPORT(png_structp,png_create_write_struct_2)
+   PNGARG((png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+   png_malloc_ptr malloc_fn, png_free_ptr free_fn));
+#endif
+
+/* Write a PNG chunk - size, type, (optional) data, CRC. */
+extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr,
+   png_bytep chunk_name, png_bytep data, png_size_t length));
+
+/* Write the start of a PNG chunk - length and chunk name. */
+extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr,
+   png_bytep chunk_name, png_uint_32 length));
+
+/* Write the data of a PNG chunk started with png_write_chunk_start(). */
+extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr,
+   png_bytep data, png_size_t length));
+
+/* Finish a chunk started with png_write_chunk_start() (includes CRC). */
+extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr));
+
+/* Allocate and initialize the info structure */
+extern PNG_EXPORT(png_infop,png_create_info_struct)
+   PNGARG((png_structp png_ptr));
+
+/* Initialize the info structure (old interface - NOT DLL EXPORTED) */
+extern void png_info_init PNGARG((png_infop info_ptr));
+
+/* Writes all the PNG information before the image. */
+extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+/* read the information before the actual image data. */
+extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+extern PNG_EXPORT(png_charp,png_convert_to_rfc1123)
+   PNGARG((png_structp png_ptr, png_timep ptime));
+#endif /* PNG_TIME_RFC1123_SUPPORTED */
+
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+/* convert from a struct tm to png_time */
+extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime,
+   struct tm FAR * ttime));
+
+/* convert from time_t to png_time.  Uses gmtime() */
+extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime,
+   time_t ttime));
+#endif /* PNG_WRITE_tIME_SUPPORTED */
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+/* Expand data to 24 bit RGB, or 8 bit grayscale, with alpha if available. */
+extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr));
+#endif /* PNG_READ_EXPAND_SUPPORTED */
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* Use blue, green, red order for pixels. */
+extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr));
+#endif /* PNG_READ_BGR_SUPPORTED || PNG_WRITE_BGR_SUPPORTED */
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+/* Expand the grayscale to 24 bit RGB if necessary. */
+extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr));
+#endif /* PNG_READ_GRAY_TO_RGB_SUPPORTED */
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+/* Reduce RGB to grayscale. */
+extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr,
+   int error_action, float red, float green ));
+extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp
+   png_ptr));
+#endif /* PNG_READ_RGB_TO_GRAY_SUPPORTED */
+
+extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth,
+   png_colorp palette));
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr));
+#endif /* PNG_READ_STRIP_ALPHA_SUPPORTED */
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr));
+#endif /* PNG_READ_SWAP_ALPHA_SUPPORTED || PNG_WRITE_SWAP_ALPHA_SUPPORTED */
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr));
+#endif /* PNG_READ_INVERT_ALPHA_SUPPORTED || PNG_WRITE_INVERT_ALPHA_SUPPORTED */
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+/* Add a filler byte to 24-bit RGB images. */
+extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr,
+   png_uint_32 filler, int flags));
+
+/* The values of the PNG_FILLER_ defines should NOT be changed */
+#define PNG_FILLER_BEFORE 0
+#define PNG_FILLER_AFTER 1
+#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* Swap bytes in 16 bit depth files. */
+extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr));
+#endif /* PNG_READ_SWAP_SUPPORTED || PNG_WRITE_SWAP_SUPPORTED */
+
+#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+/* Use 1 byte per pixel in 1, 2, or 4 bit depth files. */
+extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr));
+#endif /* PNG_READ_PACK_SUPPORTED || PNG_WRITE_PACK_SUPPORTED */
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+/* Swap packing order of pixels in bytes. */
+extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr));
+#endif /* PNG_READ_PACKSWAP_SUPPORTED || PNG_WRITE_PACKSWAP_SUPPOR */
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+/* Converts files to legal bit depths. */
+extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr,
+   png_color_8p true_bits));
+#endif /* PNG_READ_SHIFT_SUPPORTED || PNG_WRITE_SHIFT_SUPPORTED */
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
+    defined(PNG_WRITE_INTERLACING_SUPPORTED)
+/* Have the code handle the interlacing.  Returns the number of passes. */
+extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr));
+#endif /* PNG_READ_INTERLACING_SUPPORTED || PNG_WRITE_INTERLACING_SUPPORTED */
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+/* Invert monocrome files */
+extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr));
+#endif /* PNG_READ_INVERT_SUPPORTED || PNG_WRITE_INVERT_SUPPORTED */
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+/* Handle alpha and tRNS by replacing with a background color. */
+extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr,
+   png_color_16p background_color, int background_gamma_code,
+   int need_expand, double background_gamma));
+#define PNG_BACKGROUND_GAMMA_UNKNOWN 0
+#define PNG_BACKGROUND_GAMMA_SCREEN  1
+#define PNG_BACKGROUND_GAMMA_FILE    2
+#define PNG_BACKGROUND_GAMMA_UNIQUE  3
+#endif /* PNG_READ_BACKGROUND_SUPPORTED */
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+/* strip the second byte of information from a 16 bit depth file. */
+extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr));
+#endif /* PNG_READ_16_TO_8_SUPPORTED */
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+/* Turn on dithering, and reduce the palette to the number of colors available. */
+extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr,
+   png_colorp palette, int num_palette, int maximum_colors,
+   png_uint_16p histogram, int full_dither));
+#endif /* PNG_READ_DITHER_SUPPORTED */
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+/* Handle gamma correction. Screen_gamma=(display_gamma/viewing_gamma) */
+extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr,
+   double screen_gamma, double default_file_gamma));
+#endif /* PNG_READ_GAMMA_SUPPORTED */
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+/* Set how many lines between output flushes - 0 for no flushing */
+extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows));
+
+/* Flush the current PNG output buffer */
+extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr));
+#endif /* PNG_WRITE_FLUSH_SUPPORTED */
+
+/* optional update palette with requested transformations */
+extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr));
+
+/* optional call to update the users info structure */
+extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+/* read a one or more rows of image data.*/
+extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr,
+   png_bytepp row, png_bytepp display_row, png_uint_32 num_rows));
+
+/* read a row of data.*/
+extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr,
+   png_bytep row,
+   png_bytep display_row));
+
+/* read the whole image into memory at once. */
+extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr,
+   png_bytepp image));
+
+/* write a row of image data */
+extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr,
+   png_bytep row));
+
+/* write a few rows of image data */
+extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr,
+   png_bytepp row, png_uint_32 num_rows));
+
+/* write the image data */
+extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr,
+   png_bytepp image));
+
+/* writes the end of the PNG file. */
+extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+/* read the end of the PNG file. */
+extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+/* free any memory associated with the png_info_struct */
+extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr,
+   png_infopp info_ptr_ptr));
+
+/* free any memory associated with the png_struct and the png_info_structs */
+extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp
+   png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr));
+
+/* free all memory used by the read (old method - NOT DLL EXPORTED) */
+extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_infop end_info_ptr));
+
+/* free any memory associated with the png_struct and the png_info_structs */
+extern PNG_EXPORT(void,png_destroy_write_struct)
+   PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr));
+
+/* free any memory used in info_ptr struct (old method - NOT DLL EXPORTED) */
+extern void png_write_destroy_info PNGARG((png_infop info_ptr));
+
+/* free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */
+extern void png_write_destroy PNGARG((png_structp png_ptr));
+
+/* set the libpng method of handling chunk CRC errors */
+extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr,
+   int crit_action, int ancil_action));
+
+/* Values for png_set_crc_action() to say how to handle CRC errors in
+ * ancillary and critical chunks, and whether to use the data contained
+ * therein.  Note that it is impossible to "discard" data in a critical
+ * chunk.  For versions prior to 0.90, the action was always error/quit,
+ * whereas in version 0.90 and later, the action for CRC errors in ancillary
+ * chunks is warn/discard.  These values should NOT be changed.
+ *
+ *      value                       action:critical     action:ancillary
+ */
+#define PNG_CRC_DEFAULT       0  /* error/quit          warn/discard data */
+#define PNG_CRC_ERROR_QUIT    1  /* error/quit          error/quit        */
+#define PNG_CRC_WARN_DISCARD  2  /* (INVALID)           warn/discard data */
+#define PNG_CRC_WARN_USE      3  /* warn/use data       warn/use data     */
+#define PNG_CRC_QUIET_USE     4  /* quiet/use data      quiet/use data    */
+#define PNG_CRC_NO_CHANGE     5  /* use current value   use current value */
+
+/* These functions give the user control over the scan-line filtering in
+ * libpng and the compression methods used by zlib.  These functions are
+ * mainly useful for testing, as the defaults should work with most users.
+ * Those users who are tight on memory or want faster performance at the
+ * expense of compression can modify them.  See the compression library
+ * header file (zlib.h) for an explination of the compression functions.
+ */
+
+/* set the filtering method(s) used by libpng.  Currently, the only valid
+ * value for "method" is 0.
+ */
+extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method,
+   int filters));
+
+/* Flags for png_set_filter() to say which filters to use.  The flags
+ * are chosen so that they don't conflict with real filter types
+ * below, in case they are supplied instead of the #defined constants.
+ * These values should NOT be changed.
+ */
+#define PNG_NO_FILTERS     0x00
+#define PNG_FILTER_NONE    0x08
+#define PNG_FILTER_SUB     0x10
+#define PNG_FILTER_UP      0x20
+#define PNG_FILTER_AVG     0x40
+#define PNG_FILTER_PAETH   0x80
+#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \
+                         PNG_FILTER_AVG | PNG_FILTER_PAETH)
+
+/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now.
+ * These defines should NOT be changed.
+ */
+#define PNG_FILTER_VALUE_NONE  0
+#define PNG_FILTER_VALUE_SUB   1
+#define PNG_FILTER_VALUE_UP    2
+#define PNG_FILTER_VALUE_AVG   3
+#define PNG_FILTER_VALUE_PAETH 4
+#define PNG_FILTER_VALUE_LAST  5
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */
+/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_
+ * defines, either the default (minimum-sum-of-absolute-differences), or
+ * the experimental method (weighted-minimum-sum-of-absolute-differences).
+ *
+ * Weights are factors >= 1.0, indicating how important it is to keep the
+ * filter type consistent between rows.  Larger numbers mean the current
+ * filter is that many times as likely to be the same as the "num_weights"
+ * previous filters.  This is cumulative for each previous row with a weight.
+ * There needs to be "num_weights" values in "filter_weights", or it can be
+ * NULL if the weights aren't being specified.  Weights have no influence on
+ * the selection of the first row filter.  Well chosen weights can (in theory)
+ * improve the compression for a given image.
+ *
+ * Costs are factors >= 1.0 indicating the relative decoding costs of a
+ * filter type.  Higher costs indicate more decoding expense, and are
+ * therefore less likely to be selected over a filter with lower computational
+ * costs.  There needs to be a value in "filter_costs" for each valid filter
+ * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't
+ * setting the costs.  Costs try to improve the speed of decompression without
+ * unduly increasing the compressed image size.
+ *
+ * A negative weight or cost indicates the default value is to be used, and
+ * values in the range [0.0, 1.0) indicate the value is to remain unchanged.
+ * The default values for both weights and costs are currently 1.0, but may
+ * change if good general weighting/cost heuristics can be found.  If both
+ * the weights and costs are set to 1.0, this degenerates the WEIGHTED method
+ * to the UNWEIGHTED method, but with added encoding time/computation.
+ */
+extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr,
+   int heuristic_method, int num_weights, png_doublep filter_weights,
+   png_doublep filter_costs));
+#endif /*  PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
+
+/* Heuristic used for row filter selection.  These defines should NOT be
+ * changed.
+ */
+#define PNG_FILTER_HEURISTIC_DEFAULT    0  /* Currently "UNWEIGHTED" */
+#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1  /* Used by libpng < 0.95 */
+#define PNG_FILTER_HEURISTIC_WEIGHTED   2  /* Experimental feature */
+#define PNG_FILTER_HEURISTIC_LAST       3  /* Not a valid value */
+
+/* Set the library compression level.  Currently, valid values range from
+ * 0 - 9, corresponding directly to the zlib compression levels 0 - 9
+ * (0 - no compression, 9 - "maximal" compression).  Note that tests have
+ * shown that zlib compression levels 3-6 usually perform as well as level 9
+ * for PNG images, and do considerably fewer caclulations.  In the future,
+ * these values may not correspond directly to the zlib compression levels.
+ */
+extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr,
+   int level));
+
+extern PNG_EXPORT(void,png_set_compression_mem_level)
+   PNGARG((png_structp png_ptr, int mem_level));
+
+extern PNG_EXPORT(void,png_set_compression_strategy)
+   PNGARG((png_structp png_ptr, int strategy));
+
+extern PNG_EXPORT(void,png_set_compression_window_bits)
+   PNGARG((png_structp png_ptr, int window_bits));
+
+extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr,
+   int method));
+
+/* These next functions are called for input/output, memory, and error
+ * handling.  They are in the file pngrio.c, pngwio.c, and pngerror.c,
+ * and call standard C I/O routines such as fread(), fwrite(), and
+ * fprintf().  These functions can be made to use other I/O routines
+ * at run time for those applications that need to handle I/O in a
+ * different manner by calling png_set_???_fn().  See libpng.txt for
+ * more information.
+ */
+
+#if !defined(PNG_NO_STDIO)
+/* Initialize the input/output for the PNG file to the default functions. */
+extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, FILE *fp));
+#endif
+
+/* Replace the (error and abort), and warning functions with user
+ * supplied functions.  If no messages are to be printed you must still
+ * write and use replacement functions. The replacement error_fn should
+ * still do a longjmp to the last setjmp location if you are using this
+ * method of error handling.  If error_fn or warning_fn is NULL, the
+ * default function will be used.
+ */
+
+extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr,
+   png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn));
+
+/* Return the user pointer associated with the error functions */
+extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr));
+
+/* Replace the default data output functions with a user supplied one(s).
+ * If buffered output is not used, then output_flush_fn can be set to NULL.
+ * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time
+ * output_flush_fn will be ignored (and thus can be NULL).
+ */
+extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr,
+   png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn));
+
+/* Replace the default data input function with a user supplied one. */
+extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr,
+   png_voidp io_ptr, png_rw_ptr read_data_fn));
+
+/* Return the user pointer associated with the I/O functions */
+extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr));
+
+extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr,
+   png_read_status_ptr read_row_fn));
+
+extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr,
+   png_write_status_ptr write_row_fn));
+
+#ifdef PNG_USER_MEM_SUPPORTED
+/* Replace the default memory allocation functions with user supplied one(s). */
+extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr,
+   png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn));
+
+/* Return the user pointer associated with the memory functions */
+extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr));
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp
+   png_ptr, png_user_transform_ptr read_user_transform_fn));
+#endif
+
+#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
+extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp
+   png_ptr, png_user_transform_ptr write_user_transform_fn));
+#endif
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+/* Sets the function callbacks for the push reader, and a pointer to a
+ * user-defined structure available to the callback functions.
+ */
+extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr,
+   png_voidp progressive_ptr,
+   png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn,
+   png_progressive_end_ptr end_fn));
+
+/* returns the user pointer associated with the push read functions */
+extern PNG_EXPORT(png_voidp,png_get_progressive_ptr)
+   PNGARG((png_structp png_ptr));
+
+/* function to be called when data becomes available */
+extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_bytep buffer, png_size_t buffer_size));
+
+/* function that combines rows.  Not very much different than the
+ * png_combine_row() call.  Is this even used?????
+ */
+extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr,
+   png_bytep old_row, png_bytep new_row));
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr,
+   png_uint_32 size));
+
+/* frees a pointer allocated by png_malloc() */
+extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr));
+
+#ifdef PNG_USER_MEM_SUPPORTED
+extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr,
+   png_uint_32 size));
+extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr,
+   png_voidp ptr));
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr,
+   png_voidp s1, png_voidp s2, png_uint_32 size));
+
+extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr,
+   png_voidp s1, int value, png_uint_32 size));
+
+#if defined(USE_FAR_KEYWORD)  /* memory model conversion function */
+extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr,
+   int check));
+#endif /* USE_FAR_KEYWORD */
+
+/* Fatal error in PNG image of libpng - can't continue */
+extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr,
+   png_const_charp error));
+
+/* The same, but the chunk name is prepended to the error string. */
+extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr,
+   png_const_charp error));
+
+/* Non-fatal error in libpng.  Can continue, but may have a problem. */
+extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr,
+   png_const_charp message));
+
+/* Non-fatal error in libpng, chunk name is prepended to message. */
+extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr,
+   png_const_charp message));
+
+/* The png_set_<chunk> functions are for storing values in the png_info_struct.
+ * Similarly, the png_get_<chunk> calls are used to read values from the
+ * png_info_struct, either storing the parameters in the passed variables, or
+ * setting pointers into the png_info_struct where the data is stored.  The
+ * png_get_<chunk> functions return a non-zero value if the data was available
+ * in info_ptr, or return zero and do not change any of the parameters if the
+ * data was not available.
+ *
+ * These functions should be used instead of directly accessing png_info
+ * to avoid problems with future changes in the size and internal layout of
+ * png_info_struct.
+ */
+/* Returns "flag" if chunk data is valid in info_ptr. */
+extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr,
+png_infop info_ptr, png_uint_32 flag));
+
+/* Returns number of bytes needed to hold a transformed row. */
+extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+/* Returns number of color channels in image. */
+extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+#ifdef PNG_EASY_ACCESS_SUPPORTED
+/* Returns image width in pixels. */
+extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image height in pixels. */
+extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image bit_depth. */
+extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image color_type. */
+extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image filter_type. */
+extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image interlace_type. */
+extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image compression_type. */
+extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image resolution in pixels per meter, from pHYs chunk data. */
+extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns pixel aspect ratio, computed from pHYs chunk data.  */
+extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */
+extern PNG_EXPORT(png_uint_32, png_get_x_offset_pixels) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_uint_32, png_get_y_offset_pixels) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_uint_32, png_get_x_offset_microns) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_uint_32, png_get_y_offset_microns) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+#endif /* PNG_EASY_ACCESS_SUPPORTED */
+
+/* Returns pointer to signature string read from PNG header */
+extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+#if defined(PNG_READ_bKGD_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_color_16p *background));
+#endif /* PNG_READ_bKGD_SUPPORTED */
+
+#if defined(PNG_READ_bKGD_SUPPORTED) || defined(PNG_WRITE_bKGD_SUPPORTED)
+extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_color_16p background));
+#endif /* PNG_READ_bKGD_SUPPORTED || PNG_WRITE_bKGD_SUPPORTED */
+
+#if defined(PNG_READ_cHRM_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, double *white_x, double *white_y, double *red_x,
+   double *red_y, double *green_x, double *green_y, double *blue_x,
+   double *blue_y));
+#endif /* PNG_READ_cHRM_SUPPORTED */
+
+#if defined(PNG_READ_cHRM_SUPPORTED) || defined(PNG_WRITE_cHRM_SUPPORTED)
+extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, double white_x, double white_y, double red_x,
+   double red_y, double green_x, double green_y, double blue_x, double blue_y));
+#endif /* PNG_READ_cHRM_SUPPORTED || PNG_WRITE_cHRM_SUPPORTED */
+
+#if defined(PNG_READ_gAMA_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, double *file_gamma));
+#endif /* PNG_READ_gAMA_SUPPORTED */
+
+#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_WRITE_gAMA_SUPPORTED)
+extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, double file_gamma));
+#endif /* PNG_READ_gAMA_SUPPORTED || PNG_WRITE_gAMA_SUPPORTED */
+
+#if defined(PNG_READ_hIST_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_16p *hist));
+#endif /* PNG_READ_hIST_SUPPORTED */
+
+#if defined(PNG_READ_hIST_SUPPORTED) || defined(PNG_WRITE_hIST_SUPPORTED)
+extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_16p hist));
+#endif /* PNG_READ_hIST_SUPPORTED || PNG_WRITE_hIST_SUPPORTED */
+
+extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 *width, png_uint_32 *height,
+   int *bit_depth, int *color_type, int *interlace_type,
+   int *compression_type, int *filter_type));
+
+extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth,
+   int color_type, int interlace_type, int compression_type, int filter_type));
+
+#if defined(PNG_READ_oFFs_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 *offset_x, png_uint_32 *offset_y,
+   int *unit_type));
+#endif /* PNG_READ_oFFs_SUPPORTED */
+
+#if defined(PNG_READ_oFFs_SUPPORTED) || defined(PNG_WRITE_oFFs_SUPPORTED)
+extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 offset_x, png_uint_32 offset_y,
+   int unit_type));
+#endif /* PNG_READ_oFFs_SUPPORTED || PNG_WRITE_oFFs_SUPPORTED */
+
+#if defined(PNG_READ_pCAL_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1,
+   int *type, int *nparams, png_charp *units, png_charpp *params));
+#endif /* PNG_READ_pCAL_SUPPORTED */
+
+#if defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED)
+extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1,
+   int type, int nparams, png_charp units, png_charpp params));
+#endif /* PNG_READ_pCAL_SUPPORTED || PNG_WRITE_pCAL_SUPPORTED */
+
+#if defined(PNG_READ_pHYs_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type));
+#endif /* PNG_READ_pHYs_SUPPORTED */
+
+#if defined(PNG_READ_pHYs_SUPPORTED) || defined(PNG_WRITE_pHYs_SUPPORTED)
+extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type));
+#endif /* PNG_READ_pHYs_SUPPORTED || PNG_WRITE_pHYs_SUPPORTED */
+
+extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_colorp *palette, int *num_palette));
+
+extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_colorp palette, int num_palette));
+
+#if defined(PNG_READ_sBIT_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_color_8p *sig_bit));
+#endif /* PNG_READ_sBIT_SUPPORTED */
+
+#if defined(PNG_READ_sBIT_SUPPORTED) || defined(PNG_WRITE_sBIT_SUPPORTED)
+extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_color_8p sig_bit));
+#endif /* PNG_READ_sBIT_SUPPORTED || PNG_WRITE_sBIT_SUPPORTED */
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int *intent));
+#endif /* PNG_READ_sRGB_SUPPORTED */
+
+#if defined(PNG_READ_sRGB_SUPPORTED) || defined(PNG_WRITE_sRGB_SUPPORTED)
+extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int intent));
+extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int intent));
+#endif /* PNG_READ_sRGB_SUPPORTED || PNG_WRITE_sRGB_SUPPORTED */
+
+#if defined(PNG_READ_tEXt_SUPPORTED) || defined(PNG_READ_zTXt_SUPPORTED)
+/* png_get_text also returns the number of text chunks in text_ptr */
+extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_textp *text_ptr, int *num_text));
+#endif /* PNG_READ_tEXt_SUPPORTED || PNG_READ_zTXt_SUPPORTED */
+
+#if defined(PNG_READ_tEXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \
+    defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
+extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_textp text_ptr, int num_text));
+#endif /* PNG_READ_OR_WRITE_tEXt_OR_zTXt_SUPPORTED */
+
+#if defined(PNG_READ_tIME_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_timep *mod_time));
+#endif /* PNG_READ_tIME_SUPPORTED */
+
+#if defined(PNG_READ_tIME_SUPPORTED) || defined(PNG_WRITE_tIME_SUPPORTED)
+extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_timep mod_time));
+#endif /* PNG_READ_tIME_SUPPORTED || PNG_WRITE_tIME_SUPPORTED */
+
+#if defined(PNG_READ_tRNS_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_bytep *trans, int *num_trans,
+   png_color_16p *trans_values));
+#endif /* PNG_READ_tRNS_SUPPORTED */
+
+#if defined(PNG_READ_tRNS_SUPPORTED) || defined(PNG_WRITE_tRNS_SUPPORTED)
+extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_bytep trans, int num_trans,
+   png_color_16p trans_values));
+#endif /* PNG_READ_tRNS_SUPPORTED || PNG_WRITE_tRNS_SUPPORTED */
+
+/* Define PNG_DEBUG at compile time for debugging information.  Higher
+ * numbers for PNG_DEBUG mean more debugging information.  This has
+ * only been added since version 0.95 so it is not implemented throughout
+ * libpng yet, but more support will be added as needed.
+ */
+#ifdef PNG_DEBUG
+#if (PNG_DEBUG > 0)
+#ifndef PNG_DEBUG_FILE
+#define PNG_DEBUG_FILE stderr
+#endif /* PNG_DEBUG_FILE */
+
+#define png_debug(l,m)        if (PNG_DEBUG > l) \
+                                 fprintf(PNG_DEBUG_FILE,"%s"m,(l==1 ? "\t" : \
+                                    (l==2 ? "\t\t":(l>2 ? "\t\t\t":""))))
+#define png_debug1(l,m,p1)    if (PNG_DEBUG > l) \
+                                 fprintf(PNG_DEBUG_FILE,"%s"m,(l==1 ? "\t" : \
+                                    (l==2 ? "\t\t":(l>2 ? "\t\t\t":""))),p1)
+#define png_debug2(l,m,p1,p2) if (PNG_DEBUG > l) \
+                                 fprintf(PNG_DEBUG_FILE,"%s"m,(l==1 ? "\t" : \
+                                    (l==2 ? "\t\t":(l>2 ? "\t\t\t":""))),p1,p2)
+#endif /* (PNG_DEBUG > 0) */
+#endif /* PNG_DEBUG */
+#ifndef png_debug
+#define png_debug(l, m)
+#endif
+#ifndef png_debug1
+#define png_debug1(l, m, p1)
+#endif
+#ifndef png_debug2
+#define png_debug2(l, m, p1, p2)
+#endif
+
+extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr));
+extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr));
+
+#ifdef PNG_NO_EXTERN
+/* this only gets included in png.c */
+png_charp
+png_get_header_version(png_structp png_ptr)
+{
+   if(png_ptr == NULL)
+     /* silence compiler warning about unused png_ptr */ ;
+   return("\n libpng version 1.0.3 - January 14, 1999 (header)\n");
+}
+#endif
+
+#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED
+/* With these routines we avoid an integer divide, which will be slower on
+ * most machines.  However, it does take more operations than the corresponding
+ * divide method, so it may be slower on a few RISC systems.  There are two
+ * shifts (by 8 or 16 bits) and an addition, versus a single integer divide.
+ *
+ * Note that the rounding factors are NOT supposed to be the same!  128 and
+ * 32768 are correct for the NODIV code; 127 and 32767 are correct for the
+ * standard method.
+ *
+ * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ]
+ */
+
+ /* fg and bg should be in `gamma 1.0' space; alpha is the opacity          */
+
+#  define png_composite(composite, fg, alpha, bg)                            \
+     { png_uint_16 temp = ((png_uint_16)(fg) * (png_uint_16)(alpha) +        \
+                        (png_uint_16)(bg)*(png_uint_16)(255 -                \
+                        (png_uint_16)(alpha)) + (png_uint_16)128);           \
+       (composite) = (png_byte)((temp + (temp >> 8)) >> 8); }
+
+#  define png_composite_16(composite, fg, alpha, bg)                         \
+     { png_uint_32 temp = ((png_uint_32)(fg) * (png_uint_32)(alpha) +        \
+                        (png_uint_32)(bg)*(png_uint_32)(65535L -             \
+                        (png_uint_32)(alpha)) + (png_uint_32)32768L);        \
+       (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); }
+
+#else  /* standard method using integer division */
+
+#  define png_composite(composite, fg, alpha, bg)                            \
+     (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) +    \
+       (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) +       \
+       (png_uint_16)127) / 255)
+
+#  define png_composite_16(composite, fg, alpha, bg)                         \
+     (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \
+       (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) +      \
+       (png_uint_32)32767) / (png_uint_32)65535L)
+
+#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */
+
+/* These next functions are used internally in the code.  They generally
+ * shouldn't be used unless you are writing code to add or replace some
+ * functionality in libpng.  More information about most functions can
+ * be found in the files where the functions are located.
+ */
+
+#if defined(PNG_INTERNAL)
+
+/* Various modes of operation.  Note that after an init, mode is set to
+ * zero automatically when the structure is created.
+ */
+#define PNG_BEFORE_IHDR       0x00
+#define PNG_HAVE_IHDR         0x01
+#define PNG_HAVE_PLTE         0x02
+#define PNG_HAVE_IDAT         0x04
+#define PNG_AFTER_IDAT        0x08
+#define PNG_HAVE_IEND         0x10
+#define PNG_HAVE_gAMA         0x20
+#define PNG_HAVE_cHRM         0x40
+#define PNG_HAVE_sRGB         0x80
+
+/* push model modes */
+#define PNG_READ_SIG_MODE   0
+#define PNG_READ_CHUNK_MODE 1
+#define PNG_READ_IDAT_MODE  2
+#define PNG_SKIP_MODE       3
+#define PNG_READ_tEXt_MODE  4
+#define PNG_READ_zTXt_MODE  5
+#define PNG_READ_DONE_MODE  6
+#define PNG_ERROR_MODE      7
+
+/* flags for the transformations the PNG library does on the image data */
+#define PNG_BGR                0x0001
+#define PNG_INTERLACE          0x0002
+#define PNG_PACK               0x0004
+#define PNG_SHIFT              0x0008
+#define PNG_SWAP_BYTES         0x0010
+#define PNG_INVERT_MONO        0x0020
+#define PNG_DITHER             0x0040
+#define PNG_BACKGROUND         0x0080
+#define PNG_BACKGROUND_EXPAND  0x0100
+                          /*   0x0200 unused */
+#define PNG_16_TO_8            0x0400
+#define PNG_RGBA               0x0800
+#define PNG_EXPAND             0x1000
+#define PNG_GAMMA              0x2000
+#define PNG_GRAY_TO_RGB        0x4000
+#define PNG_FILLER             0x8000
+#define PNG_PACKSWAP          0x10000L
+#define PNG_SWAP_ALPHA        0x20000L
+#define PNG_STRIP_ALPHA       0x40000L
+#define PNG_INVERT_ALPHA      0x80000L
+#define PNG_USER_TRANSFORM   0x100000L
+#define PNG_RGB_TO_GRAY_ERR  0x200000L
+#define PNG_RGB_TO_GRAY_WARN 0x400000L
+#define PNG_RGB_TO_GRAY      0x600000L  /* two bits, RGB_TO_GRAY_ERR|WARN */
+
+/* flags for png_create_struct */
+#define PNG_STRUCT_PNG   0x0001
+#define PNG_STRUCT_INFO  0x0002
+
+/* Scaling factor for filter heuristic weighting calculations */
+#define PNG_WEIGHT_SHIFT 8
+#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT))
+#define PNG_COST_SHIFT 3
+#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT))
+
+/* flags for the png_ptr->flags rather than declaring a byte for each one */
+#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY     0x0001
+#define PNG_FLAG_ZLIB_CUSTOM_LEVEL        0x0002
+#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL    0x0004
+#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS  0x0008
+#define PNG_FLAG_ZLIB_CUSTOM_METHOD       0x0010
+#define PNG_FLAG_ZLIB_FINISHED            0x0020
+#define PNG_FLAG_ROW_INIT                 0x0040
+#define PNG_FLAG_FILLER_AFTER             0x0080
+#define PNG_FLAG_CRC_ANCILLARY_USE        0x0100
+#define PNG_FLAG_CRC_ANCILLARY_NOWARN     0x0200
+#define PNG_FLAG_CRC_CRITICAL_USE         0x0400
+#define PNG_FLAG_CRC_CRITICAL_IGNORE      0x0800
+#define PNG_FLAG_FREE_PALETTE             0x1000
+#define PNG_FLAG_FREE_TRANS               0x2000
+#define PNG_FLAG_FREE_HIST                0x4000
+#define PNG_FLAG_HAVE_CHUNK_HEADER        0x8000L
+#define PNG_FLAG_WROTE_tIME              0x10000L
+#define PNG_FLAG_BACKGROUND_IS_GRAY      0x20000L
+
+#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \
+                                     PNG_FLAG_CRC_ANCILLARY_NOWARN)
+
+#define PNG_FLAG_CRC_CRITICAL_MASK  (PNG_FLAG_CRC_CRITICAL_USE | \
+                                     PNG_FLAG_CRC_CRITICAL_IGNORE)
+
+#define PNG_FLAG_CRC_MASK           (PNG_FLAG_CRC_ANCILLARY_MASK | \
+                                     PNG_FLAG_CRC_CRITICAL_MASK)
+
+/* save typing and make code easier to understand */
+#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \
+   abs((int)((c1).green) - (int)((c2).green)) + \
+   abs((int)((c1).blue) - (int)((c2).blue)))
+
+/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */
+#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN)
+/* place to hold the signature string for a PNG file. */
+extern png_byte FARDATA png_sig[8];
+
+/* Constant strings for known chunk types.  If you need to add a chunk,
+ * add a string holding the name here.  See png.c for more details.  We
+ * can't selectively include these, since we still check for chunk in the
+ * wrong locations with these labels.
+ */
+extern png_byte FARDATA png_IHDR[5];
+extern png_byte FARDATA png_IDAT[5];
+extern png_byte FARDATA png_IEND[5];
+extern png_byte FARDATA png_PLTE[5];
+extern png_byte FARDATA png_bKGD[5];
+extern png_byte FARDATA png_cHRM[5];
+extern png_byte FARDATA png_gAMA[5];
+extern png_byte FARDATA png_hIST[5];
+extern png_byte FARDATA png_oFFs[5];
+extern png_byte FARDATA png_pCAL[5];
+extern png_byte FARDATA png_pHYs[5];
+extern png_byte FARDATA png_sBIT[5];
+extern png_byte FARDATA png_sRGB[5];
+extern png_byte FARDATA png_tEXt[5];
+extern png_byte FARDATA png_tIME[5];
+extern png_byte FARDATA png_tRNS[5];
+extern png_byte FARDATA png_zTXt[5];
+
+#endif /* PNG_NO_EXTERN */
+
+/* Inline macros to do direct reads of bytes from the input buffer.  These
+ * require that you are using an architecture that uses PNG byte ordering
+ * (MSB first) and supports unaligned data storage.  I think that PowerPC
+ * in big-endian mode and 680x0 are the only ones that will support this.
+ * The x86 line of processors definitely do not.  The png_get_int_32()
+ * routine also assumes we are using two's complement format for negative
+ * values, which is almost certainly true.
+ */
+#if defined(PNG_READ_BIG_ENDIAN_SUPPORTED)
+#if defined(PNG_READ_pCAL_SUPPORTED)
+#define png_get_int_32(buf) ( *((png_int_32p) (buf)))
+#endif /* PNG_READ_pCAL_SUPPORTED */
+#define png_get_uint_32(buf) ( *((png_uint_32p) (buf)))
+#define png_get_uint_16(buf) ( *((png_uint_16p) (buf)))
+#else
+#if defined(PNG_READ_pCAL_SUPPORTED)
+PNG_EXTERN png_int_32 png_get_int_32 PNGARG((png_bytep buf));
+#endif /* PNG_READ_pCAL_SUPPORTED */
+PNG_EXTERN png_uint_32 png_get_uint_32 PNGARG((png_bytep buf));
+PNG_EXTERN png_uint_16 png_get_uint_16 PNGARG((png_bytep buf));
+#endif /* PNG_READ_BIG_ENDIAN_SUPPORTED */
+
+/* Initialize png_ptr struct for reading, and allocate any other memory.
+ * (old interface - NOT DLL EXPORTED).
+ */
+extern void png_read_init PNGARG((png_structp png_ptr));
+
+/* Initialize png_ptr struct for writing, and allocate any other memory.
+ * (old interface - NOT DLL EXPORTED).
+ */
+extern void png_write_init PNGARG((png_structp png_ptr));
+
+/* allocate memory for an internal libpng struct */
+PNG_EXTERN png_voidp png_create_struct PNGARG((int type));
+
+/* free memory from internal libpng struct */
+PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr));
+
+PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr
+  malloc_fn));
+PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr,
+   png_free_ptr free_fn));
+
+/* free any memory that info_ptr points to and reset struct. */
+PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+/* Function to allocate memory for zlib. */
+PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size));
+
+/* function to free memory for zlib */
+PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr));
+
+/* reset the CRC variable */
+PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr));
+
+/* Write the "data" buffer to whatever output you are using. */
+PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data,
+   png_size_t length));
+
+/* Read data from whatever input you are using into the "data" buffer */
+PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data,
+   png_size_t length));
+
+/* read bytes into buf, and update png_ptr->crc */
+PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf,
+   png_size_t length));
+
+/* read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */
+PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip));
+
+/* read the CRC from the file and compare it to the libpng calculated CRC */
+PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr));
+
+/* Calculate the CRC over a section of data.  Note that we are only
+ * passing a maximum of 64K on systems that have this as a memory limit,
+ * since this is the maximum buffer size we can specify.
+ */
+PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr,
+   png_size_t length));
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+PNG_EXTERN void png_flush PNGARG((png_structp png_ptr));
+#endif
+
+/* Place a 32-bit number into a buffer in PNG byte order (big-endian).
+ * The only currently known PNG chunks that use signed numbers are
+ * the ancillary extension chunks, oFFs and pCAL.
+ */
+PNG_EXTERN void png_save_uint_32 PNGARG((png_bytep buf, png_uint_32 i));
+
+#if defined(PNG_WRITE_pCAL_SUPPORTED)
+PNG_EXTERN void png_save_int_32 PNGARG((png_bytep buf, png_int_32 i));
+#endif
+
+/* Place a 16 bit number into a buffer in PNG byte order.
+ * The parameter is declared unsigned int, not png_uint_16,
+ * just to avoid potential problems on pre-ANSI C compilers.
+ */
+PNG_EXTERN void png_save_uint_16 PNGARG((png_bytep buf, unsigned int i));
+
+/* simple function to write the signature */
+PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr));
+
+/* write various chunks */
+
+/* Write the IHDR chunk, and update the png_struct with the necessary
+ * information.
+ */
+PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width,
+   png_uint_32 height,
+   int bit_depth, int color_type, int compression_type, int filter_type,
+   int interlace_type));
+
+PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette,
+   png_uint_32 num_pal));
+
+PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data,
+   png_size_t length));
+
+PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr));
+
+#if defined(PNG_WRITE_gAMA_SUPPORTED)
+PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma));
+#endif
+
+#if defined(PNG_WRITE_sBIT_SUPPORTED)
+PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit,
+   int color_type));
+#endif
+
+#if defined(PNG_WRITE_cHRM_SUPPORTED)
+PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr,
+   double white_x, double white_y,
+   double red_x, double red_y, double green_x, double green_y,
+   double blue_x, double blue_y));
+#endif
+
+#if defined(PNG_WRITE_sRGB_SUPPORTED)
+PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr,
+   int intent));
+#endif
+
+#if defined(PNG_WRITE_tRNS_SUPPORTED)
+PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans,
+   png_color_16p values, int number, int color_type));
+#endif
+
+#if defined(PNG_WRITE_bKGD_SUPPORTED)
+PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr,
+   png_color_16p values, int color_type));
+#endif
+
+#if defined(PNG_WRITE_hIST_SUPPORTED)
+PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist,
+   int num_hist));
+#endif
+
+#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED) || \
+    defined(PNG_WRITE_pCAL_SUPPORTED)
+PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr,
+   png_charp key, png_charpp new_key));
+#endif
+
+#if defined(PNG_WRITE_tEXt_SUPPORTED)
+PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key,
+   png_charp text, png_size_t text_len));
+#endif
+
+#if defined(PNG_WRITE_zTXt_SUPPORTED)
+PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key,
+   png_charp text, png_size_t text_len, int compression));
+#endif
+
+#if defined(PNG_WRITE_oFFs_SUPPORTED)
+PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr,
+   png_uint_32 x_offset, png_uint_32 y_offset, int unit_type));
+#endif
+
+#if defined(PNG_WRITE_pCAL_SUPPORTED)
+PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose,
+   png_int_32 X0, png_int_32 X1, int type, int nparams,
+   png_charp units, png_charpp params));
+#endif
+
+#if defined(PNG_WRITE_pHYs_SUPPORTED)
+PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr,
+   png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit,
+   int unit_type));
+#endif
+
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr,
+   png_timep mod_time));
+#endif
+
+/* Called when finished processing a row of data */
+PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr));
+
+/* Internal use only.   Called before first row of data */
+PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr));
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr));
+#endif
+
+/* combine a row of data, dealing with alpha, etc. if requested */
+PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row,
+   int mask));
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+/* expand an interlaced row */
+PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info,
+   png_bytep row, int pass, png_uint_32 transformations));
+#endif
+
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+/* grab pixels out of a row for an interlaced pass */
+PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info,
+   png_bytep row, int pass));
+#endif
+
+/* unfilter a row */
+PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr,
+   png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter));
+
+/* Choose the best filter to use and filter the row data */
+PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr,
+   png_row_infop row_info));
+
+/* Write out the filtered row. */
+PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr,
+   png_bytep filtered_row));
+/* finish a row while reading, dealing with interlacing passes, etc. */
+PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr));
+
+/* initialize the row buffers, etc. */
+PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr));
+/* optional call to update the users info structure */
+PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+/* these are the functions that do the transformations */
+#if defined(PNG_READ_FILLER_SUPPORTED)
+PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info,
+   png_bytep row, png_uint_32 filler, png_uint_32 flags));
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
+    defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info,
+   png_bytep row, png_uint_32 flags));
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop
+   row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row,
+   png_color_8p sig_bits));
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info,
+   png_bytep row, png_bytep palette_lookup, png_bytep dither_lookup));
+
+#  if defined(PNG_CORRECT_PALETTE_SUPPORTED)
+PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr,
+   png_colorp palette, int num_palette));
+#  endif
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_PACK_SUPPORTED)
+PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info,
+   png_bytep row, png_uint_32 bit_depth));
+#endif
+
+#if defined(PNG_WRITE_SHIFT_SUPPORTED)
+PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row,
+   png_color_8p bit_depth));
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row,
+   png_color_16p trans_values, png_color_16p background,
+   png_color_16p background_1,
+   png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1,
+   png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1,
+   png_uint_16pp gamma_16_to_1, int gamma_shift));
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row,
+   png_bytep gamma_table, png_uint_16pp gamma_16_table,
+   int gamma_shift));
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info,
+   png_bytep row, png_colorp palette, png_bytep trans, int num_trans));
+PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info,
+   png_bytep row, png_color_16p trans_value));
+#endif
+
+/* The following decodes the appropriate chunks, and does error correction,
+ * then calls the appropriate callback for the chunk if it is valid.
+ */
+
+/* decode the IHDR chunk */
+PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+
+#if defined(PNG_READ_gAMA_SUPPORTED)
+PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_sBIT_SUPPORTED)
+PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_cHRM_SUPPORTED)
+PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_tRNS_SUPPORTED)
+PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_bKGD_SUPPORTED)
+PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_hIST_SUPPORTED)
+PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_oFFs_SUPPORTED)
+PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_pCAL_SUPPORTED)
+PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_pHYs_SUPPORTED)
+PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_tIME_SUPPORTED)
+PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_tEXt_SUPPORTED)
+PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_zTXt_SUPPORTED)
+PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+
+PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr,
+   png_bytep chunk_name));
+
+/* handle the transformations for reading and writing */
+PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr));
+
+PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr));
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr,
+   png_uint_32 length));
+PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr,
+   png_bytep buffer, png_size_t length));
+PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr,
+   png_bytep buffer, png_size_t buffer_length));
+PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr,
+   png_bytep buffer, png_size_t buffer_length));
+PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row));
+PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr));
+#if defined(PNG_READ_tEXt_SUPPORTED)
+PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+#endif
+
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+#endif /* PNG_INTERNAL */
+
+#ifdef __cplusplus
+}
+#endif
+
+/* do not put anything past this line */
+#endif /* _PNG_H */
diff --git a/src/png/pngconf.h b/src/png/pngconf.h
new file mode 100644 (file)
index 0000000..028f2a1
--- /dev/null
@@ -0,0 +1,705 @@
+
+/* pngconf.h - machine configurable file for libpng
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ */
+
+/* Any machine specific code is near the front of this file, so if you
+ * are configuring libpng for a machine, you may want to read the section
+ * starting here down to where it starts to typedef png_color, png_text,
+ * and png_info.
+ */
+
+#ifndef PNGCONF_H
+#define PNGCONF_H
+
+
+/* This is the size of the compression buffer, and thus the size of
+ * an IDAT chunk.  Make this whatever size you feel is best for your
+ * machine.  One of these will be allocated per png_struct.  When this
+ * is full, it writes the data to the disk, and does some other
+ * calculations.  Making this an extremely small size will slow
+ * the library down, but you may want to experiment to determine
+ * where it becomes significant, if you are concerned with memory
+ * usage.  Note that zlib allocates at least 32Kb also.  For readers,
+ * this describes the size of the buffer available to read the data in.
+ * Unless this gets smaller than the size of a row (compressed),
+ * it should not make much difference how big this is.
+ */
+
+#ifndef PNG_ZBUF_SIZE
+#define PNG_ZBUF_SIZE 8192
+#endif
+
+/* If you are running on a machine where you cannot allocate more
+ * than 64K of memory at once, uncomment this.  While libpng will not
+ * normally need that much memory in a chunk (unless you load up a very
+ * large file), zlib needs to know how big of a chunk it can use, and
+ * libpng thus makes sure to check any memory allocation to verify it
+ * will fit into memory.
+#define PNG_MAX_MALLOC_64K
+ */
+#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K)
+#define PNG_MAX_MALLOC_64K
+#endif
+
+/* This protects us against compilers that run on a windowing system
+ * and thus don't have or would rather us not use the stdio types:
+ * stdin, stdout, and stderr.  The only one currently used is stderr
+ * in png_error() and png_warning().  #defining PNG_NO_CONSOLE_IO will
+ * prevent these from being compiled and used. #defining PNG_NO_STDIO
+ * will also prevent these, plus will prevent the entire set of stdio
+ * macros and functions (FILE *, printf, etc.) from being compiled and used,
+ * unless PNG_DEBUG has been #defined.
+ *
+ * #define PNG_NO_CONSOLE_IO
+ * #define PNG_NO_STDIO
+ */
+
+#ifdef PNG_DEBUG
+#  if (PNG_DEBUG > 0)
+#    include <stdio.h>
+#  endif
+#else
+#  ifdef PNG_NO_STDIO
+#    ifndef PNG_NO_CONSOLE_IO
+#      define PNG_NO_CONSOLE_IO
+#    endif
+#  else
+#    include <stdio.h>
+#  endif
+#endif
+
+/* This macro protects us against machines that don't have function
+ * prototypes (ie K&R style headers).  If your compiler does not handle
+ * function prototypes, define this macro and use the included ansi2knr.
+ * I've always been able to use _NO_PROTO as the indicator, but you may
+ * need to drag the empty declaration out in front of here, or change the
+ * ifdef to suit your own needs.
+ */
+#ifndef PNGARG
+
+#ifdef OF /* zlib prototype munger */
+#define PNGARG(arglist) OF(arglist)
+#else
+
+#ifdef _NO_PROTO
+#define PNGARG(arglist) ()
+#else
+#define PNGARG(arglist) arglist
+#endif /* _NO_PROTO */
+
+#endif /* OF */
+
+#endif /* PNGARG */
+
+/* Try to determine if we are compiling on a Mac.  Note that testing for
+ * just __MWERKS__ is not good enough, because the Codewarrior is now used
+ * on non-Mac platforms.
+ */
+#ifndef MACOS
+#if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \
+    defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC)
+#define MACOS
+#endif
+#endif
+
+/* enough people need this for various reasons to include it here */
+#if !defined(MACOS) && !defined(RISCOS)
+#include <sys/types.h>
+#endif
+
+/* This is an attempt to force a single setjmp behaviour on Linux.  If
+ * the X config stuff didn't define _BSD_SOURCE we wouldn't need this.
+ */
+#ifdef __linux__
+#ifdef _BSD_SOURCE
+#define _PNG_SAVE_BSD_SOURCE
+#undef _BSD_SOURCE
+#endif
+#ifdef _SETJMP_H
+__png.h__ already includes setjmp.h
+__dont__ include it again
+#endif
+#endif /* __linux__ */
+
+/* include setjmp.h for error handling */
+#include <setjmp.h>
+
+#ifdef __linux__
+#ifdef _PNG_SAVE_BSD_SOURCE
+#define _BSD_SOURCE
+#undef _PNG_SAVE_BSD_SOURCE
+#endif
+#endif /* __linux__ */
+
+#ifdef BSD
+#include <strings.h>
+#else
+#include <string.h>
+#endif
+
+/* Other defines for things like memory and the like can go here.  */
+#ifdef PNG_INTERNAL
+#include <stdlib.h>
+
+/* The functions exported by PNG_EXTERN are PNG_INTERNAL functions, which
+ * aren't usually used outside the library (as far as I know), so it is
+ * debatable if they should be exported at all.  In the future, when it is
+ * possible to have run-time registry of chunk-handling functions, some of
+ * these will be made available again.
+#define PNG_EXTERN extern
+ */
+#define PNG_EXTERN
+
+/* Other defines specific to compilers can go here.  Try to keep
+ * them inside an appropriate ifdef/endif pair for portability.
+ */
+
+#if defined(MACOS)
+/* We need to check that <math.h> hasn't already been included earlier
+ * as it seems it doesn't agree with <fp.h>, yet we should really use
+ * <fp.h> if possible.
+ */
+#if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__)
+#include <fp.h>
+#endif
+#else
+#include <math.h>
+#endif
+
+/* Codewarrior on NT has linking problems without this. */
+#if defined(__MWERKS__) && defined(WIN32)
+#define PNG_ALWAYS_EXTERN
+#endif
+
+/* For some reason, Borland C++ defines memcmp, etc. in mem.h, not
+ * stdlib.h like it should (I think).  Or perhaps this is a C++
+ * "feature"?
+ */
+#ifdef __TURBOC__
+#include <mem.h>
+#include "alloc.h"
+#endif
+
+#ifdef _MSC_VER
+#include <malloc.h>
+#endif
+
+/* This controls how fine the dithering gets.  As this allocates
+ * a largish chunk of memory (32K), those who are not as concerned
+ * with dithering quality can decrease some or all of these.
+ */
+#ifndef PNG_DITHER_RED_BITS
+#define PNG_DITHER_RED_BITS 5
+#endif
+#ifndef PNG_DITHER_GREEN_BITS
+#define PNG_DITHER_GREEN_BITS 5
+#endif
+#ifndef PNG_DITHER_BLUE_BITS
+#define PNG_DITHER_BLUE_BITS 5
+#endif
+
+/* This controls how fine the gamma correction becomes when you
+ * are only interested in 8 bits anyway.  Increasing this value
+ * results in more memory being used, and more pow() functions
+ * being called to fill in the gamma tables.  Don't set this value
+ * less then 8, and even that may not work (I haven't tested it).
+ */
+
+#ifndef PNG_MAX_GAMMA_8
+#define PNG_MAX_GAMMA_8 11
+#endif
+
+/* This controls how much a difference in gamma we can tolerate before
+ * we actually start doing gamma conversion.
+ */
+#ifndef PNG_GAMMA_THRESHOLD
+#define PNG_GAMMA_THRESHOLD 0.05
+#endif
+
+#endif /* PNG_INTERNAL */
+
+/* The following uses const char * instead of char * for error
+ * and warning message functions, so some compilers won't complain.
+ * If you do not want to use const, define PNG_NO_CONST here.
+ */
+
+#ifndef PNG_NO_CONST
+#  define PNG_CONST const
+#else
+#  define PNG_CONST
+#endif
+
+/* The following defines give you the ability to remove code from the
+ * library that you will not be using.  I wish I could figure out how to
+ * automate this, but I can't do that without making it seriously hard
+ * on the users.  So if you are not using an ability, change the #define
+ * to and #undef, and that part of the library will not be compiled.  If
+ * your linker can't find a function, you may want to make sure the
+ * ability is defined here.  Some of these depend upon some others being
+ * defined.  I haven't figured out all the interactions here, so you may
+ * have to experiment awhile to get everything to compile.  If you are
+ * creating or using a shared library, you probably shouldn't touch this,
+ * as it will affect the size of the structures, and this will cause bad
+ * things to happen if the library and/or application ever change.
+ */
+
+/* Any transformations you will not be using can be undef'ed here */
+
+/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user
+   to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS
+   on the compile line, then pick and choose which ones to define without
+   having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED
+   if you only want to have a png-compliant reader/writer but don't need
+   any of the extra transformations.  This saves about 80 kbytes in a
+   typical installation of the library. (PNG_NO_* form added in version
+   1.0.1c, for consistency)
+ */
+
+
+#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \
+    !defined(PNG_NO_READ_TRANSFORMS)
+#define PNG_READ_TRANSFORMS_SUPPORTED
+#endif
+#if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \
+    !defined(PNG_NO_WRITE_TRANSFORMS)
+#define PNG_WRITE_TRANSFORMS_SUPPORTED
+#endif
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+#ifndef PNG_NO_READ_EXPAND
+#define PNG_READ_EXPAND_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_SHIFT
+#define PNG_READ_SHIFT_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_PACK
+#define PNG_READ_PACK_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_BGR
+#define PNG_READ_BGR_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_SWAP
+#define PNG_READ_SWAP_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_PACKSWAP
+#define PNG_READ_PACKSWAP_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_INVERT
+#define PNG_READ_INVERT_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_DITHER
+#define PNG_READ_DITHER_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_BACKGROUND
+#define PNG_READ_BACKGROUND_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_16_TO_8
+#define PNG_READ_16_TO_8_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_FILLER
+#define PNG_READ_FILLER_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_GAMMA
+#define PNG_READ_GAMMA_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_GRAY_TO_RGB
+#define PNG_READ_GRAY_TO_RGB_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_SWAP_ALPHA
+#define PNG_READ_SWAP_ALPHA_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_INVERT_ALPHA
+#define PNG_READ_INVERT_ALPHA_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_STRIP_ALPHA
+#define PNG_READ_STRIP_ALPHA_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_USER_TRANSFORM
+#define PNG_READ_USER_TRANSFORM_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_RGB_TO_GRAY
+#define PNG_READ_RGB_TO_GRAY_SUPPORTED
+#endif
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+#if !defined(PNG_NO_PROGRESSIVE_READ) && \
+ !defined(PNG_PROGRESSIVE_READ_NOT_SUPPORTED) /* if you don't do progressive   */
+#define PNG_PROGRESSIVE_READ_SUPPORTED       /* reading.  This is not talking */
+#endif                               /* about interlacing capability!  You'll */
+              /* still have interlacing unless you change the following line: */
+#define PNG_READ_INTERLACING_SUPPORTED /* required for PNG-compliant decoders */
+
+#ifndef PNG_NO_READ_COMPOSITED_NODIV
+#define PNG_READ_COMPOSITE_NODIV_SUPPORTED    /* well tested on Intel and SGI */
+#endif
+
+#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+#ifndef PNG_NO_WRITE_SHIFT
+#define PNG_WRITE_SHIFT_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_PACK
+#define PNG_WRITE_PACK_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_BGR
+#define PNG_WRITE_BGR_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_SWAP
+#define PNG_WRITE_SWAP_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_PACKSWAP
+#define PNG_WRITE_PACKSWAP_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_INVERT
+#define PNG_WRITE_INVERT_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_FILLER
+#define PNG_WRITE_FILLER_SUPPORTED  /* This is the same as WRITE_STRIP_ALPHA */
+#endif
+#ifndef PNG_NO_WRITE_SWAP_ALPHA
+#define PNG_WRITE_SWAP_ALPHA_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_INVERT_ALPHA
+#define PNG_WRITE_INVERT_ALPHA_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_USER_TRANSFORM
+#define PNG_WRITE_USER_TRANSFORM_SUPPORTED
+#endif
+#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */
+
+#define PNG_WRITE_INTERLACING_SUPPORTED  /* not required for PNG-compliant
+                                            encoders, but can cause trouble
+                                            if left undefined */
+
+#ifndef PNG_NO_WRITE_WEIGHTED_FILTER
+#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+#endif
+
+#ifndef PNG_NO_WRITE_FLUSH
+#define PNG_WRITE_FLUSH_SUPPORTED
+#endif
+
+#ifndef PNG_NO_STDIO
+#define PNG_TIME_RFC1123_SUPPORTED
+#endif
+
+/* This adds extra functions in pngget.c for accessing data from the
+ * info pointer (added in version 0.99)
+ * png_get_image_width()
+ * png_get_image_height()
+ * png_get_bit_depth()
+ * png_get_color_type()
+ * png_get_compression_type()
+ * png_get_filter_type()
+ * png_get_interlace_type()
+ * png_get_pixel_aspect_ratio()
+ * png_get_pixels_per_meter()
+ * png_get_x_offset_pixels()
+ * png_get_y_offset_pixels()
+ * png_get_x_offset_microns()
+ * png_get_y_offset_microns()
+ */
+#ifndef PNG_NO_EASY_ACCESS
+#define PNG_EASY_ACCESS_SUPPORTED
+#endif
+
+/* These are currently experimental features, define them if you want */
+
+/* very little testing */
+/*
+#define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
+#define PNG_USER_MEM_SUPPORTED
+*/
+
+/* This is only for PowerPC big-endian and 680x0 systems */
+/* some testing */
+/*
+#define PNG_READ_BIG_ENDIAN_SUPPORTED
+*/
+
+/* These functions are turned off by default, as they will be phased out. */
+/*
+#define  PNG_USELESS_TESTS_SUPPORTED
+#define  PNG_CORRECT_PALETTE_SUPPORTED
+*/
+
+/* Any chunks you are not interested in, you can undef here.  The
+ * ones that allocate memory may be expecially important (hIST,
+ * tEXt, zTXt, tRNS, pCAL).  Others will just save time and make png_info
+ * a bit smaller.
+ */
+
+#if !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \
+    !defined(PNG_NO_READ_ANCILLARY_CHUNKS)
+#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
+#endif
+#if !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \
+    !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS)
+#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
+#endif
+
+#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
+#ifndef PNG_NO_READ_bKGD
+#define PNG_READ_bKGD_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_cHRM
+#define PNG_READ_cHRM_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_gAMA
+#define PNG_READ_gAMA_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_hIST
+#define PNG_READ_hIST_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_oFFs
+#define PNG_READ_oFFs_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_pCAL
+#define PNG_READ_pCAL_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_pHYs
+#define PNG_READ_pHYs_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_sBIT
+#define PNG_READ_sBIT_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_sRGB
+#define PNG_READ_sRGB_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_tEXt
+#define PNG_READ_tEXt_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_tIME
+#define PNG_READ_tIME_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_tRNS
+#define PNG_READ_tRNS_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_zTXt
+#define PNG_READ_zTXt_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_OPT_PLTE
+#define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the optional */
+#endif                              /* PLTE chunk in RGB and RGBA images */
+#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */
+
+#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
+#ifndef PNG_NO_WRITE_bKGD
+#define PNG_WRITE_bKGD_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_cHRM
+#define PNG_WRITE_cHRM_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_gAMA
+#define PNG_WRITE_gAMA_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_hIST
+#define PNG_WRITE_hIST_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_oFFs
+#define PNG_WRITE_oFFs_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_pCAL
+#define PNG_WRITE_pCAL_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_pHYs
+#define PNG_WRITE_pHYs_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_sBIT
+#define PNG_WRITE_sBIT_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_sRGB
+#define PNG_WRITE_sRGB_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_tEXt
+#define PNG_WRITE_tEXt_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_tIME
+#define PNG_WRITE_tIME_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_tRNS
+#define PNG_WRITE_tRNS_SUPPORTED
+#endif
+#ifndef PNG_NO_WRITE_zTXt
+#define PNG_WRITE_zTXt_SUPPORTED
+#endif
+#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */
+
+/* need the time information for reading tIME chunks */
+#if defined(PNG_READ_tIME_SUPPORTED) || defined(PNG_WRITE_tIME_SUPPORTED)
+#include <time.h>
+#endif
+
+/* Some typedefs to get us started.  These should be safe on most of the
+ * common platforms.  The typedefs should be at least as large as the
+ * numbers suggest (a png_uint_32 must be at least 32 bits long), but they
+ * don't have to be exactly that size.  Some compilers dislike passing
+ * unsigned shorts as function parameters, so you may be better off using
+ * unsigned int for png_uint_16.  Likewise, for 64-bit systems, you may
+ * want to have unsigned int for png_uint_32 instead of unsigned long.
+ */
+
+typedef unsigned long png_uint_32;
+typedef long png_int_32;
+typedef unsigned short png_uint_16;
+typedef short png_int_16;
+typedef unsigned char png_byte;
+
+/* This is usually size_t.  It is typedef'ed just in case you need it to
+   change (I'm not sure if you will or not, so I thought I'd be safe) */
+typedef size_t png_size_t;
+
+/* The following is needed for medium model support.  It cannot be in the
+ * PNG_INTERNAL section.  Needs modification for other compilers besides
+ * MSC.  Model independent support declares all arrays and pointers to be
+ * large using the far keyword.  The zlib version used must also support
+ * model independent data.  As of version zlib 1.0.4, the necessary changes
+ * have been made in zlib.  The USE_FAR_KEYWORD define triggers other
+ * changes that are needed. (Tim Wegner)
+ */
+
+/* Separate compiler dependencies (problem here is that zlib.h always
+   defines FAR. (SJT) */
+#ifdef __BORLANDC__
+#if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__)
+#define LDATA 1
+#else
+#define LDATA 0
+#endif
+
+#if !defined(__WIN32__) && !defined(__FLAT__)
+#define PNG_MAX_MALLOC_64K
+#if (LDATA != 1)
+#ifndef FAR
+#define FAR __far
+#endif
+#define USE_FAR_KEYWORD
+#endif   /* LDATA != 1 */
+
+/* Possibly useful for moving data out of default segment.
+ * Uncomment it if you want. Could also define FARDATA as
+ * const if your compiler supports it. (SJT)
+#  define FARDATA FAR
+ */
+#endif  /* __WIN32__, __FLAT__ */
+
+#endif   /* __BORLANDC__ */
+
+
+/* Suggest testing for specific compiler first before testing for
+ * FAR.  The Watcom compiler defines both __MEDIUM__ and M_I86MM,
+ * making reliance oncertain keywords suspect. (SJT)
+ */
+
+/* MSC Medium model */
+#if defined(FAR)
+#  if defined(M_I86MM)
+#     define USE_FAR_KEYWORD
+#     define FARDATA FAR
+#     include <dos.h>
+#  endif
+#endif
+
+/* SJT: default case */
+#ifndef FAR
+#   define FAR
+#endif
+
+/* At this point FAR is always defined */
+#ifndef FARDATA
+#define FARDATA
+#endif
+
+/* Add typedefs for pointers */
+typedef void            FAR * png_voidp;
+typedef png_byte        FAR * png_bytep;
+typedef png_uint_32     FAR * png_uint_32p;
+typedef png_int_32      FAR * png_int_32p;
+typedef png_uint_16     FAR * png_uint_16p;
+typedef png_int_16      FAR * png_int_16p;
+typedef PNG_CONST char  FAR * png_const_charp;
+typedef char            FAR * png_charp;
+typedef double          FAR * png_doublep;
+
+/* Pointers to pointers; i.e. arrays */
+typedef png_byte        FAR * FAR * png_bytepp;
+typedef png_uint_32     FAR * FAR * png_uint_32pp;
+typedef png_int_32      FAR * FAR * png_int_32pp;
+typedef png_uint_16     FAR * FAR * png_uint_16pp;
+typedef png_int_16      FAR * FAR * png_int_16pp;
+typedef PNG_CONST char  FAR * FAR * png_const_charpp;
+typedef char            FAR * FAR * png_charpp;
+typedef double          FAR * FAR * png_doublepp;
+
+/* Pointers to pointers to pointers; i.e. pointer to array */
+typedef char            FAR * FAR * FAR * png_charppp;
+
+/* libpng typedefs for types in zlib. If zlib changes
+ * or another compression library is used, then change these.
+ * Eliminates need to change all the source files.
+ */
+typedef charf *         png_zcharp;
+typedef charf * FAR *   png_zcharpp;
+typedef z_stream FAR *  png_zstreamp;
+
+/* allow for compilation as dll under MS Windows */
+#ifdef __WIN32DLL__
+#define PNG_EXPORT(type,symbol) __declspec(dllexport) type symbol
+#endif
+
+/* allow for compilation as dll with BORLAND C++ 5.0 */
+#if defined(__BORLANDC__) && defined(_Windows) && defined(__DLL__)
+#   define PNG_EXPORT(type,symbol) type _export symbol
+#endif
+
+/* allow for compilation as shared lib under BeOS */
+#ifdef __BEOSDLL__
+#define PNG_EXPORT(type,symbol) __declspec(export) type symbol
+#endif
+
+#ifndef PNG_EXPORT
+#define PNG_EXPORT(type,symbol) type symbol
+#endif
+
+
+/* User may want to use these so not in PNG_INTERNAL. Any library functions
+ * that are passed far data must be model independent.
+ */
+
+#if defined(USE_FAR_KEYWORD)  /* memory model independent fns */
+/* use this to make far-to-near assignments */
+#   define CHECK   1
+#   define NOCHECK 0
+#   define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK))
+#   define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK))
+#   define png_strcpy _fstrcpy
+#   define png_strlen _fstrlen
+#   define png_memcmp _fmemcmp      /* SJT: added */
+#   define png_memcpy _fmemcpy
+#   define png_memset _fmemset
+#else /* use the usual functions */
+#   define CVT_PTR(ptr)         (ptr)
+#   define CVT_PTR_NOCHECK(ptr) (ptr)
+#   define png_strcpy strcpy
+#   define png_strlen strlen
+#   define png_memcmp memcmp     /* SJT: added */
+#   define png_memcpy memcpy
+#   define png_memset memset
+#endif
+/* End of memory model independent support */
+
+/* Just a double check that someone hasn't tried to define something
+ * contradictory.
+ */
+#if (PNG_ZBUF_SIZE > 65536) && defined(PNG_MAX_MALLOC_64K)
+#undef PNG_ZBUF_SIZE
+#define PNG_ZBUF_SIZE 65536
+#endif
+
+#endif /* PNGCONF_H */
+
diff --git a/src/png/pngerror.c b/src/png/pngerror.c
new file mode 100644 (file)
index 0000000..c591000
--- /dev/null
@@ -0,0 +1,172 @@
+
+/* pngerror.c - stub functions for i/o and memory allocation
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ *
+ * This file provides a location for all error handling.  Users who
+ * need special error handling are expected to write replacement functions
+ * and use png_set_error_fn() to use those functions.  See the instructions
+ * at each function.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+static void png_default_error PNGARG((png_structp png_ptr,
+                                      png_const_charp message));
+static void png_default_warning PNGARG((png_structp png_ptr,
+                                        png_const_charp message));
+
+/* This function is called whenever there is a fatal error.  This function
+ * should not be changed.  If there is a need to handle errors differently,
+ * you should supply a replacement error function and use png_set_error_fn()
+ * to replace the error function at run-time.
+ */
+void
+png_error(png_structp png_ptr, png_const_charp message)
+{
+   if (png_ptr->error_fn != NULL)
+      (*(png_ptr->error_fn))(png_ptr, message);
+
+   /* if the following returns or doesn't exist, use the default function,
+      which will not return */
+   png_default_error(png_ptr, message);
+}
+
+/* This function is called whenever there is a non-fatal error.  This function
+ * should not be changed.  If there is a need to handle warnings differently,
+ * you should supply a replacement warning function and use
+ * png_set_error_fn() to replace the warning function at run-time.
+ */
+void
+png_warning(png_structp png_ptr, png_const_charp message)
+{
+   if (png_ptr->warning_fn != NULL)
+      (*(png_ptr->warning_fn))(png_ptr, message);
+   else
+      png_default_warning(png_ptr, message);
+}
+
+/* These utilities are used internally to build an error message that relates
+ * to the current chunk.  The chunk name comes from png_ptr->chunk_name,
+ * this is used to prefix the message.  The message is limited in length
+ * to 63 bytes, the name characters are output as hex digits wrapped in []
+ * if the character is invalid.
+ */
+#define isnonalpha(c) ((c) < 41 || (c) > 122 || ((c) > 90 && (c) < 97))
+static PNG_CONST char png_digit[16] = {
+   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+static void
+png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp message)
+{
+   int iout = 0, iin = 0;
+
+   while (iin < 4) {
+      int c = png_ptr->chunk_name[iin++];
+      if (isnonalpha(c)) {
+         buffer[iout++] = '[';
+         buffer[iout++] = png_digit[(c & 0xf0) >> 4];
+         buffer[iout++] = png_digit[c & 0xf];
+         buffer[iout++] = ']';
+      } else {
+         buffer[iout++] = c;
+      }
+   }
+
+   if (message == NULL)
+      buffer[iout] = 0;
+   else {
+      buffer[iout++] = ':';
+      buffer[iout++] = ' ';
+      png_memcpy(buffer+iout, message, 64);
+      buffer[iout+63] = 0;
+   }
+}
+
+void
+png_chunk_error(png_structp png_ptr, png_const_charp message)
+{
+   char msg[16+64];
+   png_format_buffer(png_ptr, msg, message);
+   png_error(png_ptr, msg);
+}
+
+void
+png_chunk_warning(png_structp png_ptr, png_const_charp message)
+{
+   char msg[16+64];
+   png_format_buffer(png_ptr, msg, message);
+   png_warning(png_ptr, msg);
+}
+
+/* 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
+png_default_error(png_structp png_ptr, png_const_charp message)
+{
+#ifndef PNG_NO_CONSOLE_IO
+   fprintf(stderr, "libpng error: %s\n", message);
+#endif
+
+#ifdef USE_FAR_KEYWORD
+   {
+      jmp_buf jmpbuf;
+      png_memcpy(jmpbuf,png_ptr->jmpbuf,sizeof(jmp_buf));
+      longjmp(jmpbuf, 1);
+   }
+#else
+   longjmp(png_ptr->jmpbuf, 1);
+#endif
+}
+
+/* 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 them to.  In the default configuration, png_ptr is
+ * not used, but it is passed in case it may be useful.
+ */
+static void
+png_default_warning(png_structp png_ptr, png_const_charp message)
+{
+#ifndef PNG_NO_CONSOLE_IO
+   fprintf(stderr, "libpng warning: %s\n", message);
+#endif
+   if (png_ptr == NULL)
+      return;
+}
+
+/* This function is called when the application wants to use another method
+ * of handling errors and warnings.  Note that the error function MUST NOT
+ * return to the calling routine or serious problems will occur.  The return
+ * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1)
+ */
+void
+png_set_error_fn(png_structp png_ptr, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warning_fn)
+{
+   png_ptr->error_ptr = error_ptr;
+   png_ptr->error_fn = error_fn;
+   png_ptr->warning_fn = warning_fn;
+}
+
+
+/* This function returns a pointer to the error_ptr associated with the user
+ * functions.  The application should free any memory associated with this
+ * pointer before png_write_destroy and png_read_destroy are called.
+ */
+png_voidp
+png_get_error_ptr(png_structp png_ptr)
+{
+   return ((png_voidp)png_ptr->error_ptr);
+}
+
+
+
diff --git a/src/png/pngget.c b/src/png/pngget.c
new file mode 100644 (file)
index 0000000..484a011
--- /dev/null
@@ -0,0 +1,650 @@
+
+/* pngget.c - retrieval of values from info struct
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+png_uint_32
+png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flag)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->valid & flag);
+   else
+      return(0);
+}
+
+png_uint_32
+png_get_rowbytes(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->rowbytes);
+   else
+      return(0);
+}
+
+#ifdef PNG_EASY_ACCESS_SUPPORTED
+/* easy access to info, added in libpng-0.99 */
+png_uint_32
+png_get_image_width(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->width;
+   }
+   return (0);
+}
+
+png_uint_32
+png_get_image_height(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->height;
+   }
+   return (0);
+}
+
+png_byte
+png_get_bit_depth(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->bit_depth;
+   }
+   return (0);
+}
+
+png_byte
+png_get_color_type(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->color_type;
+   }
+   return (0);
+}
+
+png_byte
+png_get_filter_type(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->filter_type;
+   }
+   return (0);
+}
+
+png_byte
+png_get_interlace_type(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->interlace_type;
+   }
+   return (0);
+}
+
+png_byte
+png_get_compression_type(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->compression_type;
+   }
+   return (0);
+}
+
+png_uint_32
+png_get_x_pixels_per_meter(png_structp png_ptr, png_infop info_ptr)
+{
+#if defined(PNG_READ_pHYs_SUPPORTED) || defined(PNG_WRITE_pHYs_SUPPORTED)
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_x_pixels_per_meter");
+      if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER)
+          return (0);
+      else return (info_ptr->x_pixels_per_unit);
+   }
+   else
+#endif
+   return (0);
+}
+
+png_uint_32
+png_get_y_pixels_per_meter(png_structp png_ptr, png_infop info_ptr)
+{
+#if defined(PNG_READ_pHYs_SUPPORTED) || defined(PNG_WRITE_pHYs_SUPPORTED)
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_y_pixels_per_meter");
+      if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER)
+          return (0);
+      else return (info_ptr->y_pixels_per_unit);
+   }
+   else
+#endif
+   return (0);
+}
+
+png_uint_32
+png_get_pixels_per_meter(png_structp png_ptr, png_infop info_ptr)
+{
+#if defined(PNG_READ_pHYs_SUPPORTED) || defined(PNG_WRITE_pHYs_SUPPORTED)
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_pixels_per_meter");
+      if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER ||
+         info_ptr->x_pixels_per_unit != info_ptr->y_pixels_per_unit)
+          return (0);
+      else return (info_ptr->x_pixels_per_unit);
+   }
+   else
+#endif
+   return (0);
+}
+
+float
+png_get_pixel_aspect_ratio(png_structp png_ptr, png_infop info_ptr)
+   {
+#if defined(PNG_READ_pHYs_SUPPORTED) || defined(PNG_WRITE_pHYs_SUPPORTED)
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_aspect_ratio");
+      if (info_ptr->x_pixels_per_unit == 0)
+         return ((float)0.0);
+      else
+         return ((float)info_ptr->y_pixels_per_unit
+            /(float)info_ptr->x_pixels_per_unit);
+   }
+   else
+#endif
+      return ((float)0.0);
+}
+
+png_uint_32
+png_get_x_offset_microns(png_structp png_ptr, png_infop info_ptr)
+{
+#if defined(PNG_READ_oFFs_SUPPORTED) || defined(PNG_WRITE_oFFs_SUPPORTED)
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns");
+      if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER)
+          return (0);
+      else return (info_ptr->x_offset);
+   }
+   else
+#endif
+   return (0);
+}
+
+png_uint_32
+png_get_y_offset_microns(png_structp png_ptr, png_infop info_ptr)
+{
+#if defined(PNG_READ_oFFs_SUPPORTED) || defined(PNG_WRITE_oFFs_SUPPORTED)
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns");
+      if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER)
+          return (0);
+      else return (info_ptr->y_offset);
+   }
+   else
+#endif
+   return (0);
+}
+
+png_uint_32
+png_get_x_offset_pixels(png_structp png_ptr, png_infop info_ptr)
+{
+#if defined(PNG_READ_oFFs_SUPPORTED) || defined(PNG_WRITE_oFFs_SUPPORTED)
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns");
+      if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL)
+          return (0);
+      else return (info_ptr->x_offset);
+   }
+   else
+#endif
+   return (0);
+}
+
+png_uint_32
+png_get_y_offset_pixels(png_structp png_ptr, png_infop info_ptr)
+{
+#if defined(PNG_READ_oFFs_SUPPORTED) || defined(PNG_WRITE_oFFs_SUPPORTED)
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns");
+      if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL)
+          return (0);
+      else return (info_ptr->y_offset);
+   }
+   else
+#endif
+   return (0);
+}
+
+#ifdef PNG_INCH_CONVERSIONS
+png_uint_32
+png_get_pixels_per_inch(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((png_uint_32)((float)png_get_pixels_per_meter(png_ptr, info_ptr)
+     *.03937 +.5)
+}
+
+png_uint_32
+png_get_x_pixels_per_inch(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((png_uint_32)((float)png_get_x_pixels_per_meter(png_ptr, info_ptr)
+     *.03937 +.5)
+}
+
+png_uint_32
+png_get_y_pixels_per_inch(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((png_uint_32)((float)png_get_y_pixels_per_meter(png_ptr, info_ptr)
+     *.03937 +.5)
+}
+
+float
+png_get_x_offset_inches(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((float)png_get_x_offset_microns(png_ptr, info_ptr)
+     *.03937/1000000. +.5)
+}
+
+float
+png_get_y_offset_inches(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((float)png_get_y_offset_microns(png_ptr, info_ptr)
+     *.03937/1000000. +.5)
+}
+
+#if defined(PNG_READ_pHYs_SUPPORTED)
+png_uint_32
+png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)
+{
+   png_uint_32 retval = 0;
+
+   if (png_ptr != NULL && info_ptr != NULL && info_ptr->valid & PNG_INFO_pHYs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "pHYs");
+      if (res_x != NULL)
+      {
+         *res_x = info_ptr->x_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+      if (res_y != NULL)
+      {
+         *res_y = info_ptr->y_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+      if (unit_type != NULL)
+      {
+         *unit_type = (int)info_ptr->phys_unit_type;
+         retval |= PNG_INFO_pHYs;
+         if(unit_type == 1)
+         {
+            if (res_x != NULL) *res_x = (png_uint_32)(*res_x * 39.37 + .50);
+            if (res_y != NULL) *res_y = (png_uint_32)(*res_y * 39.37 + .50);
+         }
+      }
+   }
+   return (retval);
+}
+#endif /* PNG_READ_pHYs_SUPPORTED */
+#endif  /* PNG_INCH_CONVERSIONS */
+
+/* png_get_channels really belongs in here, too, but it's been around longer */
+
+#endif  /* PNG_EASY_ACCESS_SUPPORTED */
+
+png_byte
+png_get_channels(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->channels);
+   else
+      return (0);
+}
+
+png_bytep
+png_get_signature(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->signature);
+   else
+      return (NULL);
+}
+
+#if defined(PNG_READ_bKGD_SUPPORTED)
+png_uint_32
+png_get_bKGD(png_structp png_ptr, png_infop info_ptr,
+   png_color_16p *background)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)
+      && background != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "bKGD");
+      *background = &(info_ptr->background);
+      return (PNG_INFO_bKGD);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_READ_cHRM_SUPPORTED)
+png_uint_32
+png_get_cHRM(png_structp png_ptr, png_infop info_ptr,
+   double *white_x, double *white_y, double *red_x, double *red_y,
+   double *green_x, double *green_y, double *blue_x, double *blue_y)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM))
+   {
+      png_debug1(1, "in %s retrieval function\n", "cHRM");
+      if (white_x != NULL)
+         *white_x = (double)info_ptr->x_white;
+      if (white_y != NULL)
+         *white_y = (double)info_ptr->y_white;
+      if (red_x != NULL)
+         *red_x = (double)info_ptr->x_red;
+      if (red_y != NULL)
+         *red_y = (double)info_ptr->y_red;
+      if (green_x != NULL)
+         *green_x = (double)info_ptr->x_green;
+      if (green_y != NULL)
+         *green_y = (double)info_ptr->y_green;
+      if (blue_x != NULL)
+         *blue_x = (double)info_ptr->x_blue;
+      if (blue_y != NULL)
+         *blue_y = (double)info_ptr->y_blue;
+      return (PNG_INFO_cHRM);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_READ_gAMA_SUPPORTED)
+png_uint_32
+png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)
+      && file_gamma != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "gAMA");
+      *file_gamma = (double)info_ptr->gamma;
+      return (PNG_INFO_gAMA);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+png_uint_32
+png_get_sRGB(png_structp png_ptr, png_infop info_ptr, int *file_srgb_intent)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)
+      && file_srgb_intent != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "sRGB");
+      *file_srgb_intent = (int)info_ptr->srgb_intent;
+      return (PNG_INFO_sRGB);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_READ_hIST_SUPPORTED)
+png_uint_32
+png_get_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)
+      && hist != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "hIST");
+      *hist = info_ptr->hist;
+      return (PNG_INFO_hIST);
+   }
+   return (0);
+}
+#endif
+
+png_uint_32
+png_get_IHDR(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 *width, png_uint_32 *height, int *bit_depth,
+   int *color_type, int *interlace_type, int *compression_type,
+   int *filter_type)
+
+{
+   if (png_ptr != NULL && info_ptr != NULL && width != NULL && height != NULL &&
+      bit_depth != NULL && color_type != NULL)
+   {
+      int pixel_depth, channels;
+      png_uint_32 rowbytes_per_pixel;
+
+      png_debug1(1, "in %s retrieval function\n", "IHDR");
+      *width = info_ptr->width;
+      *height = info_ptr->height;
+      *bit_depth = info_ptr->bit_depth;
+      *color_type = info_ptr->color_type;
+      if (compression_type != NULL)
+         *compression_type = info_ptr->compression_type;
+      if (filter_type != NULL)
+         *filter_type = info_ptr->filter_type;
+      if (interlace_type != NULL)
+         *interlace_type = info_ptr->interlace_type;
+
+      /* check for potential overflow of rowbytes */
+      if (*color_type == PNG_COLOR_TYPE_PALETTE)
+         channels = 1;
+      else if (*color_type & PNG_COLOR_MASK_COLOR)
+         channels = 3;
+      else
+         channels = 1;
+      if (*color_type & PNG_COLOR_MASK_ALPHA)
+         channels++;
+      pixel_depth = *bit_depth * channels;
+      rowbytes_per_pixel = (pixel_depth + 7) >> 3;
+      if ((*width > (png_uint_32)2147483647L/rowbytes_per_pixel))
+      {
+         png_warning(png_ptr,
+            "Width too large for libpng to process image data.");
+      }
+      return (1);
+   }
+   return (0);
+}
+
+#if defined(PNG_READ_oFFs_SUPPORTED)
+png_uint_32
+png_get_oFFs(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 *offset_x, png_uint_32 *offset_y, int *unit_type)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)
+      && offset_x != NULL && offset_y != NULL && unit_type != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "oFFs");
+      *offset_x = info_ptr->x_offset;
+      *offset_y = info_ptr->y_offset;
+      *unit_type = (int)info_ptr->offset_unit_type;
+      return (PNG_INFO_oFFs);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_READ_pCAL_SUPPORTED)
+png_uint_32
+png_get_pCAL(png_structp png_ptr, png_infop info_ptr,
+   png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams,
+   png_charp *units, png_charpp *params)
+{
+   if (png_ptr != NULL && info_ptr != NULL && info_ptr->valid & PNG_INFO_pCAL &&
+      purpose != NULL && X0 != NULL && X1 != NULL && type != NULL &&
+      nparams != NULL && units != NULL && params != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "pCAL");
+      *purpose = info_ptr->pcal_purpose;
+      *X0 = info_ptr->pcal_X0;
+      *X1 = info_ptr->pcal_X1;
+      *type = (int)info_ptr->pcal_type;
+      *nparams = (int)info_ptr->pcal_nparams;
+      *units = info_ptr->pcal_units;
+      *params = info_ptr->pcal_params;
+      return (PNG_INFO_pCAL);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_READ_pHYs_SUPPORTED)
+png_uint_32
+png_get_pHYs(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)
+{
+   png_uint_32 retval = 0;
+
+   if (png_ptr != NULL && info_ptr != NULL && info_ptr->valid & PNG_INFO_pHYs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "pHYs");
+      if (res_x != NULL)
+      {
+         *res_x = info_ptr->x_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+      if (res_y != NULL)
+      {
+         *res_y = info_ptr->y_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+      if (unit_type != NULL)
+      {
+         *unit_type = (int)info_ptr->phys_unit_type;
+         retval |= PNG_INFO_pHYs;
+      }
+   }
+   return (retval);
+}
+#endif
+
+png_uint_32
+png_get_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp *palette,
+   int *num_palette)
+{
+   if (png_ptr != NULL && info_ptr != NULL && info_ptr->valid & PNG_INFO_PLTE &&
+       palette != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "PLTE");
+      *palette = info_ptr->palette;
+      *num_palette = info_ptr->num_palette;
+      png_debug1(3, "num_palette = %d\n", *num_palette);
+      return (PNG_INFO_PLTE);
+   }
+   return (0);
+}
+
+#if defined(PNG_READ_sBIT_SUPPORTED)
+png_uint_32
+png_get_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit)
+{
+   if (png_ptr != NULL && info_ptr != NULL && info_ptr->valid & PNG_INFO_sBIT &&
+       sig_bit != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "sBIT");
+      *sig_bit = &(info_ptr->sig_bit);
+      return (PNG_INFO_sBIT);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_READ_tEXt_SUPPORTED) || defined(PNG_READ_zTXt_SUPPORTED)
+png_uint_32
+png_get_text(png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr,
+   int *num_text)
+{
+   if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0)
+   {
+      png_debug1(1, "in %s retrieval function\n",
+         (png_ptr->chunk_name[0] == '\0' ? "text"
+             : (png_const_charp)png_ptr->chunk_name));
+      if (text_ptr != NULL)
+         *text_ptr = info_ptr->text;
+      if (num_text != NULL)
+         *num_text = info_ptr->num_text;
+      return ((png_uint_32)info_ptr->num_text);
+   }
+   return(0);
+}
+#endif
+
+#if defined(PNG_READ_tIME_SUPPORTED)
+png_uint_32
+png_get_tIME(png_structp png_ptr, png_infop info_ptr, png_timep *mod_time)
+{
+   if (png_ptr != NULL && info_ptr != NULL && info_ptr->valid & PNG_INFO_tIME &&
+       mod_time != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "tIME");
+      *mod_time = &(info_ptr->mod_time);
+      return (PNG_INFO_tIME);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_READ_tRNS_SUPPORTED)
+png_uint_32
+png_get_tRNS(png_structp png_ptr, png_infop info_ptr,
+   png_bytep *trans, int *num_trans, png_color_16p *trans_values)
+{
+   png_uint_32 retval = 0;
+   if (png_ptr != NULL && info_ptr != NULL && info_ptr->valid & PNG_INFO_tRNS)
+   {
+      png_debug1(1, "in %s retrieval function\n", "tRNS");
+      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+          if (trans != NULL)
+          {
+             *trans = info_ptr->trans;
+             retval |= PNG_INFO_tRNS;
+          }
+          if (trans_values != NULL)
+             *trans_values = &(info_ptr->trans_values);
+      }
+      else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */
+      {
+          if (trans_values != NULL)
+          {
+             *trans_values = &(info_ptr->trans_values);
+             retval |= PNG_INFO_tRNS;
+          }
+          if(trans != NULL)
+             *trans = NULL;
+      }
+      if(num_trans != NULL)
+      {
+         *num_trans = info_ptr->num_trans;
+         retval |= PNG_INFO_tRNS;
+      }
+   }
+   return (retval);
+}
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+png_byte
+png_get_rgb_to_gray_status (png_structp png_ptr)
+{
+   return png_ptr->rgb_to_gray_status;
+}
+#endif
diff --git a/src/png/pngmem.c b/src/png/pngmem.c
new file mode 100644 (file)
index 0000000..06c4c1b
--- /dev/null
@@ -0,0 +1,511 @@
+
+/* pngmem.c - stub functions for memory allocation
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ *
+ * This file provides a location for all memory allocation.  Users who
+ * need special memory handling are expected to supply replacement
+ * functions for png_malloc() and png_free(), and to use
+ * png_create_read_struct_2() and png_create_write_struct_2() to
+ * identify the replacement functions.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Borland DOS special memory handler */
+#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
+/* if you change this, be sure to change the one in png.h also */
+
+/* Allocate memory for a png_struct.  The malloc and memset can be replaced
+   by a single call to calloc() if this is thought to improve performance. */
+png_voidp
+png_create_struct(int type)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   return (png_create_struct_2(type, NULL));
+}
+
+/* Alternate version of png_create_struct, for use with user-defined malloc. */
+png_voidp
+png_create_struct_2(int type, png_malloc_ptr malloc_fn)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+   png_size_t size;
+   png_voidp struct_ptr;
+
+   if (type == PNG_STRUCT_INFO)
+     size = sizeof(png_info);
+   else if (type == PNG_STRUCT_PNG)
+     size = sizeof(png_struct);
+   else
+     return ((png_voidp)NULL);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if(malloc_fn != NULL)
+   {
+      if ((struct_ptr = (*(malloc_fn))(NULL, size)) != NULL)
+         png_memset(struct_ptr, 0, size);
+         return (struct_ptr);
+   }
+#endif /* PNG_USER_MEM_SUPPORTED */
+   if ((struct_ptr = (png_voidp)farmalloc(size)) != NULL)
+   {
+      png_memset(struct_ptr, 0, size);
+   }
+   return (struct_ptr);
+}
+
+
+/* Free memory allocated by a png_create_struct() call */
+void
+png_destroy_struct(png_voidp struct_ptr)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_destroy_struct_2(struct_ptr, (png_free_ptr)NULL);
+}
+
+/* Free memory allocated by a png_create_struct() call */
+void
+png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn)
+{
+#endif
+   if (struct_ptr != NULL)
+   {
+#ifdef PNG_USER_MEM_SUPPORTED
+      if(free_fn != NULL)
+      {
+         png_struct dummy_struct;
+         png_structp png_ptr = &dummy_struct;
+         (*(free_fn))(png_ptr, struct_ptr);
+         struct_ptr = NULL;
+         return;
+      }
+#endif /* PNG_USER_MEM_SUPPORTED */
+      farfree (struct_ptr);
+      struct_ptr = NULL;
+   }
+}
+
+/* Allocate memory.  For reasonable files, size should never exceed
+ * 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.
+ *
+ * Borland seems to have a problem in DOS mode for exactly 64K.
+ * It gives you a segment with an offset of 8 (perhaps to store its
+ * memory stuff).  zlib doesn't like this at all, so we have to
+ * detect and deal with it.  This code should not be needed in
+ * Windows or OS/2 modes, and only in 16 bit mode.  This code has
+ * been updated by Alexander Lehmann for version 0.89 to waste less
+ * memory.
+ *
+ * Note that we can't use png_size_t for the "size" declaration,
+ * since on some systems a png_size_t is a 16-bit quantity, and as a
+ * result, we would be truncating potentially larger memory requests
+ * (which should cause a fatal error) and introducing major problems.
+ */
+png_voidp
+png_malloc(png_structp png_ptr, png_uint_32 size)
+{
+#ifndef PNG_USER_MEM_SUPPORTED
+   png_voidp ret;
+#endif
+   if (png_ptr == NULL || size == 0)
+      return ((png_voidp)NULL);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if(png_ptr->malloc_fn != NULL)
+       return ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, size));
+   else
+       return png_malloc_default(png_ptr, size);
+}
+
+png_voidp
+png_malloc_default(png_structp png_ptr, png_uint_32 size)
+{
+   png_voidp ret;
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (size > (png_uint_32)65536L)
+      png_error(png_ptr, "Cannot Allocate > 64K");
+#endif
+
+   if (size == (png_uint_32)65536L)
+   {
+      if (png_ptr->offset_table == NULL)
+      {
+         /* try to see if we need to do any of this fancy stuff */
+         ret = farmalloc(size);
+         if (ret == NULL || ((png_size_t)ret & 0xffff))
+         {
+            int num_blocks;
+            png_uint_32 total_size;
+            png_bytep table;
+            int i;
+            png_byte huge * hptr;
+
+            if (ret != NULL)
+            {
+               farfree(ret);
+               ret = NULL;
+            }
+
+            num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14));
+            if (num_blocks < 1)
+               num_blocks = 1;
+            if (png_ptr->zlib_mem_level >= 7)
+               num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7));
+            else
+               num_blocks++;
+
+            total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16;
+
+            table = farmalloc(total_size);
+
+            if (table == NULL)
+            {
+               png_error(png_ptr, "Out Of Memory."); /* Note "O" and "M" */
+            }
+
+            if ((png_size_t)table & 0xfff0)
+            {
+               png_error(png_ptr, "Farmalloc didn't return normalized pointer");
+            }
+
+            png_ptr->offset_table = table;
+            png_ptr->offset_table_ptr = farmalloc(num_blocks *
+               sizeof (png_bytep));
+
+            if (png_ptr->offset_table_ptr == NULL)
+            {
+               png_error(png_ptr, "Out Of memory.");
+            }
+
+            hptr = (png_byte huge *)table;
+            if ((png_size_t)hptr & 0xf)
+            {
+               hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L);
+               hptr += 16L;
+            }
+            for (i = 0; i < num_blocks; i++)
+            {
+               png_ptr->offset_table_ptr[i] = (png_bytep)hptr;
+               hptr += (png_uint_32)65536L;
+            }
+
+            png_ptr->offset_table_number = num_blocks;
+            png_ptr->offset_table_count = 0;
+            png_ptr->offset_table_count_free = 0;
+         }
+      }
+
+      if (png_ptr->offset_table_count >= png_ptr->offset_table_number)
+         png_error(png_ptr, "Out of Memory.");
+
+      ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++];
+   }
+   else
+      ret = farmalloc(size);
+
+   if (ret == NULL)
+   {
+      png_error(png_ptr, "Out of memory."); /* Note "o" and "m" */
+   }
+
+   return (ret);
+}
+
+/* free a pointer allocated by png_malloc().  In the default
+   configuration, png_ptr is not used, but is passed in case it
+   is needed.  If ptr is NULL, return without taking any action. */
+void
+png_free(png_structp png_ptr, png_voidp ptr)
+{
+   if (png_ptr == NULL || ptr == NULL)
+      return;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if (png_ptr->free_fn != NULL)
+   {
+      (*(png_ptr->free_fn))(png_ptr, ptr);
+      ptr = NULL;
+      return;
+   }
+   else png_free_default(png_ptr, ptr);
+}
+
+void
+png_free_default(png_structp png_ptr, png_voidp ptr)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+   if (png_ptr->offset_table != NULL)
+   {
+      int i;
+
+      for (i = 0; i < png_ptr->offset_table_count; i++)
+      {
+         if (ptr == png_ptr->offset_table_ptr[i])
+         {
+            ptr = NULL;
+            png_ptr->offset_table_count_free++;
+            break;
+         }
+      }
+      if (png_ptr->offset_table_count_free == png_ptr->offset_table_count)
+      {
+         farfree(png_ptr->offset_table);
+         farfree(png_ptr->offset_table_ptr);
+         png_ptr->offset_table = NULL;
+         png_ptr->offset_table_ptr = NULL;
+      }
+   }
+
+   if (ptr != NULL)
+   {
+      farfree(ptr);
+      ptr = NULL;
+   }
+}
+
+#else /* Not the Borland DOS special memory handler */
+
+/* Allocate memory for a png_struct or a png_info.  The malloc and
+   memset can be replaced by a single call to calloc() if this is thought
+   to improve performance noticably.*/
+png_voidp
+png_create_struct(int type)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   return (png_create_struct_2(type, NULL));
+}
+
+/* Allocate memory for a png_struct or a png_info.  The malloc and
+   memset can be replaced by a single call to calloc() if this is thought
+   to improve performance noticably.*/
+png_voidp
+png_create_struct_2(int type, png_malloc_ptr malloc_fn)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+   png_size_t size;
+   png_voidp struct_ptr;
+
+   if (type == PNG_STRUCT_INFO)
+      size = sizeof(png_info);
+   else if (type == PNG_STRUCT_PNG)
+      size = sizeof(png_struct);
+   else
+      return ((png_voidp)NULL);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if(malloc_fn != NULL)
+   {
+      if ((struct_ptr = (*(malloc_fn))(NULL, size)) != NULL)
+         png_memset(struct_ptr, 0, size);
+      return (struct_ptr);
+   }
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+#if defined(__TURBOC__) && !defined(__FLAT__)
+   if ((struct_ptr = (png_voidp)farmalloc(size)) != NULL)
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+   if ((struct_ptr = (png_voidp)halloc(size,1)) != NULL)
+# else
+   if ((struct_ptr = (png_voidp)malloc(size)) != NULL)
+# endif
+#endif
+   {
+      png_memset(struct_ptr, 0, size);
+   }
+
+   return (struct_ptr);
+}
+
+
+/* Free memory allocated by a png_create_struct() call */
+void
+png_destroy_struct(png_voidp struct_ptr)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_destroy_struct_2(struct_ptr, (png_free_ptr)NULL);
+}
+
+/* Free memory allocated by a png_create_struct() call */
+void
+png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+   if (struct_ptr != NULL)
+   {
+#ifdef PNG_USER_MEM_SUPPORTED
+      if(free_fn != NULL)
+      {
+         png_struct dummy_struct;
+         png_structp png_ptr = &dummy_struct;
+         (*(free_fn))(png_ptr, struct_ptr);
+         struct_ptr = NULL;
+         return;
+      }
+#endif /* PNG_USER_MEM_SUPPORTED */
+#if defined(__TURBOC__) && !defined(__FLAT__)
+      farfree(struct_ptr);
+      struct_ptr = NULL;
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+      hfree(struct_ptr);
+      struct_ptr = NULL;
+# else
+      free(struct_ptr);
+      struct_ptr = NULL;
+# endif
+#endif
+   }
+}
+
+
+/* Allocate memory.  For reasonable files, size should never exceed
+   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. */
+
+png_voidp
+png_malloc(png_structp png_ptr, png_uint_32 size)
+{
+#ifndef PNG_USER_MEM_SUPPORTED
+   png_voidp ret;
+#endif
+   if (png_ptr == NULL || size == 0)
+      return ((png_voidp)NULL);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if(png_ptr->malloc_fn != NULL)
+       return ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, size));
+   else
+       return (png_malloc_default(png_ptr, size));
+}
+png_voidp
+png_malloc_default(png_structp png_ptr, png_uint_32 size)
+{
+   png_voidp ret;
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (size > (png_uint_32)65536L)
+      png_error(png_ptr, "Cannot Allocate > 64K");
+#endif
+
+#if defined(__TURBOC__) && !defined(__FLAT__)
+   ret = farmalloc(size);
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+   ret = halloc(size, 1);
+# else
+   ret = malloc((size_t)size);
+# endif
+#endif
+
+   if (ret == NULL)
+   {
+      png_error(png_ptr, "Out of Memory");
+   }
+
+   return (ret);
+}
+
+/* Free a pointer allocated by png_malloc().  If ptr is NULL, return
+   without taking any action. */
+void
+png_free(png_structp png_ptr, png_voidp ptr)
+{
+   if (png_ptr == NULL || ptr == NULL)
+      return;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if (png_ptr->free_fn != NULL)
+   {
+      (*(png_ptr->free_fn))(png_ptr, ptr);
+      ptr = NULL;
+      return;
+   }
+   else png_free_default(png_ptr, ptr);
+}
+void
+png_free_default(png_structp png_ptr, png_voidp ptr)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+#if defined(__TURBOC__) && !defined(__FLAT__)
+   farfree(ptr);
+   ptr = NULL;
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+   hfree(ptr);
+   ptr = NULL;
+# else
+   free(ptr);
+   ptr = NULL;
+# endif
+#endif
+}
+
+#endif /* Not Borland DOS special memory handler */
+
+png_voidp
+png_memcpy_check (png_structp png_ptr, png_voidp s1, png_voidp s2,
+   png_uint_32 length)
+{
+   png_size_t size;
+
+   size = (png_size_t)length;
+   if ((png_uint_32)size != length)
+      png_error(png_ptr,"Overflow in png_memcpy_check.");
+
+   return(png_memcpy (s1, s2, size));
+}
+
+png_voidp
+png_memset_check (png_structp png_ptr, png_voidp s1, int value,
+   png_uint_32 length)
+{
+   png_size_t size;
+
+   size = (png_size_t)length;
+   if ((png_uint_32)size != length)
+      png_error(png_ptr,"Overflow in png_memset_check.");
+
+   return (png_memset (s1, value, size));
+
+}
+
+#ifdef PNG_USER_MEM_SUPPORTED
+/* This function is called when the application wants to use another method
+ * of allocating and freeing memory.
+ */
+void
+png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr
+  malloc_fn, png_free_ptr free_fn)
+{
+   png_ptr->mem_ptr = mem_ptr;
+   png_ptr->malloc_fn = malloc_fn;
+   png_ptr->free_fn = free_fn;
+}
+
+/* This function returns a pointer to the mem_ptr associated with the user
+ * functions.  The application should free any memory associated with this
+ * pointer before png_write_destroy and png_read_destroy are called.
+ */
+png_voidp
+png_get_mem_ptr(png_structp png_ptr)
+{
+   return ((png_voidp)png_ptr->mem_ptr);
+}
+#endif /* PNG_USER_MEM_SUPPORTED */
diff --git a/src/png/pngpread.c b/src/png/pngpread.c
new file mode 100644 (file)
index 0000000..b446518
--- /dev/null
@@ -0,0 +1,1141 @@
+
+/* pngpread.c - read a png file in push mode
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+
+void
+png_process_data(png_structp png_ptr, png_infop info_ptr,
+   png_bytep buffer, png_size_t buffer_size)
+{
+   png_push_restore_buffer(png_ptr, buffer, buffer_size);
+
+   while (png_ptr->buffer_size)
+   {
+      png_process_some_data(png_ptr, info_ptr);
+   }
+}
+
+/* What we do with the incoming data depends on what we were previously
+ * doing before we ran out of data...
+ */
+void
+png_process_some_data(png_structp png_ptr, png_infop info_ptr)
+{
+   switch (png_ptr->process_mode)
+   {
+      case PNG_READ_SIG_MODE:
+      {
+         png_push_read_sig(png_ptr, info_ptr);
+         break;
+      }
+      case PNG_READ_CHUNK_MODE:
+      {
+         png_push_read_chunk(png_ptr, info_ptr);
+         break;
+      }
+      case PNG_READ_IDAT_MODE:
+      {
+         png_push_read_IDAT(png_ptr);
+         break;
+      }
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      case PNG_READ_tEXt_MODE:
+      {
+         png_push_read_tEXt(png_ptr, info_ptr);
+         break;
+      }
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      case PNG_READ_zTXt_MODE:
+      {
+         png_push_read_zTXt(png_ptr, info_ptr);
+         break;
+      }
+#endif
+      case PNG_SKIP_MODE:
+      {
+         png_push_crc_finish(png_ptr);
+         break;
+      }
+      default:
+      {
+         png_ptr->buffer_size = 0;
+         break;
+      }
+   }
+}
+
+/* Read any remaining signature bytes from the stream and compare them with
+ * the correct PNG signature.  It is possible that this routine is called
+ * with bytes already read from the signature, either because they have been
+ * checked by the calling application, or because of multiple calls to this
+ * routine.
+ */
+void
+png_push_read_sig(png_structp png_ptr, png_infop info_ptr)
+{
+   png_size_t num_checked = png_ptr->sig_bytes,
+             num_to_check = 8 - num_checked;
+
+   if (png_ptr->buffer_size < num_to_check)
+   {
+      num_to_check = png_ptr->buffer_size;
+   }
+
+   png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]),
+      num_to_check);
+   png_ptr->sig_bytes += num_to_check;
+
+   if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
+   {
+      if (num_checked < 4 &&
+          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
+         png_error(png_ptr, "Not a PNG file");
+      else
+         png_error(png_ptr, "PNG file corrupted by ASCII conversion");
+   }
+   else
+   {
+      if (png_ptr->sig_bytes >= 8)
+      {
+         png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+      }
+   }
+}
+
+void
+png_push_read_chunk(png_structp png_ptr, png_infop info_ptr)
+{
+   /* First we make sure we have enough data for the 4 byte chunk name
+    * and the 4 byte chunk length before proceeding with decoding the
+    * chunk data.  To fully decode each of these chunks, we also make
+    * sure we have enough data in the buffer for the 4 byte CRC at the
+    * end of every chunk (except IDAT, which is handled separately).
+    */
+   if (!(png_ptr->flags & PNG_FLAG_HAVE_CHUNK_HEADER))
+   {
+      png_byte chunk_length[4];
+
+      if (png_ptr->buffer_size < 8)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_fill_buffer(png_ptr, chunk_length, 4);
+      png_ptr->push_length = png_get_uint_32(chunk_length);
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+      png_ptr->flags |= PNG_FLAG_HAVE_CHUNK_HEADER;
+   }
+
+   if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length);
+   }
+   else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length);
+   }
+   else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+   {
+      /* If we reach an IDAT chunk, this means we have read all of the
+       * header chunks, and we can start reading the image (or if this
+       * is called after the image has been read - we have an error).
+       */
+      if (png_ptr->mode & PNG_HAVE_IDAT)
+      {
+         if (png_ptr->push_length == 0)
+            return;
+
+         if (png_ptr->mode & PNG_AFTER_IDAT)
+            png_error(png_ptr, "Too many IDAT's found");
+      }
+
+      png_ptr->idat_size = png_ptr->push_length;
+      png_ptr->mode |= PNG_HAVE_IDAT;
+      png_ptr->process_mode = PNG_READ_IDAT_MODE;
+      png_push_have_info(png_ptr, info_ptr);
+      png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes;
+      png_ptr->zstream.next_out = png_ptr->row_buf;
+      return;
+   }
+   else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length);
+      png_ptr->process_mode = PNG_READ_DONE_MODE;
+      png_push_have_end(png_ptr, info_ptr);
+   }
+#if defined(PNG_READ_gAMA_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_bKGD_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4))
+   {
+      png_push_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4))
+   {
+      png_push_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+   else
+   {
+      png_push_handle_unknown(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+   png_ptr->flags &= ~PNG_FLAG_HAVE_CHUNK_HEADER;
+}
+
+void
+png_push_crc_skip(png_structp png_ptr, png_uint_32 skip)
+{
+   png_ptr->process_mode = PNG_SKIP_MODE;
+   png_ptr->skip_length = skip;
+}
+
+void
+png_push_crc_finish(png_structp png_ptr)
+{
+   if (png_ptr->skip_length && png_ptr->save_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (png_ptr->skip_length < (png_uint_32)png_ptr->save_buffer_size)
+         save_size = (png_size_t)png_ptr->skip_length;
+      else
+         save_size = png_ptr->save_buffer_size;
+
+      png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);
+
+      png_ptr->skip_length -= save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->save_buffer_size -= save_size;
+      png_ptr->save_buffer_ptr += save_size;
+   }
+   if (png_ptr->skip_length && png_ptr->current_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (png_ptr->skip_length < (png_uint_32)png_ptr->current_buffer_size)
+         save_size = (png_size_t)png_ptr->skip_length;
+      else
+         save_size = png_ptr->current_buffer_size;
+
+      png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);
+
+      png_ptr->skip_length -= save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->current_buffer_size -= save_size;
+      png_ptr->current_buffer_ptr += save_size;
+   }
+   if (!png_ptr->skip_length)
+   {
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_crc_finish(png_ptr, 0);
+      png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+   }
+}
+
+void
+png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length)
+{
+   png_bytep ptr;
+
+   ptr = buffer;
+   if (png_ptr->save_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (length < png_ptr->save_buffer_size)
+         save_size = length;
+      else
+         save_size = png_ptr->save_buffer_size;
+
+      png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size);
+      length -= save_size;
+      ptr += save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->save_buffer_size -= save_size;
+      png_ptr->save_buffer_ptr += save_size;
+   }
+   if (length && png_ptr->current_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (length < png_ptr->current_buffer_size)
+         save_size = length;
+      else
+         save_size = png_ptr->current_buffer_size;
+
+      png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size);
+      png_ptr->buffer_size -= save_size;
+      png_ptr->current_buffer_size -= save_size;
+      png_ptr->current_buffer_ptr += save_size;
+   }
+}
+
+void
+png_push_save_buffer(png_structp png_ptr)
+{
+   if (png_ptr->save_buffer_size)
+   {
+      if (png_ptr->save_buffer_ptr != png_ptr->save_buffer)
+      {
+         png_size_t i,istop;
+         png_bytep sp;
+         png_bytep dp;
+
+         istop = png_ptr->save_buffer_size;
+         for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer;
+            i < istop; i++, sp++, dp++)
+         {
+            *dp = *sp;
+         }
+      }
+   }
+   if (png_ptr->save_buffer_size + png_ptr->current_buffer_size >
+      png_ptr->save_buffer_max)
+   {
+      png_size_t new_max;
+      png_bytep old_buffer;
+
+      new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256;
+      old_buffer = png_ptr->save_buffer;
+      png_ptr->save_buffer = (png_bytep)png_malloc(png_ptr,
+         (png_uint_32)new_max);
+      png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size);
+      png_free(png_ptr, old_buffer);
+      png_ptr->save_buffer_max = new_max;
+   }
+   if (png_ptr->current_buffer_size)
+   {
+      png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size,
+         png_ptr->current_buffer_ptr, png_ptr->current_buffer_size);
+      png_ptr->save_buffer_size += png_ptr->current_buffer_size;
+      png_ptr->current_buffer_size = 0;
+   }
+   png_ptr->save_buffer_ptr = png_ptr->save_buffer;
+   png_ptr->buffer_size = 0;
+}
+
+void
+png_push_restore_buffer(png_structp png_ptr, png_bytep buffer,
+   png_size_t buffer_length)
+{
+   png_ptr->current_buffer = buffer;
+   png_ptr->current_buffer_size = buffer_length;
+   png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size;
+   png_ptr->current_buffer_ptr = png_ptr->current_buffer;
+}
+
+void
+png_push_read_IDAT(png_structp png_ptr)
+{
+   if (!(png_ptr->flags & PNG_FLAG_HAVE_CHUNK_HEADER))
+   {
+      png_byte chunk_length[4];
+
+      if (png_ptr->buffer_size < 8)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_fill_buffer(png_ptr, chunk_length, 4);
+      png_ptr->push_length = png_get_uint_32(chunk_length);
+
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+      png_ptr->flags |= PNG_FLAG_HAVE_CHUNK_HEADER;
+
+      if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+      {
+         png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+         if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+            png_error(png_ptr, "Not enough compressed data");
+         return;
+      }
+
+      png_ptr->idat_size = png_ptr->push_length;
+   }
+   if (png_ptr->idat_size && png_ptr->save_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (png_ptr->idat_size < (png_uint_32)png_ptr->save_buffer_size)
+      {
+         save_size = (png_size_t)png_ptr->idat_size;
+         /* check for overflow */
+         if((png_uint_32)save_size != png_ptr->idat_size)
+            png_error(png_ptr, "save_size overflowed in pngpread");
+      }
+      else
+         save_size = png_ptr->save_buffer_size;
+
+      png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);
+      png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size);
+
+      png_ptr->idat_size -= save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->save_buffer_size -= save_size;
+      png_ptr->save_buffer_ptr += save_size;
+   }
+   if (png_ptr->idat_size && png_ptr->current_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (png_ptr->idat_size < (png_uint_32)png_ptr->current_buffer_size)
+      {
+         save_size = (png_size_t)png_ptr->idat_size;
+         /* check for overflow */
+         if((png_uint_32)save_size != png_ptr->idat_size)
+            png_error(png_ptr, "save_size overflowed in pngpread");
+      }
+      else
+         save_size = png_ptr->current_buffer_size;
+
+      png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);
+      png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size);
+
+      png_ptr->idat_size -= save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->current_buffer_size -= save_size;
+      png_ptr->current_buffer_ptr += save_size;
+   }
+   if (!png_ptr->idat_size)
+   {
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_crc_finish(png_ptr, 0);
+      png_ptr->flags &= ~PNG_FLAG_HAVE_CHUNK_HEADER;
+   }
+}
+
+void
+png_process_IDAT_data(png_structp png_ptr, png_bytep buffer,
+   png_size_t buffer_length)
+{
+   int ret;
+
+   if ((png_ptr->flags & PNG_FLAG_ZLIB_FINISHED) && buffer_length)
+      png_error(png_ptr, "Extra compression data");
+
+   png_ptr->zstream.next_in = buffer;
+   png_ptr->zstream.avail_in = (uInt)buffer_length;
+   for(;;)
+   {
+      ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+      if (ret == Z_STREAM_END)
+      {
+         if (png_ptr->zstream.avail_in)
+            png_error(png_ptr, "Extra compressed data");
+         if (!(png_ptr->zstream.avail_out))
+         {
+            png_push_process_row(png_ptr);
+         }
+
+         png_ptr->mode |= PNG_AFTER_IDAT;
+         png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+         break;
+      }
+      else if (ret == Z_BUF_ERROR)
+         break;
+      else if (ret != Z_OK)
+         png_error(png_ptr, "Decompression Error");
+      if (!(png_ptr->zstream.avail_out))
+      {
+         png_push_process_row(png_ptr);
+         png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes;
+         png_ptr->zstream.next_out = png_ptr->row_buf;
+      }
+      else
+         break;
+   }
+}
+
+void
+png_push_process_row(png_structp png_ptr)
+{
+   png_ptr->row_info.color_type = png_ptr->color_type;
+   png_ptr->row_info.width = png_ptr->iwidth;
+   png_ptr->row_info.channels = png_ptr->channels;
+   png_ptr->row_info.bit_depth = png_ptr->bit_depth;
+   png_ptr->row_info.pixel_depth = png_ptr->pixel_depth;
+
+   png_ptr->row_info.rowbytes = ((png_ptr->row_info.width *
+      (png_uint_32)png_ptr->row_info.pixel_depth + 7) >> 3);
+
+   png_read_filter_row(png_ptr, &(png_ptr->row_info),
+      png_ptr->row_buf + 1, png_ptr->prev_row + 1,
+      (int)(png_ptr->row_buf[0]));
+
+   png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf,
+      png_ptr->rowbytes + 1);
+
+   if (png_ptr->transformations)
+      png_do_read_transformations(png_ptr);
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+   /* blow up interlaced rows to full size */
+   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+   {
+      if (png_ptr->pass < 6)
+         png_do_read_interlace(&(png_ptr->row_info),
+            png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations);
+
+      switch (png_ptr->pass)
+      {
+         case 0:
+         {
+            int i;
+            for (i = 0; i < 8 && png_ptr->pass == 0; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            break;
+         }
+         case 1:
+         {
+            int i;
+            for (i = 0; i < 8 && png_ptr->pass == 1; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 2)
+            {
+               for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+               {
+                  png_push_have_row(png_ptr, NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+            break;
+         }
+         case 2:
+         {
+            int i;
+            for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+            {
+               png_push_have_row(png_ptr, NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+            break;
+         }
+         case 3:
+         {
+            int i;
+            for (i = 0; i < 4 && png_ptr->pass == 3; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 4)
+            {
+               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+               {
+                  png_push_have_row(png_ptr, NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+            break;
+         }
+         case 4:
+         {
+            int i;
+            for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+            {
+               png_push_have_row(png_ptr, NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+            break;
+         }
+         case 5:
+         {
+            int i;
+            for (i = 0; i < 2 && png_ptr->pass == 5; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 6)
+            {
+               png_push_have_row(png_ptr, NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+            break;
+         }
+         case 6:
+         {
+            png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+            png_read_push_finish_row(png_ptr);
+            if (png_ptr->pass != 6)
+               break;
+            png_push_have_row(png_ptr, NULL);
+            png_read_push_finish_row(png_ptr);
+         }
+      }
+   }
+   else
+#endif
+   {
+      png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+      png_read_push_finish_row(png_ptr);
+   }
+}
+
+void
+png_read_push_finish_row(png_structp png_ptr)
+{
+   png_ptr->row_number++;
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+   if (png_ptr->interlaced)
+   {
+      png_ptr->row_number = 0;
+      png_memset_check(png_ptr, png_ptr->prev_row, 0,
+         png_ptr->rowbytes + 1);
+      do
+      {
+         png_ptr->pass++;
+         if (png_ptr->pass >= 7)
+            break;
+
+         png_ptr->iwidth = (png_ptr->width +
+            png_pass_inc[png_ptr->pass] - 1 -
+            png_pass_start[png_ptr->pass]) /
+            png_pass_inc[png_ptr->pass];
+
+         png_ptr->irowbytes = ((png_ptr->iwidth *
+            png_ptr->pixel_depth + 7) >> 3) + 1;
+
+         if (png_ptr->transformations & PNG_INTERLACE)
+            break;
+
+         png_ptr->num_rows = (png_ptr->height +
+            png_pass_yinc[png_ptr->pass] - 1 -
+            png_pass_ystart[png_ptr->pass]) /
+            png_pass_yinc[png_ptr->pass];
+
+      } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0);
+   }
+}
+
+#if defined(PNG_READ_tEXt_SUPPORTED)
+void
+png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   if (png_ptr->mode == PNG_BEFORE_IHDR || png_ptr->mode & PNG_HAVE_IEND)
+      {
+         png_error(png_ptr, "Out of place tEXt");
+         /* to quiet some compiler warnings */
+         if(info_ptr == NULL) return;
+      }
+
+#ifdef PNG_MAX_MALLOC_64K
+   png_ptr->skip_length = 0;  /* This may not be necessary */
+
+   if (length > (png_uint_32)65535L) /* Can't hold the entire string in memory */
+   {
+      png_warning(png_ptr, "tEXt chunk too large to fit in memory");
+      png_ptr->skip_length = length - (png_uint_32)65535L;
+      length = (png_uint_32)65535L;
+   }
+#endif
+
+   png_ptr->current_text = (png_charp)png_malloc(png_ptr,
+         (png_uint_32)(length+1));
+   png_ptr->current_text[length] = '\0';
+   png_ptr->current_text_ptr = png_ptr->current_text;
+   png_ptr->current_text_size = (png_size_t)length;
+   png_ptr->current_text_left = (png_size_t)length;
+   png_ptr->process_mode = PNG_READ_tEXt_MODE;
+}
+
+void
+png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr->buffer_size && png_ptr->current_text_left)
+   {
+      png_size_t text_size;
+
+      if (png_ptr->buffer_size < png_ptr->current_text_left)
+         text_size = png_ptr->buffer_size;
+      else
+         text_size = png_ptr->current_text_left;
+      png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size);
+      png_ptr->current_text_left -= text_size;
+      png_ptr->current_text_ptr += text_size;
+   }
+   if (!(png_ptr->current_text_left))
+   {
+      png_textp text_ptr;
+      png_charp text;
+      png_charp key;
+
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_crc_finish(png_ptr);
+
+#if defined(PNG_MAX_MALLOC_64K)
+      if (png_ptr->skip_length)
+         return;
+#endif
+
+      key = png_ptr->current_text;
+      png_ptr->current_text = 0;
+
+      for (text = key; *text; text++)
+         /* empty loop */ ;
+
+      if (text != key + png_ptr->current_text_size)
+         text++;
+
+      text_ptr = (png_textp)png_malloc(png_ptr, (png_uint_32)sizeof(png_text));
+      text_ptr->compression = PNG_TEXT_COMPRESSION_NONE;
+      text_ptr->key = key;
+      text_ptr->text = text;
+
+      png_set_text(png_ptr, info_ptr, text_ptr, 1);
+
+      png_free(png_ptr, text_ptr);
+   }
+}
+#endif
+
+#if defined(PNG_READ_zTXt_SUPPORTED)
+void
+png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   if (png_ptr->mode == PNG_BEFORE_IHDR || png_ptr->mode & PNG_HAVE_IEND)
+      {
+         png_error(png_ptr, "Out of place zTXt");
+         /* to quiet some compiler warnings */
+         if(info_ptr == NULL) return;
+      }
+
+#ifdef PNG_MAX_MALLOC_64K
+   /* We can't handle zTXt chunks > 64K, since we don't have enough space
+    * to be able to store the uncompressed data.  Actually, the threshold
+    * is probably around 32K, but it isn't as definite as 64K is.
+    */
+   if (length > (png_uint_32)65535L)
+   {
+      png_warning(png_ptr, "zTXt chunk too large to fit in memory");
+      png_push_crc_skip(png_ptr, length);
+      return;
+   }
+#endif
+
+   png_ptr->current_text = (png_charp)png_malloc(png_ptr,
+       (png_uint_32)(length+1));
+   png_ptr->current_text[length] = '\0';
+   png_ptr->current_text_ptr = png_ptr->current_text;
+   png_ptr->current_text_size = (png_size_t)length;
+   png_ptr->current_text_left = (png_size_t)length;
+   png_ptr->process_mode = PNG_READ_zTXt_MODE;
+}
+
+void
+png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr->buffer_size && png_ptr->current_text_left)
+   {
+      png_size_t text_size;
+
+      if (png_ptr->buffer_size < (png_uint_32)png_ptr->current_text_left)
+         text_size = png_ptr->buffer_size;
+      else
+         text_size = png_ptr->current_text_left;
+      png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size);
+      png_ptr->current_text_left -= text_size;
+      png_ptr->current_text_ptr += text_size;
+   }
+   if (!(png_ptr->current_text_left))
+   {
+      png_textp text_ptr;
+      png_charp text;
+      png_charp key;
+      int ret;
+      png_size_t text_size, key_size;
+
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_crc_finish(png_ptr);
+
+      key = png_ptr->current_text;
+      png_ptr->current_text = 0;
+
+      for (text = key; *text; text++)
+         /* empty loop */ ;
+
+      /* zTXt can't have zero text */
+      if (text == key + png_ptr->current_text_size)
+      {
+         png_free(png_ptr, key);
+         return;
+      }
+
+      text++;
+
+      if (*text != PNG_TEXT_COMPRESSION_zTXt) /* check compression byte */
+      {
+         png_free(png_ptr, key);
+         return;
+      }
+
+      text++;
+
+      png_ptr->zstream.next_in = (png_bytep )text;
+      png_ptr->zstream.avail_in = (uInt)(png_ptr->current_text_size -
+         (text - key));
+      png_ptr->zstream.next_out = png_ptr->zbuf;
+      png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+      key_size = text - key;
+      text_size = 0;
+      text = NULL;
+      ret = Z_STREAM_END;
+
+      while (png_ptr->zstream.avail_in)
+      {
+         ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+         if (ret != Z_OK && ret != Z_STREAM_END)
+         {
+            inflateReset(&png_ptr->zstream);
+            png_ptr->zstream.avail_in = 0;
+            png_free(png_ptr, key);
+            png_free(png_ptr, text);
+            return;
+         }
+         if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END)
+         {
+            if (text == NULL)
+            {
+               text = (png_charp)png_malloc(png_ptr,
+                  (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out +
+                     key_size + 1));
+               png_memcpy(text + key_size, png_ptr->zbuf,
+                  png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+               png_memcpy(text, key, key_size);
+               text_size = key_size + png_ptr->zbuf_size -
+                  png_ptr->zstream.avail_out;
+               *(text + text_size) = '\0';
+            }
+            else
+            {
+               png_charp tmp;
+
+               tmp = text;
+               text = (png_charp)png_malloc(png_ptr, text_size +
+                  (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out
+                   + 1));
+               png_memcpy(text, tmp, text_size);
+               png_free(png_ptr, tmp);
+               png_memcpy(text + text_size, png_ptr->zbuf,
+                  png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+               text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+               *(text + text_size) = '\0';
+            }
+            if (ret != Z_STREAM_END)
+            {
+               png_ptr->zstream.next_out = png_ptr->zbuf;
+               png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+            }
+         }
+         else
+         {
+            break;
+         }
+
+         if (ret == Z_STREAM_END)
+            break;
+      }
+
+      inflateReset(&png_ptr->zstream);
+      png_ptr->zstream.avail_in = 0;
+
+      if (ret != Z_STREAM_END)
+      {
+         png_free(png_ptr, key);
+         png_free(png_ptr, text);
+         return;
+      }
+
+      png_free(png_ptr, key);
+      key = text;
+      text += key_size;
+
+      text_ptr = (png_textp)png_malloc(png_ptr, (png_uint_32)sizeof(png_text));
+      text_ptr->compression = PNG_TEXT_COMPRESSION_zTXt;
+      text_ptr->key = key;
+      text_ptr->text = text;
+
+      png_set_text(png_ptr, info_ptr, text_ptr, 1);
+
+      png_free(png_ptr, text_ptr);
+   }
+}
+#endif
+
+/* This function is called when we haven't found a handler for this
+ * chunk.  In the future we will have code here that can handle
+ * user-defined callback functions for unknown chunks before they are
+ * ignored or cause an error.  If there isn't a problem with the
+ * chunk itself (ie a bad chunk name or a critical chunk), the chunk
+ * is (currently) silently ignored.
+ */
+void
+png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+
+   if (!(png_ptr->chunk_name[0] & 0x20))
+   {
+      png_chunk_error(png_ptr, "unknown critical chunk");
+      /* to quiet some compiler warnings */
+      if(info_ptr == NULL) return;
+   }
+
+   png_push_crc_skip(png_ptr, length);
+}
+
+void
+png_push_have_info(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr->info_fn != NULL)
+      (*(png_ptr->info_fn))(png_ptr, info_ptr);
+}
+
+void
+png_push_have_end(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr->end_fn != NULL)
+      (*(png_ptr->end_fn))(png_ptr, info_ptr);
+}
+
+void
+png_push_have_row(png_structp png_ptr, png_bytep row)
+{
+   if (png_ptr->row_fn != NULL)
+      (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number,
+         (int)png_ptr->pass);
+}
+
+void
+png_progressive_combine_row (png_structp png_ptr,
+   png_bytep old_row, png_bytep new_row)
+{
+   if (new_row != NULL)    /* new_row must == png_ptr->row_buf here. */
+      png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]);
+}
+
+void
+png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr,
+   png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn,
+   png_progressive_end_ptr end_fn)
+{
+   png_ptr->info_fn = info_fn;
+   png_ptr->row_fn = row_fn;
+   png_ptr->end_fn = end_fn;
+
+   png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer);
+}
+
+png_voidp
+png_get_progressive_ptr(png_structp png_ptr)
+{
+   return png_ptr->io_ptr;
+}
+
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
diff --git a/src/png/pngread.c b/src/png/pngread.c
new file mode 100644 (file)
index 0000000..01613b1
--- /dev/null
@@ -0,0 +1,885 @@
+
+/* pngread.c - read a PNG file
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ *
+ * This file contains routines that an application calls directly to
+ * read a PNG file or stream.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Create a PNG structure for reading, and allocate any memory needed. */
+png_structp
+png_create_read_struct(png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn)
+{
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn,
+      warn_fn, NULL, NULL, NULL));
+}
+
+/* Alternate create PNG structure for reading, and allocate any memory needed. */
+png_structp
+png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+   png_malloc_ptr malloc_fn, png_free_ptr free_fn)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+   png_structp png_ptr;
+#ifdef USE_FAR_KEYWORD
+   jmp_buf jmpbuf;
+#endif
+   png_debug(1, "in png_create_read_struct\n");
+#ifdef PNG_USER_MEM_SUPPORTED
+   if ((png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
+      (png_malloc_ptr)malloc_fn)) == NULL)
+#else
+   if ((png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG)) == NULL)
+#endif
+   {
+      return (png_structp)NULL;
+   }
+#ifdef USE_FAR_KEYWORD
+   if (setjmp(jmpbuf))
+#else
+   if (setjmp(png_ptr->jmpbuf))
+#endif
+   {
+      png_free(png_ptr, png_ptr->zbuf);
+      png_destroy_struct(png_ptr);
+      return (png_structp)NULL;
+   }
+#ifdef USE_FAR_KEYWORD
+   png_memcpy(png_ptr->jmpbuf,jmpbuf,sizeof(jmp_buf));
+#endif
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+   png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
+
+   /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
+    * we must recompile any applications that use any older library version.
+    * For versions after libpng 1.0, we will be compatible, so we need
+    * only check the first digit.
+    */
+   if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
+       (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
+   {
+      png_error(png_ptr,
+         "Incompatible libpng version in application and library");
+   }
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
+     (png_uint_32)png_ptr->zbuf_size);
+   png_ptr->zstream.zalloc = png_zalloc;
+   png_ptr->zstream.zfree = png_zfree;
+   png_ptr->zstream.opaque = (voidpf)png_ptr;
+
+   switch (inflateInit(&png_ptr->zstream))
+   {
+     case Z_OK: /* Do nothing */ break;
+     case Z_MEM_ERROR:
+     case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory error"); break;
+     case Z_VERSION_ERROR: png_error(png_ptr, "zlib version error"); break;
+     default: png_error(png_ptr, "Unknown zlib error");
+   }
+
+   png_ptr->zstream.next_out = png_ptr->zbuf;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+   png_set_read_fn(png_ptr, NULL, NULL);
+
+   return (png_ptr);
+}
+
+/* Initialize PNG structure for reading, and allocate any memory needed.
+   This interface is deprecated in favour of the png_create_read_struct(),
+   and it will eventually disappear. */
+void
+png_read_init(png_structp png_ptr)
+{
+   jmp_buf tmp_jmp;  /* to save current jump buffer */
+
+   png_debug(1, "in png_read_init\n");
+   /* save jump buffer and error functions */
+   png_memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
+
+   /* reset all variables to 0 */
+   png_memset(png_ptr, 0, sizeof (png_struct));
+
+   /* restore jump buffer */
+   png_memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
+     (png_uint_32)png_ptr->zbuf_size);
+   png_ptr->zstream.zalloc = png_zalloc;
+   png_ptr->zstream.zfree = png_zfree;
+   png_ptr->zstream.opaque = (voidpf)png_ptr;
+
+   switch (inflateInit(&png_ptr->zstream))
+   {
+     case Z_OK: /* Do nothing */ break;
+     case Z_MEM_ERROR:
+     case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory"); break;
+     case Z_VERSION_ERROR: png_error(png_ptr, "zlib version"); break;
+     default: png_error(png_ptr, "Unknown zlib error");
+   }
+
+   png_ptr->zstream.next_out = png_ptr->zbuf;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+   png_set_read_fn(png_ptr, NULL, NULL);
+}
+
+/* Read the information before the actual image data.  This has been
+ * changed in v0.90 to allow reading a file that already has the magic
+ * bytes read from the stream.  You can tell libpng how many bytes have
+ * been read from the beginning of the stream (up to the maximum of 8)
+ * via png_set_sig_bytes(), and we will only check the remaining bytes
+ * here.  The application can then have access to the signature bytes we
+ * read if it is determined that this isn't a valid PNG file.
+ */
+void
+png_read_info(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_read_info\n");
+   /* save jump buffer and error functions */
+   /* If we haven't checked all of the PNG signature bytes, do so now. */
+   if (png_ptr->sig_bytes < 8)
+   {
+      png_size_t num_checked = png_ptr->sig_bytes,
+                 num_to_check = 8 - num_checked;
+
+      png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check);
+      png_ptr->sig_bytes = 8;
+
+      if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
+      {
+         if (num_checked < 4 &&
+             png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
+            png_error(png_ptr, "Not a PNG file");
+         else
+            png_error(png_ptr, "PNG file corrupted by ASCII conversion");
+      }
+   }
+
+   for(;;)
+   {
+      png_byte chunk_length[4];
+      png_uint_32 length;
+
+      png_read_data(png_ptr, chunk_length, 4);
+      length = png_get_uint_32(chunk_length);
+
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+
+      png_debug1(0, "Reading %s chunk.\n", png_ptr->chunk_name);
+
+      /* This should be a binary subdivision search or a hash for
+       * matching the chunk name rather than a linear search.
+       */
+      if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4))
+         png_handle_IHDR(png_ptr, info_ptr, length);
+      else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+         png_handle_PLTE(png_ptr, info_ptr, length);
+      else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4))
+         png_handle_IEND(png_ptr, info_ptr, length);
+      else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+      {
+         if (!(png_ptr->mode & PNG_HAVE_IHDR))
+            png_error(png_ptr, "Missing IHDR before IDAT");
+         else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+                  !(png_ptr->mode & PNG_HAVE_PLTE))
+            png_error(png_ptr, "Missing PLTE before IDAT");
+
+         png_ptr->idat_size = length;
+         png_ptr->mode |= PNG_HAVE_IDAT;
+         break;
+      }
+#if defined(PNG_READ_bKGD_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4))
+         png_handle_bKGD(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4))
+         png_handle_cHRM(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_gAMA_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4))
+         png_handle_gAMA(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4))
+         png_handle_hIST(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4))
+         png_handle_oFFs(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4))
+         png_handle_pCAL(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4))
+         png_handle_pHYs(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4))
+         png_handle_sBIT(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4))
+         png_handle_sRGB(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4))
+         png_handle_tEXt(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4))
+         png_handle_tIME(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4))
+         png_handle_tRNS(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4))
+         png_handle_zTXt(png_ptr, info_ptr, length);
+#endif
+      else
+         png_handle_unknown(png_ptr, info_ptr, length);
+   }
+}
+
+/* optional call to update the users info_ptr structure */
+void
+png_read_update_info(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_read_update_info\n");
+   /* save jump buffer and error functions */
+   if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+      png_read_start_row(png_ptr);
+   png_read_transform_info(png_ptr, info_ptr);
+}
+
+/* Initialize palette, background, etc, after transformations
+ * are set, but before any reading takes place.  This allows
+ * the user to obtain a gamma-corrected palette, for example.
+ * If the user doesn't call this, we will do it ourselves.
+ */
+void
+png_start_read_image(png_structp png_ptr)
+{
+   png_debug(1, "in png_start_read_image\n");
+   /* save jump buffer and error functions */
+   if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+      png_read_start_row(png_ptr);
+}
+
+void
+png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row)
+{
+   int ret;
+   png_debug2(1, "in png_read_row (row %d, pass %d)\n",
+      png_ptr->row_number, png_ptr->pass);
+   /* save jump buffer and error functions */
+   if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+      png_read_start_row(png_ptr);
+   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
+   {
+   /* check for transforms that have been set but were defined out */
+#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED)
+   if (png_ptr->transformations & PNG_FILLER)
+      png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && !defined(PNG_READ_PACKSWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACKSWAP)
+      png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACK)
+      png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED)
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED)
+   if (png_ptr->transformations & PNG_BGR)
+      png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined.");
+#endif
+   }
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+   /* if interlaced and we do not need a new row, combine row and return */
+   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+   {
+      switch (png_ptr->pass)
+      {
+         case 0:
+            if (png_ptr->row_number & 7)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 1:
+            if ((png_ptr->row_number & 7) || png_ptr->width < 5)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 2:
+            if ((png_ptr->row_number & 7) != 4)
+            {
+               if (dsp_row != NULL && (png_ptr->row_number & 4))
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 3:
+            if ((png_ptr->row_number & 3) || png_ptr->width < 3)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 4:
+            if ((png_ptr->row_number & 3) != 2)
+            {
+               if (dsp_row != NULL && (png_ptr->row_number & 2))
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 5:
+            if ((png_ptr->row_number & 1) || png_ptr->width < 2)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 6:
+            if (!(png_ptr->row_number & 1))
+            {
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+      }
+   }
+#endif
+
+   if (!(png_ptr->mode & PNG_HAVE_IDAT))
+      png_error(png_ptr, "Invalid attempt to read row data");
+
+   png_ptr->zstream.next_out = png_ptr->row_buf;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes;
+   do
+   {
+      if (!(png_ptr->zstream.avail_in))
+      {
+         while (!png_ptr->idat_size)
+         {
+            png_byte chunk_length[4];
+
+            png_crc_finish(png_ptr, 0);
+
+            png_read_data(png_ptr, chunk_length, 4);
+            png_ptr->idat_size = png_get_uint_32(chunk_length);
+
+            png_reset_crc(png_ptr);
+            png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+            if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+               png_error(png_ptr, "Not enough image data");
+         }
+         png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size;
+         png_ptr->zstream.next_in = png_ptr->zbuf;
+         if (png_ptr->zbuf_size > png_ptr->idat_size)
+            png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size;
+         png_crc_read(png_ptr, png_ptr->zbuf,
+            (png_size_t)png_ptr->zstream.avail_in);
+         png_ptr->idat_size -= png_ptr->zstream.avail_in;
+      }
+      ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+      if (ret == Z_STREAM_END)
+      {
+         if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in ||
+            png_ptr->idat_size)
+            png_error(png_ptr, "Extra compressed data");
+         png_ptr->mode |= PNG_AFTER_IDAT;
+         png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+         break;
+      }
+      if (ret != Z_OK)
+         png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg :
+                   "Decompression error");
+
+   } while (png_ptr->zstream.avail_out);
+
+   png_ptr->row_info.color_type = png_ptr->color_type;
+   png_ptr->row_info.width = png_ptr->iwidth;
+   png_ptr->row_info.channels = png_ptr->channels;
+   png_ptr->row_info.bit_depth = png_ptr->bit_depth;
+   png_ptr->row_info.pixel_depth = png_ptr->pixel_depth;
+   {
+      png_ptr->row_info.rowbytes = ((png_ptr->row_info.width *
+         (png_uint_32)png_ptr->row_info.pixel_depth + 7) >> 3);
+   }
+
+   png_read_filter_row(png_ptr, &(png_ptr->row_info),
+      png_ptr->row_buf + 1, png_ptr->prev_row + 1,
+      (int)(png_ptr->row_buf[0]));
+
+   png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf,
+      png_ptr->rowbytes + 1);
+
+   if (png_ptr->transformations)
+      png_do_read_transformations(png_ptr);
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+   /* blow up interlaced rows to full size */
+   if (png_ptr->interlaced &&
+      (png_ptr->transformations & PNG_INTERLACE))
+   {
+      if (png_ptr->pass < 6)
+         png_do_read_interlace(&(png_ptr->row_info),
+            png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations);
+
+      if (dsp_row != NULL)
+         png_combine_row(png_ptr, dsp_row,
+            png_pass_dsp_mask[png_ptr->pass]);
+      if (row != NULL)
+         png_combine_row(png_ptr, row,
+            png_pass_mask[png_ptr->pass]);
+   }
+   else
+#endif
+   {
+      if (row != NULL)
+         png_combine_row(png_ptr, row, 0xff);
+      if (dsp_row != NULL)
+         png_combine_row(png_ptr, dsp_row, 0xff);
+   }
+   png_read_finish_row(png_ptr);
+
+   if (png_ptr->read_row_fn != NULL)
+      (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
+}
+
+/* Read one or more rows of image data.  If the image is interlaced,
+ * and png_set_interlace_handling() has been called, the rows need to
+ * contain the contents of the rows from the previous pass.  If the
+ * image has alpha or transparency, and png_handle_alpha()[*] has been
+ * called, the rows contents must be initialized to the contents of the
+ * screen.
+ *
+ * "row" holds the actual image, and pixels are placed in it
+ * as they arrive.  If the image is displayed after each pass, it will
+ * appear to "sparkle" in.  "display_row" can be used to display a
+ * "chunky" progressive image, with finer detail added as it becomes
+ * available.  If you do not want this "chunky" display, you may pass
+ * NULL for display_row.  If you do not want the sparkle display, and
+ * you have not called png_handle_alpha(), you may pass NULL for rows.
+ * If you have called png_handle_alpha(), and the image has either an
+ * alpha channel or a transparency chunk, you must provide a buffer for
+ * rows.  In this case, you do not have to provide a display_row buffer
+ * also, but you may.  If the image is not interlaced, or if you have
+ * not called png_set_interlace_handling(), the display_row buffer will
+ * be ignored, so pass NULL to it.
+ *
+ * [*] png_handle_alpha() does not exist yet, as of libpng version 1.0.3.
+ */
+
+void
+png_read_rows(png_structp png_ptr, png_bytepp row,
+   png_bytepp display_row, png_uint_32 num_rows)
+{
+   png_uint_32 i;
+   png_bytepp rp;
+   png_bytepp dp;
+
+   png_debug(1, "in png_read_rows\n");
+   /* save jump buffer and error functions */
+   rp = row;
+   dp = display_row;
+   if (rp != NULL && dp != NULL)
+      for (i = 0; i < num_rows; i++)
+      {
+         png_bytep rptr = *rp++;
+         png_bytep dptr = *dp++;
+
+         png_read_row(png_ptr, rptr, dptr);
+      }
+   else if(rp != NULL)
+      for (i = 0; i < num_rows; i++)
+      {
+         png_bytep rptr = *rp;
+         png_read_row(png_ptr, rptr, NULL);
+         rp++;
+      }
+   else if(dp != NULL)
+      for (i = 0; i < num_rows; i++)
+      {
+         png_bytep dptr = *dp;
+         png_read_row(png_ptr, NULL, dptr);
+         dp++;
+      }
+}
+
+/* Read the entire image.  If the image has an alpha channel or a tRNS
+ * chunk, and you have called png_handle_alpha()[*], you will need to
+ * initialize the image to the current image that PNG will be overlaying.
+ * We set the num_rows again here, in case it was incorrectly set in
+ * png_read_start_row() by a call to png_read_update_info() or
+ * png_start_read_image() if png_set_interlace_handling() wasn't called
+ * prior to either of these functions like it should have been.  You can
+ * only call this function once.  If you desire to have an image for
+ * each pass of a interlaced image, use png_read_rows() instead.
+ *
+ * [*] png_handle_alpha() does not exist yet, as of libpng version 1.0.3.
+ */
+void
+png_read_image(png_structp png_ptr, png_bytepp image)
+{
+   png_uint_32 i,image_height;
+   int pass, j;
+   png_bytepp rp;
+
+   png_debug(1, "in png_read_image\n");
+   /* save jump buffer and error functions */
+   pass = png_set_interlace_handling(png_ptr);
+
+   image_height=png_ptr->height;
+   png_ptr->num_rows = image_height; /* Make sure this is set correctly */
+
+   for (j = 0; j < pass; j++)
+   {
+      rp = image;
+      for (i = 0; i < image_height; i++)
+      {
+         png_read_row(png_ptr, *rp, NULL);
+         rp++;
+      }
+   }
+}
+
+/* Read the end of the PNG file.  Will not read past the end of the
+ * file, will verify the end is accurate, and will read any comments
+ * or time information at the end of the file, if info is not NULL.
+ */
+void
+png_read_end(png_structp png_ptr, png_infop info_ptr)
+{
+   png_byte chunk_length[4];
+   png_uint_32 length;
+
+   png_debug(1, "in png_read_end\n");
+   /* save jump buffer and error functions */
+   png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */
+
+   do
+   {
+      png_read_data(png_ptr, chunk_length, 4);
+      length = png_get_uint_32(chunk_length);
+
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+
+      png_debug1(0, "Reading %s chunk.\n", png_ptr->chunk_name);
+
+      if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4))
+         png_handle_IHDR(png_ptr, info_ptr, length);
+      else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+      {
+         /* Zero length IDATs are legal after the last IDAT has been
+          * read, but not after other chunks have been read.
+          */
+         if (length > 0 || png_ptr->mode & PNG_AFTER_IDAT)
+            png_error(png_ptr, "Too many IDAT's found");
+         else
+            png_crc_finish(png_ptr, 0);
+      }
+      else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+         png_handle_PLTE(png_ptr, info_ptr, length);
+      else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4))
+         png_handle_IEND(png_ptr, info_ptr, length);
+#if defined(PNG_READ_bKGD_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4))
+         png_handle_bKGD(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4))
+         png_handle_cHRM(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_gAMA_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4))
+         png_handle_gAMA(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4))
+         png_handle_hIST(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4))
+         png_handle_oFFs(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4))
+         png_handle_pCAL(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4))
+         png_handle_pHYs(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4))
+         png_handle_sBIT(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4))
+         png_handle_sRGB(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4))
+         png_handle_tEXt(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4))
+         png_handle_tIME(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4))
+         png_handle_tRNS(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4))
+         png_handle_zTXt(png_ptr, info_ptr, length);
+#endif
+      else
+         png_handle_unknown(png_ptr, info_ptr, length);
+   } while (!(png_ptr->mode & PNG_HAVE_IEND));
+}
+
+/* free all memory used by the read */
+void
+png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr,
+   png_infopp end_info_ptr_ptr)
+{
+   png_structp png_ptr = NULL;
+   png_infop info_ptr = NULL, end_info_ptr = NULL;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_free_ptr free_fn = NULL;
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+   png_debug(1, "in png_destroy_read_struct\n");
+   /* save jump buffer and error functions */
+   if (png_ptr_ptr != NULL)
+      png_ptr = *png_ptr_ptr;
+
+   if (info_ptr_ptr != NULL)
+      info_ptr = *info_ptr_ptr;
+
+   if (end_info_ptr_ptr != NULL)
+      end_info_ptr = *end_info_ptr_ptr;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   free_fn = png_ptr->free_fn;
+#endif
+
+   png_read_destroy(png_ptr, info_ptr, end_info_ptr);
+
+   if (info_ptr != NULL)
+   {
+#if defined(PNG_READ_tEXt_SUPPORTED) || defined(PNG_READ_zTXt_SUPPORTED)
+      png_free(png_ptr, info_ptr->text);
+#endif
+
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)info_ptr, free_fn);
+#else
+      png_destroy_struct((png_voidp)info_ptr);
+#endif
+      *info_ptr_ptr = (png_infop)NULL;
+   }
+
+   if (end_info_ptr != NULL)
+   {
+#if defined(PNG_READ_tEXt_SUPPORTED) || defined(PNG_READ_zTXt_SUPPORTED)
+      png_free(png_ptr, end_info_ptr->text);
+#endif
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)end_info_ptr, free_fn);
+#else
+      png_destroy_struct((png_voidp)end_info_ptr);
+#endif
+      *end_info_ptr_ptr = (png_infop)NULL;
+   }
+
+   if (png_ptr != NULL)
+   {
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)png_ptr, free_fn);
+#else
+      png_destroy_struct((png_voidp)png_ptr);
+#endif
+      *png_ptr_ptr = (png_structp)NULL;
+   }
+}
+
+/* free all memory used by the read (old method) */
+void
+png_read_destroy(png_structp png_ptr, png_infop info_ptr, png_infop end_info_ptr)
+{
+   jmp_buf tmp_jmp;
+   png_error_ptr error_fn;
+   png_error_ptr warning_fn;
+   png_voidp error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_free_ptr free_fn;
+#endif
+
+   png_debug(1, "in png_read_destroy\n");
+   /* save jump buffer and error functions */
+   if (info_ptr != NULL)
+      png_info_destroy(png_ptr, info_ptr);
+
+   if (end_info_ptr != NULL)
+      png_info_destroy(png_ptr, end_info_ptr);
+
+   png_free(png_ptr, png_ptr->zbuf);
+   png_free(png_ptr, png_ptr->row_buf);
+   png_free(png_ptr, png_ptr->prev_row);
+#if defined(PNG_READ_DITHER_SUPPORTED)
+   png_free(png_ptr, png_ptr->palette_lookup);
+   png_free(png_ptr, png_ptr->dither_index);
+#endif
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   png_free(png_ptr, png_ptr->gamma_table);
+#endif
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_free(png_ptr, png_ptr->gamma_from_1);
+   png_free(png_ptr, png_ptr->gamma_to_1);
+#endif
+   if (png_ptr->flags & PNG_FLAG_FREE_PALETTE)
+      png_zfree(png_ptr, png_ptr->palette);
+   if (png_ptr->flags & PNG_FLAG_FREE_TRANS)
+      png_free(png_ptr, png_ptr->trans);
+#if defined(PNG_READ_hIST_SUPPORTED)
+   if (png_ptr->flags & PNG_FLAG_FREE_HIST)
+      png_free(png_ptr, png_ptr->hist);
+#endif
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if (png_ptr->gamma_16_table != NULL)
+   {
+      int i;
+      int istop = (1 << (8 - png_ptr->gamma_shift));
+      for (i = 0; i < istop; i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_table[i]);
+      }
+   png_free(png_ptr, png_ptr->gamma_16_table);
+   }
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if (png_ptr->gamma_16_from_1 != NULL)
+   {
+      int i;
+      int istop = (1 << (8 - png_ptr->gamma_shift));
+      for (i = 0; i < istop; i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_from_1[i]);
+      }
+   png_free(png_ptr, png_ptr->gamma_16_from_1);
+   }
+   if (png_ptr->gamma_16_to_1 != NULL)
+   {
+      int i;
+      int istop = (1 << (8 - png_ptr->gamma_shift));
+      for (i = 0; i < istop; i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_to_1[i]);
+      }
+   png_free(png_ptr, png_ptr->gamma_16_to_1);
+   }
+#endif
+#endif
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+   png_free(png_ptr, png_ptr->time_buffer);
+#endif /* PNG_TIME_RFC1123_SUPPORTED */
+
+   inflateEnd(&png_ptr->zstream);
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+   png_free(png_ptr, png_ptr->save_buffer);
+#endif
+
+   /* Save the important info out of the png_struct, in case it is
+    * being used again.
+    */
+   png_memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
+
+   error_fn = png_ptr->error_fn;
+   warning_fn = png_ptr->warning_fn;
+   error_ptr = png_ptr->error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   free_fn = png_ptr->free_fn;
+#endif
+
+   png_memset(png_ptr, 0, sizeof (png_struct));
+
+   png_ptr->error_fn = error_fn;
+   png_ptr->warning_fn = warning_fn;
+   png_ptr->error_ptr = error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_ptr->free_fn = free_fn;
+#endif
+
+   png_memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
+}
+
+void
+png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn)
+{
+   png_ptr->read_row_fn = read_row_fn;
+}
diff --git a/src/png/pngrio.c b/src/png/pngrio.c
new file mode 100644 (file)
index 0000000..2af0e89
--- /dev/null
@@ -0,0 +1,156 @@
+
+/* pngrio.c - functions for data input
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ *
+ * This file provides a location for all input.  Users who need
+ * special handling are expected to write a function that has the same
+ * arguments as this and performs a similar function, but that possibly
+ * has a different input method.  Note that you shouldn't change this
+ * function, but rather write a replacement function and then make
+ * libpng use it at run time with png_set_read_fn(...).
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Read the data from whatever input you are using.  The default routine
+   reads from a file pointer.  Note that this routine sometimes gets called
+   with very small lengths, so you should implement some kind of simple
+   buffering if you are using unbuffered reads.  This should never be asked
+   to read more then 64K on a 16 bit machine. */
+void
+png_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_debug1(4,"reading %d bytes\n", length);
+   if (png_ptr->read_data_fn != NULL)
+      (*(png_ptr->read_data_fn))(png_ptr, data, length);
+   else
+      png_error(png_ptr, "Call to NULL read function");
+}
+
+#if !defined(PNG_NO_STDIO)
+/* This is the function that does the actual reading of data.  If you are
+   not reading from a standard C stream, you should create a replacement
+   read_data function and use it at run time with png_set_read_fn(), rather
+   than changing the library. */
+#ifndef USE_FAR_KEYWORD
+#ifdef __VISAGECPP__
+static void _Optlink
+png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+#else
+static void
+png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+#endif
+{
+   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.
+    */
+   check = (png_size_t)fread(data, (png_size_t)1, length,
+      (FILE *)png_ptr->io_ptr);
+
+   if (check != length)
+   {
+      png_error(png_ptr, "Read Error");
+   }
+}
+#else
+/* this is the model-independent version. Since the standard I/O library
+   can't handle far buffers in the medium and small models, we have to copy
+   the data.
+*/
+
+#define NEAR_BUF_SIZE 1024
+#define MIN(a,b) (a <= b ? a : b)
+
+static void
+png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   int check;
+   png_byte *n_data;
+   FILE *io_ptr;
+
+   /* Check if data really is near. If so, use usual code. */
+   n_data = (png_byte *)CVT_PTR_NOCHECK(data);
+   io_ptr = (FILE *)CVT_PTR(png_ptr->io_ptr);
+   if ((png_bytep)n_data == data)
+   {
+      check = fread(n_data, 1, length, io_ptr);
+   }
+   else
+   {
+      png_byte buf[NEAR_BUF_SIZE];
+      png_size_t read, remaining, err;
+      check = 0;
+      remaining = length;
+      do
+      {
+         read = MIN(NEAR_BUF_SIZE, remaining);
+         err = fread(buf, (png_size_t)1, read, io_ptr);
+         png_memcpy(data, buf, read); /* copy far buffer to near buffer */
+         if(err != read)
+            break;
+         else
+            check += err;
+         data += read;
+         remaining -= read;
+      }
+      while (remaining != 0);
+   }
+   if ((png_uint_32)check != (png_uint_32)length)
+   {
+      png_error(png_ptr, "read Error");
+   }
+}
+#endif
+#endif
+
+/* This function allows the application to supply a new input function
+   for libpng if standard C streams aren't being used.
+
+   This function takes as its arguments:
+   png_ptr      - pointer to a png input data structure
+   io_ptr       - pointer to user supplied structure containing info about
+                  the input functions.  May be NULL.
+   read_data_fn - pointer to a new input function that takes as its
+                  arguments a pointer to a png_struct, a pointer to
+                  a location where input data can be stored, and a 32-bit
+                  unsigned int that is the number of bytes to be read.
+                  To exit and output any fatal error messages the new write
+                  function should call png_error(png_ptr, "Error msg"). */
+void
+png_set_read_fn(png_structp png_ptr, png_voidp io_ptr,
+   png_rw_ptr read_data_fn)
+{
+   png_ptr->io_ptr = io_ptr;
+
+#if !defined(PNG_NO_STDIO)
+   if (read_data_fn != NULL)
+      png_ptr->read_data_fn = read_data_fn;
+   else
+      png_ptr->read_data_fn = png_default_read_data;
+#else
+   png_ptr->read_data_fn = read_data_fn;
+#endif
+
+   /* It is an error to write to a read device */
+   if (png_ptr->write_data_fn != NULL)
+   {
+      png_ptr->write_data_fn = NULL;
+      png_warning(png_ptr,
+         "It's an error to set both read_data_fn and write_data_fn in the ");
+      png_warning(png_ptr,
+         "same structure.  Resetting write_data_fn to NULL.");
+   }
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+   png_ptr->output_flush_fn = NULL;
+#endif /* PNG_WRITE_FLUSH_SUPPORTED */
+}
+
diff --git a/src/png/pngrtran.c b/src/png/pngrtran.c
new file mode 100644 (file)
index 0000000..489554c
--- /dev/null
@@ -0,0 +1,3940 @@
+
+/* pngrtran.c - transforms the data in a row for PNG readers
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ *
+ * This file contains functions optionally called by an application
+ * in order to tell libpng how to handle data when reading a PNG.
+ * Transformations that are used in both reading and writing are
+ * in pngtrans.c.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(_MSC_VER) && !defined(__MWERKS__)
+#define __VISUALC__ _MSC_VER
+#endif
+#ifdef __VISUALC__
+#ifndef WIN32
+#pragma warning(disable:4135)
+#endif
+#endif
+
+/* Set the action on getting a CRC error for an ancillary or critical chunk. */
+void
+png_set_crc_action(png_structp png_ptr, int crit_action, int ancil_action)
+{
+   png_debug(1, "in png_set_crc_action\n");
+   /* Tell libpng how we react to CRC errors in critical chunks */
+   switch (crit_action)
+   {
+      case PNG_CRC_NO_CHANGE:                        /* leave setting as is */
+         break;
+      case PNG_CRC_WARN_USE:                               /* warn/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE;
+         break;
+      case PNG_CRC_QUIET_USE:                             /* quiet/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE |
+                           PNG_FLAG_CRC_CRITICAL_IGNORE;
+         break;
+      case PNG_CRC_WARN_DISCARD:    /* not a valid action for critical data */
+         png_warning(png_ptr, "Can't discard critical data on CRC error.");
+      case PNG_CRC_ERROR_QUIT:                                /* error/quit */
+      case PNG_CRC_DEFAULT:
+      default:
+         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+         break;
+   }
+
+   switch (ancil_action)
+   {
+      case PNG_CRC_NO_CHANGE:                       /* leave setting as is */
+         break;
+      case PNG_CRC_WARN_USE:                              /* warn/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE;
+         break;
+      case PNG_CRC_QUIET_USE:                            /* quiet/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE |
+                           PNG_FLAG_CRC_ANCILLARY_NOWARN;
+         break;
+      case PNG_CRC_ERROR_QUIT:                               /* error/quit */
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN;
+         break;
+      case PNG_CRC_WARN_DISCARD:                      /* warn/discard data */
+      case PNG_CRC_DEFAULT:
+      default:
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         break;
+   }
+}
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+/* handle alpha and tRNS via a background color */
+void
+png_set_background(png_structp png_ptr,
+   png_color_16p background_color, int background_gamma_code,
+   int need_expand, double background_gamma)
+{
+   png_debug(1, "in png_set_background\n");
+   if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN)
+   {
+      png_warning(png_ptr, "Application must supply a known background gamma");
+      return;
+   }
+
+   png_ptr->transformations |= PNG_BACKGROUND;
+   png_memcpy(&(png_ptr->background), background_color, sizeof(png_color_16));
+   png_ptr->background_gamma = (float)background_gamma;
+   png_ptr->background_gamma_type = (png_byte)(background_gamma_code);
+   png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0);
+
+   /* Note:  if need_expand is set and color_type is either RGB or RGB_ALPHA
+    * (in which case need_expand is superfluous anyway), the background color
+    * might actually be gray yet not be flagged as such. This is not a problem
+    * for the current code, which uses PNG_FLAG_BACKGROUND_IS_GRAY only to
+    * decide when to do the png_do_gray_to_rgb() transformation.
+    */
+   if ((need_expand && !(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) ||
+       (!need_expand && background_color->red == background_color->green &&
+        background_color->red == background_color->blue))
+      png_ptr->flags |= PNG_FLAG_BACKGROUND_IS_GRAY;
+}
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+/* strip 16 bit depth files to 8 bit depth */
+void
+png_set_strip_16(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_strip_16\n");
+   png_ptr->transformations |= PNG_16_TO_8;
+}
+#endif
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+void
+png_set_strip_alpha(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_strip_alpha\n");
+   png_ptr->transformations |= PNG_STRIP_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+/* Dither file to 8 bit.  Supply a palette, the current number
+ * of elements in the palette, the maximum number of elements
+ * allowed, and a histogram if possible.  If the current number
+ * of colors is greater then the maximum number, the palette will be
+ * modified to fit in the maximum number.  "full_dither" indicates
+ * whether we need a dithering cube set up for RGB images, or if we
+ * simply are reducing the number of colors in a paletted image.
+ */
+
+typedef struct png_dsort_struct
+{
+   struct png_dsort_struct FAR * next;
+   png_byte left;
+   png_byte right;
+} png_dsort;
+typedef png_dsort FAR *       png_dsortp;
+typedef png_dsort FAR * FAR * png_dsortpp;
+
+void
+png_set_dither(png_structp png_ptr, png_colorp palette,
+   int num_palette, int maximum_colors, png_uint_16p histogram,
+   int full_dither)
+{
+   png_debug(1, "in png_set_dither\n");
+   png_ptr->transformations |= PNG_DITHER;
+
+   if (!full_dither)
+   {
+      int i;
+
+      png_ptr->dither_index = (png_bytep)png_malloc(png_ptr,
+         (png_uint_32)(num_palette * sizeof (png_byte)));
+      for (i = 0; i < num_palette; i++)
+         png_ptr->dither_index[i] = (png_byte)i;
+   }
+
+   if (num_palette > maximum_colors)
+   {
+      if (histogram != NULL)
+      {
+         /* This is easy enough, just throw out the least used colors.
+            Perhaps not the best solution, but good enough. */
+
+         int i;
+         png_bytep sort;
+
+         /* initialize an array to sort colors */
+         sort = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_palette
+            * sizeof (png_byte)));
+
+         /* initialize the sort array */
+         for (i = 0; i < num_palette; i++)
+            sort[i] = (png_byte)i;
+
+         /* Find the least used palette entries by starting a
+            bubble sort, and running it until we have sorted
+            out enough colors.  Note that we don't care about
+            sorting all the colors, just finding which are
+            least used. */
+
+         for (i = num_palette - 1; i >= maximum_colors; i--)
+         {
+            int done; /* to stop early if the list is pre-sorted */
+            int j;
+
+            done = 1;
+            for (j = 0; j < i; j++)
+            {
+               if (histogram[sort[j]] < histogram[sort[j + 1]])
+               {
+                  png_byte t;
+
+                  t = sort[j];
+                  sort[j] = sort[j + 1];
+                  sort[j + 1] = t;
+                  done = 0;
+               }
+            }
+            if (done)
+               break;
+         }
+
+         /* swap the palette around, and set up a table, if necessary */
+         if (full_dither)
+         {
+            int j = num_palette;
+
+            /* put all the useful colors within the max, but don't
+               move the others */
+            for (i = 0; i < maximum_colors; i++)
+            {
+               if ((int)sort[i] >= maximum_colors)
+               {
+                  do
+                     j--;
+                  while ((int)sort[j] >= maximum_colors);
+                  palette[i] = palette[j];
+               }
+            }
+         }
+         else
+         {
+            int j = num_palette;
+
+            /* move all the used colors inside the max limit, and
+               develop a translation table */
+            for (i = 0; i < maximum_colors; i++)
+            {
+               /* only move the colors we need to */
+               if ((int)sort[i] >= maximum_colors)
+               {
+                  png_color tmp_color;
+
+                  do
+                     j--;
+                  while ((int)sort[j] >= maximum_colors);
+
+                  tmp_color = palette[j];
+                  palette[j] = palette[i];
+                  palette[i] = tmp_color;
+                  /* indicate where the color went */
+                  png_ptr->dither_index[j] = (png_byte)i;
+                  png_ptr->dither_index[i] = (png_byte)j;
+               }
+            }
+
+            /* find closest color for those colors we are not using */
+            for (i = 0; i < num_palette; i++)
+            {
+               if ((int)png_ptr->dither_index[i] >= maximum_colors)
+               {
+                  int min_d, k, min_k, d_index;
+
+                  /* find the closest color to one we threw out */
+                  d_index = png_ptr->dither_index[i];
+                  min_d = PNG_COLOR_DIST(palette[d_index], palette[0]);
+                  for (k = 1, min_k = 0; k < maximum_colors; k++)
+                  {
+                     int d;
+
+                     d = PNG_COLOR_DIST(palette[d_index], palette[k]);
+
+                     if (d < min_d)
+                     {
+                        min_d = d;
+                        min_k = k;
+                     }
+                  }
+                  /* point to closest color */
+                  png_ptr->dither_index[i] = (png_byte)min_k;
+               }
+            }
+         }
+         png_free(png_ptr, sort);
+      }
+      else
+      {
+         /* This is much harder to do simply (and quickly).  Perhaps
+            we need to go through a median cut routine, but those
+            don't always behave themselves with only a few colors
+            as input.  So we will just find the closest two colors,
+            and throw out one of them (chosen somewhat randomly).
+            [We don't understand this at all, so if someone wants to
+             work on improving it, be our guest - AED, GRP]
+            */
+         int i;
+         int max_d;
+         int num_new_palette;
+         png_dsortpp hash;
+         png_bytep index_to_palette;
+            /* where the original index currently is in the palette */
+         png_bytep palette_to_index;
+            /* which original index points to this palette color */
+
+         /* initialize palette index arrays */
+         index_to_palette = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)(num_palette * sizeof (png_byte)));
+         palette_to_index = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)(num_palette * sizeof (png_byte)));
+
+         /* initialize the sort array */
+         for (i = 0; i < num_palette; i++)
+         {
+            index_to_palette[i] = (png_byte)i;
+            palette_to_index[i] = (png_byte)i;
+         }
+
+         hash = (png_dsortpp)png_malloc(png_ptr, (png_uint_32)(769 *
+            sizeof (png_dsortp)));
+         for (i = 0; i < 769; i++)
+            hash[i] = NULL;
+/*         png_memset(hash, 0, 769 * sizeof (png_dsortp)); */
+
+         num_new_palette = num_palette;
+
+         /* initial wild guess at how far apart the farthest pixel
+            pair we will be eliminating will be.  Larger
+            numbers mean more areas will be allocated, Smaller
+            numbers run the risk of not saving enough data, and
+            having to do this all over again.
+
+            I have not done extensive checking on this number.
+            */
+         max_d = 96;
+
+         while (num_new_palette > maximum_colors)
+         {
+            for (i = 0; i < num_new_palette - 1; i++)
+            {
+               int j;
+
+               for (j = i + 1; j < num_new_palette; j++)
+               {
+                  int d;
+
+                  d = PNG_COLOR_DIST(palette[i], palette[j]);
+
+                  if (d <= max_d)
+                  {
+                     png_dsortp t;
+
+                     t = (png_dsortp)png_malloc(png_ptr, (png_uint_32)(sizeof
+                         (png_dsort)));
+                     t->next = hash[d];
+                     t->left = (png_byte)i;
+                     t->right = (png_byte)j;
+                     hash[d] = t;
+                  }
+               }
+            }
+
+            for (i = 0; i <= max_d; i++)
+            {
+               if (hash[i] != NULL)
+               {
+                  png_dsortp p;
+
+                  for (p = hash[i]; p; p = p->next)
+                  {
+                     if ((int)index_to_palette[p->left] < num_new_palette &&
+                        (int)index_to_palette[p->right] < num_new_palette)
+                     {
+                        int j, next_j;
+
+                        if (num_new_palette & 1)
+                        {
+                           j = p->left;
+                           next_j = p->right;
+                        }
+                        else
+                        {
+                           j = p->right;
+                           next_j = p->left;
+                        }
+
+                        num_new_palette--;
+                        palette[index_to_palette[j]] = palette[num_new_palette];
+                        if (!full_dither)
+                        {
+                           int k;
+
+                           for (k = 0; k < num_palette; k++)
+                           {
+                              if (png_ptr->dither_index[k] ==
+                                 index_to_palette[j])
+                                 png_ptr->dither_index[k] =
+                                    index_to_palette[next_j];
+                              if ((int)png_ptr->dither_index[k] ==
+                                 num_new_palette)
+                                 png_ptr->dither_index[k] =
+                                    index_to_palette[j];
+                           }
+                        }
+
+                        index_to_palette[palette_to_index[num_new_palette]] =
+                           index_to_palette[j];
+                        palette_to_index[index_to_palette[j]] =
+                           palette_to_index[num_new_palette];
+
+                        index_to_palette[j] = (png_byte)num_new_palette;
+                        palette_to_index[num_new_palette] = (png_byte)j;
+                     }
+                     if (num_new_palette <= maximum_colors)
+                        break;
+                  }
+                  if (num_new_palette <= maximum_colors)
+                     break;
+               }
+            }
+
+            for (i = 0; i < 769; i++)
+            {
+               if (hash[i] != NULL)
+               {
+                  png_dsortp p = hash[i];
+                  while (p)
+                  {
+                     png_dsortp t;
+
+                     t = p->next;
+                     png_free(png_ptr, p);
+                     p = t;
+                  }
+               }
+               hash[i] = 0;
+            }
+            max_d += 96;
+         }
+         png_free(png_ptr, hash);
+         png_free(png_ptr, palette_to_index);
+         png_free(png_ptr, index_to_palette);
+      }
+      num_palette = maximum_colors;
+   }
+   if (png_ptr->palette == NULL)
+   {
+      png_ptr->palette = palette;
+   }
+   png_ptr->num_palette = (png_uint_16)num_palette;
+
+   if (full_dither)
+   {
+      int i;
+      png_bytep distance;
+      int total_bits = PNG_DITHER_RED_BITS + PNG_DITHER_GREEN_BITS +
+         PNG_DITHER_BLUE_BITS;
+      int num_red = (1 << PNG_DITHER_RED_BITS);
+      int num_green = (1 << PNG_DITHER_GREEN_BITS);
+      int num_blue = (1 << PNG_DITHER_BLUE_BITS);
+      png_size_t num_entries = ((png_size_t)1 << total_bits);
+
+      png_ptr->palette_lookup = (png_bytep )png_malloc(png_ptr,
+         (png_uint_32)(num_entries * sizeof (png_byte)));
+
+      png_memset(png_ptr->palette_lookup, 0, num_entries * sizeof (png_byte));
+
+      distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries *
+         sizeof(png_byte)));
+
+      png_memset(distance, 0xff, num_entries * sizeof(png_byte));
+
+      for (i = 0; i < num_palette; i++)
+      {
+         int ir, ig, ib;
+         int r = (palette[i].red >> (8 - PNG_DITHER_RED_BITS));
+         int g = (palette[i].green >> (8 - PNG_DITHER_GREEN_BITS));
+         int b = (palette[i].blue >> (8 - PNG_DITHER_BLUE_BITS));
+
+         for (ir = 0; ir < num_red; ir++)
+         {
+            int dr = abs(ir - r);
+            int index_r = (ir << (PNG_DITHER_BLUE_BITS + PNG_DITHER_GREEN_BITS));
+
+            for (ig = 0; ig < num_green; ig++)
+            {
+               int dg = abs(ig - g);
+               int dt = dr + dg;
+               int dm = ((dr > dg) ? dr : dg);
+               int index_g = index_r | (ig << PNG_DITHER_BLUE_BITS);
+
+               for (ib = 0; ib < num_blue; ib++)
+               {
+                  int d_index = index_g | ib;
+                  int db = abs(ib - b);
+                  int dmax = ((dm > db) ? dm : db);
+                  int d = dmax + dt + db;
+
+                  if (d < (int)distance[d_index])
+                  {
+                     distance[d_index] = (png_byte)d;
+                     png_ptr->palette_lookup[d_index] = (png_byte)i;
+                  }
+               }
+            }
+         }
+      }
+
+      png_free(png_ptr, distance);
+   }
+}
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+/* Transform the image from the file_gamma to the screen_gamma.  We
+ * only do transformations on images where the file_gamma and screen_gamma
+ * are not close reciprocals, otherwise it slows things down slightly, and
+ * also needlessly introduces small errors.
+ */
+void
+png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma)
+{
+   png_debug(1, "in png_set_gamma\n");
+   if (fabs(scrn_gamma * file_gamma - 1.0) > PNG_GAMMA_THRESHOLD)
+      png_ptr->transformations |= PNG_GAMMA;
+   png_ptr->gamma = (float)file_gamma;
+   png_ptr->screen_gamma = (float)scrn_gamma;
+}
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+/* Expand paletted images to rgb, expand grayscale images of
+ * less than 8 bit depth to 8 bit depth, and expand tRNS chunks
+ * to alpha channels.
+ */
+void
+png_set_expand(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_expand\n");
+   png_ptr->transformations |= PNG_EXPAND;
+}
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+void
+png_set_gray_to_rgb(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_gray_to_rgb\n");
+   png_ptr->transformations |= PNG_GRAY_TO_RGB;
+}
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+/* Convert a RGB image to a grayscale of the same width.  This allows us,
+ * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image.
+ */
+void
+png_set_rgb_to_gray(png_structp png_ptr, int error_action, float red,
+  float green)
+{
+   png_debug(1, "in png_set_rgb_to_gray\n");
+   switch(error_action)
+   {
+      case 1: png_ptr->transformations |= PNG_RGB_TO_GRAY;
+              break;
+      case 2: png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN;
+              break;
+      case 3: png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR;
+   }
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+      png_ptr->transformations |= PNG_EXPAND;
+#else
+   {
+      png_warning(png_ptr, "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED.");
+      png_ptr->transformations &= ~PNG_RGB_TO_GRAY;
+   }
+#endif
+   {
+      png_byte red_byte = (png_byte)(red*255.0 + 0.5);
+      png_byte green_byte = (png_byte)(green*255.0 + 0.5);
+      if(red < 0.0 || green < 0.0)
+      {
+         red_byte = 54;
+         green_byte = 183;
+      }
+      else if(red_byte + green_byte > 255)
+      {
+         png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients");
+         red_byte = 54;
+         green_byte = 183;
+      }
+      png_ptr->rgb_to_gray_red_coeff   = red_byte;
+      png_ptr->rgb_to_gray_green_coeff = green_byte;
+      png_ptr->rgb_to_gray_blue_coeff  = 255 - red_byte - green_byte;
+   }
+}
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
+void
+png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
+   read_user_transform_fn)
+{
+   png_debug(1, "in png_set_read_user_transform_fn\n");
+   png_ptr->transformations |= PNG_USER_TRANSFORM;
+   png_ptr->read_user_transform_fn = read_user_transform_fn;
+}
+#endif
+
+/* Initialize everything needed for the read.  This includes modifying
+ * the palette.
+ */
+void
+png_init_read_transformations(png_structp png_ptr)
+{
+   png_debug(1, "in png_init_read_transformations\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if(png_ptr != NULL)
+#endif
+  {
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || defined(PNG_READ_SHIFT_SUPPORTED) \
+ || defined(PNG_READ_GAMMA_SUPPORTED)
+   int color_type = png_ptr->color_type;
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if (png_ptr->transformations & PNG_BACKGROUND_EXPAND)
+   {
+      if (!(color_type & PNG_COLOR_MASK_COLOR))  /* i.e., GRAY or GRAY_ALPHA */
+      {
+         /* expand background chunk. */
+         switch (png_ptr->bit_depth)
+         {
+            case 1:
+               png_ptr->background.gray *= (png_uint_16)0xff;
+               png_ptr->background.red = png_ptr->background.green =
+               png_ptr->background.blue = png_ptr->background.gray;
+               break;
+            case 2:
+               png_ptr->background.gray *= (png_uint_16)0x55;
+               png_ptr->background.red = png_ptr->background.green =
+               png_ptr->background.blue = png_ptr->background.gray;
+               break;
+            case 4:
+               png_ptr->background.gray *= (png_uint_16)0x11;
+               png_ptr->background.red = png_ptr->background.green =
+               png_ptr->background.blue = png_ptr->background.gray;
+               break;
+            case 8:
+            case 16:
+               png_ptr->background.red = png_ptr->background.green =
+               png_ptr->background.blue = png_ptr->background.gray;
+               break;
+         }
+      }
+      else if (color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_ptr->background.red   =
+            png_ptr->palette[png_ptr->background.index].red;
+         png_ptr->background.green =
+            png_ptr->palette[png_ptr->background.index].green;
+         png_ptr->background.blue  =
+            png_ptr->palette[png_ptr->background.index].blue;
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+        if (png_ptr->transformations & PNG_INVERT_ALPHA)
+        {
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+           if (!(png_ptr->transformations & PNG_EXPAND))
+#endif
+           {
+           /* invert the alpha channel (in tRNS) unless the pixels are
+              going to be expanded, in which case leave it for later */
+              int i,istop;
+              istop=(int)png_ptr->num_trans;
+              for (i=0; i<istop; i++)
+                 png_ptr->trans[i] = 255 - png_ptr->trans[i];
+           }
+        }
+#endif
+
+      }
+   }
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_ptr->background_1 = png_ptr->background;
+#endif
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if (png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY))
+   {
+      png_build_gamma_table(png_ptr);
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+      if (png_ptr->transformations & PNG_BACKGROUND)
+      {
+         if (color_type == PNG_COLOR_TYPE_PALETTE)
+         {
+            png_color back, back_1;
+            png_colorp palette = png_ptr->palette;
+            int num_palette = png_ptr->num_palette;
+            int i;
+
+            if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE)
+            {
+               back.red = png_ptr->gamma_table[png_ptr->background.red];
+               back.green = png_ptr->gamma_table[png_ptr->background.green];
+               back.blue = png_ptr->gamma_table[png_ptr->background.blue];
+
+               back_1.red = png_ptr->gamma_to_1[png_ptr->background.red];
+               back_1.green = png_ptr->gamma_to_1[png_ptr->background.green];
+               back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue];
+            }
+            else
+            {
+               double g, gs;
+
+               switch (png_ptr->background_gamma_type)
+               {
+                  case PNG_BACKGROUND_GAMMA_SCREEN:
+                     g = (png_ptr->screen_gamma);
+                     gs = 1.0;
+                     break;
+                  case PNG_BACKGROUND_GAMMA_FILE:
+                     g = 1.0 / (png_ptr->gamma);
+                     gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma);
+                     break;
+                  case PNG_BACKGROUND_GAMMA_UNIQUE:
+                     g = 1.0 / (png_ptr->background_gamma);
+                     gs = 1.0 / (png_ptr->background_gamma *
+                                 png_ptr->screen_gamma);
+                     break;
+                  default:
+                     g = 1.0;    /* back_1 */
+                     gs = 1.0;   /* back */
+               }
+
+               if ( fabs(gs - 1.0) < PNG_GAMMA_THRESHOLD)
+               {
+                  back.red   = (png_byte)png_ptr->background.red;
+                  back.green = (png_byte)png_ptr->background.green;
+                  back.blue  = (png_byte)png_ptr->background.blue;
+               }
+               else
+               {
+                  back.red = (png_byte)(pow(
+                     (double)png_ptr->background.red/255, gs) * 255.0 + .5);
+                  back.green = (png_byte)(pow(
+                     (double)png_ptr->background.green/255, gs) * 255.0 + .5);
+                  back.blue = (png_byte)(pow(
+                     (double)png_ptr->background.blue/255, gs) * 255.0 + .5);
+               }
+
+               back_1.red = (png_byte)(pow(
+                  (double)png_ptr->background.red/255, g) * 255.0 + .5);
+               back_1.green = (png_byte)(pow(
+                  (double)png_ptr->background.green/255, g) * 255.0 + .5);
+               back_1.blue = (png_byte)(pow(
+                  (double)png_ptr->background.blue/255, g) * 255.0 + .5);
+            }
+
+            for (i = 0; i < num_palette; i++)
+            {
+               if (i < (int)png_ptr->num_trans && png_ptr->trans[i] != 0xff)
+               {
+                  if (png_ptr->trans[i] == 0)
+                  {
+                     palette[i] = back;
+                  }
+                  else /* if (png_ptr->trans[i] != 0xff) */
+                  {
+                     png_byte v, w;
+
+                     v = png_ptr->gamma_to_1[palette[i].red];
+                     png_composite(w, v, png_ptr->trans[i], back_1.red);
+                     palette[i].red = png_ptr->gamma_from_1[w];
+
+                     v = png_ptr->gamma_to_1[palette[i].green];
+                     png_composite(w, v, png_ptr->trans[i], back_1.green);
+                     palette[i].green = png_ptr->gamma_from_1[w];
+
+                     v = png_ptr->gamma_to_1[palette[i].blue];
+                     png_composite(w, v, png_ptr->trans[i], back_1.blue);
+                     palette[i].blue = png_ptr->gamma_from_1[w];
+                  }
+               }
+               else
+               {
+                  palette[i].red = png_ptr->gamma_table[palette[i].red];
+                  palette[i].green = png_ptr->gamma_table[palette[i].green];
+                  palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+               }
+            }
+         }
+         /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN)*/
+         else
+         /* color_type != PNG_COLOR_TYPE_PALETTE */
+         {
+            double m = (double)(((png_uint_32)1 << png_ptr->bit_depth) - 1);
+            double g = 1.0;
+            double gs = 1.0;
+
+            switch (png_ptr->background_gamma_type)
+            {
+               case PNG_BACKGROUND_GAMMA_SCREEN:
+                  g = (png_ptr->screen_gamma);
+                  gs = 1.0;
+                  break;
+               case PNG_BACKGROUND_GAMMA_FILE:
+                  g = 1.0 / (png_ptr->gamma);
+                  gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma);
+                  break;
+               case PNG_BACKGROUND_GAMMA_UNIQUE:
+                  g = 1.0 / (png_ptr->background_gamma);
+                  gs = 1.0 / (png_ptr->background_gamma *
+                     png_ptr->screen_gamma);
+                  break;
+            }
+
+            if (color_type & PNG_COLOR_MASK_COLOR)
+            {
+               /* RGB or RGBA */
+               png_ptr->background_1.red = (png_uint_16)(pow(
+                  (double)png_ptr->background.red / m, g) * m + .5);
+               png_ptr->background_1.green = (png_uint_16)(pow(
+                  (double)png_ptr->background.green / m, g) * m + .5);
+               png_ptr->background_1.blue = (png_uint_16)(pow(
+                  (double)png_ptr->background.blue / m, g) * m + .5);
+               png_ptr->background.red = (png_uint_16)(pow(
+                  (double)png_ptr->background.red / m, gs) * m + .5);
+               png_ptr->background.green = (png_uint_16)(pow(
+                  (double)png_ptr->background.green / m, gs) * m + .5);
+               png_ptr->background.blue = (png_uint_16)(pow(
+                  (double)png_ptr->background.blue / m, gs) * m + .5);
+            }
+            else
+            {
+               /* GRAY or GRAY ALPHA */
+               png_ptr->background_1.gray = (png_uint_16)(pow(
+                  (double)png_ptr->background.gray / m, g) * m + .5);
+               png_ptr->background.gray = (png_uint_16)(pow(
+                  (double)png_ptr->background.gray / m, gs) * m + .5);
+            }
+         }
+      }
+      else
+      /* transformation does not include PNG_BACKGROUND */
+#endif
+      if (color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_colorp palette = png_ptr->palette;
+         int num_palette = png_ptr->num_palette;
+         int i;
+
+         for (i = 0; i < num_palette; i++)
+         {
+            palette[i].red = png_ptr->gamma_table[palette[i].red];
+            palette[i].green = png_ptr->gamma_table[palette[i].green];
+            palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+         }
+      }
+   }
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   else
+#endif
+#endif
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* No GAMMA transformation */
+   if (png_ptr->transformations & PNG_BACKGROUND &&
+       color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      int i;
+      int istop = (int)png_ptr->num_trans;
+      png_color back;
+      png_colorp palette = png_ptr->palette;
+
+      back.red   = (png_byte)png_ptr->background.red;
+      back.green = (png_byte)png_ptr->background.green;
+      back.blue  = (png_byte)png_ptr->background.blue;
+
+      for (i = 0; i < istop; i++)
+      {
+         if (png_ptr->trans[i] == 0)
+         {
+            palette[i] = back;
+         }
+         else if (png_ptr->trans[i] != 0xff)
+         {
+            /* The png_composite() macro is defined in png.h */
+            png_composite(palette[i].red, palette[i].red,
+               png_ptr->trans[i], back.red);
+            png_composite(palette[i].green, palette[i].green,
+               png_ptr->trans[i], back.green);
+            png_composite(palette[i].blue, palette[i].blue,
+               png_ptr->trans[i], back.blue);
+         }
+      }
+   }
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+   if ((png_ptr->transformations & PNG_SHIFT) &&
+      color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      png_uint_16 i;
+      png_uint_16 istop = png_ptr->num_palette;
+      int sr = 8 - png_ptr->sig_bit.red;
+      int sg = 8 - png_ptr->sig_bit.green;
+      int sb = 8 - png_ptr->sig_bit.blue;
+
+      if (sr < 0 || sr > 8)
+         sr = 0;
+      if (sg < 0 || sg > 8)
+         sg = 0;
+      if (sb < 0 || sb > 8)
+         sb = 0;
+      for (i = 0; i < istop; i++)
+      {
+         png_ptr->palette[i].red >>= sr;
+         png_ptr->palette[i].green >>= sg;
+         png_ptr->palette[i].blue >>= sb;
+      }
+   }
+#endif
+ }
+}
+
+/* Modify the info structure to reflect the transformations.  The
+ * info should be updated so a PNG file could be written with it,
+ * assuming the transformations result in valid PNG data.
+ */
+void
+png_read_transform_info(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_read_transform_info\n");
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+   if (png_ptr->transformations & PNG_EXPAND)
+   {
+      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         if (png_ptr->num_trans)
+            info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+         else
+            info_ptr->color_type = PNG_COLOR_TYPE_RGB;
+         info_ptr->bit_depth = 8;
+         info_ptr->num_trans = 0;
+      }
+      else
+      {
+         if (png_ptr->num_trans)
+            info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+         if (info_ptr->bit_depth < 8)
+            info_ptr->bit_depth = 8;
+         info_ptr->num_trans = 0;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if (png_ptr->transformations & PNG_BACKGROUND)
+   {
+      info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA;
+      info_ptr->num_trans = 0;
+      info_ptr->background = png_ptr->background;
+   }
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if (png_ptr->transformations & PNG_GAMMA)
+      info_ptr->gamma = png_ptr->gamma;
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+   if ((png_ptr->transformations & PNG_16_TO_8) && info_ptr->bit_depth == 16)
+      info_ptr->bit_depth = 8;
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+   if (png_ptr->transformations & PNG_DITHER)
+   {
+      if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
+         (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) &&
+         png_ptr->palette_lookup && info_ptr->bit_depth == 8)
+      {
+         info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+   if ((png_ptr->transformations & PNG_PACK) && info_ptr->bit_depth < 8)
+      info_ptr->bit_depth = 8;
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   if (png_ptr->transformations & PNG_GRAY_TO_RGB)
+      info_ptr->color_type |= PNG_COLOR_MASK_COLOR;
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+   if (png_ptr->transformations & PNG_RGB_TO_GRAY)
+      info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR;
+#endif
+
+   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      info_ptr->channels = 1;
+   else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      info_ptr->channels = 3;
+   else
+      info_ptr->channels = 1;
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_STRIP_ALPHA)
+      info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA;
+#endif
+
+   if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+      info_ptr->channels++;
+
+#if defined(PNG_READ_FILLER_SUPPORTED)
+   /* STRIP_ALPHA and FILLER allowed:  MASK_ALPHA bit stripped above */
+   if (png_ptr->transformations & PNG_FILLER &&
+       (info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
+        info_ptr->color_type == PNG_COLOR_TYPE_GRAY))
+      ++info_ptr->channels;
+#endif
+
+   info_ptr->pixel_depth = (png_byte)(info_ptr->channels *
+      info_ptr->bit_depth);
+   info_ptr->rowbytes = ((info_ptr->width * info_ptr->pixel_depth + 7) >> 3);
+}
+
+/* Transform the row.  The order of transformations is significant,
+ * and is very touchy.  If you add a transformation, take care to
+ * decide how it fits in with the other transformations here.
+ */
+void
+png_do_read_transformations(png_structp png_ptr)
+{
+   png_debug(1, "in png_do_read_transformations\n");
+#if !defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (png_ptr->row_buf == NULL)
+   {
+#if !defined(PNG_NO_STDIO)
+      char msg[50];
+
+      sprintf(msg, "NULL row buffer for row %ld, pass %d", png_ptr->row_number,
+         png_ptr->pass);
+      png_error(png_ptr, msg);
+#else
+      png_error(png_ptr, "NULL row buffer");
+#endif
+   }
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+   if (png_ptr->transformations & PNG_EXPAND)
+   {
+      if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1,
+            png_ptr->palette, png_ptr->trans, png_ptr->num_trans);
+      }
+      else
+      {
+         if (png_ptr->num_trans)
+            png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1,
+               &(png_ptr->trans_values));
+         else
+            png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1,
+               NULL);
+      }
+   }
+#endif
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_STRIP_ALPHA)
+      png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         PNG_FLAG_FILLER_AFTER);
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+   if (png_ptr->transformations & PNG_RGB_TO_GRAY)
+   {
+      int rgb_error =
+         png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info), png_ptr->row_buf + 1);
+      if(rgb_error)
+      {
+         png_ptr->rgb_to_gray_status=1;
+         if(png_ptr->transformations == PNG_RGB_TO_GRAY_WARN)
+            png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel");
+         if(png_ptr->transformations == PNG_RGB_TO_GRAY_ERR)
+            png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel");
+      }
+   }
+#endif
+
+/*
+From Andreas Dilger e-mail to png-implement, 26 March 1998:
+
+  In most cases, the "simple transparency" should be done prior to doing
+  gray-to-RGB, or you will have to test 3x as many bytes to check if a
+  pixel is transparent.  You would also need to make sure that the
+  transparency information is upgraded to RGB.
+
+  To summarize, the current flow is:
+  - Gray + simple transparency -> compare 1 or 2 gray bytes and composite
+                                  with background "in place" if transparent,
+                                  convert to RGB if necessary
+  - Gray + alpha -> composite with gray background and remove alpha bytes,
+                                  convert to RGB if necessary
+
+  To support RGB backgrounds for gray images we need:
+  - Gray + simple transparency -> convert to RGB + simple transparency, compare
+                                  3 or 6 bytes and composite with background
+                                  "in place" if transparent (3x compare/pixel
+                                  compared to doing composite with gray bkgrnd)
+  - Gray + alpha -> convert to RGB + alpha, composite with background and
+                                  remove alpha bytes (3x float operations/pixel
+                                  compared with composite on gray background)
+
+  Greg's change will do this.  The reason it wasn't done before is for
+  performance, as this increases the per-pixel operations.  If we would check
+  in advance if the background was gray or RGB, and position the gray-to-RGB
+  transform appropriately, then it would save a lot of work/time.
+ */
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   /* if gray -> RGB, do so now only if background is non-gray; else do later
+    * for performance reasons */
+   if (png_ptr->transformations & PNG_GRAY_TO_RGB &&
+       !(png_ptr->flags & PNG_FLAG_BACKGROUND_IS_GRAY))
+      png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if ((png_ptr->transformations & PNG_BACKGROUND) &&
+      ((png_ptr->num_trans != 0 ) ||
+      (png_ptr->color_type & PNG_COLOR_MASK_ALPHA)))
+      png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         &(png_ptr->trans_values), &(png_ptr->background),
+         &(png_ptr->background_1),
+         png_ptr->gamma_table, png_ptr->gamma_from_1,
+         png_ptr->gamma_to_1, png_ptr->gamma_16_table,
+         png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1,
+         png_ptr->gamma_shift);
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if ((png_ptr->transformations & PNG_GAMMA) &&
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+      !((png_ptr->transformations & PNG_BACKGROUND) &&
+      ((png_ptr->num_trans != 0) ||
+      (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) &&
+#endif
+      (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE))
+      png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->gamma_table, png_ptr->gamma_16_table,
+         png_ptr->gamma_shift);
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+   if (png_ptr->transformations & PNG_16_TO_8)
+      png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+   if (png_ptr->transformations & PNG_DITHER)
+   {
+      png_do_dither((png_row_infop)&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->palette_lookup, png_ptr->dither_index);
+      if(png_ptr->row_info.rowbytes == (png_uint_32)0)
+         png_error(png_ptr, "png_do_dither returned rowbytes=0");
+   }
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         &(png_ptr->shift));
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACK)
+      png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED)
+   if (png_ptr->transformations & PNG_BGR)
+      png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACKSWAP)
+      png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   /* if gray -> RGB, do so now only if we did not do so above */
+   if (png_ptr->transformations & PNG_GRAY_TO_RGB &&
+       png_ptr->flags & PNG_FLAG_BACKGROUND_IS_GRAY)
+      png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED)
+   if (png_ptr->transformations & PNG_FILLER)
+      png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         (png_uint_32)png_ptr->filler, png_ptr->flags);
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_ALPHA)
+      png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_ALPHA)
+      png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
+   if (png_ptr->transformations & PNG_USER_TRANSFORM)
+      if(png_ptr->read_user_transform_fn != NULL)
+        (*(png_ptr->read_user_transform_fn)) /* user read transform function */
+          (png_ptr,                    /* png_ptr */
+           &(png_ptr->row_info),       /* row_info:     */
+             /*  png_uint_32 width;          width of row */
+             /*  png_uint_32 rowbytes;       number of bytes in row */
+             /*  png_byte color_type;        color type of pixels */
+             /*  png_byte bit_depth;         bit depth of samples */
+             /*  png_byte channels;          number of channels (1-4) */
+             /*  png_byte pixel_depth;       bits per pixel (depth*channels) */
+           png_ptr->row_buf + 1);      /* start of pixel data for row */
+#endif
+
+}
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel,
+ * without changing the actual values.  Thus, if you had a row with
+ * a bit depth of 1, you would end up with bytes that only contained
+ * the numbers 0 or 1.  If you would rather they contain 0 and 255, use
+ * png_do_shift() after this.
+ */
+void
+png_do_unpack(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_unpack\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL && row_info->bit_depth < 8)
+#else
+   if (row_info->bit_depth < 8)
+#endif
+   {
+      png_uint_32 i;
+      png_uint_32 row_width=row_info->width;
+
+      switch (row_info->bit_depth)
+      {
+         case 1:
+         {
+            png_bytep sp = row + (png_size_t)((row_width - 1) >> 3);
+            png_bytep dp = row + (png_size_t)row_width - 1;
+            png_uint_32 shift = 7 - (int)((row_width + 7) & 7);
+            for (i = 0; i < row_width; i++)
+            {
+               *dp = (png_byte)((*sp >> shift) & 0x1);
+               if (shift == 7)
+               {
+                  shift = 0;
+                  sp--;
+               }
+               else
+                  shift++;
+
+               dp--;
+            }
+            break;
+         }
+         case 2:
+         {
+
+            png_bytep sp = row + (png_size_t)((row_width - 1) >> 2);
+            png_bytep dp = row + (png_size_t)row_width - 1;
+            png_uint_32 shift = (int)((3 - ((row_width + 3) & 3)) << 1);
+            for (i = 0; i < row_width; i++)
+            {
+               *dp = (png_byte)((*sp >> shift) & 0x3);
+               if (shift == 6)
+               {
+                  shift = 0;
+                  sp--;
+               }
+               else
+                  shift += 2;
+
+               dp--;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp = row + (png_size_t)((row_width - 1) >> 1);
+            png_bytep dp = row + (png_size_t)row_width - 1;
+            png_uint_32 shift = (int)((1 - ((row_width + 1) & 1)) << 2);
+            for (i = 0; i < row_width; i++)
+            {
+               *dp = (png_byte)((*sp >> shift) & 0xf);
+               if (shift == 4)
+               {
+                  shift = 0;
+                  sp--;
+               }
+               else
+                  shift = 4;
+
+               dp--;
+            }
+            break;
+         }
+      }
+      row_info->bit_depth = 8;
+      row_info->pixel_depth = (png_byte)(8 * row_info->channels);
+      row_info->rowbytes = row_width * row_info->channels;
+   }
+}
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+/* Reverse the effects of png_do_shift.  This routine merely shifts the
+ * pixels back to their significant bits values.  Thus, if you have
+ * a row of bit depth 8, but only 5 are significant, this will shift
+ * the values back to 0 through 31.
+ */
+void
+png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits)
+{
+   png_debug(1, "in png_do_unshift\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL && sig_bits != NULL &&
+#endif
+       row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      int shift[4];
+      int channels = 0;
+      int c;
+      png_uint_16 value = 0;
+      png_uint_32 row_width = row_info->width;
+
+      if (row_info->color_type & PNG_COLOR_MASK_COLOR)
+      {
+         shift[channels++] = row_info->bit_depth - sig_bits->red;
+         shift[channels++] = row_info->bit_depth - sig_bits->green;
+         shift[channels++] = row_info->bit_depth - sig_bits->blue;
+      }
+      else
+      {
+         shift[channels++] = row_info->bit_depth - sig_bits->gray;
+      }
+      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+      {
+         shift[channels++] = row_info->bit_depth - sig_bits->alpha;
+      }
+
+      for (c = 0; c < channels; c++)
+      {
+         if (shift[c] <= 0)
+            shift[c] = 0;
+         else
+            value = 1;
+      }
+
+      if (!value)
+         return;
+
+      switch (row_info->bit_depth)
+      {
+         case 2:
+         {
+            png_bytep bp;
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+
+            for (bp = row, i = 0; i < istop; i++)
+            {
+               *bp >>= 1;
+               *bp++ &= 0x55;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_bytep bp = row;
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_byte mask = (png_byte)(((int)0xf0 >> shift[0]) & (int)0xf0) |
+               (png_byte)((int)0xf >> shift[0]);
+
+            for (i = 0; i < istop; i++)
+            {
+               *bp >>= shift[0];
+               *bp++ &= mask;
+            }
+            break;
+         }
+         case 8:
+         {
+            png_bytep bp = row;
+            png_uint_32 i;
+            png_uint_32 istop = row_width * channels;
+
+            for (i = 0; i < istop; i++)
+            {
+               *bp++ >>= shift[i%channels];
+            }
+            break;
+         }
+         case 16:
+         {
+            png_bytep bp = row;
+            png_uint_32 i;
+            png_uint_32 istop = channels * row_width;
+
+            for (i = 0; i < istop; i++)
+            {
+               value = (png_uint_16)((*bp << 8) + *(bp + 1));
+               value >>= shift[i%channels];
+               *bp++ = (png_byte)(value >> 8);
+               *bp++ = (png_byte)(value & 0xff);
+            }
+            break;
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+/* chop rows of bit depth 16 down to 8 */
+void
+png_do_chop(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_chop\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL && row_info->bit_depth == 16)
+#else
+   if (row_info->bit_depth == 16)
+#endif
+   {
+      png_bytep sp = row;
+      png_bytep dp = row;
+      png_uint_32 i;
+      png_uint_32 istop = row_info->width * row_info->channels;
+
+      for (i = 0; i<istop; i++, sp += 2, dp++)
+      {
+#if defined(PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED)
+      /* This does a more accurate scaling of the 16-bit color
+       * value, rather than a simple low-byte truncation.
+       *
+       * What the ideal calculation should be:
+       *   *dp = (((((png_uint_32)(*sp) << 8) |
+       *          (png_uint_32)(*(sp + 1))) * 255 + 127) / (png_uint_32)65535L;
+       *
+       * GRR: no, I think this is what it really should be:
+       *   *dp = (((((png_uint_32)(*sp) << 8) |
+       *           (png_uint_32)(*(sp + 1))) + 128L) / (png_uint_32)257L;
+       *
+       * GRR: here's the exact calculation with shifts:
+       *   temp = (((png_uint_32)(*sp) << 8) | (png_uint_32)(*(sp + 1))) + 128L;
+       *   *dp = (temp - (temp >> 8)) >> 8;
+       *
+       * Approximate calculation with shift/add instead of multiply/divide:
+       *   *dp = ((((png_uint_32)(*sp) << 8) |
+       *          (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8;
+       *
+       * What we actually do to avoid extra shifting and conversion:
+       */
+
+         *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0);
+#else
+       /* Simply discard the low order byte */
+         *dp = *sp;
+#endif
+      }
+      row_info->bit_depth = 8;
+      row_info->pixel_depth = (png_byte)(8 * row_info->channels);
+      row_info->rowbytes = row_info->width * row_info->channels;
+   }
+}
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
+void
+png_do_read_swap_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_read_swap_alpha\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      png_uint_32 row_width = row_info->width;
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This converts from RGBA to ARGB */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save;
+            }
+         }
+         /* This converts from RRGGBBAA to AARRGGBB */
+         else
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save[2];
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save[0] = *(--sp);
+               save[1] = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save[0];
+               *(--dp) = save[1];
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This converts from GA to AG */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save;
+            }
+         }
+         /* This converts from GGAA to AAGG */
+         else
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save[2];
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save[0] = *(--sp);
+               save[1] = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save[0];
+               *(--dp) = save[1];
+            }
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+void
+png_do_read_invert_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_read_invert_alpha\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      png_uint_32 row_width = row_info->width;
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This inverts the alpha channel in RGBA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = 255 - *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+         }
+         /* This inverts the alpha channel in RRGGBBAA */
+         else
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = 255 - *(--sp);
+               *(--dp) = 255 - *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This inverts the alpha channel in GA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = 255 - *(--sp);
+               *(--dp) = *(--sp);
+            }
+         }
+         /* This inverts the alpha channel in GGAA */
+         else
+         {
+            png_bytep sp  = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = 255 - *(--sp);
+               *(--dp) = 255 - *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED)
+/* Add filler channel if we have RGB color */
+void
+png_do_read_filler(png_row_infop row_info, png_bytep row,
+   png_uint_32 filler, png_uint_32 flags)
+{
+   png_uint_32 i;
+   png_uint_32 row_width = row_info->width;
+
+   png_byte hi_filler = (png_byte)((filler>>8) & 0xff);
+   png_byte lo_filler = (png_byte)(filler & 0xff);
+
+   png_debug(1, "in png_do_read_filler\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL  && row_info != NULL &&
+#endif
+       row_info->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      if(row_info->bit_depth == 8)
+      {
+         /* This changes the data from G to GX */
+         if (flags & PNG_FLAG_FILLER_AFTER)
+         {
+            png_bytep sp = row + (png_size_t)row_width;
+            png_bytep dp =  sp + (png_size_t)row_width;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = lo_filler;
+            row_info->channels = 2;
+            row_info->pixel_depth = 16;
+            row_info->rowbytes = row_width * 2;
+         }
+      /* This changes the data from G to XG */
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width;
+            png_bytep dp = sp  + (png_size_t)row_width;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 2;
+            row_info->pixel_depth = 16;
+            row_info->rowbytes = row_width * 2;
+         }
+      }
+      else if(row_info->bit_depth == 16)
+      {
+         /* This changes the data from GG to GGXX */
+         if (flags & PNG_FLAG_FILLER_AFTER)
+         {
+            png_bytep sp = row + (png_size_t)row_width;
+            png_bytep dp = sp  + (png_size_t)row_width;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = hi_filler;
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = hi_filler;
+            *(--dp) = lo_filler;
+            row_info->channels = 2;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 2;
+         }
+         /* This changes the data from GG to XXGG */
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width;
+            png_bytep dp = sp  + (png_size_t)row_width;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = hi_filler;
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 2;
+            row_info->pixel_depth = 16;
+            row_info->rowbytes = row_width * 2;
+         }
+      }
+   } /* COLOR_TYPE == GRAY */
+   else if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+   {
+      if(row_info->bit_depth == 8)
+      {
+         /* This changes the data from RGB to RGBX */
+         if (flags & PNG_FLAG_FILLER_AFTER)
+         {
+            png_bytep sp = row + (png_size_t)row_width * 3;
+            png_bytep dp = sp  + (png_size_t)row_width;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = lo_filler;
+            row_info->channels = 4;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+      /* This changes the data from RGB to XRGB */
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 3;
+            png_bytep dp = sp + (png_size_t)row_width;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 4;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+      }
+      else if(row_info->bit_depth == 16)
+      {
+         /* This changes the data from RRGGBB to RRGGBBXX */
+         if (flags & PNG_FLAG_FILLER_AFTER)
+         {
+            png_bytep sp = row + (png_size_t)row_width * 3;
+            png_bytep dp = sp  + (png_size_t)row_width;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = hi_filler;
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = hi_filler;
+            *(--dp) = lo_filler;
+            row_info->channels = 4;
+            row_info->pixel_depth = 64;
+            row_info->rowbytes = row_width * 4;
+         }
+         /* This changes the data from RRGGBB to XXRRGGBB */
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 3;
+            png_bytep dp = sp  + (png_size_t)row_width;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = hi_filler;
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 4;
+            row_info->pixel_depth = 64;
+            row_info->rowbytes = row_width * 4;
+         }
+      }
+   } /* COLOR_TYPE == RGB */
+}
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+/* expand grayscale files to RGB, with or without alpha */
+void
+png_do_gray_to_rgb(png_row_infop row_info, png_bytep row)
+{
+   png_uint_32 i;
+   png_uint_32 row_width = row_info->width;
+
+   png_debug(1, "in png_do_gray_to_rgb\n");
+   if (row_info->bit_depth >= 8 &&
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+      !(row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + (png_size_t)row_width - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               sp--;
+            }
+         }
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 2 - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 4;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               sp--;
+               sp--;
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + (png_size_t)row_width * 2 - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *(sp--);
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               sp--;
+            }
+         }
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 4 - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 4;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *(sp--);
+               *(dp--) = *(sp--);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               sp--;
+               sp--;
+            }
+         }
+      }
+      row_info->channels += (png_byte)2;
+      row_info->color_type |= PNG_COLOR_MASK_COLOR;
+      row_info->pixel_depth = (png_byte)(row_info->channels *
+         row_info->bit_depth);
+      row_info->rowbytes = ((row_width *
+         row_info->pixel_depth + 7) >> 3);
+   }
+}
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+/* reduce RGB files to grayscale, with or without alpha 
+ * using the equation given in Poynton's ColorFAQ at
+ * <http://www.inforamp.net/~poynton/>
+ * Copyright (c) 1998-01-04 Charles Poynton poynton@inforamp.net
+ *
+ *     Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
+ *
+ *  We approximate this with
+ * 
+ *     Y = 0.211 * R    + 0.715 * G    + 0.074 * B
+ *
+ *  which can be expressed with integers as
+ *
+ *     Y = (54 * R + 183 * G + 19 * B)/256
+ *
+ *  The calculation is to be done in a linear colorspace.
+ *
+ *  Other integer coefficents can be used via png_set_rgb_to_gray().
+ */
+int
+png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row)
+
+{
+   png_uint_32 i;
+
+   png_uint_32 row_width = row_info->width;
+   int rgb_error = 0;
+
+   png_debug(1, "in png_do_rgb_to_gray\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+      (row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      png_byte rc = png_ptr->rgb_to_gray_red_coeff;
+      png_byte gc = png_ptr->rgb_to_gray_green_coeff;
+      png_byte bc = png_ptr->rgb_to_gray_blue_coeff;
+
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+      {
+         if (row_info->bit_depth == 8)
+         {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+            if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+
+               for (i = 0; i < row_width; i++)
+               {
+                  png_byte red   = png_ptr->gamma_to_1[*(sp++)];
+                  png_byte green = png_ptr->gamma_to_1[*(sp++)];
+                  png_byte blue  = png_ptr->gamma_to_1[*(sp++)];
+                  if(red != green || red != blue)
+                  {
+                     rgb_error |= 1;
+                     *(dp++) = png_ptr->gamma_from_1[
+                       (rc*red+gc*green+bc*blue)>>8];
+                  }
+                  else
+                     *(dp++) = *(sp-1);
+               }
+            }
+            else
+#endif
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_byte red   = *(sp++);
+                  png_byte green = *(sp++);
+                  png_byte blue  = *(sp++);
+                  if(red != green || red != blue)
+                  {
+                     rgb_error |= 1;
+                     *(dp++) = (rc*red+gc*green+bc*blue)>>8;
+                  }
+                  else
+                     *(dp++) = *(sp-1);
+               }
+            }
+         }
+         else /* RGB bit_depth == 16 */
+         {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+            if (png_ptr->gamma_16_to_1 != NULL &&
+                png_ptr->gamma_16_from_1 != NULL)
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 red, green, blue, w;
+
+                  red   = ((*(sp))<<8) | *(sp+1); sp+=2;
+                  green = ((*(sp))<<8) | *(sp+1); sp+=2;
+                  blue  = ((*(sp))<<8) | *(sp+1); sp+=2;
+
+                  if(red == green && red == blue)
+                     w = red;
+                  else
+                  {
+                     png_uint_16 red_1   = png_ptr->gamma_16_to_1[(red&0xff) >>
+                                  png_ptr->gamma_shift][red>>8];
+                     png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >>
+                                  png_ptr->gamma_shift][green>>8];
+                     png_uint_16 blue_1  = png_ptr->gamma_16_to_1[(blue&0xff) >> 
+                                  png_ptr->gamma_shift][blue>>8];
+                     png_uint_16 gray16  =  (rc * red_1 + gc * green_1
+                                  + bc * blue_1)>>8;
+                     w = png_ptr->gamma_16_from_1[(gray16&0xff) >>
+                         png_ptr->gamma_shift][gray16 >> 8];
+                     rgb_error |= 1;
+                  }
+                  
+                  *(dp++) = (w>>8) & 0xff;
+                  *(dp++) = w & 0xff;
+               }
+            }
+            else
+#endif
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 red, green, blue, gray16;
+
+                  red   = ((*(sp))<<8) | *(sp+1); sp+=2;
+                  green = ((*(sp))<<8) | *(sp+1); sp+=2;
+                  blue  = ((*(sp))<<8) | *(sp+1); sp+=2;
+
+                  if(red != green || red != blue)
+                     rgb_error |= 1;
+                  gray16  =  (rc * red + gc * green + bc * blue)>>8;
+                  *(dp++) = (gray16>>8) & 0xff;
+                  *(dp++) = gray16 & 0xff;
+               }
+            }
+         }
+      }
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         if (row_info->bit_depth == 8)
+         {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+            if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_byte red   = png_ptr->gamma_to_1[*(sp++)];
+                  png_byte green = png_ptr->gamma_to_1[*(sp++)];
+                  png_byte blue  = png_ptr->gamma_to_1[*(sp++)];
+                  if(red != green || red != blue)
+                     rgb_error |= 1;
+                  *(dp++) =  png_ptr->gamma_from_1
+                             [(rc*red + gc*green + bc*blue)>>8];
+                  *(dp++) = *(sp++);  /* alpha */
+               }
+            }
+            else
+#endif
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_byte red   = *(sp++);
+                  png_byte green = *(sp++);
+                  png_byte blue  = *(sp++);
+                  if(red != green || red != blue)
+                     rgb_error |= 1;
+                  *(dp++) =  (gc*red + gc*green + bc*blue)>>8;
+                  *(dp++) = *(sp++);  /* alpha */
+               }
+            }
+         }
+         else /* RGBA bit_depth == 16 */
+         {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+            if (png_ptr->gamma_16_to_1 != NULL &&
+                png_ptr->gamma_16_from_1 != NULL)
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 red, green, blue, w;
+
+                  red   = ((*(sp))<<8) | *(sp+1); sp+=2;
+                  green = ((*(sp))<<8) | *(sp+1); sp+=2;
+                  blue  = ((*(sp))<<8) | *(sp+1); sp+=2;
+
+                  if(red == green && red == blue)
+                     w = red;
+                  else
+                  {
+                     png_uint_16 red_1   = png_ptr->gamma_16_to_1[(red&0xff) >>
+                                  png_ptr->gamma_shift][red>>8];
+                     png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >>
+                                  png_ptr->gamma_shift][green>>8];
+                     png_uint_16 blue_1  = png_ptr->gamma_16_to_1[(blue&0xff) >> 
+                                  png_ptr->gamma_shift][blue>>8];
+                     png_uint_16 gray16  =  (rc * red_1 + gc * green_1
+                                  + bc * blue_1)>>8;
+                     w = png_ptr->gamma_16_from_1[(gray16&0xff) >>
+                         png_ptr->gamma_shift][gray16 >> 8];
+                     rgb_error |= 1;
+                  }
+                  
+                  *(dp++) = (w>>8) & 0xff;
+                  *(dp++) = w & 0xff;
+                  *(dp++) = *(sp++);  /* alpha */
+                  *(dp++) = *(sp++);
+               }
+            }
+            else
+#endif
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 red, green, blue, gray16;
+                  red   = (*(sp)<<8) | *(sp+1); sp+=2;
+                  green = (*(sp)<<8) | *(sp+1); sp+=2;
+                  blue  = (*(sp)<<8) | *(sp+1); sp+=2;
+                  if(red != green || red != blue)
+                     rgb_error |= 1;
+                  gray16  =  (rc * red + gc * green + bc * blue)>>8;
+                  *(dp++) = (gray16>>8) & 0xff;
+                  *(dp++) = gray16 & 0xff;
+                  *(dp++) = *(sp++);  /* alpha */
+                  *(dp++) = *(sp++);
+               }
+            }
+         }
+      }
+   row_info->channels -= (png_byte)2;
+      row_info->color_type &= ~PNG_COLOR_MASK_COLOR;
+      row_info->pixel_depth = (png_byte)(row_info->channels *
+         row_info->bit_depth);
+      row_info->rowbytes = ((row_width *
+         row_info->pixel_depth + 7) >> 3);
+   }
+   return rgb_error;
+}
+#endif
+
+/* Build a grayscale palette.  Palette is assumed to be 1 << bit_depth
+ * large of png_color.  This lets grayscale images be treated as
+ * paletted.  Most useful for gamma correction and simplification
+ * of code.
+ */
+void
+png_build_grayscale_palette(int bit_depth, png_colorp palette)
+{
+   int num_palette;
+   int color_inc;
+   int i;
+   int v;
+
+   png_debug(1, "in png_do_build_grayscale_palette\n");
+   if (palette == NULL)
+      return;
+
+   switch (bit_depth)
+   {
+      case 1:
+         num_palette = 2;
+         color_inc = 0xff;
+         break;
+      case 2:
+         num_palette = 4;
+         color_inc = 0x55;
+         break;
+      case 4:
+         num_palette = 16;
+         color_inc = 0x11;
+         break;
+      case 8:
+         num_palette = 256;
+         color_inc = 1;
+         break;
+      default:
+         num_palette = 0;
+         color_inc = 0;
+         break;
+   }
+
+   for (i = 0, v = 0; i < num_palette; i++, v += color_inc)
+   {
+      palette[i].red = (png_byte)v;
+      palette[i].green = (png_byte)v;
+      palette[i].blue = (png_byte)v;
+   }
+}
+
+/* This function is currently unused.  Do we really need it? */
+#if defined(PNG_READ_DITHER_SUPPORTED) && defined(PNG_CORRECT_PALETTE_SUPPORTED)
+void
+png_correct_palette(png_structp png_ptr, png_colorp palette,
+   int num_palette)
+{
+   png_debug(1, "in png_correct_palette\n");
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED)
+   if (png_ptr->transformations & (PNG_GAMMA | PNG_BACKGROUND))
+   {
+      png_color back, back_1;
+
+      if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE)
+      {
+         back.red = png_ptr->gamma_table[png_ptr->background.red];
+         back.green = png_ptr->gamma_table[png_ptr->background.green];
+         back.blue = png_ptr->gamma_table[png_ptr->background.blue];
+
+         back_1.red = png_ptr->gamma_to_1[png_ptr->background.red];
+         back_1.green = png_ptr->gamma_to_1[png_ptr->background.green];
+         back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue];
+      }
+      else
+      {
+         double g;
+
+         g = 1.0 / (png_ptr->background_gamma * png_ptr->screen_gamma);
+
+         if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_SCREEN ||
+             fabs(g - 1.0) < PNG_GAMMA_THRESHOLD)
+         {
+            back.red = png_ptr->background.red;
+            back.green = png_ptr->background.green;
+            back.blue = png_ptr->background.blue;
+         }
+         else
+         {
+            back.red =
+               (png_byte)(pow((double)png_ptr->background.red/255, g) *
+                255.0 + 0.5);
+            back.green =
+               (png_byte)(pow((double)png_ptr->background.green/255, g) *
+                255.0 + 0.5);
+            back.blue =
+               (png_byte)(pow((double)png_ptr->background.blue/255, g) *
+                255.0 + 0.5);
+         }
+
+         g = 1.0 / png_ptr->background_gamma;
+
+         back_1.red =
+            (png_byte)(pow((double)png_ptr->background.red/255, g) *
+             255.0 + 0.5);
+         back_1.green =
+            (png_byte)(pow((double)png_ptr->background.green/255, g) *
+             255.0 + 0.5);
+         back_1.blue =
+            (png_byte)(pow((double)png_ptr->background.blue/255, g) *
+             255.0 + 0.5);
+      }
+
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_uint_32 i;
+
+         for (i = 0; i < (png_uint_32)num_palette; i++)
+         {
+            if (i < png_ptr->num_trans && png_ptr->trans[i] == 0)
+            {
+               palette[i] = back;
+            }
+            else if (i < png_ptr->num_trans && png_ptr->trans[i] != 0xff)
+            {
+               png_byte v, w;
+
+               v = png_ptr->gamma_to_1[png_ptr->palette[i].red];
+               png_composite(w, v, png_ptr->trans[i], back_1.red);
+               palette[i].red = png_ptr->gamma_from_1[w];
+
+               v = png_ptr->gamma_to_1[png_ptr->palette[i].green];
+               png_composite(w, v, png_ptr->trans[i], back_1.green);
+               palette[i].green = png_ptr->gamma_from_1[w];
+
+               v = png_ptr->gamma_to_1[png_ptr->palette[i].blue];
+               png_composite(w, v, png_ptr->trans[i], back_1.blue);
+               palette[i].blue = png_ptr->gamma_from_1[w];
+            }
+            else
+            {
+               palette[i].red = png_ptr->gamma_table[palette[i].red];
+               palette[i].green = png_ptr->gamma_table[palette[i].green];
+               palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+            }
+         }
+      }
+      else
+      {
+         int i;
+
+         for (i = 0; i < num_palette; i++)
+         {
+            if (palette[i].red == (png_byte)png_ptr->trans_values.gray)
+            {
+               palette[i] = back;
+            }
+            else
+            {
+               palette[i].red = png_ptr->gamma_table[palette[i].red];
+               palette[i].green = png_ptr->gamma_table[palette[i].green];
+               palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+            }
+         }
+      }
+   }
+   else
+#endif
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if (png_ptr->transformations & PNG_GAMMA)
+   {
+      int i;
+
+      for (i = 0; i < num_palette; i++)
+      {
+         palette[i].red = png_ptr->gamma_table[palette[i].red];
+         palette[i].green = png_ptr->gamma_table[palette[i].green];
+         palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+      }
+   }
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   else
+#endif
+#endif
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if (png_ptr->transformations & PNG_BACKGROUND)
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_color back;
+
+         back.red   = (png_byte)png_ptr->background.red;
+         back.green = (png_byte)png_ptr->background.green;
+         back.blue  = (png_byte)png_ptr->background.blue;
+
+         for (i = 0; i < (int)png_ptr->num_trans; i++)
+         {
+            if (png_ptr->trans[i] == 0)
+            {
+               palette[i].red = back.red;
+               palette[i].green = back.green;
+               palette[i].blue = back.blue;
+            }
+            else if (png_ptr->trans[i] != 0xff)
+            {
+               png_composite(palette[i].red, png_ptr->palette[i].red,
+                  png_ptr->trans[i], back.red);
+               png_composite(palette[i].green, png_ptr->palette[i].green,
+                  png_ptr->trans[i], back.green);
+               png_composite(palette[i].blue, png_ptr->palette[i].blue,
+                  png_ptr->trans[i], back.blue);
+            }
+         }
+      }
+      else /* assume grayscale palette (what else could it be?) */
+      {
+         int i;
+
+         for (i = 0; i < num_palette; i++)
+         {
+            if (i == (png_byte)png_ptr->trans_values.gray)
+            {
+               palette[i].red = (png_byte)png_ptr->background.red;
+               palette[i].green = (png_byte)png_ptr->background.green;
+               palette[i].blue = (png_byte)png_ptr->background.blue;
+            }
+         }
+      }
+   }
+#endif
+}
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+/* Replace any alpha or transparency with the supplied background color.
+ * "background" is already in the screen gamma, while "background_1" is
+ * at a gamma of 1.0.  Paletted files have already been taken care of.
+ */
+void
+png_do_background(png_row_infop row_info, png_bytep row,
+   png_color_16p trans_values, png_color_16p background,
+   png_color_16p background_1,
+   png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1,
+   png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1,
+   png_uint_16pp gamma_16_to_1, int gamma_shift)
+{
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+   int shift;
+
+   png_debug(1, "in png_do_background\n");
+   if (background != NULL &&
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+      (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) ||
+      (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_values)))
+   {
+      switch (row_info->color_type)
+      {
+         case PNG_COLOR_TYPE_GRAY:
+         {
+            switch (row_info->bit_depth)
+            {
+               case 1:
+               {
+                  sp = row;
+                  shift = 7;
+                  for (i = 0; i < row_width; i++)
+                  {
+                     if ((png_uint_16)((*sp >> shift) & 0x1)
+                        == trans_values->gray)
+                     {
+                        *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+                        *sp |= (png_byte)(background->gray << shift);
+                     }
+                     if (!shift)
+                     {
+                        shift = 7;
+                        sp++;
+                     }
+                     else
+                        shift--;
+                  }
+                  break;
+               }
+               case 2:
+               {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                  if (gamma_table != NULL)
+                  {
+                     sp = row;
+                     shift = 6;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x3)
+                            == trans_values->gray)
+                        {
+                           *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                           *sp |= (png_byte)(background->gray << shift);
+                        }
+                        else
+                        {
+                           png_byte p = (*sp >> shift) & 0x3;
+                           png_byte g = (gamma_table [p | (p << 2) | (p << 4) |
+                               (p << 6)] >> 6) & 0x3;
+                           *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                           *sp |= (png_byte)(g << shift);
+                        }
+                        if (!shift)
+                        {
+                           shift = 6;
+                           sp++;
+                        }
+                        else
+                           shift -= 2;
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     shift = 6;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x3)
+                            == trans_values->gray)
+                        {
+                           *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                           *sp |= (png_byte)(background->gray << shift);
+                        }
+                        if (!shift)
+                        {
+                           shift = 6;
+                           sp++;
+                        }
+                        else
+                           shift -= 2;
+                     }
+                  }
+                  break;
+               }
+               case 4:
+               {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                  if (gamma_table != NULL)
+                  {
+                     sp = row;
+                     shift = 4;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0xf)
+                            == trans_values->gray)
+                        {
+                           *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                           *sp |= (png_byte)(background->gray << shift);
+                        }
+                        else
+                        {
+                           png_byte p = (*sp >> shift) & 0xf;
+                           png_byte g = (gamma_table[p | (p << 4)] >> 4) & 0xf;
+                           *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                           *sp |= (png_byte)(g << shift);
+                        }
+                        if (!shift)
+                        {
+                           shift = 4;
+                           sp++;
+                        }
+                        else
+                           shift -= 4;
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     shift = 4;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0xf)
+                            == trans_values->gray)
+                        {
+                           *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                           *sp |= (png_byte)(background->gray << shift);
+                        }
+                        if (!shift)
+                        {
+                           shift = 4;
+                           sp++;
+                        }
+                        else
+                           shift -= 4;
+                     }
+                  }
+                  break;
+               }
+               case 8:
+               {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                  if (gamma_table != NULL)
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp++)
+                     {
+                        if (*sp == trans_values->gray)
+                        {
+                           *sp = (png_byte)background->gray;
+                        }
+                        else
+                        {
+                           *sp = gamma_table[*sp];
+                        }
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp++)
+                     {
+                        if (*sp == trans_values->gray)
+                        {
+                           *sp = (png_byte)background->gray;
+                        }
+                     }
+                  }
+                  break;
+               }
+               case 16:
+               {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                  if (gamma_16 != NULL)
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp += 2)
+                     {
+                        png_uint_16 v;
+
+                        v = ((png_uint_16)(*sp) << 8) + *(sp + 1);
+                        if (v == trans_values->gray)
+                        {
+                           /* background is already in screen gamma */
+                           *sp = (png_byte)((background->gray >> 8) & 0xff);
+                           *(sp + 1) = (png_byte)(background->gray & 0xff);
+                        }
+                        else
+                        {
+                           v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                           *sp = (png_byte)((v >> 8) & 0xff);
+                           *(sp + 1) = (png_byte)(v & 0xff);
+                        }
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp += 2)
+                     {
+                        png_uint_16 v;
+
+                        v = ((png_uint_16)(*sp) << 8) + *(sp + 1);
+                        if (v == trans_values->gray)
+                        {
+                           *sp = (png_byte)((background->gray >> 8) & 0xff);
+                           *(sp + 1) = (png_byte)(background->gray & 0xff);
+                        }
+                     }
+                  }
+                  break;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_RGB:
+         {
+            if (row_info->bit_depth == 8)
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_table != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 3)
+                  {
+                     if (*sp == trans_values->red &&
+                        *(sp + 1) == trans_values->green &&
+                        *(sp + 2) == trans_values->blue)
+                     {
+                        *sp = (png_byte)background->red;
+                        *(sp + 1) = (png_byte)background->green;
+                        *(sp + 2) = (png_byte)background->blue;
+                     }
+                     else
+                     {
+                        *sp = gamma_table[*sp];
+                        *(sp + 1) = gamma_table[*(sp + 1)];
+                        *(sp + 2) = gamma_table[*(sp + 2)];
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 3)
+                  {
+                     if (*sp == trans_values->red &&
+                        *(sp + 1) == trans_values->green &&
+                        *(sp + 2) == trans_values->blue)
+                     {
+                        *sp = (png_byte)background->red;
+                        *(sp + 1) = (png_byte)background->green;
+                        *(sp + 2) = (png_byte)background->blue;
+                     }
+                  }
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_16 != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 6)
+                  {
+                     png_uint_16 r = ((png_uint_16)(*sp) << 8) + *(sp + 1);
+                     png_uint_16 g = ((png_uint_16)(*(sp + 2)) << 8) + *(sp + 3);
+                     png_uint_16 b = ((png_uint_16)(*(sp + 4)) << 8) + *(sp + 5);
+                     if (r == trans_values->red && g == trans_values->green &&
+                        b == trans_values->blue)
+                     {
+                        /* background is already in screen gamma */
+                        *sp = (png_byte)((background->red >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(background->red & 0xff);
+                        *(sp + 2) = (png_byte)((background->green >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(background->green & 0xff);
+                        *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(background->blue & 0xff);
+                     }
+                     else
+                     {
+                        png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *sp = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(v & 0xff);
+                        v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                        *(sp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(v & 0xff);
+                        v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                        *(sp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(v & 0xff);
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 6)
+                  {
+                     png_uint_16 r = ((png_uint_16)(*sp) << 8) + *(sp + 1);
+                     png_uint_16 g = ((png_uint_16)(*(sp + 2)) << 8) + *(sp + 3);
+                     png_uint_16 b = ((png_uint_16)(*(sp + 4)) << 8) + *(sp + 5);
+
+                     if (r == trans_values->red && g == trans_values->green &&
+                        b == trans_values->blue)
+                     {
+                        *sp = (png_byte)((background->red >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(background->red & 0xff);
+                        *(sp + 2) = (png_byte)((background->green >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(background->green & 0xff);
+                        *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(background->blue & 0xff);
+                     }
+                  }
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_GRAY_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
+                   gamma_table != NULL)
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 2, dp++)
+                  {
+                     png_uint_16 a = *(sp + 1);
+
+                     if (a == 0xff)
+                     {
+                        *dp = gamma_table[*sp];
+                     }
+                     else if (a == 0)
+                     {
+                        /* background is already in screen gamma */
+                        *dp = (png_byte)background->gray;
+                     }
+                     else
+                     {
+                        png_byte v, w;
+
+                        v = gamma_to_1[*sp];
+                        png_composite(w, v, a, background_1->gray);
+                        *dp = gamma_from_1[w];
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 2, dp++)
+                  {
+                     png_byte a = *(sp + 1);
+
+                     if (a == 0xff)
+                     {
+                        *dp = *sp;
+                     }
+                     else if (a == 0)
+                     {
+                        *dp = (png_byte)background->gray;
+                     }
+                     else
+                     {
+                        png_composite(*dp, *sp, a, background_1->gray);
+                     }
+                  }
+               }
+            }
+            else /* if (png_ptr->bit_depth == 16) */
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
+                   gamma_16_to_1 != NULL)
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 4, dp += 2)
+                  {
+                     png_uint_16 a = ((png_uint_16)(*(sp + 2)) << 8) + *(sp + 3);
+
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_uint_16 v;
+
+                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                     }
+                     else if (a == 0)
+                     {
+                        /* background is already in screen gamma */
+                        *dp = (png_byte)((background->gray >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(background->gray & 0xff);
+                     }
+                     else
+                     {
+                        png_uint_16 g, v, w;
+
+                        g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
+                        png_composite_16(v, g, a, background_1->gray);
+                        w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8];
+                        *dp = (png_byte)((w >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(w & 0xff);
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 4, dp += 2)
+                  {
+                     png_uint_16 a = ((png_uint_16)(*(sp + 2)) << 8) + *(sp + 3);
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_memcpy(dp, sp, 2);
+                     }
+                     else if (a == 0)
+                     {
+                        *dp = (png_byte)((background->gray >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(background->gray & 0xff);
+                     }
+                     else
+                     {
+                        png_uint_16 g, v;
+
+                        g = ((png_uint_16)(*sp) << 8) + *(sp + 1);
+                        png_composite_16(v, g, a, background_1->gray);
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                     }
+                  }
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_RGB_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
+                   gamma_table != NULL)
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 4, dp += 3)
+                  {
+                     png_byte a = *(sp + 3);
+
+                     if (a == 0xff)
+                     {
+                        *dp = gamma_table[*sp];
+                        *(dp + 1) = gamma_table[*(sp + 1)];
+                        *(dp + 2) = gamma_table[*(sp + 2)];
+                     }
+                     else if (a == 0)
+                     {
+                        /* background is already in screen gamma */
+                        *dp = (png_byte)background->red;
+                        *(dp + 1) = (png_byte)background->green;
+                        *(dp + 2) = (png_byte)background->blue;
+                     }
+                     else
+                     {
+                        png_byte v, w;
+
+                        v = gamma_to_1[*sp];
+                        png_composite(w, v, a, background_1->red);
+                        *dp = gamma_from_1[w];
+                        v = gamma_to_1[*(sp + 1)];
+                        png_composite(w, v, a, background_1->green);
+                        *(dp + 1) = gamma_from_1[w];
+                        v = gamma_to_1[*(sp + 2)];
+                        png_composite(w, v, a, background_1->blue);
+                        *(dp + 2) = gamma_from_1[w];
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 4, dp += 3)
+                  {
+                     png_byte a = *(sp + 3);
+
+                     if (a == 0xff)
+                     {
+                        *dp = *sp;
+                        *(dp + 1) = *(sp + 1);
+                        *(dp + 2) = *(sp + 2);
+                     }
+                     else if (a == 0)
+                     {
+                        *dp = (png_byte)background->red;
+                        *(dp + 1) = (png_byte)background->green;
+                        *(dp + 2) = (png_byte)background->blue;
+                     }
+                     else
+                     {
+                        png_composite(*dp, *sp, a, background->red);
+                        png_composite(*(dp + 1), *(sp + 1), a,
+                           background->green);
+                        png_composite(*(dp + 2), *(sp + 2), a,
+                           background->blue);
+                     }
+                  }
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
+                   gamma_16_to_1 != NULL)
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 8, dp += 6)
+                  {
+                     png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
+                         << 8) + (png_uint_16)(*(sp + 7)));
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_uint_16 v;
+
+                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                        v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                        *(dp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(v & 0xff);
+                        v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                        *(dp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(v & 0xff);
+                     }
+                     else if (a == 0)
+                     {
+                        /* background is already in screen gamma */
+                        *dp = (png_byte)((background->red >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(background->red & 0xff);
+                        *(dp + 2) = (png_byte)((background->green >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(background->green & 0xff);
+                        *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(background->blue & 0xff);
+                     }
+                     else
+                     {
+                        png_uint_16 v, w, x;
+
+                        v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
+                        png_composite_16(w, v, a, background->red);
+                        x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
+                        *dp = (png_byte)((x >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(x & 0xff);
+                        v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                        png_composite_16(w, v, a, background->green);
+                        x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
+                        *(dp + 2) = (png_byte)((x >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(x & 0xff);
+                        v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                        png_composite_16(w, v, a, background->blue);
+                        x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8];
+                        *(dp + 4) = (png_byte)((x >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(x & 0xff);
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 8, dp += 6)
+                  {
+                     png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
+                        << 8) + (png_uint_16)(*(sp + 7)));
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_memcpy(dp, sp, 6);
+                     }
+                     else if (a == 0)
+                     {
+                        *dp = (png_byte)((background->red >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(background->red & 0xff);
+                        *(dp + 2) = (png_byte)((background->green >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(background->green & 0xff);
+                        *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(background->blue & 0xff);
+                     }
+                     else
+                     {
+                        png_uint_16 v;
+
+                        png_uint_16 r = ((png_uint_16)(*sp) << 8) + *(sp + 1);
+                        png_uint_16 g = ((png_uint_16)(*(sp + 2)) << 8)
+                            + *(sp + 3);
+                        png_uint_16 b = ((png_uint_16)(*(sp + 4)) << 8)
+                            + *(sp + 5);
+
+                        png_composite_16(v, r, a, background->red);
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                        png_composite_16(v, g, a, background->green);
+                        *(dp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(v & 0xff);
+                        png_composite_16(v, b, a, background->blue);
+                        *(dp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(v & 0xff);
+                     }
+                  }
+               }
+            }
+            break;
+         }
+      }
+
+      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+      {
+         row_info->color_type &= ~PNG_COLOR_MASK_ALPHA;
+         row_info->channels--;
+         row_info->pixel_depth = (png_byte)(row_info->channels *
+            row_info->bit_depth);
+         row_info->rowbytes = ((row_width *
+            row_info->pixel_depth + 7) >> 3);
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+/* Gamma correct the image, avoiding the alpha channel.  Make sure
+ * you do this after you deal with the transparency issue on grayscale
+ * or rgb images. If your bit depth is 8, use gamma_table, if it
+ * is 16, use gamma_16_table and gamma_shift.  Build these with
+ * build_gamma_table().
+ */
+void
+png_do_gamma(png_row_infop row_info, png_bytep row,
+   png_bytep gamma_table, png_uint_16pp gamma_16_table,
+   int gamma_shift)
+{
+   png_bytep sp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_gamma\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       ((row_info->bit_depth <= 8 && gamma_table != NULL) ||
+        (row_info->bit_depth == 16 && gamma_16_table != NULL)))
+   {
+      switch (row_info->color_type)
+      {
+         case PNG_COLOR_TYPE_RGB:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v;
+
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_RGB_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  sp++;
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 4;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_GRAY_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp += 2;
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 4;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_GRAY:
+         {
+            if (row_info->bit_depth == 2)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i += 4)
+               {
+                  int a = *sp & 0xc0;
+                  int b = *sp & 0x30;
+                  int c = *sp & 0x0c;
+                  int d = *sp & 0x03;
+
+                  *sp = ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)])   ) & 0xc0)|
+                        ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)|
+                        ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)|
+                        ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6)       );
+                  sp++;
+               }
+            }
+            if (row_info->bit_depth == 4)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i += 2)
+               {
+                  int msb = *sp & 0xf0;
+                  int lsb = *sp & 0x0f;
+
+                  *sp = (((int)gamma_table[msb | (msb >> 4)]) & 0xf0) |
+                        (((int)gamma_table[(lsb << 4) | lsb]) >> 4);
+                  sp++;
+               }
+            }
+            else if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+               }
+            }
+            else if (row_info->bit_depth == 16)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+               }
+            }
+            break;
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+/* Expands a palette row to an rgb or rgba row depending
+ * upon whether you supply trans and num_trans.
+ */
+void
+png_do_expand_palette(png_row_infop row_info, png_bytep row,
+   png_colorp palette, png_bytep trans, int num_trans)
+{
+   int shift, value;
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_expand_palette\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       row_info->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (row_info->bit_depth < 8)
+      {
+         switch (row_info->bit_depth)
+         {
+            case 1:
+            {
+               sp = row + (png_size_t)((row_width - 1) >> 3);
+               dp = row + (png_size_t)row_width - 1;
+               shift = 7 - (int)((row_width + 7) & 7);
+               for (i = 0; i < row_width; i++)
+               {
+                  if ((*sp >> shift) & 0x1)
+                     *dp = 1;
+                  else
+                     *dp = 0;
+                  if (shift == 7)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift++;
+
+                  dp--;
+               }
+               break;
+            }
+            case 2:
+            {
+               sp = row + (png_size_t)((row_width - 1) >> 2);
+               dp = row + (png_size_t)row_width - 1;
+               shift = (int)((3 - ((row_width + 3) & 3)) << 1);
+               for (i = 0; i < row_width; i++)
+               {
+                  value = (*sp >> shift) & 0x3;
+                  *dp = (png_byte)value;
+                  if (shift == 6)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift += 2;
+
+                  dp--;
+               }
+               break;
+            }
+            case 4:
+            {
+               sp = row + (png_size_t)((row_width - 1) >> 1);
+               dp = row + (png_size_t)row_width - 1;
+               shift = (int)((row_width & 1) << 2);
+               for (i = 0; i < row_width; i++)
+               {
+                  value = (*sp >> shift) & 0xf;
+                  *dp = (png_byte)value;
+                  if (shift == 4)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift += 4;
+
+                  dp--;
+               }
+               break;
+            }
+         }
+         row_info->bit_depth = 8;
+         row_info->pixel_depth = 8;
+         row_info->rowbytes = row_width;
+      }
+      switch (row_info->bit_depth)
+      {
+         case 8:
+         {
+            if (trans != NULL)
+            {
+               sp = row + (png_size_t)row_width - 1;
+               dp = row + (png_size_t)(row_width << 2) - 1;
+
+               for (i = 0; i < row_width; i++)
+               {
+                  if ((int)(*sp) >= num_trans)
+                     *dp-- = 0xff;
+                  else
+                     *dp-- = trans[*sp];
+                  *dp-- = palette[*sp].blue;
+                  *dp-- = palette[*sp].green;
+                  *dp-- = palette[*sp].red;
+                  sp--;
+               }
+               row_info->bit_depth = 8;
+               row_info->pixel_depth = 32;
+               row_info->rowbytes = row_width * 4;
+               row_info->color_type = 6;
+               row_info->channels = 4;
+            }
+            else
+            {
+               sp = row + (png_size_t)row_width - 1;
+               dp = row + (png_size_t)(row_width * 3) - 1;
+
+               for (i = 0; i < row_width; i++)
+               {
+                  *dp-- = palette[*sp].blue;
+                  *dp-- = palette[*sp].green;
+                  *dp-- = palette[*sp].red;
+                  sp--;
+               }
+               row_info->bit_depth = 8;
+               row_info->pixel_depth = 24;
+               row_info->rowbytes = row_width * 3;
+               row_info->color_type = 2;
+               row_info->channels = 3;
+            }
+            break;
+         }
+      }
+   }
+}
+
+/* If the bit depth < 8, it is expanded to 8.  Also, if the
+ * transparency value is supplied, an alpha channel is built.
+ */
+void
+png_do_expand(png_row_infop row_info, png_bytep row,
+   png_color_16p trans_value)
+{
+   int shift, value;
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_expand\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         png_uint_16 gray = trans_value ? trans_value->gray : 0;
+
+         if (row_info->bit_depth < 8)
+         {
+            switch (row_info->bit_depth)
+            {
+               case 1:
+               {
+                  gray *= 0xff;
+                  sp = row + (png_size_t)((row_width - 1) >> 3);
+                  dp = row + (png_size_t)row_width - 1;
+                  shift = 7 - (int)((row_width + 7) & 7);
+                  for (i = 0; i < row_width; i++)
+                  {
+                     if ((*sp >> shift) & 0x1)
+                        *dp = 0xff;
+                     else
+                        *dp = 0;
+                     if (shift == 7)
+                     {
+                        shift = 0;
+                        sp--;
+                     }
+                     else
+                        shift++;
+
+                     dp--;
+                  }
+                  break;
+               }
+               case 2:
+               {
+                  gray *= 0x55;
+                  sp = row + (png_size_t)((row_width - 1) >> 2);
+                  dp = row + (png_size_t)row_width - 1;
+                  shift = (int)((3 - ((row_width + 3) & 3)) << 1);
+                  for (i = 0; i < row_width; i++)
+                  {
+                     value = (*sp >> shift) & 0x3;
+                     *dp = (png_byte)(value | (value << 2) | (value << 4) |
+                        (value << 6));
+                     if (shift == 6)
+                     {
+                        shift = 0;
+                        sp--;
+                     }
+                     else
+                        shift += 2;
+
+                     dp--;
+                  }
+                  break;
+               }
+               case 4:
+               {
+                  gray *= 0x11;
+                  sp = row + (png_size_t)((row_width - 1) >> 1);
+                  dp = row + (png_size_t)row_width - 1;
+                  shift = (int)((1 - ((row_width + 1) & 1)) << 2);
+                  for (i = 0; i < row_width; i++)
+                  {
+                     value = (*sp >> shift) & 0xf;
+                     *dp = (png_byte)(value | (value << 4));
+                     if (shift == 4)
+                     {
+                        shift = 0;
+                        sp--;
+                     }
+                     else
+                        shift = 4;
+
+                     dp--;
+                  }
+                  break;
+               }
+            }
+            row_info->bit_depth = 8;
+            row_info->pixel_depth = 8;
+            row_info->rowbytes = row_width;
+         }
+
+         if (trans_value != NULL)
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row + (png_size_t)row_width - 1;
+               dp = row + (png_size_t)(row_width << 1) - 1;
+               for (i = 0; i < row_width; i++)
+               {
+                  if (*sp == gray)
+                     *dp-- = 0;
+                  else
+                     *dp-- = 0xff;
+                  *dp-- = *sp--;
+               }
+            }
+            else if (row_info->bit_depth == 16)
+            {
+               sp = row + row_info->rowbytes - 1;
+               dp = row + (row_info->rowbytes << 1) - 1;
+               for (i = 0; i < row_width; i++)
+               {
+                  if (((png_uint_16)*(sp) |
+                     ((png_uint_16)*(sp - 1) << 8)) == gray)
+                  {
+                     *dp-- = 0;
+                     *dp-- = 0;
+                  }
+                  else
+                  {
+                     *dp-- = 0xff;
+                     *dp-- = 0xff;
+                  }
+                  *dp-- = *sp--;
+                  *dp-- = *sp--;
+               }
+            }
+            row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+            row_info->channels = 2;
+            row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1);
+            row_info->rowbytes =
+               ((row_width * row_info->pixel_depth) >> 3);
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            sp = row + (png_size_t)row_info->rowbytes - 1;
+            dp = row + (png_size_t)(row_width << 2) - 1;
+            for (i = 0; i < row_width; i++)
+            {
+               if (*(sp - 2) == trans_value->red &&
+                  *(sp - 1) == trans_value->green &&
+                  *(sp - 0) == trans_value->blue)
+                  *dp-- = 0;
+               else
+                  *dp-- = 0xff;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+            }
+         }
+         else if (row_info->bit_depth == 16)
+         {
+            sp = row + row_info->rowbytes - 1;
+            dp = row + (png_size_t)(row_width << 3) - 1;
+            for (i = 0; i < row_width; i++)
+            {
+               if ((((png_uint_16)*(sp - 4) |
+                  ((png_uint_16)*(sp - 5) << 8)) == trans_value->red) &&
+                  (((png_uint_16)*(sp - 2) |
+                  ((png_uint_16)*(sp - 3) << 8)) == trans_value->green) &&
+                  (((png_uint_16)*(sp - 0) |
+                  ((png_uint_16)*(sp - 1) << 8)) == trans_value->blue))
+               {
+                  *dp-- = 0;
+                  *dp-- = 0;
+               }
+               else
+               {
+                  *dp-- = 0xff;
+                  *dp-- = 0xff;
+               }
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+            }
+         }
+         row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+         row_info->channels = 4;
+         row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2);
+         row_info->rowbytes =
+            ((row_width * row_info->pixel_depth) >> 3);
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+void
+png_do_dither(png_row_infop row_info, png_bytep row,
+    png_bytep palette_lookup, png_bytep dither_lookup)
+{
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_dither\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB &&
+         palette_lookup && row_info->bit_depth == 8)
+      {
+         int r, g, b, p;
+         sp = row;
+         dp = row;
+         for (i = 0; i < row_width; i++)
+         {
+            r = *sp++;
+            g = *sp++;
+            b = *sp++;
+
+            /* this looks real messy, but the compiler will reduce
+               it down to a reasonable formula.  For example, with
+               5 bits per color, we get:
+               p = (((r >> 3) & 0x1f) << 10) |
+                  (((g >> 3) & 0x1f) << 5) |
+                  ((b >> 3) & 0x1f);
+               */
+            p = (((r >> (8 - PNG_DITHER_RED_BITS)) &
+               ((1 << PNG_DITHER_RED_BITS) - 1)) <<
+               (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) |
+               (((g >> (8 - PNG_DITHER_GREEN_BITS)) &
+               ((1 << PNG_DITHER_GREEN_BITS) - 1)) <<
+               (PNG_DITHER_BLUE_BITS)) |
+               ((b >> (8 - PNG_DITHER_BLUE_BITS)) &
+               ((1 << PNG_DITHER_BLUE_BITS) - 1));
+
+            *dp++ = palette_lookup[p];
+         }
+         row_info->color_type = PNG_COLOR_TYPE_PALETTE;
+         row_info->channels = 1;
+         row_info->pixel_depth = row_info->bit_depth;
+         row_info->rowbytes =
+             ((row_width * row_info->pixel_depth + 7) >> 3);
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
+         palette_lookup != NULL && row_info->bit_depth == 8)
+      {
+         int r, g, b, p;
+         sp = row;
+         dp = row;
+         for (i = 0; i < row_width; i++)
+         {
+            r = *sp++;
+            g = *sp++;
+            b = *sp++;
+            sp++;
+
+            p = (((r >> (8 - PNG_DITHER_RED_BITS)) &
+               ((1 << PNG_DITHER_RED_BITS) - 1)) <<
+               (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) |
+               (((g >> (8 - PNG_DITHER_GREEN_BITS)) &
+               ((1 << PNG_DITHER_GREEN_BITS) - 1)) <<
+               (PNG_DITHER_BLUE_BITS)) |
+               ((b >> (8 - PNG_DITHER_BLUE_BITS)) &
+               ((1 << PNG_DITHER_BLUE_BITS) - 1));
+
+            *dp++ = palette_lookup[p];
+         }
+         row_info->color_type = PNG_COLOR_TYPE_PALETTE;
+         row_info->channels = 1;
+         row_info->pixel_depth = row_info->bit_depth;
+         row_info->rowbytes =
+            ((row_width * row_info->pixel_depth + 7) >> 3);
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&
+         dither_lookup && row_info->bit_depth == 8)
+      {
+         sp = row;
+         for (i = 0; i < row_width; i++, sp++)
+         {
+            *sp = dither_lookup[*sp];
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+static int png_gamma_shift[] =
+   {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0};
+
+/* We build the 8- or 16-bit gamma tables here.  Note that for 16-bit
+ * tables, we don't make a full table if we are reducing to 8-bit in
+ * the future.  Note also how the gamma_16 tables are segmented so that
+ * we don't need to allocate > 64K chunks for a full 16-bit table.
+ */
+void
+png_build_gamma_table(png_structp png_ptr)
+{
+  png_debug(1, "in png_build_gamma_table\n");
+  if(png_ptr->gamma != 0.0)
+  {
+   if (png_ptr->bit_depth <= 8)
+   {
+      int i;
+      double g;
+
+      if (png_ptr->screen_gamma > .000001)
+         g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma);
+      else
+         g = 1.0;
+
+      png_ptr->gamma_table = (png_bytep)png_malloc(png_ptr,
+         (png_uint_32)256);
+
+      for (i = 0; i < 256; i++)
+      {
+         png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0,
+            g) * 255.0 + .5);
+      }
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+    defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+      if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY))
+      {
+
+         g = 1.0 / (png_ptr->gamma);
+
+         png_ptr->gamma_to_1 = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)256);
+
+         for (i = 0; i < 256; i++)
+         {
+            png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0,
+               g) * 255.0 + .5);
+         }
+
+         
+         png_ptr->gamma_from_1 = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)256);
+
+         if(png_ptr->screen_gamma > 0.000001)
+            g = 1.0 / png_ptr->screen_gamma;
+         else
+            g = png_ptr->gamma;   /* probably doing rgb_to_gray */
+
+         for (i = 0; i < 256; i++)
+         {
+            png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0,
+               g) * 255.0 + .5);
+
+         }
+      }
+#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */
+   }
+   else
+   {
+      double g;
+      int i, j, shift, num;
+      int sig_bit;
+      png_uint_32 ig;
+
+      if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      {
+         sig_bit = (int)png_ptr->sig_bit.red;
+         if ((int)png_ptr->sig_bit.green > sig_bit)
+            sig_bit = png_ptr->sig_bit.green;
+         if ((int)png_ptr->sig_bit.blue > sig_bit)
+            sig_bit = png_ptr->sig_bit.blue;
+      }
+      else
+      {
+         sig_bit = (int)png_ptr->sig_bit.gray;
+      }
+
+      if (sig_bit > 0)
+         shift = 16 - sig_bit;
+      else
+         shift = 0;
+
+      if (png_ptr->transformations & PNG_16_TO_8)
+      {
+         if (shift < (16 - PNG_MAX_GAMMA_8))
+            shift = (16 - PNG_MAX_GAMMA_8);
+      }
+
+      if (shift > 8)
+         shift = 8;
+      if (shift < 0)
+         shift = 0;
+
+      png_ptr->gamma_shift = (png_byte)shift;
+
+      num = (1 << (8 - shift));
+
+      if (png_ptr->screen_gamma > .000001)
+         g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma);
+      else
+         g = 1.0;
+
+      png_ptr->gamma_16_table = (png_uint_16pp)png_malloc(png_ptr,
+         (png_uint_32)(num * sizeof (png_uint_16p)));
+
+      if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND))
+      {
+         double fin, fout;
+         png_uint_32 last, max;
+
+         for (i = 0; i < num; i++)
+         {
+            png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr,
+               (png_uint_32)(256 * sizeof (png_uint_16)));
+         }
+
+         g = 1.0 / g;
+         last = 0;
+         for (i = 0; i < 256; i++)
+         {
+            fout = ((double)i + 0.5) / 256.0;
+            fin = pow(fout, g);
+            max = (png_uint_32)(fin * (double)((png_uint_32)num << 8));
+            while (last <= max)
+            {
+               png_ptr->gamma_16_table[(int)(last & (0xff >> shift))]
+                  [(int)(last >> (8 - shift))] = (png_uint_16)(
+                  (png_uint_16)i | ((png_uint_16)i << 8));
+               last++;
+            }
+         }
+         while (last < ((png_uint_32)num << 8))
+         {
+            png_ptr->gamma_16_table[(int)(last & (0xff >> shift))]
+               [(int)(last >> (8 - shift))] = (png_uint_16)65535L;
+            last++;
+         }
+      }
+      else
+      {
+         for (i = 0; i < num; i++)
+         {
+            png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr,
+               (png_uint_32)(256 * sizeof (png_uint_16)));
+
+            ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4);
+            for (j = 0; j < 256; j++)
+            {
+               png_ptr->gamma_16_table[i][j] =
+                  (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) /
+                     65535.0, g) * 65535.0 + .5);
+            }
+         }
+      }
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+    defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+      if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY))
+      {
+
+         g = 1.0 / (png_ptr->gamma);
+
+         png_ptr->gamma_16_to_1 = (png_uint_16pp)png_malloc(png_ptr,
+            (png_uint_32)(num * sizeof (png_uint_16p )));
+
+         for (i = 0; i < num; i++)
+         {
+            png_ptr->gamma_16_to_1[i] = (png_uint_16p)png_malloc(png_ptr,
+               (png_uint_32)(256 * sizeof (png_uint_16)));
+
+            ig = (((png_uint_32)i *
+               (png_uint_32)png_gamma_shift[shift]) >> 4);
+            for (j = 0; j < 256; j++)
+            {
+               png_ptr->gamma_16_to_1[i][j] =
+                  (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) /
+                     65535.0, g) * 65535.0 + .5);
+            }
+         }
+
+         if(png_ptr->screen_gamma > 0.000001)
+            g = 1.0 / png_ptr->screen_gamma;
+         else
+            g = png_ptr->gamma;   /* probably doing rgb_to_gray */
+
+         png_ptr->gamma_16_from_1 = (png_uint_16pp)png_malloc(png_ptr,
+            (png_uint_32)(num * sizeof (png_uint_16p)));
+
+         for (i = 0; i < num; i++)
+         {
+            png_ptr->gamma_16_from_1[i] = (png_uint_16p)png_malloc(png_ptr,
+               (png_uint_32)(256 * sizeof (png_uint_16)));
+
+            ig = (((png_uint_32)i *
+               (png_uint_32)png_gamma_shift[shift]) >> 4);
+            for (j = 0; j < 256; j++)
+            {
+               png_ptr->gamma_16_from_1[i][j] =
+                  (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) /
+                     65535.0, g) * 65535.0 + .5);
+            }
+         }
+      }
+#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */
+   }
+ }
+}
+#endif
+
diff --git a/src/png/pngrutil.c b/src/png/pngrutil.c
new file mode 100644 (file)
index 0000000..9320a71
--- /dev/null
@@ -0,0 +1,2272 @@
+
+/* pngrutil.c - utilities to read a PNG file
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ *
+ * This file contains routines that are only called from within
+ * libpng itself during the course of reading an image.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED
+/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */
+png_uint_32
+png_get_uint_32(png_bytep buf)
+{
+   png_uint_32 i = ((png_uint_32)(*buf) << 24) +
+      ((png_uint_32)(*(buf + 1)) << 16) +
+      ((png_uint_32)(*(buf + 2)) << 8) +
+      (png_uint_32)(*(buf + 3));
+
+   return (i);
+}
+
+#if defined(PNG_READ_pCAL_SUPPORTED)
+/* Grab a signed 32-bit integer from a buffer in big-endian format.  The
+ * data is stored in the PNG file in two's complement format, and it is
+ * assumed that the machine format for signed integers is the same. */
+png_int_32
+png_get_int_32(png_bytep buf)
+{
+   png_int_32 i = ((png_int_32)(*buf) << 24) +
+      ((png_int_32)(*(buf + 1)) << 16) +
+      ((png_int_32)(*(buf + 2)) << 8) +
+      (png_int_32)(*(buf + 3));
+
+   return (i);
+}
+#endif /* PNG_READ_pCAL_SUPPORTED */
+
+/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */
+png_uint_16
+png_get_uint_16(png_bytep buf)
+{
+   png_uint_16 i = (png_uint_16)(((png_uint_16)(*buf) << 8) +
+      (png_uint_16)(*(buf + 1)));
+
+   return (i);
+}
+#endif /* PNG_READ_BIG_ENDIAN_SUPPORTED */
+
+/* Read data, and (optionally) run it through the CRC. */
+void
+png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length)
+{
+   png_read_data(png_ptr, buf, length);
+   png_calculate_crc(png_ptr, buf, length);
+}
+
+/* Optionally skip data and then check the CRC.  Depending on whether we
+   are reading a ancillary or critical chunk, and how the program has set
+   things up, we may calculate the CRC on the data and print a message.
+   Returns '1' if there was a CRC error, '0' otherwise. */
+int
+png_crc_finish(png_structp png_ptr, png_uint_32 skip)
+{
+   png_size_t i;
+   png_size_t istop = png_ptr->zbuf_size;
+
+   for (i = (png_size_t)skip; i > istop; i -= istop)
+   {
+      png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+   }
+   if (i)
+   {
+      png_crc_read(png_ptr, png_ptr->zbuf, i);
+   }
+
+   if (png_crc_error(png_ptr))
+   {
+      if ((png_ptr->chunk_name[0] & 0x20 &&                /* Ancillary */
+           !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) ||
+          (!(png_ptr->chunk_name[0] & 0x20) &&             /* Critical  */
+           png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))
+      {
+         png_chunk_warning(png_ptr, "CRC error");
+      }
+      else
+      {
+         png_chunk_error(png_ptr, "CRC error");
+      }
+      return (1);
+   }
+
+   return (0);
+}
+
+/* Compare the CRC stored in the PNG file with that calculated by libpng from
+   the data it has read thus far. */
+int
+png_crc_error(png_structp png_ptr)
+{
+   png_byte crc_bytes[4];
+   png_uint_32 crc;
+   int need_crc = 1;
+
+   if (png_ptr->chunk_name[0] & 0x20)                     /* ancillary */
+   {
+      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
+          (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
+         need_crc = 0;
+   }
+   else                                                    /* critical */
+   {
+      if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE)
+         need_crc = 0;
+   }
+
+   png_read_data(png_ptr, crc_bytes, 4);
+
+   if (need_crc)
+   {
+      crc = png_get_uint_32(crc_bytes);
+      return ((int)(crc != png_ptr->crc));
+   }
+   else
+      return (0);
+}
+
+
+/* read and check the IDHR chunk */
+void
+png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[13];
+   png_uint_32 width, height;
+   int bit_depth, color_type, compression_type, filter_type;
+   int interlace_type;
+
+   png_debug(1, "in png_handle_IHDR\n");
+
+   if (png_ptr->mode != PNG_BEFORE_IHDR)
+      png_error(png_ptr, "Out of place IHDR");
+
+   /* check the length */
+   if (length != 13)
+      png_error(png_ptr, "Invalid IHDR chunk");
+
+   png_ptr->mode |= PNG_HAVE_IHDR;
+
+   png_crc_read(png_ptr, buf, 13);
+   png_crc_finish(png_ptr, 0);
+
+   width = png_get_uint_32(buf);
+   height = png_get_uint_32(buf + 4);
+   bit_depth = buf[8];
+   color_type = buf[9];
+   compression_type = buf[10];
+   filter_type = buf[11];
+   interlace_type = buf[12];
+
+   /* check for width and height valid values */
+   if (width == 0 || width > (png_uint_32)2147483647L || height == 0 ||
+        height > (png_uint_32)2147483647L)
+      png_error(png_ptr, "Invalid image size in IHDR");
+
+   /* check other values */
+   if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 &&
+      bit_depth != 8 && bit_depth != 16)
+      png_error(png_ptr, "Invalid bit depth in IHDR");
+
+   if (color_type < 0 || color_type == 1 ||
+      color_type == 5 || color_type > 6)
+      png_error(png_ptr, "Invalid color type in IHDR");
+
+   if ((color_type == PNG_COLOR_TYPE_PALETTE && bit_depth) > 8 ||
+       ((color_type == PNG_COLOR_TYPE_RGB ||
+         color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
+         color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8))
+      png_error(png_ptr, "Invalid color type/bit depth combination in IHDR");
+
+   if (interlace_type >= PNG_INTERLACE_LAST)
+      png_error(png_ptr, "Unknown interlace method in IHDR");
+
+   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+      png_error(png_ptr, "Unknown compression method in IHDR");
+
+   if (filter_type != PNG_FILTER_TYPE_BASE)
+      png_error(png_ptr, "Unknown filter method in IHDR");
+
+   /* set internal variables */
+   png_ptr->width = width;
+   png_ptr->height = height;
+   png_ptr->bit_depth = (png_byte)bit_depth;
+   png_ptr->interlaced = (png_byte)interlace_type;
+   png_ptr->color_type = (png_byte)color_type;
+
+   /* find number of channels */
+   switch (png_ptr->color_type)
+   {
+      case PNG_COLOR_TYPE_GRAY:
+      case PNG_COLOR_TYPE_PALETTE:
+         png_ptr->channels = 1;
+         break;
+      case PNG_COLOR_TYPE_RGB:
+         png_ptr->channels = 3;
+         break;
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+         png_ptr->channels = 2;
+         break;
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+         png_ptr->channels = 4;
+         break;
+   }
+
+   /* set up other useful info */
+   png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth *
+   png_ptr->channels);
+   png_ptr->rowbytes = ((png_ptr->width *
+      (png_uint_32)png_ptr->pixel_depth + 7) >> 3);
+   png_debug1(3,"bit_depth = %d\n", png_ptr->bit_depth);
+   png_debug1(3,"channels = %d\n", png_ptr->channels);
+   png_debug1(3,"rowbytes = %d\n", png_ptr->rowbytes);
+   png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
+      color_type, interlace_type, compression_type, filter_type);
+}
+
+/* read and check the palette */
+void
+png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_colorp palette;
+   int num, i;
+
+   png_debug(1, "in png_handle_PLTE\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before PLTE");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid PLTE after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      png_error(png_ptr, "Duplicate PLTE chunk");
+
+   png_ptr->mode |= PNG_HAVE_PLTE;
+
+#if !defined(PNG_READ_OPT_PLTE_SUPPORTED)
+   if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+#endif
+
+   if (length % 3)
+   {
+      if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+      {
+         png_warning(png_ptr, "Invalid palette chunk");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+      else
+      {
+         png_error(png_ptr, "Invalid palette chunk");
+      }
+   }
+
+   num = (int)length / 3;
+   palette = (png_colorp)png_zalloc(png_ptr, (uInt)num, sizeof (png_color));
+   png_ptr->flags |= PNG_FLAG_FREE_PALETTE;
+   for (i = 0; i < num; i++)
+   {
+      png_byte buf[3];
+
+      png_crc_read(png_ptr, buf, 3);
+      /* don't depend upon png_color being any order */
+      palette[i].red = buf[0];
+      palette[i].green = buf[1];
+      palette[i].blue = buf[2];
+   }
+
+   /* If we actually NEED the PLTE chunk (ie for a paletted image), we do
+      whatever the normal CRC configuration tells us.  However, if we
+      have an RGB image, the PLTE can be considered ancillary, so
+      we will act as though it is. */
+#if !defined(PNG_READ_OPT_PLTE_SUPPORTED)
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+#endif
+   {
+      png_crc_finish(png_ptr, 0);
+   }
+#if !defined(PNG_READ_OPT_PLTE_SUPPORTED)
+   else if (png_crc_error(png_ptr))  /* Only if we have a CRC error */
+   {
+      /* If we don't want to use the data from an ancillary chunk,
+         we have two options: an error abort, or a warning and we
+         ignore the data in this chunk (which should be OK, since
+         it's considered ancillary for a RGB or RGBA image). */
+      if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE))
+      {
+         if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)
+         {
+            png_chunk_error(png_ptr, "CRC error");
+         }
+         else
+         {
+            png_chunk_warning(png_ptr, "CRC error");
+            png_ptr->flags &= ~PNG_FLAG_FREE_PALETTE;
+            png_zfree(png_ptr, palette);
+            return;
+         }
+      }
+      /* Otherwise, we (optionally) emit a warning and use the chunk. */
+      else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN))
+      {
+         png_chunk_warning(png_ptr, "CRC error");
+      }
+   }
+#endif
+   png_ptr->palette = palette;
+   png_ptr->num_palette = (png_uint_16)num;
+   png_set_PLTE(png_ptr, info_ptr, palette, num);
+
+#if defined (PNG_READ_tRNS_SUPPORTED)
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (info_ptr != NULL && info_ptr->valid & PNG_INFO_tRNS)
+      {
+         if (png_ptr->num_trans > png_ptr->num_palette)
+         {
+            png_warning(png_ptr, "Truncating incorrect tRNS chunk length");
+            png_ptr->num_trans = png_ptr->num_palette;
+         }
+      }
+   }
+#endif
+
+}
+
+void
+png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_debug(1, "in png_handle_IEND\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT))
+   {
+      png_error(png_ptr, "No image in file");
+
+      /* to quiet compiler warnings about unused info_ptr */
+      if (info_ptr == NULL)
+         return;
+   }
+
+   png_ptr->mode |= PNG_AFTER_IDAT | PNG_HAVE_IEND;
+
+   if (length != 0)
+   {
+      png_warning(png_ptr, "Incorrect IEND chunk length");
+   }
+   png_crc_finish(png_ptr, length);
+}
+
+#if defined(PNG_READ_gAMA_SUPPORTED)
+void
+png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_uint_32 igamma;
+   float file_gamma;
+   png_byte buf[4];
+
+   png_debug(1, "in png_handle_gAMA\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before gAMA");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid gAMA after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Out of place gAMA chunk");
+
+   else if (info_ptr != NULL && info_ptr->valid & PNG_INFO_gAMA
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      && !(info_ptr->valid & PNG_INFO_sRGB)
+#endif
+      )
+   {
+      png_warning(png_ptr, "Duplicate gAMA chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 4)
+   {
+      png_warning(png_ptr, "Incorrect gAMA chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 4);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   igamma = png_get_uint_32(buf);
+   /* check for zero gamma */
+   if (igamma == 0)
+      return;
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sRGB)
+      if(igamma != (png_uint_32)45000L)
+      {
+         png_warning(png_ptr,
+           "Ignoring incorrect gAMA value when sRGB is also present");
+#ifndef PNG_NO_CONSOLE_IO
+         fprintf(stderr, "igamma = %lu\n", igamma);
+#endif
+         return;
+      }
+#endif /* PNG_READ_sRGB_SUPPORTED */
+
+   file_gamma = (float)igamma / (float)100000.0;
+#ifdef PNG_READ_GAMMA_SUPPORTED
+   png_ptr->gamma = file_gamma;
+#endif
+   png_set_gAMA(png_ptr, info_ptr, file_gamma);
+}
+#endif
+
+#if defined(PNG_READ_sBIT_SUPPORTED)
+void
+png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_size_t truelen;
+   png_byte buf[4];
+
+   png_debug(1, "in png_handle_sBIT\n");
+
+   buf[0] = buf[1] = buf[2] = buf[3] = 0;
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before sBIT");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid sBIT after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+   {
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Out of place sBIT chunk");
+   }
+   else if (info_ptr != NULL && info_ptr->valid & PNG_INFO_sBIT)
+   {
+      png_warning(png_ptr, "Duplicate sBIT chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      truelen = 3;
+   else
+      truelen = (png_size_t)png_ptr->channels;
+
+   if (length != truelen)
+   {
+      png_warning(png_ptr, "Incorrect sBIT chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, truelen);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+   {
+      png_ptr->sig_bit.red = buf[0];
+      png_ptr->sig_bit.green = buf[1];
+      png_ptr->sig_bit.blue = buf[2];
+      png_ptr->sig_bit.alpha = buf[3];
+   }
+   else
+   {
+      png_ptr->sig_bit.gray = buf[0];
+      png_ptr->sig_bit.red = buf[0];
+      png_ptr->sig_bit.green = buf[0];
+      png_ptr->sig_bit.blue = buf[0];
+      png_ptr->sig_bit.alpha = buf[1];
+   }
+   png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit));
+}
+#endif
+
+#if defined(PNG_READ_cHRM_SUPPORTED)
+void
+png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[4];
+   png_uint_32 val;
+   float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y;
+
+   png_debug(1, "in png_handle_cHRM\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before sBIT");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid cHRM after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Missing PLTE before cHRM");
+
+   else if (info_ptr != NULL && info_ptr->valid & PNG_INFO_cHRM
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      && !(info_ptr->valid & PNG_INFO_sRGB)
+#endif
+      )
+   {
+      png_warning(png_ptr, "Duplicate cHRM chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 32)
+   {
+      png_warning(png_ptr, "Incorrect cHRM chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 4);
+   val = png_get_uint_32(buf);
+   white_x = (float)val / (float)100000.0;
+
+   png_crc_read(png_ptr, buf, 4);
+   val = png_get_uint_32(buf);
+   white_y = (float)val / (float)100000.0;
+
+   if (white_x < 0 || white_x > 0.8 || white_y < 0 || white_y > 0.8 ||
+       white_x + white_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM white point");
+      png_crc_finish(png_ptr, 24);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 4);
+   val = png_get_uint_32(buf);
+   red_x = (float)val / (float)100000.0;
+
+   png_crc_read(png_ptr, buf, 4);
+   val = png_get_uint_32(buf);
+   red_y = (float)val / (float)100000.0;
+
+   if (red_x < 0 || red_x > 0.8 || red_y < 0 || red_y > 0.8 ||
+       red_x + red_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM red point");
+      png_crc_finish(png_ptr, 16);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 4);
+   val = png_get_uint_32(buf);
+   green_x = (float)val / (float)100000.0;
+
+   png_crc_read(png_ptr, buf, 4);
+   val = png_get_uint_32(buf);
+   green_y = (float)val / (float)100000.0;
+
+   if (green_x < 0 || green_x > 0.8 || green_y < 0 || green_y > 0.8 ||
+       green_x + green_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM green point");
+      png_crc_finish(png_ptr, 8);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 4);
+   val = png_get_uint_32(buf);
+   blue_x = (float)val / (float)100000.0;
+
+   png_crc_read(png_ptr, buf, 4);
+   val = png_get_uint_32(buf);
+   blue_y = (float)val / (float)100000.0;
+
+   if (blue_x < (float)0 || blue_x > (float)0.8 || blue_y < (float)0 ||
+       blue_y > (float)0.8 || blue_x + blue_y > (float)1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM blue point");
+      png_crc_finish(png_ptr, 0);
+      return;
+   }
+
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sRGB)
+      {
+      if (fabs(white_x - (float).3127) > (float).001 ||
+          fabs(white_y - (float).3290) > (float).001 ||
+          fabs(  red_x - (float).6400) > (float).001 ||
+          fabs(  red_y - (float).3300) > (float).001 ||
+          fabs(green_x - (float).3000) > (float).001 ||
+          fabs(green_y - (float).6000) > (float).001 ||
+          fabs( blue_x - (float).1500) > (float).001 ||
+          fabs( blue_y - (float).0600) > (float).001)
+         {
+
+            png_warning(png_ptr,
+              "Ignoring incorrect cHRM value when sRGB is also present");
+#ifndef PNG_NO_CONSOLE_IO
+            fprintf(stderr,"wx=%f, wy=%f, rx=%f, ry=%f\n",
+               white_x, white_y, red_x, red_y);
+            fprintf(stderr,"gx=%f, gy=%f, bx=%f, by=%f\n",
+               green_x, green_y, blue_x, blue_y);
+#endif
+         }
+         return;
+      }
+#endif /* PNG_READ_sRGB_SUPPORTED */
+
+   png_set_cHRM(png_ptr, info_ptr,
+      white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y);
+}
+#endif
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+void
+png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   int intent;
+   png_byte buf[1];
+
+   png_debug(1, "in png_handle_sRGB\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before sRGB");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid sRGB after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Out of place sRGB chunk");
+
+   else if (info_ptr != NULL && info_ptr->valid & PNG_INFO_sRGB)
+   {
+      png_warning(png_ptr, "Duplicate sRGB chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 1)
+   {
+      png_warning(png_ptr, "Incorrect sRGB chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 1);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   intent = buf[0];
+   /* check for bad intent */
+   if (intent >= PNG_sRGB_INTENT_LAST)
+   {
+      png_warning(png_ptr, "Unknown sRGB intent");
+      return;
+   }
+
+#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED)
+   if ((info_ptr->valid & PNG_INFO_gAMA))
+      if((png_uint_32)(png_ptr->gamma*(float)100000.+.5) != (png_uint_32)45000L)
+      {
+         png_warning(png_ptr,
+           "Ignoring incorrect gAMA value when sRGB is also present");
+#ifndef PNG_NO_CONSOLE_IO
+           fprintf(stderr,"gamma=%f\n",png_ptr->gamma);
+#endif
+      }
+#endif /* PNG_READ_gAMA_SUPPORTED */
+
+#ifdef PNG_READ_cHRM_SUPPORTED
+   if (info_ptr->valid & PNG_INFO_cHRM)
+      if (fabs(info_ptr->x_white - (float).3127) > (float).001 ||
+          fabs(info_ptr->y_white - (float).3290) > (float).001 ||
+          fabs(  info_ptr->x_red - (float).6400) > (float).001 ||
+          fabs(  info_ptr->y_red - (float).3300) > (float).001 ||
+          fabs(info_ptr->x_green - (float).3000) > (float).001 ||
+          fabs(info_ptr->y_green - (float).6000) > (float).001 ||
+          fabs( info_ptr->x_blue - (float).1500) > (float).001 ||
+          fabs( info_ptr->y_blue - (float).0600) > (float).001)
+         {
+            png_warning(png_ptr,
+              "Ignoring incorrect cHRM value when sRGB is also present");
+         }
+#endif /* PNG_READ_cHRM_SUPPORTED */
+
+   png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent);
+}
+#endif /* PNG_READ_sRGB_SUPPORTED */
+
+#if defined(PNG_READ_tRNS_SUPPORTED)
+void
+png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_debug(1, "in png_handle_tRNS\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before tRNS");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid tRNS after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && info_ptr->valid & PNG_INFO_tRNS)
+   {
+      png_warning(png_ptr, "Duplicate tRNS chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (!(png_ptr->mode & PNG_HAVE_PLTE))
+      {
+         /* Should be an error, but we can cope with it */
+         png_warning(png_ptr, "Missing PLTE before tRNS");
+      }
+      else if (length > png_ptr->num_palette)
+      {
+         png_warning(png_ptr, "Incorrect tRNS chunk length");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+      if (length == 0)
+      {
+         png_warning(png_ptr, "Zero length tRNS chunk");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+
+      png_ptr->trans = (png_bytep)png_malloc(png_ptr, length);
+      png_ptr->flags |= PNG_FLAG_FREE_TRANS;
+      png_crc_read(png_ptr, png_ptr->trans, (png_size_t)length);
+      png_ptr->num_trans = (png_uint_16)length;
+   }
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+   {
+      png_byte buf[6];
+
+      if (length != 6)
+      {
+         png_warning(png_ptr, "Incorrect tRNS chunk length");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+
+      png_crc_read(png_ptr, buf, (png_size_t)length);
+      png_ptr->num_trans = 1;
+      png_ptr->trans_values.red = png_get_uint_16(buf);
+      png_ptr->trans_values.green = png_get_uint_16(buf + 2);
+      png_ptr->trans_values.blue = png_get_uint_16(buf + 4);
+   }
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      png_byte buf[6];
+
+      if (length != 2)
+      {
+         png_warning(png_ptr, "Incorrect tRNS chunk length");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+
+      png_crc_read(png_ptr, buf, 2);
+      png_ptr->num_trans = 1;
+      png_ptr->trans_values.gray = png_get_uint_16(buf);
+   }
+   else
+   {
+      png_warning(png_ptr, "tRNS chunk not allowed with alpha channel");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   png_set_tRNS(png_ptr, info_ptr, png_ptr->trans, png_ptr->num_trans,
+      &(png_ptr->trans_values));
+}
+#endif
+
+#if defined(PNG_READ_bKGD_SUPPORTED)
+void
+png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_size_t truelen;
+   png_byte buf[6];
+
+   png_debug(1, "in png_handle_bKGD\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before bKGD");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid bKGD after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+            !(png_ptr->mode & PNG_HAVE_PLTE))
+   {
+      png_warning(png_ptr, "Missing PLTE before bKGD");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && info_ptr->valid & PNG_INFO_bKGD)
+   {
+      png_warning(png_ptr, "Duplicate bKGD chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      truelen = 1;
+   else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      truelen = 6;
+   else
+      truelen = 2;
+
+   if (length != truelen)
+   {
+      png_warning(png_ptr, "Incorrect bKGD chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, truelen);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   /* We convert the index value into RGB components so that we can allow
+    * arbitrary RGB values for background when we have transparency, and
+    * so it is easy to determine the RGB values of the background color
+    * from the info_ptr struct. */
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      png_ptr->background.index = buf[0];
+      png_ptr->background.red = (png_uint_16)png_ptr->palette[buf[0]].red;
+      png_ptr->background.green = (png_uint_16)png_ptr->palette[buf[0]].green;
+      png_ptr->background.blue = (png_uint_16)png_ptr->palette[buf[0]].blue;
+   }
+   else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */
+   {
+      png_ptr->background.red =
+      png_ptr->background.green =
+      png_ptr->background.blue =
+      png_ptr->background.gray = png_get_uint_16(buf);
+   }
+   else
+   {
+      png_ptr->background.red = png_get_uint_16(buf);
+      png_ptr->background.green = png_get_uint_16(buf + 2);
+      png_ptr->background.blue = png_get_uint_16(buf + 4);
+   }
+
+   png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background));
+}
+#endif
+
+#if defined(PNG_READ_hIST_SUPPORTED)
+void
+png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   int num, i;
+
+   png_debug(1, "in png_handle_hIST\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before hIST");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid hIST after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (!(png_ptr->mode & PNG_HAVE_PLTE))
+   {
+      png_warning(png_ptr, "Missing PLTE before hIST");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && info_ptr->valid & PNG_INFO_hIST)
+   {
+      png_warning(png_ptr, "Duplicate hIST chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != (png_uint_32)(2 * png_ptr->num_palette))
+   {
+      png_warning(png_ptr, "Incorrect hIST chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   num = (int)length / 2;
+   png_ptr->hist = (png_uint_16p)png_malloc(png_ptr,
+      (png_uint_32)(num * sizeof (png_uint_16)));
+   png_ptr->flags |= PNG_FLAG_FREE_HIST;
+   for (i = 0; i < num; i++)
+   {
+      png_byte buf[2];
+
+      png_crc_read(png_ptr, buf, 2);
+      png_ptr->hist[i] = png_get_uint_16(buf);
+   }
+
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   png_set_hIST(png_ptr, info_ptr, png_ptr->hist);
+}
+#endif
+
+#if defined(PNG_READ_pHYs_SUPPORTED)
+void
+png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[9];
+   png_uint_32 res_x, res_y;
+   int unit_type;
+
+   png_debug(1, "in png_handle_pHYs\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before pHYS");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid pHYS after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && info_ptr->valid & PNG_INFO_pHYs)
+   {
+      png_warning(png_ptr, "Duplicate pHYS chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 9)
+   {
+      png_warning(png_ptr, "Incorrect pHYs chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 9);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   res_x = png_get_uint_32(buf);
+   res_y = png_get_uint_32(buf + 4);
+   unit_type = buf[8];
+   png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type);
+}
+#endif
+
+#if defined(PNG_READ_oFFs_SUPPORTED)
+void
+png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[9];
+   png_uint_32 offset_x, offset_y;
+   int unit_type;
+
+   png_debug(1, "in png_handle_oFFs\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before oFFs");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid oFFs after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && info_ptr->valid & PNG_INFO_oFFs)
+   {
+      png_warning(png_ptr, "Duplicate oFFs chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 9)
+   {
+      png_warning(png_ptr, "Incorrect oFFs chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 9);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   offset_x = png_get_uint_32(buf);
+   offset_y = png_get_uint_32(buf + 4);
+   unit_type = buf[8];
+   png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type);
+}
+#endif
+
+#if defined(PNG_READ_pCAL_SUPPORTED)
+/* read the pCAL chunk (png-scivis-19970203) */
+void
+png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_charp purpose;
+   png_int_32 X0, X1;
+   png_byte type, nparams;
+   png_charp buf, units, endptr;
+   png_charpp params;
+   png_size_t slength;
+   int i;
+
+   png_debug(1, "in png_handle_pCAL\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before pCAL");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid pCAL after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && info_ptr->valid & PNG_INFO_pCAL)
+   {
+      png_warning(png_ptr, "Duplicate pCAL chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_debug1(2, "Allocating and reading pCAL chunk data (%d bytes)\n",
+      length + 1);
+   purpose = (png_charp)png_malloc(png_ptr, length + 1);
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)purpose, slength);
+
+   if (png_crc_finish(png_ptr, 0))
+   {
+      png_free(png_ptr, purpose);
+      return;
+   }
+
+   purpose[slength] = 0x00; /* null terminate the last string */
+
+   png_debug(3, "Finding end of pCAL purpose string\n");
+   for (buf = purpose; *buf; buf++)
+      /* empty loop */ ;
+
+   endptr = purpose + slength;
+
+   /* We need to have at least 12 bytes after the purpose string
+      in order to get the parameter information. */
+   if (endptr <= buf + 12)
+   {
+      png_warning(png_ptr, "Invalid pCAL data");
+      png_free(png_ptr, purpose);
+      return;
+   }
+
+   png_debug(3, "Reading pCAL X0, X1, type, nparams, and units\n");
+   X0 = png_get_int_32((png_bytep)buf+1);
+   X1 = png_get_int_32((png_bytep)buf+5);
+   type = buf[9];
+   nparams = buf[10];
+   units = buf + 11;
+
+   png_debug(3, "Checking pCAL equation type and number of parameters\n");
+   /* Check that we have the right number of parameters for known
+      equation types. */
+   if ((type == PNG_EQUATION_LINEAR && nparams != 2) ||
+       (type == PNG_EQUATION_BASE_E && nparams != 3) ||
+       (type == PNG_EQUATION_ARBITRARY && nparams != 3) ||
+       (type == PNG_EQUATION_HYPERBOLIC && nparams != 4))
+   {
+      png_warning(png_ptr, "Invalid pCAL parameters for equation type");
+      png_free(png_ptr, purpose);
+      return;
+   }
+   else if (type >= PNG_EQUATION_LAST)
+   {
+      png_warning(png_ptr, "Unrecognized equation type for pCAL chunk");
+   }
+
+   for (buf = units; *buf; buf++)
+      /* Empty loop to move past the units string. */ ;
+
+   png_debug(3, "Allocating pCAL parameters array\n");
+   params = (png_charpp)png_malloc(png_ptr, (png_uint_32)(nparams
+      *sizeof(png_charp))) ;
+
+   /* Get pointers to the start of each parameter string. */
+   for (i = 0; i < (int)nparams; i++)
+   {
+      buf++; /* Skip the null string terminator from previous parameter. */
+
+      png_debug1(3, "Reading pCAL parameter %d\n", i);
+      for (params[i] = buf; *buf != 0x00 && buf <= endptr; buf++)
+         /* Empty loop to move past each parameter string */ ;
+
+      /* Make sure we haven't run out of data yet */
+      if (buf > endptr)
+      {
+         png_warning(png_ptr, "Invalid pCAL data");
+         png_free(png_ptr, purpose);
+         png_free(png_ptr, params);
+         return;
+      }
+   }
+
+   png_set_pCAL(png_ptr, info_ptr, purpose, X0, X1, type, nparams,
+      units, params);
+
+   png_free(png_ptr, purpose);
+   png_free(png_ptr, params);
+}
+#endif
+
+#if defined(PNG_READ_tIME_SUPPORTED)
+void
+png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[7];
+   png_time mod_time;
+
+   png_debug(1, "in png_handle_tIME\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Out of place tIME chunk");
+   else if (info_ptr != NULL && info_ptr->valid & PNG_INFO_tIME)
+   {
+      png_warning(png_ptr, "Duplicate tIME chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+   if (length != 7)
+   {
+      png_warning(png_ptr, "Incorrect tIME chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 7);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   mod_time.second = buf[6];
+   mod_time.minute = buf[5];
+   mod_time.hour = buf[4];
+   mod_time.day = buf[3];
+   mod_time.month = buf[2];
+   mod_time.year = png_get_uint_16(buf);
+
+   png_set_tIME(png_ptr, info_ptr, &mod_time);
+}
+#endif
+
+#if defined(PNG_READ_tEXt_SUPPORTED)
+/* Note: this does not properly handle chunks that are > 64K under DOS */
+void
+png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_textp text_ptr;
+   png_charp key;
+   png_charp text;
+   png_uint_32 skip = 0;
+   png_size_t slength;
+
+   png_debug(1, "in png_handle_tEXt\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before tEXt");
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (length > (png_uint_32)65535L)
+   {
+      png_warning(png_ptr, "tEXt chunk too large to fit in memory");
+      skip = length - (png_uint_32)65535L;
+      length = (png_uint_32)65535L;
+   }
+#endif
+
+   key = (png_charp)png_malloc(png_ptr, length + 1);
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)key, slength);
+
+   if (png_crc_finish(png_ptr, skip))
+   {
+      png_free(png_ptr, key);
+      return;
+   }
+
+   key[slength] = 0x00;
+
+   for (text = key; *text; text++)
+      /* empty loop to find end of key */ ;
+
+   if (text != key + slength)
+      text++;
+
+   text_ptr = (png_textp)png_malloc(png_ptr, (png_uint_32)sizeof(png_text));
+   text_ptr->compression = PNG_TEXT_COMPRESSION_NONE;
+   text_ptr->key = key;
+   text_ptr->text = text;
+
+   png_set_text(png_ptr, info_ptr, text_ptr, 1);
+
+   png_free(png_ptr, text_ptr);
+}
+#endif
+
+#if defined(PNG_READ_zTXt_SUPPORTED)
+/* note: this does not correctly handle chunks that are > 64K under DOS */
+void
+png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   static char msg[] = "Error decoding zTXt chunk";
+   png_textp text_ptr;
+   png_charp key;
+   png_charp text;
+   int comp_type = PNG_TEXT_COMPRESSION_NONE;
+   png_size_t slength;
+
+   png_debug(1, "in png_handle_zTXt\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before zTXt");
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+#ifdef PNG_MAX_MALLOC_64K
+   /* We will no doubt have problems with chunks even half this size, but
+      there is no hard and fast rule to tell us where to stop. */
+   if (length > (png_uint_32)65535L)
+   {
+     png_warning(png_ptr,"zTXt chunk too large to fit in memory");
+     png_crc_finish(png_ptr, length);
+     return;
+   }
+#endif
+
+   key = (png_charp)png_malloc(png_ptr, length + 1);
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)key, slength);
+   if (png_crc_finish(png_ptr, 0))
+   {
+      png_free(png_ptr, key);
+      return;
+   }
+
+   key[slength] = 0x00;
+
+   for (text = key; *text; text++)
+      /* empty loop */ ;
+
+   /* zTXt must have some text after the keyword */
+   if (text == key + slength)
+   {
+      png_warning(png_ptr, "Zero length zTXt chunk");
+   }
+   else if ((comp_type = *(++text)) == PNG_TEXT_COMPRESSION_zTXt)
+   {
+      png_size_t text_size, key_size;
+      text++;
+
+      png_ptr->zstream.next_in = (png_bytep)text;
+      png_ptr->zstream.avail_in = (uInt)(length - (text - key));
+      png_ptr->zstream.next_out = png_ptr->zbuf;
+      png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+      key_size = (png_size_t)(text - key);
+      text_size = 0;
+      text = NULL;
+
+      while (png_ptr->zstream.avail_in)
+      {
+         int ret;
+
+         ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+         if (ret != Z_OK && ret != Z_STREAM_END)
+         {
+            if (png_ptr->zstream.msg != NULL)
+               png_warning(png_ptr, png_ptr->zstream.msg);
+            else
+               png_warning(png_ptr, msg);
+            inflateReset(&png_ptr->zstream);
+            png_ptr->zstream.avail_in = 0;
+
+            if (text ==  NULL)
+            {
+               text_size = key_size + sizeof(msg) + 1;
+               text = (png_charp)png_malloc(png_ptr, (png_uint_32)text_size);
+               png_memcpy(text, key, key_size);
+            }
+
+            text[text_size - 1] = 0x00;
+
+            /* Copy what we can of the error message into the text chunk */
+            text_size = (png_size_t)(slength - (text - key) - 1);
+            text_size = sizeof(msg) > text_size ? text_size : sizeof(msg);
+            png_memcpy(text + key_size, msg, text_size + 1);
+            break;
+         }
+         if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END)
+         {
+            if (text == NULL)
+            {
+               text = (png_charp)png_malloc(png_ptr,
+                  (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out
+                     + key_size + 1));
+               png_memcpy(text + key_size, png_ptr->zbuf,
+                  png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+               png_memcpy(text, key, key_size);
+               text_size = key_size + png_ptr->zbuf_size -
+                  png_ptr->zstream.avail_out;
+               *(text + text_size) = 0x00;
+            }
+            else
+            {
+               png_charp tmp;
+
+               tmp = text;
+               text = (png_charp)png_malloc(png_ptr, (png_uint_32)(text_size +
+                  png_ptr->zbuf_size - png_ptr->zstream.avail_out + 1));
+               png_memcpy(text, tmp, text_size);
+               png_free(png_ptr, tmp);
+               png_memcpy(text + text_size, png_ptr->zbuf,
+                  (png_ptr->zbuf_size - png_ptr->zstream.avail_out));
+               text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+               *(text + text_size) = 0x00;
+            }
+            if (ret != Z_STREAM_END)
+            {
+               png_ptr->zstream.next_out = png_ptr->zbuf;
+               png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+            }
+            else
+            {
+               break;
+            }
+         }
+      }
+
+      inflateReset(&png_ptr->zstream);
+      png_ptr->zstream.avail_in = 0;
+
+      png_free(png_ptr, key);
+      key = text;
+      text += key_size;
+   }
+   else /* if (comp_type >= PNG_TEXT_COMPRESSION_LAST) */
+   {
+      png_size_t text_size;
+#if !defined(PNG_NO_STDIO)
+      char umsg[50];
+
+      sprintf(umsg, "Unknown zTXt compression type %d", comp_type);
+      png_warning(png_ptr, umsg);
+#else
+      png_warning(png_ptr, "Unknown zTXt compression type");
+#endif
+
+      /* Copy what we can of the error message into the text chunk */
+      text_size = (png_size_t)(slength - (text - key) - 1);
+      text_size = sizeof(msg) > text_size ? text_size : sizeof(msg);
+      png_memcpy(text, msg, text_size + 1);
+   }
+
+   text_ptr = (png_textp)png_malloc(png_ptr, (png_uint_32)sizeof(png_text));
+   text_ptr->compression = comp_type;
+   text_ptr->key = key;
+   text_ptr->text = text;
+
+   png_set_text(png_ptr, info_ptr, text_ptr, 1);
+
+   png_free(png_ptr, text_ptr);
+}
+#endif
+
+/* This function is called when we haven't found a handler for a
+   chunk.  If there isn't a problem with the chunk itself (ie bad
+   chunk name, CRC, or a critical chunk), the chunk is silently ignored. */
+void
+png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_debug(1, "in png_handle_unknown\n");
+
+   /* In the future we can have code here that calls user-supplied
+    * callback functions for unknown chunks before they are ignored or
+    * cause an error.
+    */
+   png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+
+   if (!(png_ptr->chunk_name[0] & 0x20))
+   {
+      png_chunk_error(png_ptr, "unknown critical chunk");
+
+      /* to quiet compiler warnings about unused info_ptr */
+      if (info_ptr == NULL)
+         return;
+   }
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+   png_crc_finish(png_ptr, length);
+
+}
+
+/* This function is called to verify that a chunk name is valid.
+   This function can't have the "critical chunk check" incorporated
+   into it, since in the future we will need to be able to call user
+   functions to handle unknown critical chunks after we check that
+   the chunk name itself is valid. */
+
+#define isnonalpha(c) ((c) < 41 || (c) > 122 || ((c) > 90 && (c) < 97))
+
+void
+png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name)
+{
+   png_debug(1, "in png_check_chunk_name\n");
+   if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) ||
+       isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3]))
+   {
+      png_chunk_error(png_ptr, "invalid chunk type");
+   }
+}
+
+/* Combines the row recently read in with the existing pixels in the
+   row.  This routine takes care of alpha and transparency if requested.
+   This routine also handles the two methods of progressive display
+   of interlaced images, depending on the mask value.
+   The mask value describes which pixels are to be combined with
+   the row.  The pattern always repeats every 8 pixels, so just 8
+   bits are needed.  A one indicates the pixel is to be combined,
+   a zero indicates the pixel is to be skipped.  This is in addition
+   to any alpha or transparency value associated with the pixel.  If
+   you want all pixels to be combined, pass 0xff (255) in mask.  */
+void
+png_combine_row(png_structp png_ptr, png_bytep row,
+   int mask)
+{
+   png_debug(1,"in png_combine_row\n");
+   if (mask == 0xff)
+   {
+      png_memcpy(row, png_ptr->row_buf + 1,
+         (png_size_t)((png_ptr->width *
+         png_ptr->row_info.pixel_depth + 7) >> 3));
+   }
+   else
+   {
+      switch (png_ptr->row_info.pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp = png_ptr->row_buf + 1;
+            png_bytep dp = row;
+            int s_inc, s_start, s_end;
+            int m = 0x80;
+            int shift;
+            png_uint_32 i;
+            png_uint_32 row_width = png_ptr->width;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+                s_start = 0;
+                s_end = 7;
+                s_inc = 1;
+            }
+            else
+#endif
+            {
+                s_start = 7;
+                s_end = 0;
+                s_inc = -1;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (m & mask)
+               {
+                  int value;
+
+                  value = (*sp >> shift) & 0x1;
+                  *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+         case 2:
+         {
+            png_bytep sp = png_ptr->row_buf + 1;
+            png_bytep dp = row;
+            int s_start, s_end, s_inc;
+            int m = 0x80;
+            int shift;
+            png_uint_32 i;
+            png_uint_32 row_width = png_ptr->width;
+            int value;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+            else
+#endif
+            {
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0x3;
+                  *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp = png_ptr->row_buf + 1;
+            png_bytep dp = row;
+            int s_start, s_end, s_inc;
+            int m = 0x80;
+            int shift;
+            png_uint_32 i;
+            png_uint_32 row_width = png_ptr->width;
+            int value;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+            else
+#endif
+            {
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            shift = s_start;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0xf;
+                  *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+         default:
+         {
+            png_bytep sp = png_ptr->row_buf + 1;
+            png_bytep dp = row;
+            png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+            png_uint_32 i;
+            png_uint_32 row_width = png_ptr->width;
+            png_byte m = 0x80;
+
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (m & mask)
+               {
+                  png_memcpy(dp, sp, pixel_bytes);
+               }
+
+               sp += pixel_bytes;
+               dp += pixel_bytes;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+      }
+   }
+}
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+void
+png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass,
+   png_uint_32 transformations)
+{
+   png_debug(1,"in png_do_read_interlace\n");
+   if (row != NULL && row_info != NULL)
+   {
+      png_uint_32 final_width;
+
+      final_width = row_info->width * png_pass_inc[pass];
+
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3);
+            png_bytep dp = row + (png_size_t)((final_width - 1) >> 3);
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            int jstop = png_pass_inc[pass];
+            png_byte v;
+            png_uint_32 i;
+            int j;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+                sshift = (int)((row_info->width + 7) & 7);
+                dshift = (int)((final_width + 7) & 7);
+                s_start = 7;
+                s_end = 0;
+                s_inc = -1;
+            }
+            else
+#endif
+            {
+                sshift = 7 - (int)((row_info->width + 7) & 7);
+                dshift = 7 - (int)((final_width + 7) & 7);
+                s_start = 0;
+                s_end = 7;
+                s_inc = 1;
+            }
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               v = (png_byte)((*sp >> sshift) & 0x1);
+               for (j = 0; j < jstop; j++)
+               {
+                  *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+         case 2:
+         {
+            png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2);
+            png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2);
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            int jstop = png_pass_inc[pass];
+            png_uint_32 i;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (int)(((row_info->width + 3) & 3) << 1);
+               dshift = (int)(((final_width + 3) & 3) << 1);
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+            else
+#endif
+            {
+               sshift = (int)((3 - ((row_info->width + 3) & 3)) << 1);
+               dshift = (int)((3 - ((final_width + 3) & 3)) << 1);
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0x3);
+               for (j = 0; j < jstop; j++)
+               {
+                  *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1);
+            png_bytep dp = row + (png_size_t)((final_width - 1) >> 1);
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+            int jstop = png_pass_inc[pass];
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (int)(((row_info->width + 1) & 1) << 2);
+               dshift = (int)(((final_width + 1) & 1) << 2);
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            else
+#endif
+            {
+               sshift = (int)((1 - ((row_info->width + 1) & 1)) << 2);
+               dshift = (int)((1 - ((final_width + 1) & 1)) << 2);
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               png_byte v = (png_byte)((*sp >> sshift) & 0xf);
+               int j;
+
+               for (j = 0; j < jstop; j++)
+               {
+                  *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+         default:
+         {
+            png_size_t pixel_bytes = (row_info->pixel_depth >> 3);
+            png_bytep sp = row + (png_size_t)(row_info->width - 1) * pixel_bytes;
+            png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes;
+            int jstop = png_pass_inc[pass];
+            png_uint_32 i;
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               png_byte v[8];
+               int j;
+
+               png_memcpy(v, sp, pixel_bytes);
+               for (j = 0; j < jstop; j++)
+               {
+                  png_memcpy(dp, v, pixel_bytes);
+                  dp -= pixel_bytes;
+               }
+               sp -= pixel_bytes;
+            }
+            break;
+         }
+      }
+      row_info->width = final_width;
+      row_info->rowbytes = ((final_width *
+         (png_uint_32)row_info->pixel_depth + 7) >> 3);
+   }
+}
+#endif
+
+void
+png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row,
+   png_bytep prev_row, int filter)
+{
+   png_debug(1, "in png_read_filter_row\n");
+   png_debug2(2,"row = %d, filter = %d\n", png_ptr->row_number, filter);
+
+
+   switch (filter)
+   {
+      case PNG_FILTER_VALUE_NONE:
+         break;
+      case PNG_FILTER_VALUE_SUB:
+      {
+         png_uint_32 i;
+         png_uint_32 istop = row_info->rowbytes;
+         png_uint_32 bpp = (row_info->pixel_depth + 7) / 8;
+         png_bytep rp = row + bpp;
+         png_bytep lp = row;
+
+         for (i = bpp; i < istop; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff);
+            rp++;
+         }
+         break;
+      }
+      case PNG_FILTER_VALUE_UP:
+      {
+         png_uint_32 i;
+         png_uint_32 istop = row_info->rowbytes;
+         png_bytep rp = row;
+         png_bytep pp = prev_row;
+
+         for (i = 0; i < istop; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+            rp++;
+         }
+         break;
+      }
+      case PNG_FILTER_VALUE_AVG:
+      {
+         png_uint_32 i;
+         png_bytep rp = row;
+         png_bytep pp = prev_row;
+         png_bytep lp = row;
+         png_uint_32 bpp = (row_info->pixel_depth + 7) / 8;
+         png_uint_32 istop = row_info->rowbytes - bpp;
+
+         for (i = 0; i < bpp; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) +
+               ((int)(*pp++) / 2)) & 0xff);
+            rp++;
+         }
+
+         for (i = 0; i < istop; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) +
+               (int)(*pp++ + *lp++) / 2) & 0xff);
+            rp++;
+         }
+         break;
+      }
+      case PNG_FILTER_VALUE_PAETH:
+      {
+         png_uint_32 i;
+         png_bytep rp = row;
+         png_bytep pp = prev_row;
+         png_bytep lp = row;
+         png_bytep cp = prev_row;
+         png_uint_32 bpp = (row_info->pixel_depth + 7) / 8;
+         png_uint_32 istop=row_info->rowbytes - bpp;
+
+         for (i = 0; i < bpp; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+            rp++;
+         }
+
+         for (i = 0; i < istop; i++)   /* use leftover rp,pp */
+         {
+            int a, b, c, pa, pb, pc, p;
+
+            a = *lp++;
+            b = *pp++;
+            c = *cp++;
+
+            p = b - c;
+            pc = a - c;
+
+#ifdef PNG_USE_ABS
+            pa = abs(p);
+            pb = abs(pc);
+            pc = abs(p + pc);
+#else
+            pa = p < 0 ? -p : p;
+            pb = pc < 0 ? -pc : pc;
+            pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+            /*
+               if (pa <= pb && pa <= pc)
+                  p = a;
+               else if (pb <= pc)
+                  p = b;
+               else
+                  p = c;
+             */
+
+            p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+
+            *rp = (png_byte)(((int)(*rp) + p) & 0xff);
+            rp++;
+         }
+         break;
+      }
+      default:
+         png_warning(png_ptr, "Ignoring bad adaptive filter type");
+         *row=0;
+         break;
+   }
+}
+
+void
+png_read_finish_row(png_structp png_ptr)
+{
+   png_debug(1, "in png_read_finish_row\n");
+   png_ptr->row_number++;
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+   if (png_ptr->interlaced)
+   {
+      png_ptr->row_number = 0;
+      png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
+      do
+      {
+         png_ptr->pass++;
+         if (png_ptr->pass >= 7)
+            break;
+         png_ptr->iwidth = (png_ptr->width +
+            png_pass_inc[png_ptr->pass] - 1 -
+            png_pass_start[png_ptr->pass]) /
+            png_pass_inc[png_ptr->pass];
+            png_ptr->irowbytes = ((png_ptr->iwidth *
+               (png_uint_32)png_ptr->pixel_depth + 7) >> 3) +1;
+
+         if (!(png_ptr->transformations & PNG_INTERLACE))
+         {
+            png_ptr->num_rows = (png_ptr->height +
+               png_pass_yinc[png_ptr->pass] - 1 -
+               png_pass_ystart[png_ptr->pass]) /
+               png_pass_yinc[png_ptr->pass];
+            if (!(png_ptr->num_rows))
+               continue;
+         }
+         else  /* if (png_ptr->transformations & PNG_INTERLACE) */
+            break;
+      } while (png_ptr->iwidth == 0);
+
+      if (png_ptr->pass < 7)
+         return;
+   }
+
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+   {
+      char extra;
+      int ret;
+
+      png_ptr->zstream.next_out = (Byte *)&extra;
+      png_ptr->zstream.avail_out = (uInt)1;
+      for(;;)
+      {
+         if (!(png_ptr->zstream.avail_in))
+         {
+            while (!png_ptr->idat_size)
+            {
+               png_byte chunk_length[4];
+
+               png_crc_finish(png_ptr, 0);
+
+               png_read_data(png_ptr, chunk_length, 4);
+               png_ptr->idat_size = png_get_uint_32(chunk_length);
+
+               png_reset_crc(png_ptr);
+               png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+               if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+                  png_error(png_ptr, "Not enough image data");
+
+            }
+            png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size;
+            png_ptr->zstream.next_in = png_ptr->zbuf;
+            if (png_ptr->zbuf_size > png_ptr->idat_size)
+               png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size;
+            png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in);
+            png_ptr->idat_size -= png_ptr->zstream.avail_in;
+         }
+         ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+         if (ret == Z_STREAM_END)
+         {
+            if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in ||
+               png_ptr->idat_size)
+               png_error(png_ptr, "Extra compressed data");
+            png_ptr->mode |= PNG_AFTER_IDAT;
+            png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+            break;
+         }
+         if (ret != Z_OK)
+            png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg :
+                      "Decompression Error");
+
+         if (!(png_ptr->zstream.avail_out))
+            png_error(png_ptr, "Extra compressed data");
+
+      }
+      png_ptr->zstream.avail_out = 0;
+   }
+
+   if (png_ptr->idat_size || png_ptr->zstream.avail_in)
+      png_error(png_ptr, "Extra compression data");
+
+   inflateReset(&png_ptr->zstream);
+
+   png_ptr->mode |= PNG_AFTER_IDAT;
+}
+
+void
+png_read_start_row(png_structp png_ptr)
+{
+   int max_pixel_depth;
+   png_uint_32 row_bytes;
+
+   png_debug(1, "in png_read_start_row\n");
+   png_ptr->zstream.avail_in = 0;
+   png_init_read_transformations(png_ptr);
+   if (png_ptr->interlaced)
+   {
+      if (!(png_ptr->transformations & PNG_INTERLACE))
+         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
+            png_pass_ystart[0]) / png_pass_yinc[0];
+      else
+         png_ptr->num_rows = png_ptr->height;
+
+      png_ptr->iwidth = (png_ptr->width +
+         png_pass_inc[png_ptr->pass] - 1 -
+         png_pass_start[png_ptr->pass]) /
+         png_pass_inc[png_ptr->pass];
+
+         row_bytes = ((png_ptr->iwidth *
+            (png_uint_32)png_ptr->pixel_depth + 7) >> 3) +1;
+         png_ptr->irowbytes = (png_size_t)row_bytes;
+         if((png_uint_32)png_ptr->irowbytes != row_bytes)
+            png_error(png_ptr, "Rowbytes overflow in png_read_start_row");
+   }
+   else
+   {
+      png_ptr->num_rows = png_ptr->height;
+      png_ptr->iwidth = png_ptr->width;
+      png_ptr->irowbytes = png_ptr->rowbytes + 1;
+   }
+   max_pixel_depth = png_ptr->pixel_depth;
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+   if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8)
+      max_pixel_depth = 8;
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+   if (png_ptr->transformations & PNG_EXPAND)
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         if (png_ptr->num_trans)
+            max_pixel_depth = 32;
+         else
+            max_pixel_depth = 24;
+      }
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (max_pixel_depth < 8)
+            max_pixel_depth = 8;
+         if (png_ptr->num_trans)
+            max_pixel_depth *= 2;
+      }
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+      {
+         if (png_ptr->num_trans)
+         {
+            max_pixel_depth *= 4;
+            max_pixel_depth /= 3;
+         }
+      }
+   }
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED)
+   if (png_ptr->transformations & (PNG_FILLER))
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+         max_pixel_depth = 32;
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (max_pixel_depth <= 8)
+            max_pixel_depth = 16;
+         else
+            max_pixel_depth = 32;
+      }
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+      {
+         if (max_pixel_depth <= 32)
+            max_pixel_depth = 32;
+         else
+            max_pixel_depth = 64;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   if (png_ptr->transformations & PNG_GRAY_TO_RGB)
+   {
+      if (
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+        (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) ||
+#endif
+#if defined(PNG_READ_FILLER_SUPPORTED)
+        (png_ptr->transformations & (PNG_FILLER)) ||
+#endif
+        png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         if (max_pixel_depth <= 16)
+            max_pixel_depth = 32;
+         else
+            max_pixel_depth = 64;
+      }
+      else
+      {
+         if (max_pixel_depth <= 8)
+            max_pixel_depth = 24;
+         else
+            max_pixel_depth = 48;
+      }
+   }
+#endif
+
+   /* align the width on the next larger 8 pixels.  Mainly used
+      for interlacing */
+   row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7));
+   /* calculate the maximum bytes needed, adding a byte and a pixel
+      for safety's sake */
+   row_bytes = ((row_bytes * (png_uint_32)max_pixel_depth + 7) >> 3) +
+      1 + ((max_pixel_depth + 7) >> 3);
+#ifdef PNG_MAX_MALLOC_64K
+   if (row_bytes > (png_uint_32)65536L)
+      png_error(png_ptr, "This image requires a row greater than 64KB");
+#endif
+   png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, row_bytes);
+
+#ifdef PNG_MAX_MALLOC_64K
+   if ((png_uint_32)png_ptr->rowbytes + 1 > (png_uint_32)65536L)
+      png_error(png_ptr, "This image requires a row greater than 64KB");
+#endif
+   png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)(
+      png_ptr->rowbytes + 1));
+
+   png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
+
+   png_debug1(3, "width = %d,\n", png_ptr->width);
+   png_debug1(3, "height = %d,\n", png_ptr->height);
+   png_debug1(3, "iwidth = %d,\n", png_ptr->iwidth);
+   png_debug1(3, "num_rows = %d\n", png_ptr->num_rows);
+   png_debug1(3, "rowbytes = %d,\n", png_ptr->rowbytes);
+   png_debug1(3, "irowbytes = %d,\n", png_ptr->irowbytes);
+
+   png_ptr->flags |= PNG_FLAG_ROW_INIT;
+}
diff --git a/src/png/pngset.c b/src/png/pngset.c
new file mode 100644 (file)
index 0000000..3200836
--- /dev/null
@@ -0,0 +1,380 @@
+
+/* pngset.c - storage of image information into info struct
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ *
+ * The functions here are used during reads to store data from the file
+ * into the info struct, and during writes to store application data
+ * into the info struct for writing into the file.  This abstracts the
+ * info struct and allows us to change the structure in the future.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_READ_bKGD_SUPPORTED) || defined(PNG_WRITE_bKGD_SUPPORTED)
+void
+png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background)
+{
+   png_debug1(1, "in %s storage function\n", "bKGD");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_memcpy(&(info_ptr->background), background, sizeof(png_color_16));
+   info_ptr->valid |= PNG_INFO_bKGD;
+}
+#endif
+
+#if defined(PNG_READ_cHRM_SUPPORTED) || defined(PNG_WRITE_cHRM_SUPPORTED)
+void
+png_set_cHRM(png_structp png_ptr, png_infop info_ptr,
+   double white_x, double white_y, double red_x, double red_y,
+   double green_x, double green_y, double blue_x, double blue_y)
+{
+   png_debug1(1, "in %s storage function\n", "cHRM");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->x_white = (float)white_x;
+   info_ptr->y_white = (float)white_y;
+   info_ptr->x_red   = (float)red_x;
+   info_ptr->y_red   = (float)red_y;
+   info_ptr->x_green = (float)green_x;
+   info_ptr->y_green = (float)green_y;
+   info_ptr->x_blue  = (float)blue_x;
+   info_ptr->y_blue  = (float)blue_y;
+   info_ptr->valid |= PNG_INFO_cHRM;
+}
+#endif
+
+#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_WRITE_gAMA_SUPPORTED)
+void
+png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma)
+{
+   png_debug1(1, "in %s storage function\n", "gAMA");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->gamma = (float)file_gamma;
+   info_ptr->valid |= PNG_INFO_gAMA;
+}
+#endif
+
+#if defined(PNG_READ_hIST_SUPPORTED) || defined(PNG_WRITE_hIST_SUPPORTED)
+void
+png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist)
+{
+   png_debug1(1, "in %s storage function\n", "hIST");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->hist = hist;
+   info_ptr->valid |= PNG_INFO_hIST;
+}
+#endif
+
+void
+png_set_IHDR(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 width, png_uint_32 height, int bit_depth,
+   int color_type, int interlace_type, int compression_type,
+   int filter_type)
+{
+   int rowbytes_per_pixel;
+   png_debug1(1, "in %s storage function\n", "IHDR");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->width = width;
+   info_ptr->height = height;
+   info_ptr->bit_depth = (png_byte)bit_depth;
+   info_ptr->color_type =(png_byte) color_type;
+   info_ptr->compression_type = (png_byte)compression_type;
+   info_ptr->filter_type = (png_byte)filter_type;
+   info_ptr->interlace_type = (png_byte)interlace_type;
+   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      info_ptr->channels = 1;
+   else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      info_ptr->channels = 3;
+   else
+      info_ptr->channels = 1;
+   if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+      info_ptr->channels++;
+   info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth);
+
+   /* check for overflow */
+   rowbytes_per_pixel = (info_ptr->pixel_depth + 7) >> 3;
+   if (( width > (png_uint_32)2147483647L/rowbytes_per_pixel))
+   {
+      png_warning(png_ptr,
+         "Width too large to process image data; rowbytes will overflow.");
+      info_ptr->rowbytes = (png_size_t)0;
+   }
+   else
+      info_ptr->rowbytes = (info_ptr->width * info_ptr->pixel_depth + 7) >> 3;
+}
+
+#if defined(PNG_READ_oFFs_SUPPORTED) || defined(PNG_WRITE_oFFs_SUPPORTED)
+void
+png_set_oFFs(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 offset_x, png_uint_32 offset_y, int unit_type)
+{
+   png_debug1(1, "in %s storage function\n", "oFFs");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->x_offset = offset_x;
+   info_ptr->y_offset = offset_y;
+   info_ptr->offset_unit_type = (png_byte)unit_type;
+   info_ptr->valid |= PNG_INFO_oFFs;
+}
+#endif
+
+#if defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED)
+void
+png_set_pCAL(png_structp png_ptr, png_infop info_ptr,
+   png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams,
+   png_charp units, png_charpp params)
+{
+   png_uint_32 length;
+   int i;
+
+   png_debug1(1, "in %s storage function\n", "pCAL");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   length = png_strlen(purpose) + 1;
+   png_debug1(3, "allocating purpose for info (%d bytes)\n", length);
+   info_ptr->pcal_purpose = (png_charp)png_malloc(png_ptr, length);
+   png_memcpy(info_ptr->pcal_purpose, purpose, (png_size_t)length);
+
+   png_debug(3, "storing X0, X1, type, and nparams in info\n");
+   info_ptr->pcal_X0 = X0;
+   info_ptr->pcal_X1 = X1;
+   info_ptr->pcal_type = (png_byte)type;
+   info_ptr->pcal_nparams = (png_byte)nparams;
+
+   length = png_strlen(units) + 1;
+   png_debug1(3, "allocating units for info (%d bytes)\n", length);
+   info_ptr->pcal_units = (png_charp)png_malloc(png_ptr, length);
+   png_memcpy(info_ptr->pcal_units, units, (png_size_t)length);
+
+   info_ptr->pcal_params = (png_charpp)png_malloc(png_ptr,
+      (png_uint_32)((nparams + 1) * sizeof(png_charp)));
+   info_ptr->pcal_params[nparams] = NULL;
+
+   for (i = 0; i < nparams; i++)
+   {
+      length = png_strlen(params[i]) + 1;
+      png_debug2(3, "allocating parameter %d for info (%d bytes)\n", i, length);
+      info_ptr->pcal_params[i] = (png_charp)png_malloc(png_ptr, length);
+      png_memcpy(info_ptr->pcal_params[i], params[i], (png_size_t)length);
+   }
+
+   info_ptr->valid |= PNG_INFO_pCAL;
+}
+#endif
+
+#if defined(PNG_READ_pHYs_SUPPORTED) || defined(PNG_WRITE_pHYs_SUPPORTED)
+void
+png_set_pHYs(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 res_x, png_uint_32 res_y, int unit_type)
+{
+   png_debug1(1, "in %s storage function\n", "pHYs");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->x_pixels_per_unit = res_x;
+   info_ptr->y_pixels_per_unit = res_y;
+   info_ptr->phys_unit_type = (png_byte)unit_type;
+   info_ptr->valid |= PNG_INFO_pHYs;
+}
+#endif
+
+void
+png_set_PLTE(png_structp png_ptr, png_infop info_ptr,
+   png_colorp palette, int num_palette)
+{
+   png_debug1(1, "in %s storage function\n", "PLTE");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->palette = palette;
+   info_ptr->num_palette = (png_uint_16)num_palette;
+   info_ptr->valid |= PNG_INFO_PLTE;
+}
+
+#if defined(PNG_READ_sBIT_SUPPORTED) || defined(PNG_WRITE_sBIT_SUPPORTED)
+void
+png_set_sBIT(png_structp png_ptr, png_infop info_ptr,
+   png_color_8p sig_bit)
+{
+   png_debug1(1, "in %s storage function\n", "sBIT");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_memcpy(&(info_ptr->sig_bit), sig_bit, sizeof (png_color_8));
+   info_ptr->valid |= PNG_INFO_sBIT;
+}
+#endif
+
+#if defined(PNG_READ_sRGB_SUPPORTED) || defined(PNG_WRITE_sRGB_SUPPORTED)
+void
+png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent)
+{
+   png_debug1(1, "in %s storage function\n", "sRGB");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->srgb_intent = (png_byte)intent;
+   info_ptr->valid |= PNG_INFO_sRGB;
+}
+void
+png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr,
+   int intent)
+{
+#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_WRITE_gAMA_SUPPORTED)
+   float file_gamma;
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED) || defined(PNG_WRITE_cHRM_SUPPORTED)
+   float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y;
+#endif
+   png_debug1(1, "in %s storage function\n", "sRGB_gAMA_and_cHRM");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_set_sRGB(png_ptr, info_ptr, intent);
+
+#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_WRITE_gAMA_SUPPORTED)
+   file_gamma = (float).45;
+   png_set_gAMA(png_ptr, info_ptr, file_gamma);
+#endif
+
+#if defined(PNG_READ_cHRM_SUPPORTED) || defined(PNG_WRITE_cHRM_SUPPORTED)
+   white_x = (float).3127;
+   white_y = (float).3290;
+   red_x   = (float).64;
+   red_y   = (float).33;
+   green_x = (float).30;
+   green_y = (float).60;
+   blue_x  = (float).15;
+   blue_y  = (float).06;
+
+   png_set_cHRM(png_ptr, info_ptr,
+      white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y);
+
+#endif
+}
+#endif
+
+#if defined(PNG_READ_tEXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \
+    defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
+void
+png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr,
+   int num_text)
+{
+   int i;
+
+   png_debug1(1, "in %s storage function\n", (png_ptr->chunk_name[0] == '\0' ?
+      "text" : (png_const_charp)png_ptr->chunk_name));
+
+   if (png_ptr == NULL || info_ptr == NULL || num_text == 0)
+      return;
+
+   /* Make sure we have enough space in the "text" array in info_struct
+    * to hold all of the incoming text_ptr objects.
+    */
+   if (info_ptr->num_text + num_text > info_ptr->max_text)
+   {
+      if (info_ptr->text != NULL)
+      {
+         png_textp old_text;
+         int old_max;
+
+         old_max = info_ptr->max_text;
+         info_ptr->max_text = info_ptr->num_text + num_text + 8;
+         old_text = info_ptr->text;
+         info_ptr->text = (png_textp)png_malloc(png_ptr,
+            (png_uint_32)(info_ptr->max_text * sizeof (png_text)));
+         png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max *
+            sizeof(png_text)));
+         png_free(png_ptr, old_text);
+      }
+      else
+      {
+         info_ptr->max_text = num_text + 8;
+         info_ptr->num_text = 0;
+         info_ptr->text = (png_textp)png_malloc(png_ptr,
+            (png_uint_32)(info_ptr->max_text * sizeof (png_text)));
+      }
+      png_debug1(3, "allocated %d entries for info_ptr->text\n",
+         info_ptr->max_text);
+   }
+
+   for (i = 0; i < num_text; i++)
+   {
+      png_textp textp = &(info_ptr->text[info_ptr->num_text]);
+
+      if (text_ptr[i].text == NULL)
+         text_ptr[i].text = (png_charp)"";
+
+      if (text_ptr[i].text[0] == '\0')
+      {
+         textp->text_length = 0;
+         textp->compression = PNG_TEXT_COMPRESSION_NONE;
+      }
+      else
+      {
+         textp->text_length = png_strlen(text_ptr[i].text);
+         textp->compression = text_ptr[i].compression;
+      }
+      textp->text = text_ptr[i].text;
+      textp->key = text_ptr[i].key;
+      info_ptr->num_text++;
+      png_debug1(3, "transferred text chunk %d\n", info_ptr->num_text);
+   }
+}
+#endif
+
+#if defined(PNG_READ_tIME_SUPPORTED) || defined(PNG_WRITE_tIME_SUPPORTED)
+void
+png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time)
+{
+   png_debug1(1, "in %s storage function\n", "tIME");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_memcpy(&(info_ptr->mod_time), mod_time, sizeof (png_time));
+   info_ptr->valid |= PNG_INFO_tIME;
+}
+#endif
+
+#if defined(PNG_READ_tRNS_SUPPORTED) || defined(PNG_WRITE_tRNS_SUPPORTED)
+void
+png_set_tRNS(png_structp png_ptr, png_infop info_ptr,
+   png_bytep trans, int num_trans, png_color_16p trans_values)
+{
+   png_debug1(1, "in %s storage function\n", "tRNS");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (trans != NULL)
+   {
+      info_ptr->trans = trans;
+   }
+
+   if (trans_values != NULL)
+   {
+      png_memcpy(&(info_ptr->trans_values), trans_values,
+         sizeof(png_color_16));
+      if (num_trans == 0)
+        num_trans = 1;
+   }
+   info_ptr->num_trans = (png_uint_16)num_trans;
+   info_ptr->valid |= PNG_INFO_tRNS;
+}
+#endif
+
diff --git a/src/png/pngtest.c b/src/png/pngtest.c
new file mode 100644 (file)
index 0000000..a1ba8ec
--- /dev/null
@@ -0,0 +1,1081 @@
+
+/* pngtest.c - a simple test program to test libpng
+ *
+ * libpng 1.0.3 -January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ *
+ * This program reads in a PNG image, writes it out again, and then
+ * compares the two files.  If the files are identical, this shows that
+ * the basic chunk handling, filtering, and (de)compression code is working
+ * properly.  It does not currently test all of the transforms, although
+ * it probably should.
+ *
+ * The program will report "FAIL" in certain legitimate cases:
+ * 1) when the compression level or filter selection method is changed.
+ * 2) when the chunk size is not 8K.
+ * 3) unknown ancillary chunks exist in the input file.
+ * 4) others not listed here...
+ * In these cases, it is best to check with another tool such as "pngcheck"
+ * to see what the differences between the two images are.
+ *
+ * If a filename is given on the command-line, then this file is used
+ * for the input, rather than the default "pngtest.png".  This allows
+ * testing a wide variety of files easily.  You can also test a number
+ * of files at once by typing "pngtest -m file1.png file2.png ..."
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Makes pngtest verbose so we can find problems (needs to be before png.h) */
+#ifndef PNG_DEBUG
+#define PNG_DEBUG 0
+#endif
+
+#include "png.h"
+
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+static int tIME_chunk_present=0;
+static char tIME_string[30] = "no tIME chunk present in file";
+#endif /* PNG_TIME_RFC1123_SUPPORTED */
+
+int test_one_file PNGARG((PNG_CONST char *inname, PNG_CONST char *outname));
+
+#ifdef __TURBOC__
+#include <mem.h>
+#endif
+
+/* defined so I can write to a file on gui/windowing platforms */
+/*  #define STDERR stderr  */
+#define STDERR stdout   /* for DOS */
+
+/* example of using row callbacks to make a simple progress meter */
+static int status_pass=1;
+static int status_dots_requested=0;
+static int status_dots=1;
+
+void
+read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass)
+{
+    if(png_ptr == NULL || row_number > 0x3fffffffL) return;
+    if(status_pass != pass)
+    {
+       fprintf(stdout,"\n Pass %d: ",pass);
+       status_pass = pass;
+       status_dots = 30;
+    }
+    status_dots--;
+    if(status_dots == 0)
+    {
+       fprintf(stdout, "\n         ");
+       status_dots=30;
+    }
+    fprintf(stdout, "r");
+}
+
+void
+write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass)
+{
+    if(png_ptr == NULL || row_number > 0x3fffffffL || pass > 7) return;
+    fprintf(stdout, "w");
+}
+
+
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+/* example of using user transform callback (we don't transform anything,
+   but merely count the zero samples) */
+
+static png_uint_32 zero_samples;
+
+void
+count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data)
+{
+   png_bytep dp = data;
+   if(png_ptr == NULL)return;
+
+   /* contents of row_info:
+    *  png_uint_32 width      width of row
+    *  png_uint_32 rowbytes   number of bytes in row
+    *  png_byte color_type    color type of pixels
+    *  png_byte bit_depth     bit depth of samples
+    *  png_byte channels      number of channels (1-4)
+    *  png_byte pixel_depth   bits per pixel (depth*channels)
+    */
+
+    /* counts the number of zero samples (or zero pixels if color_type is 3 */
+
+    if(row_info->color_type == 0 || row_info->color_type == 3)
+    {
+       int pos=0;
+       png_uint_32 n, nstop;
+       for (n=0, nstop=row_info->width; n<nstop; n++)
+       {
+          if(row_info->bit_depth == 1)
+          {
+             if(((*dp << pos++ )& 0x80) == 0) zero_samples++;
+             if(pos == 8)
+             {
+                pos = 0;
+                dp++;
+             }
+          }
+          if(row_info->bit_depth == 2)
+          {
+             if(((*dp << (pos+=2))& 0xc0) == 0) zero_samples++;
+             if(pos == 8)
+             {
+                pos = 0;
+                dp++;
+             }
+          }
+          if(row_info->bit_depth == 4)
+          {
+             if(((*dp << (pos+=4))& 0xf0) == 0) zero_samples++;
+             if(pos == 8)
+             {
+                pos = 0;
+                dp++;
+             }
+          }
+          if(row_info->bit_depth == 8)
+             if(*dp++ == 0) zero_samples++;
+          if(row_info->bit_depth == 16)
+          {
+             if((*dp | *(dp+1)) == 0) zero_samples++;
+             dp+=2;
+          }
+       }
+    }
+    else /* other color types */
+    {
+       png_uint_32 n, nstop;
+       int channel;
+       int color_channels = row_info->channels;
+       if(row_info->color_type > 3)color_channels--;
+
+       for (n=0, nstop=row_info->width; n<nstop; n++)
+       {
+          for (channel = 0; channel < color_channels; channel++)
+          {
+             if(row_info->bit_depth == 8)
+                if(*dp++ == 0) zero_samples++;
+             if(row_info->bit_depth == 16)
+             {
+                if((*dp | *(dp+1)) == 0) zero_samples++;
+                dp+=2;
+             }
+          }
+          if(row_info->color_type > 3)
+          {
+             dp++;
+             if(row_info->bit_depth == 16)dp++;
+          }
+       }
+    }
+}
+#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */
+
+static int verbose = 0;
+static int wrote_question = 0;
+
+#if defined(PNG_NO_STDIO)
+/* START of code to validate stdio-free compilation */
+/* 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. */
+#ifndef USE_FAR_KEYWORD
+static void
+png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   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.
+    */
+   check = (png_size_t)fread(data, (png_size_t)1, length,
+      (FILE *)png_ptr->io_ptr);
+
+   if (check != length)
+   {
+      png_error(png_ptr, "Read Error");
+   }
+}
+#else
+/* this is the model-independent version. Since the standard I/O library
+   can't handle far buffers in the medium and small models, we have to copy
+   the data.
+*/
+
+#define NEAR_BUF_SIZE 1024
+#define MIN(a,b) (a <= b ? a : b)
+
+static void
+png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   int check;
+   png_byte *n_data;
+   FILE *io_ptr;
+
+   /* Check if data really is near. If so, use usual code. */
+   n_data = (png_byte *)CVT_PTR_NOCHECK(data);
+   io_ptr = (FILE *)CVT_PTR(png_ptr->io_ptr);
+   if ((png_bytep)n_data == data)
+   {
+      check = fread(n_data, 1, length, io_ptr);
+   }
+   else
+   {
+      png_byte buf[NEAR_BUF_SIZE];
+      png_size_t read, remaining, err;
+      check = 0;
+      remaining = length;
+      do
+      {
+         read = MIN(NEAR_BUF_SIZE, remaining);
+         err = fread(buf, (png_size_t)1, read, io_ptr);
+         png_memcpy(data, buf, read); /* copy far buffer to near buffer */
+         if(err != read)
+            break;
+         else
+            check += err;
+         data += read;
+         remaining -= read;
+      }
+      while (remaining != 0);
+   }
+   if (check != length)
+   {
+      png_error(png_ptr, "read Error");
+   }
+}
+#endif /* USE_FAR_KEYWORD */
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+static void
+png_default_flush(png_structp png_ptr)
+{
+   FILE *io_ptr;
+   io_ptr = (FILE *)CVT_PTR((png_ptr->io_ptr));
+   if (io_ptr != NULL)
+      fflush(io_ptr);
+}
+#endif
+
+/* This is the function that does the actual writing of data.  If you are
+   not writing to a standard C stream, you should create a replacement
+   write_data function and use it at run time with png_set_write_fn(), rather
+   than changing the library. */
+#ifndef USE_FAR_KEYWORD
+static void
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_uint_32 check;
+
+   check = fwrite(data, 1, length, (FILE *)(png_ptr->io_ptr));
+   if (check != length)
+   {
+      png_error(png_ptr, "Write Error");
+   }
+}
+#else
+/* this is the model-independent version. Since the standard I/O library
+   can't handle far buffers in the medium and small models, we have to copy
+   the data.
+*/
+
+#define NEAR_BUF_SIZE 1024
+#define MIN(a,b) (a <= b ? a : b)
+
+static void
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_uint_32 check;
+   png_byte *near_data;  /* Needs to be "png_byte *" instead of "png_bytep" */
+   FILE *io_ptr;
+
+   /* Check if data really is near. If so, use usual code. */
+   near_data = (png_byte *)CVT_PTR_NOCHECK(data);
+   io_ptr = (FILE *)CVT_PTR(png_ptr->io_ptr);
+   if ((png_bytep)near_data == data)
+   {
+      check = fwrite(near_data, 1, length, io_ptr);
+   }
+   else
+   {
+      png_byte buf[NEAR_BUF_SIZE];
+      png_size_t written, remaining, err;
+      check = 0;
+      remaining = length;
+      do
+      {
+         written = MIN(NEAR_BUF_SIZE, remaining);
+         png_memcpy(buf, data, written); /* copy far buffer to near buffer */
+         err = fwrite(buf, 1, written, io_ptr);
+         if (err != written)
+            break;
+         else
+            check += err;
+         data += written;
+         remaining -= written;
+      }
+      while (remaining != 0);
+   }
+   if (check != length)
+   {
+      png_error(png_ptr, "Write Error");
+   }
+}
+
+#endif /* USE_FAR_KEYWORD */
+
+/* This function is called when there is a warning, but the library thinks
+ * it can continue anyway.  Replacement functions don't have to do anything
+ * here if you don't want to.  In the default configuration, png_ptr is
+ * not used, but it is passed in case it may be useful.
+ */
+static void
+png_default_warning(png_structp png_ptr, png_const_charp message)
+{
+   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
+png_default_error(png_structp png_ptr, png_const_charp message)
+{
+   png_default_warning(png_ptr, message);
+   /* We can return because png_error calls the default handler, which is
+    * actually OK in this case. */
+}
+#endif /* PNG_NO_STDIO */
+/* END of code to validate stdio-free compilation */
+
+/* START of code to validate memory allocation and deallocation */
+#ifdef PNG_USER_MEM_SUPPORTED
+
+/* Allocate memory.  For reasonable files, size should never exceed
+   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. */
+typedef struct memory_information {
+   png_uint_32                    size;
+   png_voidp                 pointer;
+   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;
+
+extern PNG_EXPORT(png_voidp,png_debug_malloc) PNGARG((png_structp png_ptr,
+   png_uint_32 size));
+extern PNG_EXPORT(void,png_debug_free) PNGARG((png_structp png_ptr,
+   png_voidp ptr));
+
+png_voidp
+png_debug_malloc(png_structp png_ptr, png_uint_32 size) {
+
+   /* png_malloc has already tested for NULL; png_create_struct calls
+      png_debug_malloc directly, with png_ptr == NULL which is OK */
+
+   if (size == 0)
+      return (png_voidp)(NULL);
+
+   /* This calls the library allocator twice, once to get the requested
+      buffer and once to get a new free list entry. */
+   {
+      memory_infop pinfo = png_malloc_default(png_ptr, sizeof *pinfo);
+      pinfo->size = size;
+      current_allocation += size;
+      if (current_allocation > maximum_allocation)
+         maximum_allocation = current_allocation;
+      pinfo->pointer = png_malloc_default(png_ptr, size);
+      pinfo->next = pinformation;
+      pinformation = pinfo;
+      /* Make sure the caller isn't assuming zeroed memory. */
+      png_memset(pinfo->pointer, 0xdd, pinfo->size);
+      return (png_voidp)(pinfo->pointer);
+   }
+}
+
+/* Free a pointer.  It is removed from the list at the same time. */
+void
+png_debug_free(png_structp png_ptr, png_voidp ptr)
+{
+   if (png_ptr == NULL)
+      fprintf(STDERR, "NULL pointer to png_debug_free.\n");
+   if (ptr == 0) {
+#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;
+      for (;;) {
+         memory_infop pinfo = *ppinfo;
+         if (pinfo->pointer == ptr) {
+            *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
+               the memory that is to be freed. */
+            memset(ptr, 0x55, pinfo->size);
+            png_free_default(png_ptr, pinfo);
+            break;
+         }
+         if (pinfo->next == NULL) {
+            fprintf(STDERR, "Pointer %x not found\n", ptr);
+            break;
+         }
+         ppinfo = &pinfo->next;
+      }
+   }
+
+   /* Finally free the data. */
+   png_free_default(png_ptr, ptr);
+}
+#endif /* PNG_USER_MEM_SUPPORTED */
+/* END of code to test memory allocation/deallocation */
+
+/* Test one file */
+int
+test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
+{
+   static FILE *fpin, *fpout;  /* "static" prevents setjmp corruption */
+   png_structp read_ptr, write_ptr;
+   png_infop read_info_ptr, write_info_ptr, end_info_ptr;
+   png_bytep row_buf;
+   png_uint_32 y;
+   png_uint_32 width, height;
+   int num_pass, pass;
+   int bit_depth, color_type;
+#ifdef USE_FAR_KEYWORD
+   jmp_buf jmpbuf;
+#endif   
+
+   char inbuf[256], outbuf[256];
+
+   row_buf = (png_bytep)NULL;
+
+   if ((fpin = fopen(inname, "rb")) == NULL)
+   {
+      fprintf(STDERR, "Could not find input file %s\n", inname);
+      return (1);
+   }
+
+   if ((fpout = fopen(outname, "wb")) == NULL)
+   {
+      fprintf(STDERR, "Could not open output file %s\n", outname);
+      fclose(fpin);
+      return (1);
+   }
+
+   png_debug(0, "Allocating read and write structures\n");
+#ifdef PNG_USER_MEM_SUPPORTED
+   read_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
+      (png_error_ptr)NULL, (png_error_ptr)NULL, (png_voidp)NULL,
+      (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free);
+#else
+   read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
+      (png_error_ptr)NULL, (png_error_ptr)NULL);
+#endif
+#if defined(PNG_NO_STDIO)
+   png_set_error_fn(read_ptr, (png_voidp)inname, png_default_error,
+       png_default_warning);
+#endif
+#ifdef PNG_USER_MEM_SUPPORTED
+   write_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
+      (png_error_ptr)NULL, (png_error_ptr)NULL, (png_voidp)NULL,
+      (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free);
+#else
+   write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
+      (png_error_ptr)NULL, (png_error_ptr)NULL);
+#endif
+#if defined(PNG_NO_STDIO)
+   png_set_error_fn(write_ptr, (png_voidp)inname, png_default_error,
+       png_default_warning);
+#endif
+   png_debug(0, "Allocating read_info, write_info and end_info structures\n");
+   read_info_ptr = png_create_info_struct(read_ptr);
+   write_info_ptr = png_create_info_struct(write_ptr);
+   end_info_ptr = png_create_info_struct(read_ptr);
+#ifdef PNG_USER_MEM_SUPPORTED
+#endif
+
+   png_debug(0, "Setting jmpbuf for read struct\n");
+#ifdef USE_FAR_KEYWORD
+   if (setjmp(jmpbuf))
+#else
+   if (setjmp(read_ptr->jmpbuf))
+#endif
+   {
+      fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname);
+      png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
+      png_destroy_write_struct(&write_ptr, &write_info_ptr);
+      fclose(fpin);
+      fclose(fpout);
+      return (1);
+   }
+#ifdef USE_FAR_KEYWORD
+   png_memcpy(read_ptr->jmpbuf,jmpbuf,sizeof(jmp_buf));
+#endif
+
+   png_debug(0, "Setting jmpbuf for write struct\n");
+#ifdef USE_FAR_KEYWORD
+   if (setjmp(jmpbuf))
+#else
+   if (setjmp(write_ptr->jmpbuf))
+#endif
+   {
+      fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname);
+      png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
+      png_destroy_write_struct(&write_ptr, &write_info_ptr);
+      fclose(fpin);
+      fclose(fpout);
+      return (1);
+   }
+#ifdef USE_FAR_KEYWORD
+   png_memcpy(write_ptr->jmpbuf,jmpbuf,sizeof(jmp_buf));
+#endif
+
+   png_debug(0, "Initializing input and output streams\n");
+#if !defined(PNG_NO_STDIO)
+   png_init_io(read_ptr, fpin);
+   png_init_io(write_ptr, fpout);
+#else
+   png_set_read_fn(read_ptr, (png_voidp)fpin, png_default_read_data);
+   png_set_write_fn(write_ptr, (png_voidp)fpout,  png_default_write_data,
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+      png_default_flush);
+#else
+      NULL);
+#endif
+#endif
+   if(status_dots_requested == 1)
+   {
+      png_set_write_status_fn(write_ptr, write_row_callback);
+      png_set_read_status_fn(read_ptr, read_row_callback);
+   }
+   else
+   {
+      png_set_write_status_fn(write_ptr, NULL);
+      png_set_read_status_fn(read_ptr, NULL);
+   }
+
+#  if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+     zero_samples=0;
+     png_set_write_user_transform_fn(write_ptr, count_zero_samples);
+#  endif
+
+   png_debug(0, "Reading info struct\n");
+   png_read_info(read_ptr, read_info_ptr);
+
+   png_debug(0, "Transferring info struct\n");
+   {
+      int interlace_type, compression_type, filter_type;
+
+      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,
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+            color_type, interlace_type, compression_type, filter_type);
+#else
+            color_type, PNG_INTERLACE_NONE, compression_type, filter_type);
+#endif
+      }
+   }
+#if defined(PNG_READ_bKGD_SUPPORTED) && defined(PNG_WRITE_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_READ_cHRM_SUPPORTED) && defined(PNG_WRITE_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_READ_gAMA_SUPPORTED) && defined(PNG_WRITE_gAMA_SUPPORTED)
+   {
+      double gamma;
+
+      if (png_get_gAMA(read_ptr, read_info_ptr, &gamma))
+      {
+         png_set_gAMA(write_ptr, write_info_ptr, gamma);
+      }
+   }
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED) && defined(PNG_WRITE_sRGB_SUPPORTED)
+   {
+      int intent;
+
+      if (png_get_sRGB(read_ptr, read_info_ptr, &intent))
+      {
+         png_set_sRGB(write_ptr, write_info_ptr, intent);
+      }
+   }
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED) && defined(PNG_WRITE_hIST_SUPPORTED)
+   {
+      png_uint_16p hist;
+
+      if (png_get_hIST(read_ptr, read_info_ptr, &hist))
+      {
+         png_set_hIST(write_ptr, write_info_ptr, hist);
+      }
+   }
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED) && defined(PNG_WRITE_oFFs_SUPPORTED)
+   {
+      png_uint_32 offset_x, offset_y;
+      int unit_type;
+
+      if (png_get_oFFs(read_ptr, read_info_ptr,&offset_x,&offset_y,&unit_type))
+      {
+         png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type);
+      }
+   }
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED) && defined(PNG_WRITE_pCAL_SUPPORTED)
+   {
+      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
+#if defined(PNG_READ_pHYs_SUPPORTED) && defined(PNG_WRITE_pHYs_SUPPORTED)
+   {
+      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
+   {
+      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_READ_sBIT_SUPPORTED) && defined(PNG_WRITE_sBIT_SUPPORTED)
+   {
+      png_color_8p sig_bit;
+
+      if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit))
+      {
+         png_set_sBIT(write_ptr, write_info_ptr, sig_bit);
+      }
+   }
+#endif
+#if (defined(PNG_READ_tEXt_SUPPORTED) && defined(PNG_WRITE_tEXt_SUPPORTED)) || \
+    (defined(PNG_READ_zTXt_SUPPORTED) && defined(PNG_WRITE_zTXt_SUPPORTED))
+   {
+      png_textp text_ptr;
+      int num_text;
+
+      if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0)
+      {
+         png_debug1(0, "Handling %d tEXt/zTXt chunks\n", num_text);
+         png_set_text(write_ptr, write_info_ptr, text_ptr, num_text);
+      }
+   }
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED) && defined(PNG_WRITE_tIME_SUPPORTED)
+   {
+      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);
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+         /* we have to use png_strcpy instead of "=" because the string
+            pointed to by png_convert_to_rfc1123() gets free'ed before
+            we use it */
+         png_strcpy(tIME_string,png_convert_to_rfc1123(read_ptr, mod_time));
+         tIME_chunk_present++;
+#endif /* PNG_TIME_RFC1123_SUPPORTED */
+      }
+   }
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED) && defined(PNG_WRITE_tRNS_SUPPORTED)
+   {
+      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))
+      {
+         png_set_tRNS(write_ptr, write_info_ptr, trans, num_trans,
+            trans_values);
+      }
+   }
+#endif
+
+   png_debug(0, "\nWriting info struct\n");
+   png_write_info(write_ptr, write_info_ptr);
+
+   png_debug(0, "\nAllocating row buffer \n");
+   row_buf = (png_bytep)png_malloc(read_ptr,
+      png_get_rowbytes(read_ptr, read_info_ptr));
+   if (row_buf == NULL)
+   {
+      fprintf(STDERR, "No memory to allocate row buffer\n");
+      png_destroy_read_struct(&read_ptr, &read_info_ptr, (png_infopp)NULL);
+      png_destroy_write_struct(&write_ptr, &write_info_ptr);
+      fclose(fpin);
+      fclose(fpout);
+      return (1);
+   }
+   png_debug(0, "Writing row data\n");
+
+   num_pass = png_set_interlace_handling(read_ptr);
+   png_set_interlace_handling(write_ptr);
+
+   for (pass = 0; pass < num_pass; pass++)
+   {
+      png_debug1(0, "Writing row data for pass %d\n",pass);
+      for (y = 0; y < height; y++)
+      {
+         png_read_rows(read_ptr, (png_bytepp)&row_buf, (png_bytepp)NULL, 1);
+         png_write_rows(write_ptr, (png_bytepp)&row_buf, 1);
+      }
+   }
+
+   png_debug(0, "Reading and writing end_info data\n");
+   png_read_end(read_ptr, end_info_ptr);
+   png_write_end(write_ptr, end_info_ptr);
+
+#ifdef PNG_EASY_ACCESS_SUPPORTED
+   if(verbose)
+   {
+      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);
+      fprintf(STDERR, "Image width = %lu, height = %lu\n",
+         iwidth, iheight);
+   }
+#endif
+
+   png_debug(0, "Destroying data structs\n");
+   png_free(read_ptr, row_buf);
+   png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
+   png_destroy_write_struct(&write_ptr, &write_info_ptr);
+
+   fclose(fpin);
+   fclose(fpout);
+
+   png_debug(0, "Opening files for comparison\n");
+   if ((fpin = fopen(inname, "rb")) == NULL)
+   {
+      fprintf(STDERR, "Could not find file %s\n", inname);
+      return (1);
+   }
+
+   if ((fpout = fopen(outname, "rb")) == NULL)
+   {
+      fprintf(STDERR, "Could not find file %s\n", outname);
+      fclose(fpin);
+      return (1);
+   }
+
+   for(;;)
+   {
+      png_size_t num_in, num_out;
+
+      num_in = fread(inbuf, 1, 1, fpin);
+      num_out = fread(outbuf, 1, 1, fpout);
+
+      if (num_in != num_out)
+      {
+         fprintf(STDERR, "Files %s and %s are of a different size\n",
+                 inname, outname);
+         if(wrote_question == 0)
+         {
+            fprintf(STDERR,
+              "   Was %s written with the same chunk size (8k),",inname);
+            fprintf(STDERR,
+              " filtering\n   heuristic (libpng default), compression");
+            fprintf(STDERR,
+              " level (zlib default)\n   and zlib version (%s)?\n\n",
+              ZLIB_VERSION);
+            wrote_question=1;
+         }
+         fclose(fpin);
+         fclose(fpout);
+         return (0);
+      }
+
+      if (!num_in)
+         break;
+
+      if (png_memcmp(inbuf, outbuf, num_in))
+      {
+         fprintf(STDERR, "Files %s and %s are different\n", inname, outname);
+         if(wrote_question == 0)
+         {
+            fprintf(STDERR,
+              "   Was %s written with the same chunk size (8k),",inname);
+            fprintf(STDERR,
+              " filtering\n   heuristic (libpng default), compression");
+            fprintf(STDERR,
+              " level (zlib default)\n   and zlib version (%s)?\n\n",
+              ZLIB_VERSION);
+            wrote_question=1;
+         }
+         fclose(fpin);
+         fclose(fpout);
+         return (0);
+      }
+   }
+
+   fclose(fpin);
+   fclose(fpout);
+
+   return (0);
+}
+
+/* input and output filenames */
+#ifdef RISCOS
+PNG_CONST char *inname = "pngtest/png";
+PNG_CONST char *outname = "pngout/png";
+#else
+static PNG_CONST char *inname = "pngtest.png";
+static PNG_CONST char *outname = "pngout.png";
+#endif
+
+int
+main(int argc, char *argv[])
+{
+   int multiple = 0;
+   int ierror = 0;
+
+   fprintf(STDERR, "Testing libpng version %s\n", PNG_LIBPNG_VER_STRING);
+   fprintf(STDERR, "   with zlib   version %s\n", ZLIB_VERSION);
+   fprintf(STDERR,"%s",png_get_copyright(NULL));
+
+   /* Do some consistency checking on the memory allocation settings, I'm
+      not sure this matters, but it is nice to know, the first of these
+      tests should be impossible because of the way the macros are set
+      in pngconf.h */
+#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K)
+      fprintf(STDERR, " NOTE: Zlib compiled for max 64k, libpng not\n");
+#endif
+   /* I think the following can happen. */
+#if !defined(MAXSEG_64K) && defined(PNG_MAX_MALLOC_64K)
+      fprintf(STDERR, " NOTE: libpng compiled for max 64k, zlib not\n");
+#endif
+
+   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)
+   {
+      if (strcmp(argv[1], "-m") == 0)
+      {
+         multiple = 1;
+         status_dots_requested = 0;
+      }
+      else if (strcmp(argv[1], "-mv") == 0 ||
+               strcmp(argv[1], "-vm") == 0 )
+      {
+         multiple = 1;
+         verbose = 1;
+         status_dots_requested = 1;
+      }
+      else if (strcmp(argv[1], "-v") == 0)
+      {
+         verbose = 1;
+         status_dots_requested = 1;
+         inname = argv[2];
+      }
+      else
+      {
+         inname = argv[1];
+         status_dots_requested = 0;
+      }
+   }
+
+   if (!multiple && argc == 3+verbose)
+     outname = argv[2+verbose];
+
+   if ((!multiple && argc > 3+verbose) || (multiple && argc < 2))
+   {
+     fprintf(STDERR,
+       "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n",
+        argv[0], argv[0]);
+     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);
+     exit(1);
+   }
+
+   if (multiple)
+   {
+      int i;
+#ifdef PNG_USER_MEM_SUPPORTED
+      int allocation_now = current_allocation;
+#endif
+      for (i=2; i<argc; ++i)
+      {
+         int kerror;
+         fprintf(STDERR, "Testing %s:",argv[i]);
+         kerror = test_one_file(argv[i], outname);
+         if (kerror == 0)
+         {
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+            fprintf(STDERR, " PASS (%lu zero samples)\n",zero_samples);
+#else
+            fprintf(STDERR, " PASS\n");
+#endif
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+         if(tIME_chunk_present != 0)
+            fprintf(STDERR, " tIME = %s\n",tIME_string);
+         tIME_chunk_present = 0;
+#endif /* PNG_TIME_RFC1123_SUPPORTED */
+         }
+         else
+         {
+            fprintf(STDERR, " FAIL\n");
+            ierror += kerror;
+         }
+#ifdef PNG_USER_MEM_SUPPORTED
+         if (allocation_now != current_allocation)
+            fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n",
+               current_allocation-allocation_now);
+         if (current_allocation != 0) {
+            memory_infop pinfo = pinformation;
+
+            fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n",
+               current_allocation);
+            while (pinfo != NULL) {
+               fprintf(STDERR, " %d bytes at %x\n", pinfo->size, pinfo->pointer);
+               pinfo = pinfo->next;
+               }
+         }
+#endif
+      }
+#ifdef PNG_USER_MEM_SUPPORTED
+         fprintf(STDERR, "Maximum memory allocation: %d bytes\n",
+            maximum_allocation);
+#endif
+   }
+   else
+   {
+      int i;
+      for (i=0; i<3; ++i) {
+         int kerror;
+#ifdef PNG_USER_MEM_SUPPORTED
+         int allocation_now = current_allocation;
+#endif
+         if (i == 1) status_dots_requested = 1;
+         else if(verbose == 0)status_dots_requested = 0;
+         if (i == 0 || verbose == 1 || ierror != 0)
+            fprintf(STDERR, "Testing %s:",inname);
+         kerror = test_one_file(inname, outname);
+         if(kerror == 0)
+         {
+            if(verbose == 1 || i == 2)
+            {
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+                fprintf(STDERR, " PASS (%lu zero samples)\n",zero_samples);
+#else
+                fprintf(STDERR, " PASS\n");
+#endif
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+         if(tIME_chunk_present != 0)
+            fprintf(STDERR, " tIME = %s\n",tIME_string);
+#endif /* PNG_TIME_RFC1123_SUPPORTED */
+            }
+         }
+         else
+         {
+            if(verbose == 0 && i != 2)
+               fprintf(STDERR, "Testing %s:",inname);
+            fprintf(STDERR, " FAIL\n");
+            ierror += kerror;
+         }
+#ifdef PNG_USER_MEM_SUPPORTED
+         if (allocation_now != current_allocation)
+             fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n",
+               current_allocation-allocation_now);
+         if (current_allocation != 0) {
+             memory_infop pinfo = pinformation;
+
+             fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n",
+                current_allocation);
+             while (pinfo != NULL) {
+                fprintf(STDERR, " %d bytes at %x\n", pinfo->size, pinfo->pointer);
+                pinfo = pinfo->next;
+             }
+          }
+#endif
+       }
+#ifdef PNG_USER_MEM_SUPPORTED
+       fprintf(STDERR, "Maximum memory allocation: %d bytes\n",
+          maximum_allocation);
+#endif
+   }
+
+   if (ierror == 0)
+      fprintf(STDERR, "libpng passes test\n");
+   else
+      fprintf(STDERR, "libpng FAILS test\n");
+   return (int)(ierror != 0);
+}
+
diff --git a/src/png/pngtrans.c b/src/png/pngtrans.c
new file mode 100644 (file)
index 0000000..397c988
--- /dev/null
@@ -0,0 +1,572 @@
+
+/* pngtrans.c - transforms the data in a row (used by both readers and writers)
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* turn on bgr to rgb mapping */
+void
+png_set_bgr(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_bgr\n");
+   png_ptr->transformations |= PNG_BGR;
+}
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* turn on 16 bit byte swapping */
+void
+png_set_swap(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_swap\n");
+   if (png_ptr->bit_depth == 16)
+      png_ptr->transformations |= PNG_SWAP_BYTES;
+}
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+/* turn on pixel packing */
+void
+png_set_packing(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_packing\n");
+   if (png_ptr->bit_depth < 8)
+   {
+      png_ptr->transformations |= PNG_PACK;
+      png_ptr->usr_bit_depth = 8;
+   }
+}
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+/* turn on packed pixel swapping */
+void
+png_set_packswap(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_packswap\n");
+   if (png_ptr->bit_depth < 8)
+      png_ptr->transformations |= PNG_PACKSWAP;
+}
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+void
+png_set_shift(png_structp png_ptr, png_color_8p true_bits)
+{
+   png_debug(1, "in png_set_shift\n");
+   png_ptr->transformations |= PNG_SHIFT;
+   png_ptr->shift = *true_bits;
+}
+#endif
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
+    defined(PNG_WRITE_INTERLACING_SUPPORTED)
+int
+png_set_interlace_handling(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_interlace handling\n");
+   if (png_ptr->interlaced)
+   {
+      png_ptr->transformations |= PNG_INTERLACE;
+      return (7);
+   }
+
+   return (1);
+}
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+/* Add a filler byte on read, or remove a filler or alpha byte on write.
+ * The filler type has changed in v0.95 to allow future 2-byte fillers
+ * for 48-bit input data, as well as to avoid problems with some compilers
+ * that don't like bytes as parameters.
+ */
+void
+png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc)
+{
+   png_debug(1, "in png_set_filler\n");
+   png_ptr->transformations |= PNG_FILLER;
+   png_ptr->filler = (png_byte)filler;
+   if (filler_loc == PNG_FILLER_AFTER)
+      png_ptr->flags |= PNG_FLAG_FILLER_AFTER;
+   else
+      png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER;
+
+   /* This should probably go in the "do_filler" routine.
+    * I attempted to do that in libpng-1.0.1a but that caused problems
+    * so I restored it in libpng-1.0.2a
+   */
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+   {
+      png_ptr->usr_channels = 4;
+   }
+
+   /* Also I added this in libpng-1.0.2a (what happens when we expand
+    * a less-than-8-bit grayscale to GA? */
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8)
+   {
+      png_ptr->usr_channels = 2;
+   }
+}
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+void
+png_set_swap_alpha(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_swap_alpha\n");
+   png_ptr->transformations |= PNG_SWAP_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+void
+png_set_invert_alpha(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_invert_alpha\n");
+   png_ptr->transformations |= PNG_INVERT_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+void
+png_set_invert_mono(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_invert_mono\n");
+   png_ptr->transformations |= PNG_INVERT_MONO;
+}
+
+/* invert monochrome grayscale data */
+void
+png_do_invert(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_invert\n");
+   if (row_info->bit_depth == 1 &&
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       row_info->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      png_bytep rp = row;
+      png_uint_32 i;
+      png_uint_32 istop = row_info->rowbytes;
+
+      for (i = 0; i < istop; i++)
+      {
+         *rp = (png_byte)(~(*rp));
+         rp++;
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* swaps byte order on 16 bit depth images */
+void
+png_do_swap(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_swap\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       row_info->bit_depth == 16)
+   {
+      png_bytep rp = row;
+      png_uint_32 i;
+      png_uint_32 istop= row_info->width * row_info->channels;
+
+      for (i = 0; i < istop; i++, rp += 2)
+      {
+         png_byte t = *rp;
+         *rp = *(rp + 1);
+         *(rp + 1) = t;
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+static png_byte onebppswaptable[256] = {
+   0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+   0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+   0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+   0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+   0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+   0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+   0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+   0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+   0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+   0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+   0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+   0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+   0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+   0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+   0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+   0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+   0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+   0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+   0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+   0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+   0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+   0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+   0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+   0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+   0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+   0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+   0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+   0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+   0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+   0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+   0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+   0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
+};
+
+static png_byte twobppswaptable[256] = {
+   0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,
+   0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,
+   0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,
+   0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4,
+   0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8,
+   0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8,
+   0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC,
+   0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC,
+   0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1,
+   0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1,
+   0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5,
+   0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5,
+   0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9,
+   0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9,
+   0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD,
+   0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD,
+   0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2,
+   0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2,
+   0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6,
+   0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6,
+   0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA,
+   0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA,
+   0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE,
+   0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE,
+   0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3,
+   0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3,
+   0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7,
+   0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7,
+   0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB,
+   0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB,
+   0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF,
+   0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF
+};
+
+static png_byte fourbppswaptable[256] = {
+   0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
+   0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
+   0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,
+   0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,
+   0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,
+   0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2,
+   0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73,
+   0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3,
+   0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74,
+   0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4,
+   0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75,
+   0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5,
+   0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76,
+   0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6,
+   0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77,
+   0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7,
+   0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,
+   0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8,
+   0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79,
+   0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9,
+   0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A,
+   0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA,
+   0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B,
+   0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB,
+   0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C,
+   0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC,
+   0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D,
+   0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD,
+   0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E,
+   0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE,
+   0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F,
+   0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF
+};
+
+/* swaps pixel packing order within bytes */
+void
+png_do_packswap(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_packswap\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       row_info->bit_depth < 8)
+   {
+      png_bytep rp, end, table;
+
+      end = row + row_info->rowbytes;
+
+      if (row_info->bit_depth == 1)
+         table = onebppswaptable;
+      else if (row_info->bit_depth == 2)
+         table = twobppswaptable;
+      else if (row_info->bit_depth == 4)
+         table = fourbppswaptable;
+      else
+         return;
+
+      for (rp = row; rp < end; rp++)
+         *rp = table[*rp];
+   }
+}
+#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
+    defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+/* remove filler or alpha byte(s) */
+void
+png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags)
+{
+   png_debug(1, "in png_do_strip_filler\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+/*
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB ||
+          row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+*/
+      png_bytep sp=row;
+      png_bytep dp=row;
+      png_uint_32 row_width=row_info->width;
+      png_uint_32 i;
+
+      if (row_info->channels == 4)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            /* This converts from RGBX or RGBA to RGB */
+            if (flags & PNG_FLAG_FILLER_AFTER)
+            {
+               dp+=3; sp+=4;
+               for (i = 1; i < row_width; i++)
+               {
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  sp++;
+               }
+            }
+            /* This converts from XRGB or ARGB to RGB */
+            else
+            {
+               for (i = 0; i < row_width; i++)
+               {
+                  sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+               }
+            }
+            row_info->pixel_depth = 24;
+            row_info->rowbytes = row_width * 3;
+         }
+         else /* if (row_info->bit_depth == 16) */
+         {
+            if (flags & PNG_FLAG_FILLER_AFTER)
+            {
+               /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */
+               sp += 8; dp += 6;
+               for (i = 1; i < row_width; i++)
+               {
+                  /* This could be (although memcpy is probably slower):
+                  png_memcpy(dp, sp, 6);
+                  sp += 8;
+                  dp += 6;
+                  */
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  sp += 2;
+               }
+            }
+            else
+            {
+               /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */
+               for (i = 0; i < row_width; i++)
+               {
+                  /* This could be (although memcpy is probably slower):
+                  png_memcpy(dp, sp, 6);
+                  sp += 8;
+                  dp += 6;
+                  */
+                  sp+=2;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+               }
+            }
+            row_info->pixel_depth = 48;
+            row_info->rowbytes = row_width * 6;
+         }
+         row_info->channels = 3;
+         row_info->color_type &= ~PNG_COLOR_MASK_ALPHA;
+      }
+/*
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY ||
+               row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+*/
+      else if (row_info->channels == 2)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            /* This converts from GX or GA to G */
+            if (flags & PNG_FLAG_FILLER_AFTER)
+            {
+               for (i = 0; i < row_width; i++)
+               {
+                  *dp++ = *sp++;
+                  sp++;
+               }
+            }
+            /* This converts from XG or AG to G */
+            else
+            {
+               for (i = 0; i < row_width; i++)
+               {
+                  sp++;
+                  *dp++ = *sp++;
+               }
+            }
+            row_info->pixel_depth = 8;
+            row_info->rowbytes = row_width;
+         }
+         else /* if (row_info->bit_depth == 16) */
+         {
+            if (flags & PNG_FLAG_FILLER_AFTER)
+            {
+               /* This converts from GGXX or GGAA to GG */
+               sp += 4; dp += 2;
+               for (i = 1; i < row_width; i++)
+               {
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  sp += 2;
+               }
+            }
+            else
+            {
+               /* This converts from XXGG or AAGG to GG */
+               for (i = 0; i < row_width; i++)
+               {
+                  sp += 2;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+               }
+            }
+            row_info->pixel_depth = 16;
+            row_info->rowbytes = row_width * 2;
+         }
+         row_info->channels = 1;
+         row_info->color_type &= ~PNG_COLOR_MASK_ALPHA;
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* swaps red and blue bytes within a pixel */
+void
+png_do_bgr(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_bgr\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       (row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      png_uint_32 row_width = row_info->width;
+      if (row_info->bit_depth == 8)
+      {
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 3)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 2);
+               *(rp + 2) = save;
+            }
+         }
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 4)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 2);
+               *(rp + 2) = save;
+            }
+         }
+      }
+      else if (row_info->bit_depth == 16)
+      {
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 6)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 4);
+               *(rp + 4) = save;
+               save = *(rp + 1);
+               *(rp + 1) = *(rp + 5);
+               *(rp + 5) = save;
+            }
+         }
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 8)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 4);
+               *(rp + 4) = save;
+               save = *(rp + 1);
+               *(rp + 1) = *(rp + 5);
+               *(rp + 5) = save;
+            }
+         }
+      }
+   }
+}
+#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */
+
diff --git a/src/png/pngwio.c b/src/png/pngwio.c
new file mode 100644 (file)
index 0000000..05fe6dd
--- /dev/null
@@ -0,0 +1,228 @@
+
+/* pngwio.c - functions for data output
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ *
+ * This file provides a location for all output.  Users who need
+ * special handling are expected to write functions that have the same
+ * arguments as these and perform similar functions, but that possibly
+ * use different output methods.  Note that you shouldn't change these
+ * functions, but rather write replacement functions and then change
+ * them at run time with png_set_write_fn(...).
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Write the data to whatever output you are using.  The default routine
+   writes to a file pointer.  Note that this routine sometimes gets called
+   with very small lengths, so you should implement some kind of simple
+   buffering if you are using unbuffered writes.  This should never be asked
+   to write more than 64K on a 16 bit machine.  */
+
+void
+png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   if (png_ptr->write_data_fn != NULL )
+      (*(png_ptr->write_data_fn))(png_ptr, data, length);
+   else
+      png_error(png_ptr, "Call to NULL write function");
+}
+
+#if !defined(PNG_NO_STDIO)
+/* This is the function that does the actual writing of data.  If you are
+   not writing to a standard C stream, you should create a replacement
+   write_data function and use it at run time with png_set_write_fn(), rather
+   than changing the library. */
+#ifndef USE_FAR_KEYWORD
+#ifdef __VISAGECPP__
+static void _Optlink
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+#else
+static void
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+#endif
+{
+   png_uint_32 check;
+
+   check = fwrite(data, 1, length, (FILE *)(png_ptr->io_ptr));
+   if (check != length)
+   {
+      png_error(png_ptr, "Write Error");
+   }
+}
+#else
+/* this is the model-independent version. Since the standard I/O library
+   can't handle far buffers in the medium and small models, we have to copy
+   the data.
+*/
+
+#define NEAR_BUF_SIZE 1024
+#define MIN(a,b) (a <= b ? a : b)
+
+#ifdef __VISAGECPP__
+static void _Optlink
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+#else
+static void
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+#endif
+{
+   png_uint_32 check;
+   png_byte *near_data;  /* Needs to be "png_byte *" instead of "png_bytep" */
+   FILE *io_ptr;
+
+   /* Check if data really is near. If so, use usual code. */
+   near_data = (png_byte *)CVT_PTR_NOCHECK(data);
+   io_ptr = (FILE *)CVT_PTR(png_ptr->io_ptr);
+   if ((png_bytep)near_data == data)
+   {
+      check = fwrite(near_data, 1, length, io_ptr);
+   }
+   else
+   {
+      png_byte buf[NEAR_BUF_SIZE];
+      png_size_t written, remaining, err;
+      check = 0;
+      remaining = length;
+      do
+      {
+         written = MIN(NEAR_BUF_SIZE, remaining);
+         png_memcpy(buf, data, written); /* copy far buffer to near buffer */
+         err = fwrite(buf, 1, written, io_ptr);
+         if (err != written)
+            break;
+         else
+            check += err;
+         data += written;
+         remaining -= written;
+      }
+      while (remaining != 0);
+   }
+   if (check != length)
+   {
+      png_error(png_ptr, "Write Error");
+   }
+}
+
+#endif
+#endif
+
+/* This function is called to output any data pending writing (normally
+   to disk).  After png_flush is called, there should be no data pending
+   writing in any buffers. */
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+void
+png_flush(png_structp png_ptr)
+{
+   if (png_ptr->output_flush_fn != NULL)
+      (*(png_ptr->output_flush_fn))(png_ptr);
+}
+
+#if !defined(PNG_NO_STDIO)
+#ifdef __VISAGECPP__
+static void _Optlink
+png_default_flush(png_structp png_ptr)
+#else
+static void
+png_default_flush(png_structp png_ptr)
+#endif
+{
+   FILE *io_ptr;
+   io_ptr = (FILE *)CVT_PTR((png_ptr->io_ptr));
+   if (io_ptr != NULL)
+      fflush(io_ptr);
+}
+#endif
+#endif
+
+/* This function allows the application to supply new output functions for
+   libpng if standard C streams aren't being used.
+
+   This function takes as its arguments:
+   png_ptr       - pointer to a png output data structure
+   io_ptr        - pointer to user supplied structure containing info about
+                   the output functions.  May be NULL.
+   write_data_fn - pointer to a new output function that takes as its
+                   arguments a pointer to a png_struct, a pointer to
+                   data to be written, and a 32-bit unsigned int that is
+                   the number of bytes to be written.  The new write
+                   function should call png_error(png_ptr, "Error msg")
+                   to exit and output any fatal error messages.
+   flush_data_fn - pointer to a new flush function that takes as its
+                   arguments a pointer to a png_struct.  After a call to
+                   the flush function, there should be no data in any buffers
+                   or pending transmission.  If the output method doesn't do
+                   any buffering of ouput, a function prototype must still be
+                   supplied although it doesn't have to do anything.  If
+                   PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile
+                   time, output_flush_fn will be ignored, although it must be
+                   supplied for compatibility. */
+void
+png_set_write_fn(png_structp png_ptr, png_voidp io_ptr,
+   png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)
+{
+   png_ptr->io_ptr = io_ptr;
+
+#if !defined(PNG_NO_STDIO)
+   if (write_data_fn != NULL)
+      png_ptr->write_data_fn = write_data_fn;
+   else
+      png_ptr->write_data_fn = png_default_write_data;
+#else
+   png_ptr->write_data_fn = write_data_fn;
+#endif
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+#if !defined(PNG_NO_STDIO)
+   if (output_flush_fn != NULL)
+      png_ptr->output_flush_fn = output_flush_fn;
+   else
+      png_ptr->output_flush_fn = png_default_flush;
+#else
+   png_ptr->output_flush_fn = output_flush_fn;
+#endif
+#endif /* PNG_WRITE_FLUSH_SUPPORTED */
+
+   /* It is an error to read while writing a png file */
+   if (png_ptr->read_data_fn != NULL)
+   {
+      png_ptr->read_data_fn = NULL;
+      png_warning(png_ptr,
+         "Attempted to set both read_data_fn and write_data_fn in");
+      png_warning(png_ptr,
+         "the same structure.  Resetting read_data_fn to NULL.");
+   }
+}
+
+#if defined(USE_FAR_KEYWORD)
+#if defined(_MSC_VER)
+void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check)
+{
+   void *near_ptr;
+   void FAR *far_ptr;
+   FP_OFF(near_ptr) = FP_OFF(ptr);
+   far_ptr = (void FAR *)near_ptr;
+   if(check != 0)
+      if(FP_SEG(ptr) != FP_SEG(far_ptr))
+         png_error(png_ptr,"segment lost in conversion");
+   return(near_ptr);
+}
+#  else
+void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check)
+{
+   void *near_ptr;
+   void FAR *far_ptr;
+   near_ptr = (void FAR *)ptr;
+   far_ptr = (void FAR *)near_ptr;
+   if(check != 0)
+      if(far_ptr != ptr)
+         png_error(png_ptr,"segment lost in conversion");
+   return(near_ptr);
+}
+#   endif
+#   endif
diff --git a/src/png/pngwrite.c b/src/png/pngwrite.c
new file mode 100644 (file)
index 0000000..3b533e5
--- /dev/null
@@ -0,0 +1,1004 @@
+
+/* pngwrite.c - general routines to write a PNG file
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ */
+
+/* get internal access to png.h */
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Writes all the PNG information.  This is the suggested way to use the
+ * library.  If you have a new chunk to add, make a function to write it,
+ * and put it in the correct location here.  If you want the chunk written
+ * after the image data, put it in png_write_end().  I strongly encourage
+ * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing
+ * the chunk, as that will keep the code from breaking if you want to just
+ * write a plain PNG file.  If you have long comments, I suggest writing
+ * them in png_write_end(), and compressing them.
+ */
+void
+png_write_info(png_structp png_ptr, png_infop info_ptr)
+{
+#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
+   int i;
+#endif
+
+   png_debug(1, "in png_write_info\n");
+   png_write_sig(png_ptr); /* write PNG signature */
+   /* write IHDR information. */
+   png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
+      info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
+      info_ptr->filter_type,
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+      info_ptr->interlace_type);
+#else
+      0);
+#endif
+   /* the rest of these check to see if the valid field has the appropriate
+      flag set, and if it does, writes the chunk. */
+#if defined(PNG_WRITE_gAMA_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_gAMA)
+      png_write_gAMA(png_ptr, info_ptr->gamma);
+#endif
+#if defined(PNG_WRITE_sRGB_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sRGB)
+      png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent);
+#endif
+#if defined(PNG_WRITE_sBIT_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sBIT)
+      png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
+#endif
+#if defined(PNG_WRITE_cHRM_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_cHRM)
+      png_write_cHRM(png_ptr,
+         info_ptr->x_white, info_ptr->y_white,
+         info_ptr->x_red, info_ptr->y_red,
+         info_ptr->x_green, info_ptr->y_green,
+         info_ptr->x_blue, info_ptr->y_blue);
+#endif
+   if (info_ptr->valid & PNG_INFO_PLTE)
+      png_write_PLTE(png_ptr, info_ptr->palette,
+         (png_uint_32)info_ptr->num_palette);
+   else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      png_error(png_ptr, "Valid palette required for paletted images\n");
+
+#if defined(PNG_WRITE_tRNS_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_tRNS)
+      {
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+         /* invert the alpha channel (in tRNS) */
+         if (png_ptr->transformations & PNG_INVERT_ALPHA &&
+            info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+         {
+            int j;
+            for (j=0; j<(int)info_ptr->num_trans; j++)
+               info_ptr->trans[j] = 255 - info_ptr->trans[j];
+         }
+#endif
+      png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values),
+         info_ptr->num_trans, info_ptr->color_type);
+      }
+#endif
+#if defined(PNG_WRITE_bKGD_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_bKGD)
+      png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
+#endif
+#if defined(PNG_WRITE_hIST_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_hIST)
+      png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
+#endif
+#if defined(PNG_WRITE_oFFs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_oFFs)
+      png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
+         info_ptr->offset_unit_type);
+#endif
+#if defined(PNG_WRITE_pCAL_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pCAL)
+      png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
+         info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
+         info_ptr->pcal_units, info_ptr->pcal_params);
+#endif
+#if defined(PNG_WRITE_pHYs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pHYs)
+      png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
+         info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
+#endif
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_tIME)
+   {
+      png_write_tIME(png_ptr, &(info_ptr->mod_time));
+      png_ptr->flags |= PNG_FLAG_WROTE_tIME;
+   }
+#endif
+#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
+   /* Check to see if we need to write text chunks */
+   for (i = 0; i < info_ptr->num_text; i++)
+   {
+      png_debug2(2, "Writing header text chunk %d, type %d\n", i,
+         info_ptr->text[i].compression);
+      /* If we want a compressed text chunk */
+      if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
+      {
+#if defined(PNG_WRITE_zTXt_SUPPORTED)
+         /* write compressed chunk */
+         png_write_zTXt(png_ptr, info_ptr->text[i].key,
+            info_ptr->text[i].text, info_ptr->text[i].text_length,
+            info_ptr->text[i].compression);
+#else
+         png_warning(png_ptr, "Unable to write compressed text\n");
+#endif
+         /* Mark this chunk as written */
+         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+      }
+      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
+      {
+#if defined(PNG_WRITE_tEXt_SUPPORTED)
+         /* write uncompressed chunk */
+         png_write_tEXt(png_ptr, info_ptr->text[i].key,
+            info_ptr->text[i].text, info_ptr->text[i].text_length);
+#else
+         png_warning(png_ptr, "Unable to write uncompressed text\n");
+#endif
+         /* Mark this chunk as written */
+         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+      }
+   }
+#endif
+}
+
+/* Writes the end of the PNG file.  If you don't want to write comments or
+ * time information, you can pass NULL for info.  If you already wrote these
+ * in png_write_info(), do not write them again here.  If you have long
+ * comments, I suggest writing them here, and compressing them.
+ */
+void
+png_write_end(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_write_end\n");
+   if (!(png_ptr->mode & PNG_HAVE_IDAT))
+      png_error(png_ptr, "No IDATs written into file");
+
+   /* see if user wants us to write information chunks */
+   if (info_ptr != NULL)
+   {
+#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
+      int i; /* local index variable */
+#endif
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+      /* check to see if user has supplied a time chunk */
+      if (info_ptr->valid & PNG_INFO_tIME &&
+         !(png_ptr->flags & PNG_FLAG_WROTE_tIME))
+         png_write_tIME(png_ptr, &(info_ptr->mod_time));
+#endif
+#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
+      /* loop through comment chunks */
+      for (i = 0; i < info_ptr->num_text; i++)
+      {
+         png_debug2(2, "Writing trailer text chunk %d, type %d\n", i,
+            info_ptr->text[i].compression);
+         if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
+         {
+#if defined(PNG_WRITE_zTXt_SUPPORTED)
+            /* write compressed chunk */
+            png_write_zTXt(png_ptr, info_ptr->text[i].key,
+               info_ptr->text[i].text, info_ptr->text[i].text_length,
+               info_ptr->text[i].compression);
+#else
+            png_warning(png_ptr, "Unable to write compressed text\n");
+#endif
+            /* Mark this chunk as written */
+            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+         }
+         else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
+         {
+#if defined(PNG_WRITE_tEXt_SUPPORTED)
+            /* write uncompressed chunk */
+            png_write_tEXt(png_ptr, info_ptr->text[i].key,
+               info_ptr->text[i].text, info_ptr->text[i].text_length);
+#else
+            png_warning(png_ptr, "Unable to write uncompressed text\n");
+#endif
+
+            /* Mark this chunk as written */
+            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+         }
+      }
+#endif
+   }
+
+   png_ptr->mode |= PNG_AFTER_IDAT;
+
+   /* write end of PNG file */
+   png_write_IEND(png_ptr);
+}
+
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+void
+png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime)
+{
+   png_debug(1, "in png_convert_from_struct_tm\n");
+   ptime->year = (png_uint_16)(1900 + ttime->tm_year);
+   ptime->month = (png_byte)(ttime->tm_mon + 1);
+   ptime->day = (png_byte)ttime->tm_mday;
+   ptime->hour = (png_byte)ttime->tm_hour;
+   ptime->minute = (png_byte)ttime->tm_min;
+   ptime->second = (png_byte)ttime->tm_sec;
+}
+
+void
+png_convert_from_time_t(png_timep ptime, time_t ttime)
+{
+   struct tm *tbuf;
+
+   png_debug(1, "in png_convert_from_time_t\n");
+   tbuf = gmtime(&ttime);
+   png_convert_from_struct_tm(ptime, tbuf);
+}
+#endif
+
+/* Initialize png_ptr structure, and allocate any memory needed */
+png_structp
+png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
+      warn_fn, NULL, NULL, NULL));
+}
+
+/* Alternate initialize png_ptr structure, and allocate any memory needed */
+png_structp
+png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+   png_malloc_ptr malloc_fn, png_free_ptr free_fn)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+   png_structp png_ptr;
+#ifdef USE_FAR_KEYWORD
+   jmp_buf jmpbuf;
+#endif
+   png_debug(1, "in png_create_write_struct\n");
+#ifdef PNG_USER_MEM_SUPPORTED
+   if ((png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
+      (png_malloc_ptr)malloc_fn)) == NULL)
+#else
+   if ((png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG)) == NULL)
+#endif /* PNG_USER_MEM_SUPPORTED */
+   {
+      return ((png_structp)NULL);
+   }
+#ifdef USE_FAR_KEYWORD
+   if (setjmp(jmpbuf))
+#else
+   if (setjmp(png_ptr->jmpbuf))
+#endif
+   {
+      png_free(png_ptr, png_ptr->zbuf);
+      png_destroy_struct(png_ptr);
+      return ((png_structp)NULL);
+   }
+#ifdef USE_FAR_KEYWORD
+   png_memcpy(png_ptr->jmpbuf,jmpbuf,sizeof(jmp_buf));
+#endif
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
+#endif /* PNG_USER_MEM_SUPPORTED */
+   png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
+
+   /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
+    * we must recompile any applications that use any older library version.
+    * For versions after libpng 1.0, we will be compatible, so we need
+    * only check the first digit.
+    */
+   if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
+       (png_libpng_ver[0] == '0' && user_png_ver[2] < '9'))
+   {
+      png_error(png_ptr,
+         "Incompatible libpng version in application and library");
+   }
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
+      (png_uint_32)png_ptr->zbuf_size);
+
+   png_set_write_fn(png_ptr, NULL, NULL, NULL);
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
+      1, NULL, NULL);
+#endif
+
+   return ((png_structp)png_ptr);
+}
+
+/* Initialize png_ptr structure, and allocate any memory needed */
+void
+png_write_init(png_structp png_ptr)
+{
+   jmp_buf tmp_jmp; /* to save current jump buffer */
+
+   png_debug(1, "in png_write_init\n");
+   /* save jump buffer and error functions */
+   png_memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
+
+   /* reset all variables to 0 */
+   png_memset(png_ptr, 0, sizeof (png_struct));
+
+   /* restore jump buffer */
+   png_memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
+      (png_uint_32)png_ptr->zbuf_size);
+   png_set_write_fn(png_ptr, NULL, NULL, NULL);
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
+      1, NULL, NULL);
+#endif
+}
+
+/* Write a few rows of image data.  If the image is interlaced,
+ * either you will have to write the 7 sub images, or, if you
+ * have called png_set_interlace_handling(), you will have to
+ * "write" the image seven times.
+ */
+void
+png_write_rows(png_structp png_ptr, png_bytepp row,
+   png_uint_32 num_rows)
+{
+   png_uint_32 i; /* row counter */
+   png_bytepp rp; /* row pointer */
+
+   png_debug(1, "in png_write_rows\n");
+   /* loop through the rows */
+   for (i = 0, rp = row; i < num_rows; i++, rp++)
+   {
+      png_write_row(png_ptr, *rp);
+   }
+}
+
+/* Write the image.  You only need to call this function once, even
+ * if you are writing an interlaced image.
+ */
+void
+png_write_image(png_structp png_ptr, png_bytepp image)
+{
+   png_uint_32 i; /* row index */
+   int pass, num_pass; /* pass variables */
+   png_bytepp rp; /* points to current row */
+
+   png_debug(1, "in png_write_image\n");
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+   /* intialize interlace handling.  If image is not interlaced,
+      this will set pass to 1 */
+   num_pass = png_set_interlace_handling(png_ptr);
+#else
+   num_pass = 1;
+#endif
+   /* loop through passes */
+   for (pass = 0; pass < num_pass; pass++)
+   {
+      /* loop through image */
+      for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
+      {
+         png_write_row(png_ptr, *rp);
+      }
+   }
+}
+
+/* called by user to write a row of image data */
+void
+png_write_row(png_structp png_ptr, png_bytep row)
+{
+   png_debug2(1, "in png_write_row (row %ld, pass %d)\n",
+      png_ptr->row_number, png_ptr->pass);
+   /* initialize transformations and other stuff if first time */
+   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
+   {
+   /* check for transforms that have been set but were defined out */
+#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
+   if (png_ptr->transformations & PNG_FILLER)
+      png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && defined(PNG_READ_PACKSWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACKSWAP)
+      png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACK)
+      png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
+   if (png_ptr->transformations & PNG_BGR)
+      png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined.");
+#endif
+
+      png_write_start_row(png_ptr);
+   }
+
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+   /* if interlaced and not interested in row, return */
+   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+   {
+      switch (png_ptr->pass)
+      {
+         case 0:
+            if (png_ptr->row_number & 7)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 1:
+            if ((png_ptr->row_number & 7) || png_ptr->width < 5)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 2:
+            if ((png_ptr->row_number & 7) != 4)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 3:
+            if ((png_ptr->row_number & 3) || png_ptr->width < 3)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 4:
+            if ((png_ptr->row_number & 3) != 2)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 5:
+            if ((png_ptr->row_number & 1) || png_ptr->width < 2)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 6:
+            if (!(png_ptr->row_number & 1))
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+      }
+   }
+#endif
+
+   /* set up row info for transformations */
+   png_ptr->row_info.color_type = png_ptr->color_type;
+   png_ptr->row_info.width = png_ptr->usr_width;
+   png_ptr->row_info.channels = png_ptr->usr_channels;
+   png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth;
+   png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth *
+      png_ptr->row_info.channels);
+
+   png_ptr->row_info.rowbytes = ((png_ptr->row_info.width *
+      (png_uint_32)png_ptr->row_info.pixel_depth + 7) >> 3);
+
+   png_debug1(3, "row_info->color_type = %d\n", png_ptr->row_info.color_type);
+   png_debug1(3, "row_info->width = %d\n", png_ptr->row_info.width);
+   png_debug1(3, "row_info->channels = %d\n", png_ptr->row_info.channels);
+   png_debug1(3, "row_info->bit_depth = %d\n", png_ptr->row_info.bit_depth);
+   png_debug1(3, "row_info->pixel_depth = %d\n", png_ptr->row_info.pixel_depth);
+   png_debug1(3, "row_info->rowbytes = %d\n", png_ptr->row_info.rowbytes);
+
+   /* Copy user's row into buffer, leaving room for filter byte. */
+   png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row,
+      png_ptr->row_info.rowbytes);
+
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+   /* handle interlacing */
+   if (png_ptr->interlaced && png_ptr->pass < 6 &&
+      (png_ptr->transformations & PNG_INTERLACE))
+   {
+      png_do_write_interlace(&(png_ptr->row_info),
+         png_ptr->row_buf + 1, png_ptr->pass);
+      /* this should always get caught above, but still ... */
+      if (!(png_ptr->row_info.width))
+      {
+         png_write_finish_row(png_ptr);
+         return;
+      }
+   }
+#endif
+
+   /* handle other transformations */
+   if (png_ptr->transformations)
+      png_do_write_transformations(png_ptr);
+
+   /* Find a filter if necessary, filter the row and write it out. */
+   png_write_find_filter(png_ptr, &(png_ptr->row_info));
+
+   if (png_ptr->write_row_fn != NULL)
+      (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
+}
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+/* Set the automatic flush interval or 0 to turn flushing off */
+void
+png_set_flush(png_structp png_ptr, int nrows)
+{
+   png_debug(1, "in png_set_flush\n");
+   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
+}
+
+/* flush the current output buffers now */
+void
+png_write_flush(png_structp png_ptr)
+{
+   int wrote_IDAT;
+
+   png_debug(1, "in png_write_flush\n");
+   /* We have already written out all of the data */
+   if (png_ptr->row_number >= png_ptr->num_rows)
+     return;
+
+   do
+   {
+      int ret;
+
+      /* compress the data */
+      ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
+      wrote_IDAT = 0;
+
+      /* check for compression errors */
+      if (ret != Z_OK)
+      {
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+
+      if (!(png_ptr->zstream.avail_out))
+      {
+         /* write the IDAT and reset the zlib output buffer */
+         png_write_IDAT(png_ptr, png_ptr->zbuf,
+                        png_ptr->zbuf_size);
+         png_ptr->zstream.next_out = png_ptr->zbuf;
+         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+         wrote_IDAT = 1;
+      }
+   } while(wrote_IDAT == 1);
+
+   /* If there is any data left to be output, write it into a new IDAT */
+   if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
+   {
+      /* write the IDAT and reset the zlib output buffer */
+      png_write_IDAT(png_ptr, png_ptr->zbuf,
+                     png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+      png_ptr->zstream.next_out = png_ptr->zbuf;
+      png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+   }
+   png_ptr->flush_rows = 0;
+   png_flush(png_ptr);
+}
+#endif /* PNG_WRITE_FLUSH_SUPPORTED */
+
+/* free all memory used by the write */
+void
+png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
+{
+   png_structp png_ptr = NULL;
+   png_infop info_ptr = NULL;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_free_ptr free_fn = NULL;
+#endif
+
+   png_debug(1, "in png_destroy_write_struct\n");
+   if (png_ptr_ptr != NULL)
+   {
+      png_ptr = *png_ptr_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+      free_fn = png_ptr->free_fn;
+#endif
+   }
+
+   if (info_ptr_ptr != NULL)
+      info_ptr = *info_ptr_ptr;
+
+   if (info_ptr != NULL)
+   {
+#ifdef PNG_WRITE_tEXt_SUPPORTED
+      png_free(png_ptr, info_ptr->text);
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      png_free(png_ptr, info_ptr->pcal_purpose);
+      png_free(png_ptr, info_ptr->pcal_units);
+      if (info_ptr->pcal_params != NULL)
+      {
+         int i;
+         for (i = 0; i < (int)info_ptr->pcal_nparams; i++)
+         {
+            png_free(png_ptr, info_ptr->pcal_params[i]);
+         }
+         png_free(png_ptr, info_ptr->pcal_params);
+      }
+#endif
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)info_ptr, free_fn);
+#else
+      png_destroy_struct((png_voidp)info_ptr);
+#endif
+      *info_ptr_ptr = (png_infop)NULL;
+   }
+
+   if (png_ptr != NULL)
+   {
+      png_write_destroy(png_ptr);
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)png_ptr, free_fn);
+#else
+      png_destroy_struct((png_voidp)png_ptr);
+#endif
+      *png_ptr_ptr = (png_structp)NULL;
+   }
+}
+
+
+/* Free any memory used in png_ptr struct (old method) */
+void
+png_write_destroy(png_structp png_ptr)
+{
+   jmp_buf tmp_jmp; /* save jump buffer */
+   png_error_ptr error_fn;
+   png_error_ptr warning_fn;
+   png_voidp error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_free_ptr free_fn;
+#endif
+
+   png_debug(1, "in png_write_destroy\n");
+   /* free any memory zlib uses */
+   deflateEnd(&png_ptr->zstream);
+
+   /* free our memory.  png_free checks NULL for us. */
+   png_free(png_ptr, png_ptr->zbuf);
+   png_free(png_ptr, png_ptr->row_buf);
+   png_free(png_ptr, png_ptr->prev_row);
+   png_free(png_ptr, png_ptr->sub_row);
+   png_free(png_ptr, png_ptr->up_row);
+   png_free(png_ptr, png_ptr->avg_row);
+   png_free(png_ptr, png_ptr->paeth_row);
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+   png_free(png_ptr, png_ptr->time_buffer);
+#endif /* PNG_TIME_RFC1123_SUPPORTED */
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   png_free(png_ptr, png_ptr->prev_filters);
+   png_free(png_ptr, png_ptr->filter_weights);
+   png_free(png_ptr, png_ptr->inv_filter_weights);
+   png_free(png_ptr, png_ptr->filter_costs);
+   png_free(png_ptr, png_ptr->inv_filter_costs);
+#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
+
+   /* reset structure */
+   png_memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
+
+   error_fn = png_ptr->error_fn;
+   warning_fn = png_ptr->warning_fn;
+   error_ptr = png_ptr->error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   free_fn = png_ptr->free_fn;
+#endif
+
+   png_memset(png_ptr, 0, sizeof (png_struct));
+
+   png_ptr->error_fn = error_fn;
+   png_ptr->warning_fn = warning_fn;
+   png_ptr->error_ptr = error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_ptr->free_fn = free_fn;
+#endif
+
+   png_memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
+}
+
+/* Allow the application to select one or more row filters to use. */
+void
+png_set_filter(png_structp png_ptr, int method, int filters)
+{
+   png_debug(1, "in png_set_filter\n");
+   /* We allow 'method' only for future expansion of the base filter method. */
+   if (method == PNG_FILTER_TYPE_BASE)
+   {
+      switch (filters & (PNG_ALL_FILTERS | 0x07))
+      {
+         case 5:
+         case 6:
+         case 7: png_warning(png_ptr, "Unknown row filter for method 0");
+         case PNG_FILTER_VALUE_NONE:  png_ptr->do_filter=PNG_FILTER_NONE; break;
+         case PNG_FILTER_VALUE_SUB:   png_ptr->do_filter=PNG_FILTER_SUB;  break;
+         case PNG_FILTER_VALUE_UP:    png_ptr->do_filter=PNG_FILTER_UP;   break;
+         case PNG_FILTER_VALUE_AVG:   png_ptr->do_filter=PNG_FILTER_AVG;  break;
+         case PNG_FILTER_VALUE_PAETH: png_ptr->do_filter=PNG_FILTER_PAETH;break;
+         default: png_ptr->do_filter = (png_byte)filters; break;
+      }
+
+      /* If we have allocated the row_buf, this means we have already started
+       * with the image and we should have allocated all of the filter buffers
+       * that have been selected.  If prev_row isn't already allocated, then
+       * it is too late to start using the filters that need it, since we
+       * will be missing the data in the previous row.  If an application
+       * wants to start and stop using particular filters during compression,
+       * it should start out with all of the filters, and then add and
+       * remove them after the start of compression.
+       */
+      if (png_ptr->row_buf != NULL)
+      {
+         if (png_ptr->do_filter & PNG_FILTER_SUB && png_ptr->sub_row == NULL)
+         {
+            png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
+              (png_ptr->rowbytes + 1));
+            png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
+         }
+
+         if (png_ptr->do_filter & PNG_FILTER_UP && png_ptr->up_row == NULL)
+         {
+            if (png_ptr->prev_row == NULL)
+            {
+               png_warning(png_ptr, "Can't add Up filter after starting");
+               png_ptr->do_filter &= ~PNG_FILTER_UP;
+            }
+            else
+            {
+               png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
+                  (png_ptr->rowbytes + 1));
+               png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
+            }
+         }
+
+         if (png_ptr->do_filter & PNG_FILTER_AVG && png_ptr->avg_row == NULL)
+         {
+            if (png_ptr->prev_row == NULL)
+            {
+               png_warning(png_ptr, "Can't add Average filter after starting");
+               png_ptr->do_filter &= ~PNG_FILTER_AVG;
+            }
+            else
+            {
+               png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
+                  (png_ptr->rowbytes + 1));
+               png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
+            }
+         }
+
+         if (png_ptr->do_filter & PNG_FILTER_PAETH &&
+             png_ptr->paeth_row == NULL)
+         {
+            if (png_ptr->prev_row == NULL)
+            {
+               png_warning(png_ptr, "Can't add Paeth filter after starting");
+               png_ptr->do_filter &= ~PNG_FILTER_PAETH;
+            }
+            else
+            {
+               png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
+                  (png_ptr->rowbytes + 1));
+               png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
+            }
+         }
+
+         if (png_ptr->do_filter == PNG_NO_FILTERS)
+            png_ptr->do_filter = PNG_FILTER_NONE;
+      }
+   }
+   else
+      png_error(png_ptr, "Unknown custom filter method");
+}
+
+/* This allows us to influence the way in which libpng chooses the "best"
+ * filter for the current scanline.  While the "minimum-sum-of-absolute-
+ * differences metric is relatively fast and effective, there is some
+ * question as to whether it can be improved upon by trying to keep the
+ * filtered data going to zlib more consistent, hopefully resulting in
+ * better compression.
+ */
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)      /* GRR 970116 */
+void
+png_set_filter_heuristics(png_structp png_ptr, int heuristic_method,
+   int num_weights, png_doublep filter_weights,
+   png_doublep filter_costs)
+{
+   int i;
+
+   png_debug(1, "in png_set_filter_heuristics\n");
+   if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST)
+   {
+      png_warning(png_ptr, "Unknown filter heuristic method");
+      return;
+   }
+
+   if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT)
+   {
+      heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;
+   }
+
+   if (num_weights < 0 || filter_weights == NULL ||
+      heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)
+   {
+      num_weights = 0;
+   }
+
+   png_ptr->num_prev_filters = num_weights;
+   png_ptr->heuristic_method = heuristic_method;
+
+   if (num_weights > 0)
+   {
+      if (png_ptr->prev_filters == NULL)
+      {
+         png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)(sizeof(png_byte) * num_weights));
+
+         /* To make sure that the weighting starts out fairly */
+         for (i = 0; i < num_weights; i++)
+         {
+            png_ptr->prev_filters[i] = 255;
+         }
+      }
+
+      if (png_ptr->filter_weights == NULL)
+      {
+         png_ptr->filter_weights = (png_uint_16p) png_malloc(png_ptr,
+            (png_uint_32)(sizeof(png_uint_16) * num_weights));
+
+         png_ptr->inv_filter_weights = (png_uint_16p) png_malloc(png_ptr,
+            (png_uint_32)(sizeof(png_uint_16) * num_weights));
+
+         for (i = 0; i < num_weights; i++)
+         {
+            png_ptr->inv_filter_weights[i] =
+            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
+         }
+      }
+
+      for (i = 0; i < num_weights; i++)
+      {
+         if (filter_weights[i] < 0.0)
+         {
+            png_ptr->inv_filter_weights[i] =
+            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
+         }
+         else
+         {
+            png_ptr->inv_filter_weights[i] =
+               (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5);
+            png_ptr->filter_weights[i] =
+               (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5);
+         }
+      }
+   }
+
+   /* If, in the future, there are other filter methods, this would
+    * need to be based on png_ptr->filter.
+    */
+   if (png_ptr->filter_costs == NULL)
+   {
+      png_ptr->filter_costs = (png_uint_16p) png_malloc(png_ptr,
+         (png_uint_32)(sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
+
+      png_ptr->inv_filter_costs = (png_uint_16p) png_malloc(png_ptr,
+         (png_uint_32)(sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
+
+      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
+      {
+         png_ptr->inv_filter_costs[i] =
+         png_ptr->filter_costs[i] = PNG_COST_FACTOR;
+      }
+   }
+
+   /* Here is where we set the relative costs of the different filters.  We
+    * should take the desired compression level into account when setting
+    * the costs, so that Paeth, for instance, has a high relative cost at low
+    * compression levels, while it has a lower relative cost at higher
+    * compression settings.  The filter types are in order of increasing
+    * relative cost, so it would be possible to do this with an algorithm.
+    */
+   for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
+   {
+      if (filter_costs == NULL || filter_costs[i] < 0.0)
+      {
+         png_ptr->inv_filter_costs[i] =
+         png_ptr->filter_costs[i] = PNG_COST_FACTOR;
+      }
+      else if (filter_costs[i] >= 1.0)
+      {
+         png_ptr->inv_filter_costs[i] =
+            (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5);
+         png_ptr->filter_costs[i] =
+            (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5);
+      }
+   }
+}
+#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
+
+void
+png_set_compression_level(png_structp png_ptr, int level)
+{
+   png_debug(1, "in png_set_compression_level\n");
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
+   png_ptr->zlib_level = level;
+}
+
+void
+png_set_compression_mem_level(png_structp png_ptr, int mem_level)
+{
+   png_debug(1, "in png_set_compression_mem_level\n");
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
+   png_ptr->zlib_mem_level = mem_level;
+}
+
+void
+png_set_compression_strategy(png_structp png_ptr, int strategy)
+{
+   png_debug(1, "in png_set_compression_strategy\n");
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
+   png_ptr->zlib_strategy = strategy;
+}
+
+void
+png_set_compression_window_bits(png_structp png_ptr, int window_bits)
+{
+   if (window_bits > 15)
+      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
+   png_ptr->zlib_window_bits = window_bits;
+}
+
+void
+png_set_compression_method(png_structp png_ptr, int method)
+{
+   png_debug(1, "in png_set_compression_method\n");
+   if (method != 8)
+      png_warning(png_ptr, "Only compression method 8 is supported by PNG");
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
+   png_ptr->zlib_method = method;
+}
+
+void
+png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn)
+{
+   png_ptr->write_row_fn = write_row_fn;
+}
+
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+void
+png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
+   write_user_transform_fn)
+{
+   png_debug(1, "in png_set_write_user_transform_fn\n");
+   png_ptr->transformations |= PNG_USER_TRANSFORM;
+   png_ptr->write_user_transform_fn = write_user_transform_fn;
+}
+#endif
+
diff --git a/src/png/pngwtran.c b/src/png/pngwtran.c
new file mode 100644 (file)
index 0000000..76cf3a3
--- /dev/null
@@ -0,0 +1,497 @@
+
+/* pngwtran.c - transforms the data in a row for PNG writers
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Transform the data according to the user's wishes.  The order of
+ * transformations is significant.
+ */
+void
+png_do_write_transformations(png_structp png_ptr)
+{
+   png_debug(1, "in png_do_write_transformations\n");
+
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+   if (png_ptr->transformations & PNG_USER_TRANSFORM)
+      if(png_ptr->write_user_transform_fn != NULL)
+        (*(png_ptr->write_user_transform_fn)) /* user write transform function */
+          (png_ptr,                    /* png_ptr */
+           &(png_ptr->row_info),       /* row_info:     */
+             /*  png_uint_32 width;          width of row */
+             /*  png_uint_32 rowbytes;       number of bytes in row */
+             /*  png_byte color_type;        color type of pixels */
+             /*  png_byte bit_depth;         bit depth of samples */
+             /*  png_byte channels;          number of channels (1-4) */
+             /*  png_byte pixel_depth;       bits per pixel (depth*channels) */
+           png_ptr->row_buf + 1);      /* start of pixel data for row */
+#endif
+#if defined(PNG_WRITE_FILLER_SUPPORTED)
+   if (png_ptr->transformations & PNG_FILLER)
+      png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->flags);
+#endif
+#if defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACKSWAP)
+      png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_PACK_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACK)
+      png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         (png_uint_32)png_ptr->bit_depth);
+#endif
+#if defined(PNG_WRITE_SWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_SHIFT_SUPPORTED)
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         &(png_ptr->shift));
+#endif
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_ALPHA)
+      png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_ALPHA)
+      png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_BGR_SUPPORTED)
+   if (png_ptr->transformations & PNG_BGR)
+      png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_INVERT_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+}
+
+#if defined(PNG_WRITE_PACK_SUPPORTED)
+/* Pack pixels into bytes.  Pass the true bit depth in bit_depth.  The
+ * row_info bit depth should be 8 (one pixel per byte).  The channels
+ * should be 1 (this only happens on grayscale and paletted images).
+ */
+void
+png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth)
+{
+   png_debug(1, "in png_do_pack\n");
+   if (row_info->bit_depth == 8 &&
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+      row_info->channels == 1)
+   {
+      switch ((int)bit_depth)
+      {
+         case 1:
+         {
+            png_bytep sp, dp;
+            int mask, v;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            sp = row;
+            dp = row;
+            mask = 0x80;
+            v = 0;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (*sp != 0)
+                  v |= mask;
+               sp++;
+               if (mask > 1)
+                  mask >>= 1;
+               else
+               {
+                  mask = 0x80;
+                  *dp = (png_byte)v;
+                  dp++;
+                  v = 0;
+               }
+            }
+            if (mask != 0x80)
+               *dp = (png_byte)v;
+            break;
+         }
+         case 2:
+         {
+            png_bytep sp, dp;
+            int shift, v;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            sp = row;
+            dp = row;
+            shift = 6;
+            v = 0;
+            for (i = 0; i < row_width; i++)
+            {
+               png_byte value;
+
+               value = (png_byte)(*sp & 0x3);
+               v |= (value << shift);
+               if (shift == 0)
+               {
+                  shift = 6;
+                  *dp = (png_byte)v;
+                  dp++;
+                  v = 0;
+               }
+               else
+                  shift -= 2;
+               sp++;
+            }
+            if (shift != 6)
+               *dp = (png_byte)v;
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp, dp;
+            int shift, v;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            sp = row;
+            dp = row;
+            shift = 4;
+            v = 0;
+            for (i = 0; i < row_width; i++)
+            {
+               png_byte value;
+
+               value = (png_byte)(*sp & 0xf);
+               v |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 4;
+                  *dp = (png_byte)v;
+                  dp++;
+                  v = 0;
+               }
+               else
+                  shift -= 4;
+
+               sp++;
+            }
+            if (shift != 4)
+               *dp = (png_byte)v;
+            break;
+         }
+      }
+      row_info->bit_depth = (png_byte)bit_depth;
+      row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels);
+      row_info->rowbytes =
+         ((row_info->width * row_info->pixel_depth + 7) >> 3);
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_SHIFT_SUPPORTED)
+/* Shift pixel values to take advantage of whole range.  Pass the
+ * true number of bits in bit_depth.  The row should be packed
+ * according to row_info->bit_depth.  Thus, if you had a row of
+ * bit depth 4, but the pixels only had values from 0 to 7, you
+ * would pass 3 as bit_depth, and this routine would translate the
+ * data to 0 to 15.
+ */
+void
+png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth)
+{
+   png_debug(1, "in png_do_shift\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL &&
+#else
+   if (
+#endif
+      row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      int shift_start[4], shift_dec[4];
+      int channels = 0;
+
+      if (row_info->color_type & PNG_COLOR_MASK_COLOR)
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->red;
+         shift_dec[channels] = bit_depth->red;
+         channels++;
+         shift_start[channels] = row_info->bit_depth - bit_depth->green;
+         shift_dec[channels] = bit_depth->green;
+         channels++;
+         shift_start[channels] = row_info->bit_depth - bit_depth->blue;
+         shift_dec[channels] = bit_depth->blue;
+         channels++;
+      }
+      else
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->gray;
+         shift_dec[channels] = bit_depth->gray;
+         channels++;
+      }
+      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->alpha;
+         shift_dec[channels] = bit_depth->alpha;
+         channels++;
+      }
+
+      /* with low row depths, could only be grayscale, so one channel */
+      if (row_info->bit_depth < 8)
+      {
+         png_bytep bp = row;
+         png_uint_32 i;
+         png_byte mask;
+         png_uint_32 row_bytes = row_info->rowbytes;
+
+         if (bit_depth->gray == 1 && row_info->bit_depth == 2)
+            mask = 0x55;
+         else if (row_info->bit_depth == 4 && bit_depth->gray == 3)
+            mask = 0x11;
+         else
+            mask = 0xff;
+
+         for (i = 0; i < row_bytes; i++, bp++)
+         {
+            png_uint_16 v;
+            int j;
+
+            v = *bp;
+            *bp = 0;
+            for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
+            {
+               if (j > 0)
+                  *bp |= (png_byte)((v << j) & 0xff);
+               else
+                  *bp |= (png_byte)((v >> (-j)) & mask);
+            }
+         }
+      }
+      else if (row_info->bit_depth == 8)
+      {
+         png_bytep bp = row;
+         png_uint_32 i;
+         png_uint_32 istop = channels * row_info->width;
+
+         for (i = 0; i < istop; i++, bp++)
+         {
+
+            png_uint_16 v;
+            int j;
+            int c = (int)(i%channels);
+
+            v = *bp;
+            *bp = 0;
+            for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+            {
+               if (j > 0)
+                  *bp |= (png_byte)((v << j) & 0xff);
+               else
+                  *bp |= (png_byte)((v >> (-j)) & 0xff);
+            }
+         }
+      }
+      else
+      {
+         png_bytep bp;
+         png_uint_32 i;
+         png_uint_32 istop = channels * row_info->width;
+
+         for (bp = row, i = 0; i < istop; i++)
+         {
+            int c = (int)(i%channels);
+            png_uint_16 value, v;
+            int j;
+
+            v = ((png_uint_16)(*bp) << 8) + *(bp + 1);
+            value = 0;
+            for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+            {
+               if (j > 0)
+                  value |= (png_uint_16)((v << j) & (png_uint_16)0xffff);
+               else
+                  value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff);
+            }
+            *bp++ = (png_byte)(value >> 8);
+            *bp++ = (png_byte)(value & 0xff);
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+void
+png_do_write_swap_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_write_swap_alpha\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This converts from ARGB to RGBA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save;
+            }
+         }
+         /* This converts from AARRGGBB to RRGGBBAA */
+         else
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save[2];
+               save[0] = *(sp++);
+               save[1] = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save[0];
+               *(dp++) = save[1];
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This converts from AG to GA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save;
+            }
+         }
+         /* This converts from AAGG to GGAA */
+         else
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save[2];
+               save[0] = *(sp++);
+               save[1] = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save[0];
+               *(dp++) = save[1];
+            }
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+void
+png_do_write_invert_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_write_invert_alpha\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This inverts the alpha channel in RGBA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = 255 - *(sp++);
+            }
+         }
+         /* This inverts the alpha channel in RRGGBBAA */
+         else
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = 255 - *(sp++);
+               *(dp++) = 255 - *(sp++);
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This inverts the alpha channel in GA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               *(dp++) = *(sp++);
+               *(dp++) = 255 - *(sp++);
+            }
+         }
+         /* This inverts the alpha channel in GGAA */
+         else
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = 255 - *(sp++);
+               *(dp++) = 255 - *(sp++);
+            }
+         }
+      }
+   }
+}
+#endif
diff --git a/src/png/pngwutil.c b/src/png/pngwutil.c
new file mode 100644 (file)
index 0000000..4736361
--- /dev/null
@@ -0,0 +1,2063 @@
+
+/* pngwutil.c - utilities to write a PNG file
+ *
+ * libpng 1.0.3 - January 14, 1999
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Place a 32-bit number into a buffer in PNG byte order.  We work
+ * with unsigned numbers for convenience, although one supported
+ * ancillary chunk uses signed (two's complement) numbers.
+ */
+void
+png_save_uint_32(png_bytep buf, png_uint_32 i)
+{
+   buf[0] = (png_byte)((i >> 24) & 0xff);
+   buf[1] = (png_byte)((i >> 16) & 0xff);
+   buf[2] = (png_byte)((i >> 8) & 0xff);
+   buf[3] = (png_byte)(i & 0xff);
+}
+
+#if defined(PNG_WRITE_pCAL_SUPPORTED)
+/* The png_save_int_32 function assumes integers are stored in two's
+ * complement format.  If this isn't the case, then this routine needs to
+ * be modified to write data in two's complement format.
+ */
+void
+png_save_int_32(png_bytep buf, png_int_32 i)
+{
+   buf[0] = (png_byte)((i >> 24) & 0xff);
+   buf[1] = (png_byte)((i >> 16) & 0xff);
+   buf[2] = (png_byte)((i >> 8) & 0xff);
+   buf[3] = (png_byte)(i & 0xff);
+}
+#endif
+
+/* Place a 16-bit number into a buffer in PNG byte order.
+ * The parameter is declared unsigned int, not png_uint_16,
+ * just to avoid potential problems on pre-ANSI C compilers.
+ */
+void
+png_save_uint_16(png_bytep buf, unsigned int i)
+{
+   buf[0] = (png_byte)((i >> 8) & 0xff);
+   buf[1] = (png_byte)(i & 0xff);
+}
+
+/* Write a PNG chunk all at once.  The type is an array of ASCII characters
+ * representing the chunk name.  The array must be at least 4 bytes in
+ * length, and does not need to be null terminated.  To be safe, pass the
+ * pre-defined chunk names here, and if you need a new one, define it
+ * where the others are defined.  The length is the length of the data.
+ * All the data must be present.  If that is not possible, use the
+ * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end()
+ * functions instead.
+ */
+void
+png_write_chunk(png_structp png_ptr, png_bytep chunk_name,
+   png_bytep data, png_size_t length)
+{
+   png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length);
+   png_write_chunk_data(png_ptr, data, length);
+   png_write_chunk_end(png_ptr);
+}
+
+/* Write the start of a PNG chunk.  The type is the chunk type.
+ * The total_length is the sum of the lengths of all the data you will be
+ * passing in png_write_chunk_data().
+ */
+void
+png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name,
+   png_uint_32 length)
+{
+   png_byte buf[4];
+   png_debug2(0, "Writing %s chunk (%d bytes)\n", chunk_name, length);
+
+   /* write the length */
+   png_save_uint_32(buf, length);
+   png_write_data(png_ptr, buf, (png_size_t)4);
+
+   /* write the chunk name */
+   png_write_data(png_ptr, chunk_name, (png_size_t)4);
+   /* reset the crc and run it over the chunk name */
+   png_reset_crc(png_ptr);
+   png_calculate_crc(png_ptr, chunk_name, (png_size_t)4);
+}
+
+/* Write the data of a PNG chunk started with png_write_chunk_start().
+ * Note that multiple calls to this function are allowed, and that the
+ * sum of the lengths from these calls *must* add up to the total_length
+ * given to png_write_chunk_start().
+ */
+void
+png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   /* write the data, and run the CRC over it */
+   if (data != NULL && length > 0)
+   {
+      png_calculate_crc(png_ptr, data, length);
+      png_write_data(png_ptr, data, length);
+   }
+}
+
+/* Finish a chunk started with png_write_chunk_start(). */
+void
+png_write_chunk_end(png_structp png_ptr)
+{
+   png_byte buf[4];
+
+   /* write the crc */
+   png_save_uint_32(buf, png_ptr->crc);
+
+   png_write_data(png_ptr, buf, (png_size_t)4);
+}
+
+/* Simple function to write the signature.  If we have already written
+ * the magic bytes of the signature, or more likely, the PNG stream is
+ * being embedded into another stream and doesn't need its own signature,
+ * we should call png_set_sig_bytes() to tell libpng how many of the
+ * bytes have already been written.
+ */
+void
+png_write_sig(png_structp png_ptr)
+{
+   /* write the rest of the 8 byte signature */
+   png_write_data(png_ptr, &png_sig[png_ptr->sig_bytes],
+      (png_size_t)8 - png_ptr->sig_bytes);
+}
+
+/* Write the IHDR chunk, and update the png_struct with the necessary
+ * information.  Note that the rest of this code depends upon this
+ * information being correct.
+ */
+void
+png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height,
+   int bit_depth, int color_type, int compression_type, int filter_type,
+   int interlace_type)
+{
+   png_byte buf[13]; /* buffer to store the IHDR info */
+
+   png_debug(1, "in png_write_IHDR\n");
+   /* Check that we have valid input data from the application info */
+   switch (color_type)
+   {
+      case PNG_COLOR_TYPE_GRAY:
+         switch (bit_depth)
+         {
+            case 1:
+            case 2:
+            case 4:
+            case 8:
+            case 16: png_ptr->channels = 1; break;
+            default: png_error(png_ptr,"Invalid bit depth for grayscale image");
+         }
+         break;
+      case PNG_COLOR_TYPE_RGB:
+         if (bit_depth != 8 && bit_depth != 16)
+            png_error(png_ptr, "Invalid bit depth for RGB image");
+         png_ptr->channels = 3;
+         break;
+      case PNG_COLOR_TYPE_PALETTE:
+         switch (bit_depth)
+         {
+            case 1:
+            case 2:
+            case 4:
+            case 8: png_ptr->channels = 1; break;
+            default: png_error(png_ptr, "Invalid bit depth for paletted image");
+         }
+         break;
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+         if (bit_depth != 8 && bit_depth != 16)
+            png_error(png_ptr, "Invalid bit depth for grayscale+alpha image");
+         png_ptr->channels = 2;
+         break;
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+         if (bit_depth != 8 && bit_depth != 16)
+            png_error(png_ptr, "Invalid bit depth for RGBA image");
+         png_ptr->channels = 4;
+         break;
+      default:
+         png_error(png_ptr, "Invalid image color type specified");
+   }
+
+   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+   {
+      png_warning(png_ptr, "Invalid compression type specified");
+      compression_type = PNG_COMPRESSION_TYPE_BASE;
+   }
+
+   if (filter_type != PNG_FILTER_TYPE_BASE)
+   {
+      png_warning(png_ptr, "Invalid filter type specified");
+      filter_type = PNG_FILTER_TYPE_BASE;
+   }
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   if (interlace_type != PNG_INTERLACE_NONE &&
+      interlace_type != PNG_INTERLACE_ADAM7)
+   {
+      png_warning(png_ptr, "Invalid interlace type specified");
+      interlace_type = PNG_INTERLACE_ADAM7;
+   }
+#else
+   interlace_type=PNG_INTERLACE_NONE;
+#endif
+
+   /* save off the relevent information */
+   png_ptr->bit_depth = (png_byte)bit_depth;
+   png_ptr->color_type = (png_byte)color_type;
+   png_ptr->interlaced = (png_byte)interlace_type;
+   png_ptr->width = width;
+   png_ptr->height = height;
+
+   png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels);
+   png_ptr->rowbytes = ((width * (png_size_t)png_ptr->pixel_depth + 7) >> 3);
+   /* set the usr info, so any transformations can modify it */
+   png_ptr->usr_width = png_ptr->width;
+   png_ptr->usr_bit_depth = png_ptr->bit_depth;
+   png_ptr->usr_channels = png_ptr->channels;
+
+   /* pack the header information into the buffer */
+   png_save_uint_32(buf, width);
+   png_save_uint_32(buf + 4, height);
+   buf[8] = (png_byte)bit_depth;
+   buf[9] = (png_byte)color_type;
+   buf[10] = (png_byte)compression_type;
+   buf[11] = (png_byte)filter_type;
+   buf[12] = (png_byte)interlace_type;
+
+   /* write the chunk */
+   png_write_chunk(png_ptr, png_IHDR, buf, (png_size_t)13);
+
+   /* initialize zlib with PNG info */
+   png_ptr->zstream.zalloc = png_zalloc;
+   png_ptr->zstream.zfree = png_zfree;
+   png_ptr->zstream.opaque = (voidpf)png_ptr;
+   if (!(png_ptr->do_filter))
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
+         png_ptr->bit_depth < 8)
+         png_ptr->do_filter = PNG_FILTER_NONE;
+      else
+         png_ptr->do_filter = PNG_ALL_FILTERS;
+   }
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY))
+   {
+      if (png_ptr->do_filter != PNG_FILTER_NONE)
+         png_ptr->zlib_strategy = Z_FILTERED;
+      else
+         png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY;
+   }
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL))
+      png_ptr->zlib_level = Z_DEFAULT_COMPRESSION;
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL))
+      png_ptr->zlib_mem_level = 8;
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS))
+      png_ptr->zlib_window_bits = 15;
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD))
+      png_ptr->zlib_method = 8;
+   deflateInit2(&png_ptr->zstream, png_ptr->zlib_level,
+      png_ptr->zlib_method, png_ptr->zlib_window_bits,
+      png_ptr->zlib_mem_level, png_ptr->zlib_strategy);
+   png_ptr->zstream.next_out = png_ptr->zbuf;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+   png_ptr->mode = PNG_HAVE_IHDR;
+}
+
+/* write the palette.  We are careful not to trust png_color to be in the
+ * correct order for PNG, so people can redefine it to any convenient
+ * structure.
+ */
+void
+png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal)
+{
+   png_uint_32 i;
+   png_colorp pal_ptr;
+   png_byte buf[3];
+
+   png_debug(1, "in png_write_PLTE\n");
+   if (num_pal == 0 || num_pal > 256)
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_error(png_ptr, "Invalid number of colors in palette");
+      }
+      else
+      {
+         png_warning(png_ptr, "Invalid number of colors in palette");
+         return;
+      }
+   }
+
+   png_ptr->num_palette = (png_uint_16)num_pal;
+   png_debug1(3, "num_palette = %d\n", png_ptr->num_palette);
+
+   png_write_chunk_start(png_ptr, png_PLTE, num_pal * 3);
+   for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++)
+   {
+      buf[0] = pal_ptr->red;
+      buf[1] = pal_ptr->green;
+      buf[2] = pal_ptr->blue;
+      png_write_chunk_data(png_ptr, buf, (png_size_t)3);
+   }
+   png_write_chunk_end(png_ptr);
+   png_ptr->mode |= PNG_HAVE_PLTE;
+}
+
+/* write an IDAT chunk */
+void
+png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_debug(1, "in png_write_IDAT\n");
+   png_write_chunk(png_ptr, png_IDAT, data, length);
+   png_ptr->mode |= PNG_HAVE_IDAT;
+}
+
+/* write an IEND chunk */
+void
+png_write_IEND(png_structp png_ptr)
+{
+   png_debug(1, "in png_write_IEND\n");
+   png_write_chunk(png_ptr, png_IEND, NULL, (png_size_t)0);
+   png_ptr->mode |= PNG_HAVE_IEND;
+}
+
+#if defined(PNG_WRITE_gAMA_SUPPORTED)
+/* write a gAMA chunk */
+void
+png_write_gAMA(png_structp png_ptr, double file_gamma)
+{
+   png_uint_32 igamma;
+   png_byte buf[4];
+
+   png_debug(1, "in png_write_gAMA\n");
+   /* file_gamma is saved in 1/1000000ths */
+   igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5);
+   png_save_uint_32(buf, igamma);
+   png_write_chunk(png_ptr, png_gAMA, buf, (png_size_t)4);
+}
+#endif
+
+#if defined(PNG_WRITE_sRGB_SUPPORTED)
+/* write a sRGB chunk */
+void
+png_write_sRGB(png_structp png_ptr, int srgb_intent)
+{
+   png_byte buf[1];
+
+   png_debug(1, "in png_write_sRGB\n");
+   if(srgb_intent >= PNG_sRGB_INTENT_LAST)
+         png_warning(png_ptr,
+            "Invalid sRGB rendering intent specified");
+   buf[0]=(png_byte)srgb_intent;
+   png_write_chunk(png_ptr, png_sRGB, buf, (png_size_t)1);
+}
+#endif
+
+#if defined(PNG_WRITE_sBIT_SUPPORTED)
+/* write the sBIT chunk */
+void
+png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type)
+{
+   png_byte buf[4];
+   png_size_t size;
+
+   png_debug(1, "in png_write_sBIT\n");
+   /* make sure we don't depend upon the order of PNG_COLOR_8 */
+   if (color_type & PNG_COLOR_MASK_COLOR)
+   {
+      png_byte maxbits;
+
+      maxbits = color_type==PNG_COLOR_TYPE_PALETTE ? 8 : png_ptr->usr_bit_depth;
+      if (sbit->red == 0 || sbit->red > maxbits ||
+          sbit->green == 0 || sbit->green > maxbits ||
+          sbit->blue == 0 || sbit->blue > maxbits)
+      {
+         png_warning(png_ptr, "Invalid sBIT depth specified");
+         return;
+      }
+      buf[0] = sbit->red;
+      buf[1] = sbit->green;
+      buf[2] = sbit->blue;
+      size = 3;
+   }
+   else
+   {
+      if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth)
+      {
+         png_warning(png_ptr, "Invalid sBIT depth specified");
+         return;
+      }
+      buf[0] = sbit->gray;
+      size = 1;
+   }
+
+   if (color_type & PNG_COLOR_MASK_ALPHA)
+   {
+      if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth)
+      {
+         png_warning(png_ptr, "Invalid sBIT depth specified");
+         return;
+      }
+      buf[size++] = sbit->alpha;
+   }
+
+   png_write_chunk(png_ptr, png_sBIT, buf, size);
+}
+#endif
+
+#if defined(PNG_WRITE_cHRM_SUPPORTED)
+/* write the cHRM chunk */
+void
+png_write_cHRM(png_structp png_ptr, double white_x, double white_y,
+   double red_x, double red_y, double green_x, double green_y,
+   double blue_x, double blue_y)
+{
+   png_uint_32 itemp;
+   png_byte buf[32];
+
+   png_debug(1, "in png_write_cHRM\n");
+   /* each value is saved int 1/1000000ths */
+   if (white_x < 0 || white_x > 0.8 || white_y < 0 || white_y > 0.8 ||
+       white_x + white_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM white point specified");
+      return;
+   }
+   itemp = (png_uint_32)(white_x * 100000.0 + 0.5);
+   png_save_uint_32(buf, itemp);
+   itemp = (png_uint_32)(white_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 4, itemp);
+
+   if (red_x < 0 || red_x > 0.8 || red_y < 0 || red_y > 0.8 ||
+       red_x + red_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM red point specified");
+      return;
+   }
+   itemp = (png_uint_32)(red_x * 100000.0 + 0.5);
+   png_save_uint_32(buf + 8, itemp);
+   itemp = (png_uint_32)(red_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 12, itemp);
+
+   if (green_x < 0 || green_x > 0.8 || green_y < 0 || green_y > 0.8 ||
+       green_x + green_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM green point specified");
+      return;
+   }
+   itemp = (png_uint_32)(green_x * 100000.0 + 0.5);
+   png_save_uint_32(buf + 16, itemp);
+   itemp = (png_uint_32)(green_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 20, itemp);
+
+   if (blue_x < 0 || blue_x > 0.8 || blue_y < 0 || blue_y > 0.8 ||
+       blue_x + blue_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM blue point specified");
+      return;
+   }
+   itemp = (png_uint_32)(blue_x * 100000.0 + 0.5);
+   png_save_uint_32(buf + 24, itemp);
+   itemp = (png_uint_32)(blue_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 28, itemp);
+
+   png_write_chunk(png_ptr, png_cHRM, buf, (png_size_t)32);
+}
+#endif
+
+#if defined(PNG_WRITE_tRNS_SUPPORTED)
+/* write the tRNS chunk */
+void
+png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran,
+   int num_trans, int color_type)
+{
+   png_byte buf[6];
+
+   png_debug(1, "in png_write_tRNS\n");
+   if (color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette)
+      {
+         png_warning(png_ptr,"Invalid number of transparent colors specified");
+         return;
+      }
+      /* write the chunk out as it is */
+      png_write_chunk(png_ptr, png_tRNS, trans, (png_size_t)num_trans);
+   }
+   else if (color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      /* one 16 bit value */
+      png_save_uint_16(buf, tran->gray);
+      png_write_chunk(png_ptr, png_tRNS, buf, (png_size_t)2);
+   }
+   else if (color_type == PNG_COLOR_TYPE_RGB)
+   {
+      /* three 16 bit values */
+      png_save_uint_16(buf, tran->red);
+      png_save_uint_16(buf + 2, tran->green);
+      png_save_uint_16(buf + 4, tran->blue);
+      png_write_chunk(png_ptr, png_tRNS, buf, (png_size_t)6);
+   }
+   else
+   {
+      png_warning(png_ptr, "Can't write tRNS with an alpha channel");
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_bKGD_SUPPORTED)
+/* write the background chunk */
+void
+png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type)
+{
+   png_byte buf[6];
+
+   png_debug(1, "in png_write_bKGD\n");
+   if (color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (back->index > png_ptr->num_palette)
+      {
+         png_warning(png_ptr, "Invalid background palette index");
+         return;
+      }
+      buf[0] = back->index;
+      png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)1);
+   }
+   else if (color_type & PNG_COLOR_MASK_COLOR)
+   {
+      png_save_uint_16(buf, back->red);
+      png_save_uint_16(buf + 2, back->green);
+      png_save_uint_16(buf + 4, back->blue);
+      png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)6);
+   }
+   else
+   {
+      png_save_uint_16(buf, back->gray);
+      png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)2);
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_hIST_SUPPORTED)
+/* write the histogram */
+void
+png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist)
+{
+   int i;
+   png_byte buf[3];
+
+   png_debug(1, "in png_write_hIST\n");
+   if (num_hist > (int)png_ptr->num_palette)
+   {
+      png_debug2(3, "num_hist = %d, num_palette = %d\n", num_hist,
+         png_ptr->num_palette);
+      png_warning(png_ptr, "Invalid number of histogram entries specified");
+      return;
+   }
+
+   png_write_chunk_start(png_ptr, png_hIST, (png_uint_32)(num_hist * 2));
+   for (i = 0; i < num_hist; i++)
+   {
+      png_save_uint_16(buf, hist[i]);
+      png_write_chunk_data(png_ptr, buf, (png_size_t)2);
+   }
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED) || \
+    defined(PNG_WRITE_pCAL_SUPPORTED)
+/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification,
+ * and if invalid, correct the keyword rather than discarding the entire
+ * chunk.  The PNG 1.0 specification requires keywords 1-79 characters in
+ * length, forbids leading or trailing whitespace, multiple internal spaces,
+ * and the non-break space (0x80) from ISO 8859-1.  Returns keyword length.
+ *
+ * The new_key is allocated to hold the corrected keyword and must be freed
+ * by the calling routine.  This avoids problems with trying to write to
+ * static keywords without having to have duplicate copies of the strings.
+ */
+png_size_t
+png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key)
+{
+   png_size_t key_len;
+   png_charp kp, dp;
+   int kflag;
+
+   png_debug(1, "in png_check_keyword\n");
+   *new_key = NULL;
+
+   if (key == NULL || (key_len = png_strlen(key)) == 0)
+   {
+      png_chunk_warning(png_ptr, "zero length keyword");
+      return ((png_size_t)0);
+   }
+
+   png_debug1(2, "Keyword to be checked is '%s'\n", key);
+
+   *new_key = (png_charp)png_malloc(png_ptr, (png_uint_32)(key_len + 1));
+
+   /* Replace non-printing characters with a blank and print a warning */
+   for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++)
+   {
+      if (*kp < 0x20 || (*kp > 0x7E && (png_byte)*kp < 0xA1))
+      {
+#if !defined(PNG_NO_STDIO)
+         char msg[40];
+
+         sprintf(msg, "invalid keyword character 0x%02X", *kp);
+         png_chunk_warning(png_ptr, msg);
+#else
+         png_chunk_warning(png_ptr, "invalid character in keyword");
+#endif
+         *dp = ' ';
+      }
+      else
+      {
+         *dp = *kp;
+      }
+   }
+   *dp = '\0';
+
+   /* Remove any trailing white space. */
+   kp = *new_key + key_len - 1;
+   if (*kp == ' ')
+   {
+      png_chunk_warning(png_ptr, "trailing spaces removed from keyword");
+
+      while (*kp == ' ')
+      {
+        *(kp--) = '\0';
+        key_len--;
+      }
+   }
+
+   /* Remove any leading white space. */
+   kp = *new_key;
+   if (*kp == ' ')
+   {
+      png_chunk_warning(png_ptr, "leading spaces removed from keyword");
+
+      while (*kp == ' ')
+      {
+        kp++;
+        key_len--;
+      }
+   }
+
+   png_debug1(2, "Checking for multiple internal spaces in '%s'\n", kp);
+
+   /* Remove multiple internal spaces. */
+   for (kflag = 0, dp = *new_key; *kp != '\0'; kp++)
+   {
+      if (*kp == ' ' && kflag == 0)
+      {
+         *(dp++) = *kp;
+         kflag = 1;
+      }
+      else if (*kp == ' ')
+      {
+         key_len--;
+      }
+      else
+      {
+         *(dp++) = *kp;
+         kflag = 0;
+      }
+   }
+   *dp = '\0';
+
+   if (key_len == 0)
+   {
+      png_chunk_warning(png_ptr, "zero length keyword");
+   }
+
+   if (key_len > 79)
+   {
+      png_chunk_warning(png_ptr, "keyword length must be 1 - 79 characters");
+      new_key[79] = '\0';
+      key_len = 79;
+   }
+
+   return (key_len);
+}
+#endif
+
+#if defined(PNG_WRITE_tEXt_SUPPORTED)
+/* write a tEXt chunk */
+void
+png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text,
+   png_size_t text_len)
+{
+   png_size_t key_len;
+   png_charp new_key;
+
+   png_debug(1, "in png_write_tEXt\n");
+   if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0)
+   {
+      png_warning(png_ptr, "Empty keyword in tEXt chunk");
+      return;
+   }
+
+   if (text == NULL || *text == '\0')
+      text_len = 0;
+
+   /* make sure we include the 0 after the key */
+   png_write_chunk_start(png_ptr, png_tEXt, (png_uint_32)key_len+text_len+1);
+   png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1);
+   if (text_len)
+      png_write_chunk_data(png_ptr, (png_bytep)text, text_len);
+
+   png_write_chunk_end(png_ptr);
+   png_free(png_ptr, new_key);
+}
+#endif
+
+#if defined(PNG_WRITE_zTXt_SUPPORTED)
+/* write a compressed text chunk */
+void
+png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text,
+   png_size_t text_len, int compression)
+{
+   png_size_t key_len;
+   char buf[1];
+   png_charp new_key;
+   int i, ret;
+   png_charpp output_ptr = NULL; /* array of pointers to output */
+   int num_output_ptr = 0; /* number of output pointers used */
+   int max_output_ptr = 0; /* size of output_ptr */
+
+   png_debug(1, "in png_write_zTXt\n");
+
+   if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0)
+   {
+      png_warning(png_ptr, "Empty keyword in zTXt chunk");
+      return;
+   }
+
+   if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE)
+   {
+      png_write_tEXt(png_ptr, new_key, text, (png_size_t)0);
+      png_free(png_ptr, new_key);
+      return;
+   }
+
+   png_free(png_ptr, new_key);
+
+   if (compression >= PNG_TEXT_COMPRESSION_LAST)
+   {
+#if !defined(PNG_NO_STDIO)
+      char msg[50];
+      sprintf(msg, "Unknown zTXt compression type %d", compression);
+      png_warning(png_ptr, msg);
+#else
+      png_warning(png_ptr, "Unknown zTXt compression type");
+#endif
+      compression = PNG_TEXT_COMPRESSION_zTXt;
+   }
+
+   /* We can't write the chunk until we find out how much data we have,
+    * which means we need to run the compressor first and save the
+    * output.  This shouldn't be a problem, as the vast majority of
+    * comments should be reasonable, but we will set up an array of
+    * malloc'd pointers to be sure.
+    *
+    * If we knew the application was well behaved, we could simplify this
+    * greatly by assuming we can always malloc an output buffer large
+    * enough to hold the compressed text ((1001 * text_len / 1000) + 12)
+    * and malloc this directly.  The only time this would be a bad idea is
+    * if we can't malloc more than 64K and we have 64K of random input
+    * data, or if the input string is incredibly large (although this
+    * wouldn't cause a failure, just a slowdown due to swapping).
+    */
+
+   /* set up the compression buffers */
+   png_ptr->zstream.avail_in = (uInt)text_len;
+   png_ptr->zstream.next_in = (Bytef *)text;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+   png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf;
+
+   /* this is the same compression loop as in png_write_row() */
+   do
+   {
+      /* compress the data */
+      ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
+      if (ret != Z_OK)
+      {
+         /* error */
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+      /* check to see if we need more room */
+      if (!png_ptr->zstream.avail_out && png_ptr->zstream.avail_in)
+      {
+         /* make sure the output array has room */
+         if (num_output_ptr >= max_output_ptr)
+         {
+            int old_max;
+
+            old_max = max_output_ptr;
+            max_output_ptr = num_output_ptr + 4;
+            if (output_ptr != NULL)
+            {
+               png_charpp old_ptr;
+
+               old_ptr = output_ptr;
+               output_ptr = (png_charpp)png_malloc(png_ptr,
+                  (png_uint_32)(max_output_ptr * sizeof (png_charpp)));
+               png_memcpy(output_ptr, old_ptr, old_max * sizeof (png_charp));
+               png_free(png_ptr, old_ptr);
+            }
+            else
+               output_ptr = (png_charpp)png_malloc(png_ptr,
+                  (png_uint_32)(max_output_ptr * sizeof (png_charp)));
+         }
+
+         /* save the data */
+         output_ptr[num_output_ptr] = (png_charp)png_malloc(png_ptr,
+            (png_uint_32)png_ptr->zbuf_size);
+         png_memcpy(output_ptr[num_output_ptr], png_ptr->zbuf,
+            png_ptr->zbuf_size);
+         num_output_ptr++;
+
+         /* and reset the buffer */
+         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+         png_ptr->zstream.next_out = png_ptr->zbuf;
+      }
+   /* continue until we don't have any more to compress */
+   } while (png_ptr->zstream.avail_in);
+
+   /* finish the compression */
+   do
+   {
+      /* tell zlib we are finished */
+      ret = deflate(&png_ptr->zstream, Z_FINISH);
+      if (ret != Z_OK && ret != Z_STREAM_END)
+      {
+         /* we got an error */
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+
+      /* check to see if we need more room */
+      if (!(png_ptr->zstream.avail_out) && ret == Z_OK)
+      {
+         /* check to make sure our output array has room */
+         if (num_output_ptr >= max_output_ptr)
+         {
+            int old_max;
+
+            old_max = max_output_ptr;
+            max_output_ptr = num_output_ptr + 4;
+            if (output_ptr != NULL)
+            {
+               png_charpp old_ptr;
+
+               old_ptr = output_ptr;
+               /* This could be optimized to realloc() */
+               output_ptr = (png_charpp)png_malloc(png_ptr,
+                  (png_uint_32)(max_output_ptr * sizeof (png_charpp)));
+               png_memcpy(output_ptr, old_ptr, old_max * sizeof (png_charp));
+               png_free(png_ptr, old_ptr);
+            }
+            else
+               output_ptr = (png_charpp)png_malloc(png_ptr,
+                  (png_uint_32)(max_output_ptr * sizeof (png_charp)));
+         }
+
+         /* save off the data */
+         output_ptr[num_output_ptr] = (png_charp)png_malloc(png_ptr,
+            (png_uint_32)png_ptr->zbuf_size);
+         png_memcpy(output_ptr[num_output_ptr], png_ptr->zbuf,
+            png_ptr->zbuf_size);
+         num_output_ptr++;
+
+         /* and reset the buffer pointers */
+         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+         png_ptr->zstream.next_out = png_ptr->zbuf;
+      }
+   } while (ret != Z_STREAM_END);
+
+   /* text length is number of buffers plus last buffer */
+   text_len = png_ptr->zbuf_size * num_output_ptr;
+   if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
+      text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out;
+
+   /* write start of chunk */
+   png_write_chunk_start(png_ptr, png_zTXt, (png_uint_32)(key_len+text_len+2));
+   /* write key */
+   png_write_chunk_data(png_ptr, (png_bytep)key, key_len + 1);
+   buf[0] = (png_byte)compression;
+   /* write compression */
+   png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1);
+
+   /* write saved output buffers, if any */
+   for (i = 0; i < num_output_ptr; i++)
+   {
+      png_write_chunk_data(png_ptr,(png_bytep)output_ptr[i],png_ptr->zbuf_size);
+      png_free(png_ptr, output_ptr[i]);
+   }
+   if (max_output_ptr != 0)
+      png_free(png_ptr, output_ptr);
+   /* write anything left in zbuf */
+   if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size)
+      png_write_chunk_data(png_ptr, png_ptr->zbuf,
+         png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+   /* close the chunk */
+   png_write_chunk_end(png_ptr);
+
+   /* reset zlib for another zTXt or the image data */
+   deflateReset(&png_ptr->zstream);
+}
+#endif
+
+
+#if defined(PNG_WRITE_oFFs_SUPPORTED)
+/* write the oFFs chunk */
+void
+png_write_oFFs(png_structp png_ptr, png_uint_32 x_offset,
+   png_uint_32 y_offset,
+   int unit_type)
+{
+   png_byte buf[9];
+
+   png_debug(1, "in png_write_oFFs\n");
+   if (unit_type >= PNG_OFFSET_LAST)
+      png_warning(png_ptr, "Unrecognized unit type for oFFs chunk");
+
+   png_save_uint_32(buf, x_offset);
+   png_save_uint_32(buf + 4, y_offset);
+   buf[8] = (png_byte)unit_type;
+
+   png_write_chunk(png_ptr, png_oFFs, buf, (png_size_t)9);
+}
+#endif
+
+#if defined(PNG_WRITE_pCAL_SUPPORTED)
+/* write the pCAL chunk (png-scivis-19970203) */
+void
+png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0,
+   png_int_32 X1, int type, int nparams, png_charp units, png_charpp params)
+{
+   png_size_t purpose_len, units_len, total_len;
+   png_uint_32p params_len;
+   png_byte buf[10];
+   png_charp new_purpose;
+   int i;
+
+   png_debug1(1, "in png_write_pCAL (%d parameters)\n", nparams);
+   if (type >= PNG_EQUATION_LAST)
+      png_warning(png_ptr, "Unrecognized equation type for pCAL chunk");
+
+   purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1;
+   png_debug1(3, "pCAL purpose length = %d\n", purpose_len);
+   units_len = png_strlen(units) + (nparams == 0 ? 0 : 1);
+   png_debug1(3, "pCAL units length = %d\n", units_len);
+   total_len = purpose_len + units_len + 10;
+
+   params_len = (png_uint_32p)png_malloc(png_ptr, (png_uint_32)(nparams
+      *sizeof(png_uint_32)));
+
+   /* Find the length of each parameter, making sure we don't count the
+      null terminator for the last parameter. */
+   for (i = 0; i < nparams; i++)
+   {
+      params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1);
+      png_debug2(3, "pCAL parameter %d length = %d\n", i, params_len[i]);
+      total_len += (png_size_t)params_len[i];
+   }
+
+   png_debug1(3, "pCAL total length = %d\n", total_len);
+   png_write_chunk_start(png_ptr, png_pCAL, (png_uint_32)total_len);
+   png_write_chunk_data(png_ptr, (png_bytep)new_purpose, purpose_len);
+   png_save_int_32(buf, X0);
+   png_save_int_32(buf + 4, X1);
+   buf[8] = (png_byte)type;
+   buf[9] = (png_byte)nparams;
+   png_write_chunk_data(png_ptr, buf, (png_size_t)10);
+   png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len);
+
+   png_free(png_ptr, new_purpose);
+
+   for (i = 0; i < nparams; i++)
+   {
+      png_write_chunk_data(png_ptr, (png_bytep)params[i],
+         (png_size_t)params_len[i]);
+   }
+
+   png_free(png_ptr, params_len);
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#if defined(PNG_WRITE_pHYs_SUPPORTED)
+/* write the pHYs chunk */
+void
+png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit,
+   png_uint_32 y_pixels_per_unit,
+   int unit_type)
+{
+   png_byte buf[9];
+
+   png_debug(1, "in png_write_pHYs\n");
+   if (unit_type >= PNG_RESOLUTION_LAST)
+      png_warning(png_ptr, "Unrecognized unit type for pHYs chunk");
+
+   png_save_uint_32(buf, x_pixels_per_unit);
+   png_save_uint_32(buf + 4, y_pixels_per_unit);
+   buf[8] = (png_byte)unit_type;
+
+   png_write_chunk(png_ptr, png_pHYs, buf, (png_size_t)9);
+}
+#endif
+
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+/* Write the tIME chunk.  Use either png_convert_from_struct_tm()
+ * or png_convert_from_time_t(), or fill in the structure yourself.
+ */
+void
+png_write_tIME(png_structp png_ptr, png_timep mod_time)
+{
+   png_byte buf[7];
+
+   png_debug(1, "in png_write_tIME\n");
+   if (mod_time->month  > 12 || mod_time->month  < 1 ||
+       mod_time->day    > 31 || mod_time->day    < 1 ||
+       mod_time->hour   > 23 || mod_time->second > 60)
+   {
+      png_warning(png_ptr, "Invalid time specified for tIME chunk");
+      return;
+   }
+
+   png_save_uint_16(buf, mod_time->year);
+   buf[2] = mod_time->month;
+   buf[3] = mod_time->day;
+   buf[4] = mod_time->hour;
+   buf[5] = mod_time->minute;
+   buf[6] = mod_time->second;
+
+   png_write_chunk(png_ptr, png_tIME, buf, (png_size_t)7);
+}
+#endif
+
+/* initializes the row writing capability of libpng */
+void
+png_write_start_row(png_structp png_ptr)
+{
+   png_size_t buf_size;
+
+   png_debug(1, "in png_write_start_row\n");
+   buf_size = (png_size_t)(((png_ptr->width * png_ptr->usr_channels *
+                            png_ptr->usr_bit_depth + 7) >> 3) + 1);
+
+   /* set up row buffer */
+   png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size);
+   png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE;
+
+   /* set up filtering buffer, if using this filter */
+   if (png_ptr->do_filter & PNG_FILTER_SUB)
+   {
+      png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
+         (png_ptr->rowbytes + 1));
+      png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
+   }
+
+   /* We only need to keep the previous row if we are using one of these. */
+   if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH))
+   {
+     /* set up previous row buffer */
+      png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size);
+      png_memset(png_ptr->prev_row, 0, buf_size);
+
+      if (png_ptr->do_filter & PNG_FILTER_UP)
+      {
+         png_ptr->up_row = (png_bytep )png_malloc(png_ptr,
+            (png_ptr->rowbytes + 1));
+         png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
+      }
+
+      if (png_ptr->do_filter & PNG_FILTER_AVG)
+      {
+         png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
+            (png_ptr->rowbytes + 1));
+         png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
+      }
+
+      if (png_ptr->do_filter & PNG_FILTER_PAETH)
+      {
+         png_ptr->paeth_row = (png_bytep )png_malloc(png_ptr,
+            (png_ptr->rowbytes + 1));
+         png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
+      }
+   }
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* if interlaced, we need to set up width and height of pass */
+   if (png_ptr->interlaced)
+   {
+      if (!(png_ptr->transformations & PNG_INTERLACE))
+      {
+         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
+            png_pass_ystart[0]) / png_pass_yinc[0];
+         png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 -
+            png_pass_start[0]) / png_pass_inc[0];
+      }
+      else
+      {
+         png_ptr->num_rows = png_ptr->height;
+         png_ptr->usr_width = png_ptr->width;
+      }
+   }
+   else
+#endif
+   {
+      png_ptr->num_rows = png_ptr->height;
+      png_ptr->usr_width = png_ptr->width;
+   }
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+   png_ptr->zstream.next_out = png_ptr->zbuf;
+}
+
+/* Internal use only.  Called when finished processing a row of data. */
+void
+png_write_finish_row(png_structp png_ptr)
+{
+   int ret;
+
+   png_debug(1, "in png_write_finish_row\n");
+   /* next row */
+   png_ptr->row_number++;
+
+   /* see if we are done */
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* if interlaced, go to next pass */
+   if (png_ptr->interlaced)
+   {
+      png_ptr->row_number = 0;
+      if (png_ptr->transformations & PNG_INTERLACE)
+      {
+         png_ptr->pass++;
+      }
+      else
+      {
+         /* loop until we find a non-zero width or height pass */
+         do
+         {
+            png_ptr->pass++;
+            if (png_ptr->pass >= 7)
+               break;
+            png_ptr->usr_width = (png_ptr->width +
+               png_pass_inc[png_ptr->pass] - 1 -
+               png_pass_start[png_ptr->pass]) /
+               png_pass_inc[png_ptr->pass];
+            png_ptr->num_rows = (png_ptr->height +
+               png_pass_yinc[png_ptr->pass] - 1 -
+               png_pass_ystart[png_ptr->pass]) /
+               png_pass_yinc[png_ptr->pass];
+            if (png_ptr->transformations & PNG_INTERLACE)
+               break;
+         } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0);
+
+      }
+
+      /* reset the row above the image for the next pass */
+      if (png_ptr->pass < 7)
+      {
+         if (png_ptr->prev_row != NULL)
+            png_memset(png_ptr->prev_row, 0,
+               (png_size_t) (((png_uint_32)png_ptr->usr_channels *
+               (png_uint_32)png_ptr->usr_bit_depth *
+               png_ptr->width + 7) >> 3) + 1);
+         return;
+      }
+   }
+#endif
+
+   /* if we get here, we've just written the last row, so we need
+      to flush the compressor */
+   do
+   {
+      /* tell the compressor we are done */
+      ret = deflate(&png_ptr->zstream, Z_FINISH);
+      /* check for an error */
+      if (ret != Z_OK && ret != Z_STREAM_END)
+      {
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+      /* check to see if we need more room */
+      if (!(png_ptr->zstream.avail_out) && ret == Z_OK)
+      {
+         png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+         png_ptr->zstream.next_out = png_ptr->zbuf;
+         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+      }
+   } while (ret != Z_STREAM_END);
+
+   /* write any extra space */
+   if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
+   {
+      png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size -
+         png_ptr->zstream.avail_out);
+   }
+
+   deflateReset(&png_ptr->zstream);
+}
+
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+/* Pick out the correct pixels for the interlace pass.
+ * The basic idea here is to go through the row with a source
+ * pointer and a destination pointer (sp and dp), and copy the
+ * correct pixels for the pass.  As the row gets compacted,
+ * sp will always be >= dp, so we should never overwrite anything.
+ * See the default: case for the easiest code to understand.
+ */
+void
+png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass)
+{
+   png_debug(1, "in png_do_write_interlace\n");
+   /* we don't have to do anything on the last pass (6) */
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL && pass < 6)
+#else
+   if (pass < 6)
+#endif
+   {
+      /* each pixel depth is handled separately */
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            dp = row;
+            d = 0;
+            shift = 7;
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 3);
+               value = (int)(*sp >> (7 - (int)(i & 7))) & 0x1;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 7;
+                  *dp++ = (png_byte)d;
+                  d = 0;
+               }
+               else
+                  shift--;
+
+            }
+            if (shift != 7)
+               *dp = (png_byte)d;
+            break;
+         }
+         case 2:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            dp = row;
+            shift = 6;
+            d = 0;
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 2);
+               value = (*sp >> ((3 - (int)(i & 3)) << 1)) & 0x3;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 6;
+                  *dp++ = (png_byte)d;
+                  d = 0;
+               }
+               else
+                  shift -= 2;
+            }
+            if (shift != 6)
+                   *dp = (png_byte)d;
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            dp = row;
+            shift = 4;
+            d = 0;
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 1);
+               value = (*sp >> ((1 - (int)(i & 1)) << 2)) & 0xf;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 4;
+                  *dp++ = (png_byte)d;
+                  d = 0;
+               }
+               else
+                  shift -= 4;
+            }
+            if (shift != 4)
+               *dp = (png_byte)d;
+            break;
+         }
+         default:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+            png_size_t pixel_bytes;
+
+            /* start at the beginning */
+            dp = row;
+            /* find out how many bytes each pixel takes up */
+            pixel_bytes = (row_info->pixel_depth >> 3);
+            /* loop through the row, only looking at the pixels that
+               matter */
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               /* find out where the original pixel is */
+               sp = row + (png_size_t)i * pixel_bytes;
+               /* move the pixel */
+               if (dp != sp)
+                  png_memcpy(dp, sp, pixel_bytes);
+               /* next pixel */
+               dp += pixel_bytes;
+            }
+            break;
+         }
+      }
+      /* set new row width */
+      row_info->width = (row_info->width +
+         png_pass_inc[pass] - 1 -
+         png_pass_start[pass]) /
+         png_pass_inc[pass];
+         row_info->rowbytes = ((row_info->width *
+            row_info->pixel_depth + 7) >> 3);
+   }
+}
+#endif
+
+/* This filters the row, chooses which filter to use, if it has not already
+ * been specified by the application, and then writes the row out with the
+ * chosen filter.
+ */
+#define PNG_MAXSUM (~((png_uint_32)0) >> 1)
+#define PNG_HISHIFT 10
+#define PNG_LOMASK ((png_uint_32)0xffffL)
+#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT))
+void
+png_write_find_filter(png_structp png_ptr, png_row_infop row_info)
+{
+   png_bytep prev_row, best_row, row_buf;
+   png_uint_32 mins, bpp;
+   png_byte filter_to_do = png_ptr->do_filter;
+   png_uint_32 row_bytes = row_info->rowbytes;
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   int num_p_filters = (int)png_ptr->num_prev_filters;
+#endif
+
+   png_debug(1, "in png_write_find_filter\n");
+   /* find out how many bytes offset each pixel is */
+   bpp = (row_info->pixel_depth + 7) / 8;
+
+   prev_row = png_ptr->prev_row;
+   best_row = row_buf = png_ptr->row_buf;
+   mins = PNG_MAXSUM;
+
+   /* The prediction method we use is to find which method provides the
+    * smallest value when summing the absolute values of the distances
+    * from zero, using anything >= 128 as negative numbers.  This is known
+    * as the "minimum sum of absolute differences" heuristic.  Other
+    * heuristics are the "weighted minimum sum of absolute differences"
+    * (experimental and can in theory improve compression), and the "zlib
+    * predictive" method (not implemented yet), which does test compressions
+    * of lines using different filter methods, and then chooses the
+    * (series of) filter(s) that give minimum compressed data size (VERY
+    * computationally expensive).
+    *
+    * GRR 980525:  consider also
+    *   (1) minimum sum of absolute differences from running average (i.e.,
+    *       keep running sum of non-absolute differences & count of bytes)
+    *       [track dispersion, too?  restart average if dispersion too large?]
+    *  (1b) minimum sum of absolute differences from sliding average, probably
+    *       with window size <= deflate window (usually 32K)
+    *   (2) minimum sum of squared differences from zero or running average
+    *       (i.e., ~ root-mean-square approach)
+    */
+
+
+   /* We don't need to test the 'no filter' case if this is the only filter
+    * that has been chosen, as it doesn't actually do anything to the data.
+    */
+   if (filter_to_do & PNG_FILTER_NONE &&
+       filter_to_do != PNG_FILTER_NONE)
+   {
+      png_bytep rp;
+      png_uint_32 sum = 0;
+      png_uint_32 i;
+      int v;
+
+      for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
+      {
+         v = *rp;
+         sum += (v < 128) ? v : 256 - v;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         png_uint_32 sumhi, sumlo;
+         int j;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */
+
+         /* Reduce the sum if we match any of the previous rows */
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)
+            {
+               sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         /* Factor in the cost of this filter (this is here for completeness,
+          * but it makes no sense to have a "cost" for the NONE filter, as
+          * it has the minimum possible computational cost - none).
+          */
+         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+      mins = sum;
+   }
+
+   /* sub filter */
+   if (filter_to_do == PNG_FILTER_SUB)
+   /* it's the only filter so no testing is needed */
+   {
+      png_bytep rp, lp, dp;
+      png_uint_32 i;
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
+           i++, rp++, dp++)
+      {
+         *dp = *rp;
+      }
+      for (lp = row_buf + 1; i < row_bytes;
+         i++, rp++, lp++, dp++)
+      {
+         *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
+      }
+      best_row = png_ptr->sub_row;
+   }
+
+   else if (filter_to_do & PNG_FILTER_SUB)
+   {
+      png_bytep rp, dp, lp;
+      png_uint_32 sum = 0, lmins = mins;
+      png_uint_32 i;
+      int v;
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      /* We temporarily increase the "minimum sum" by the factor we
+       * would reduce the sum of this filter, so that we can do the
+       * early exit comparison without scaling the sum each time.
+       */
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 lmhi, lmlo;
+         lmlo = lmins & PNG_LOMASK;
+         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
+            {
+               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+            PNG_COST_SHIFT;
+         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+            PNG_COST_SHIFT;
+
+         if (lmhi > PNG_HIMASK)
+            lmins = PNG_MAXSUM;
+         else
+            lmins = (lmhi << PNG_HISHIFT) + lmlo;
+      }
+#endif
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
+           i++, rp++, dp++)
+      {
+         v = *dp = *rp;
+
+         sum += (v < 128) ? v : 256 - v;
+      }
+      for (lp = row_buf + 1; i < row_info->rowbytes;
+         i++, rp++, lp++, dp++)
+      {
+         v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+
+         if (sum > lmins)  /* We are already worse, don't continue. */
+            break;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 sumhi, sumlo;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
+            {
+               sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+
+      if (sum < mins)
+      {
+         mins = sum;
+         best_row = png_ptr->sub_row;
+      }
+   }
+
+   /* up filter */
+   if (filter_to_do == PNG_FILTER_UP)
+   {
+      png_bytep rp, dp, pp;
+      png_uint_32 i;
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
+           pp = prev_row + 1; i < row_bytes;
+           i++, rp++, pp++, dp++)
+      {
+         *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
+      }
+      best_row = png_ptr->up_row;
+   }
+
+   else if (filter_to_do & PNG_FILTER_UP)
+   {
+      png_bytep rp, dp, pp;
+      png_uint_32 sum = 0, lmins = mins;
+      png_uint_32 i;
+      int v;
+
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 lmhi, lmlo;
+         lmlo = lmins & PNG_LOMASK;
+         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
+            {
+               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
+            PNG_COST_SHIFT;
+         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
+            PNG_COST_SHIFT;
+
+         if (lmhi > PNG_HIMASK)
+            lmins = PNG_MAXSUM;
+         else
+            lmins = (lmhi << PNG_HISHIFT) + lmlo;
+      }
+#endif
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
+           pp = prev_row + 1; i < row_bytes; i++)
+      {
+         v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+
+         if (sum > lmins)  /* We are already worse, don't continue. */
+            break;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 sumhi, sumlo;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
+            {
+               sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+
+      if (sum < mins)
+      {
+         mins = sum;
+         best_row = png_ptr->up_row;
+      }
+   }
+
+   /* avg filter */
+   if (filter_to_do == PNG_FILTER_AVG)
+   {
+      png_bytep rp, dp, pp, lp;
+      png_uint_32 i;
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
+           pp = prev_row + 1; i < bpp; i++)
+      {
+         *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
+      }
+      for (lp = row_buf + 1; i < row_bytes; i++)
+      {
+         *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
+                 & 0xff);
+      }
+      best_row = png_ptr->avg_row;
+   }
+
+   else if (filter_to_do & PNG_FILTER_AVG)
+   {
+      png_bytep rp, dp, pp, lp;
+      png_uint_32 sum = 0, lmins = mins;
+      png_uint_32 i;
+      int v;
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 lmhi, lmlo;
+         lmlo = lmins & PNG_LOMASK;
+         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG)
+            {
+               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
+            PNG_COST_SHIFT;
+         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
+            PNG_COST_SHIFT;
+
+         if (lmhi > PNG_HIMASK)
+            lmins = PNG_MAXSUM;
+         else
+            lmins = (lmhi << PNG_HISHIFT) + lmlo;
+      }
+#endif
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
+           pp = prev_row + 1; i < bpp; i++)
+      {
+         v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+      }
+      for (lp = row_buf + 1; i < row_bytes; i++)
+      {
+         v = *dp++ =
+          (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+
+         if (sum > lmins)  /* We are already worse, don't continue. */
+            break;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 sumhi, sumlo;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)
+            {
+               sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+
+      if (sum < mins)
+      {
+         mins = sum;
+         best_row = png_ptr->avg_row;
+      }
+   }
+
+   /* Paeth filter */
+   if (filter_to_do == PNG_FILTER_PAETH)
+   {
+      png_bytep rp, dp, pp, cp, lp;
+      png_uint_32 i;
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
+           pp = prev_row + 1; i < bpp; i++)
+      {
+         *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+      }
+
+      for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
+      {
+         int a, b, c, pa, pb, pc, p;
+
+         b = *pp++;
+         c = *cp++;
+         a = *lp++;
+
+         p = b - c;
+         pc = a - c;
+
+#ifdef PNG_USE_ABS
+         pa = abs(p);
+         pb = abs(pc);
+         pc = abs(p + pc);
+#else
+         pa = p < 0 ? -p : p;
+         pb = pc < 0 ? -pc : pc;
+         pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+         p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+
+         *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
+      }
+      best_row = png_ptr->paeth_row;
+   }
+
+   else if (filter_to_do & PNG_FILTER_PAETH)
+   {
+      png_bytep rp, dp, pp, cp, lp;
+      png_uint_32 sum = 0, lmins = mins;
+      png_uint_32 i;
+      int v;
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 lmhi, lmlo;
+         lmlo = lmins & PNG_LOMASK;
+         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
+            {
+               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+            PNG_COST_SHIFT;
+         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+            PNG_COST_SHIFT;
+
+         if (lmhi > PNG_HIMASK)
+            lmins = PNG_MAXSUM;
+         else
+            lmins = (lmhi << PNG_HISHIFT) + lmlo;
+      }
+#endif
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
+           pp = prev_row + 1; i < bpp; i++)
+      {
+         v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+      }
+
+      for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
+      {
+         int a, b, c, pa, pb, pc, p;
+
+         b = *pp++;
+         c = *cp++;
+         a = *lp++;
+
+#ifndef PNG_SLOW_PAETH
+         p = b - c;
+         pc = a - c;
+#ifdef PNG_USE_ABS
+         pa = abs(p);
+         pb = abs(pc);
+         pc = abs(p + pc);
+#else
+         pa = p < 0 ? -p : p;
+         pb = pc < 0 ? -pc : pc;
+         pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+         p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+#else /* PNG_SLOW_PAETH */
+         p = a + b - c;
+         pa = abs(p - a);
+         pb = abs(p - b);
+         pc = abs(p - c);
+         if (pa <= pb && pa <= pc)
+            p = a;
+         else if (pb <= pc)
+            p = b;
+         else
+            p = c;
+#endif /* PNG_SLOW_PAETH */
+
+         v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+
+         if (sum > lmins)  /* We are already worse, don't continue. */
+            break;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 sumhi, sumlo;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
+            {
+               sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+
+      if (sum < mins)
+      {
+         best_row = png_ptr->paeth_row;
+      }
+   }
+
+   /* Do the actual writing of the filtered row data from the chosen filter. */
+
+   png_write_filtered_row(png_ptr, best_row);
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   /* Save the type of filter we picked this time for future calculations */
+   if (png_ptr->num_prev_filters > 0)
+   {
+      int j;
+      for (j = 1; j < num_p_filters; j++)
+      {
+         png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1];
+      }
+      png_ptr->prev_filters[j] = best_row[0];
+   }
+#endif
+}
+
+
+/* Do the actual writing of a previously filtered row. */
+void
+png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row)
+{
+   png_debug(1, "in png_write_filtered_row\n");
+   png_debug1(2, "filter = %d\n", filtered_row[0]);
+   /* set up the zlib input buffer */
+   png_ptr->zstream.next_in = filtered_row;
+   png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1;
+   /* repeat until we have compressed all the data */
+   do
+   {
+      int ret; /* return of zlib */
+
+      /* compress the data */
+      ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
+      /* check for compression errors */
+      if (ret != Z_OK)
+      {
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+
+      /* see if it is time to write another IDAT */
+      if (!(png_ptr->zstream.avail_out))
+      {
+         /* write the IDAT and reset the zlib output buffer */
+         png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+         png_ptr->zstream.next_out = png_ptr->zbuf;
+         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+      }
+   /* repeat until all data has been compressed */
+   } while (png_ptr->zstream.avail_in);
+
+   /* swap the current and previous rows */
+   if (png_ptr->prev_row != NULL)
+   {
+      png_bytep tptr;
+
+      tptr = png_ptr->prev_row;
+      png_ptr->prev_row = png_ptr->row_buf;
+      png_ptr->row_buf = tptr;
+   }
+
+   /* finish row - updates counters and flushes zlib if last row */
+   png_write_finish_row(png_ptr);
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+   png_ptr->flush_rows++;
+
+   if (png_ptr->flush_dist > 0 &&
+       png_ptr->flush_rows >= png_ptr->flush_dist)
+   {
+      png_write_flush(png_ptr);
+   }
+#endif /* PNG_WRITE_FLUSH_SUPPORTED */
+}