Imported from libpng-1.0.4f.tar
diff --git a/ANNOUNCE b/ANNOUNCE
index 32fa33f..bbd2d4a 100644
--- a/ANNOUNCE
+++ b/ANNOUNCE
@@ -1,5 +1,5 @@
 
-Libpng 1.0.4e - October 10, 1999
+Libpng 1.0.4f - October 12, 1999
 
 This is not intended to be a public release.  It will be replaced
 within a few weeks by a public version or by another test version.
@@ -80,13 +80,20 @@
     a helpful compiler error if an old png.h is found in the search path.
 version 1.0.4c October 1, 1999
   Changed type of png_user_transform_depth|channels from int to png_byte.
-version 1.0.4e [October 10, 1999]
+version 1.0.4f [October 12, 1999]
   Changed 0.45 to 0.45455 in png_set_sRGB()
   Removed unused PLTE entries from pngnow.png
   Re-enabled some parts of pngvcrd.c (png_combine_row) that work properly.
 version 1.0.4e [October 10, 1999]
   Fixed sign error in pngvcrd.c (Greg Roelofs)
   Replaced some instances of memcpy with simple assignments in pngvcrd (GR-P)
+version 1.0.4f [October 12, 1999]
+  Surrounded example.c code with #if 0 .. #endif to prevent people from
+    inadvertently trying to compile it.
+  Changed png_get_header_version() from a function to a macro in png.h
+  Added type casting mostly in pngrtran.c and pngwtran.c
+  Removed some pointless "ptr = NULL" in pngmem.c
+  Added a "contrib" directory containing the source code from Greg's book.
 
 Send comments/corrections/commendations to
 png-implement@ccrc.wustl.edu or to randeg@alum.rpi.edu
diff --git a/CHANGES b/CHANGES
index 691f337..5099ead 100644
--- a/CHANGES
+++ b/CHANGES
@@ -467,3 +467,10 @@
 version 1.0.4e [October 10, 1999]
   Fixed sign error in pngvcrd.c (Greg Roelofs)
   Replaced some instances of memcpy with simple assignments in pngvcrd (GR-P)
+version 1.0.4f [October 12, 1999]
+  Surrounded example.c code with #if 0 .. #endif to prevent people from
+    inadvertently trying to compile it.
+  Changed png_get_header_version() from a function to a macro in png.h
+  Added type casting mostly in pngrtran.c and pngwtran.c
+  Removed some pointless "ptr = NULL" in pngmem.c
+  Added a "contrib" directory containing the source code from Greg's book.
diff --git a/INSTALL b/INSTALL
index 328430f..ee6e85a 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,5 +1,5 @@
 
-Installing libpng version 1.0.4e - October 10, 1999
+Installing libpng version 1.0.4f - October 12, 1999
 
 Before installing libpng, you must first install zlib.  zlib
 can usually be found wherever you got libpng.  zlib can be
@@ -10,7 +10,7 @@
 version of zlib that's installed.
 
 You can rename the directories that you downloaded (they
-might be called "libpng-1.0.4e" or "lpng103" and "zlib-1.1.3"
+might be called "libpng-1.0.4f" 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:
@@ -47,8 +47,8 @@
  makefile.hpux    =>  HPUX (10.20 and 11.00) makefile
  makefile.sgi     =>  Silicon Graphics IRIX makefile
  makefile.sunos   =>  Sun makefile
- makefile.solaris =>  Solaris 2.X makefile (gcc, creates libpng.so.2.1.0.4e)
- makefile.linux   =>  Linux/ELF makefile (gcc, creates libpng.so.2.1.0.4e)
+ makefile.solaris =>  Solaris 2.X makefile (gcc, creates libpng.so.2.1.0.4f)
+ makefile.linux   =>  Linux/ELF makefile (gcc, creates libpng.so.2.1.0.4f)
  makefile.sco     =>  For SCO OSr5  ELF and Unixware 7 with Native cc
  makefile.mips    =>  MIPS makefile
  makefile.acorn   =>  Acorn makefile
@@ -77,7 +77,7 @@
 Copy the file (or files) that you need from the
 scripts directory into this directory, for example
 
-   MSDOS example: copy scripts\makefile.msd makefile
+   MSDOS example: copy scripts\makefile.msc makefile
    UNIX example:    cp scripts/makefile.std makefile
 
 Read the makefile to see if you need to change any source or
diff --git a/LICENSE b/LICENSE
index 359888e..595c855 100644
--- a/LICENSE
+++ b/LICENSE
@@ -5,7 +5,7 @@
 Copyright (c) 1996, 1997 Andreas Dilger
 (libpng versions 0.90, December 1996, through 0.96, May 1997)
 Copyright (c) 1998, 1999 Glenn Randers-Pehrson
-(libpng versions 0.97, January 1998, through 1.0.4e, October 10, 1999)
+(libpng versions 0.97, January 1998, through 1.0.4f, October 12, 1999)
 
 For the purposes of this copyright and license, "Contributing Authors"
 is defined as the following set of individuals:
@@ -54,6 +54,17 @@
 source code in a product, acknowledgment is not required but would be
 appreciated.
 
+A "png_get_copyright" function is available, for convenient use in "about"
+boxes and the like:
+
+   printf("%s",png_get_copyright(NULL));
+
+Also, the PNG logo (in PNG format, of course) is supplied in the
+file "pngnow.png".
+
+Libpng is OSI Certified Open Source Software.  OSI Certified is a
+certification mark of the Open Source Initiative.
+
 Glenn Randers-Pehrson
 randeg@alum.rpi.edu
-October 10, 1999
+October 12, 1999
diff --git a/README b/README
index 68f898e..c459ab2 100644
--- a/README
+++ b/README
@@ -1,14 +1,15 @@
-README for libpng 1.0.4e - October 10, 1999 (shared library 2.1)
+README for libpng 1.0.4f - October 12, 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
+Version 0.89 was 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 since mid-1995.  By late 1997 it had
+finally gotten to the stage where there hadn't been significant
 changes to the API in some time, and people have a bad feeling about
-libraries with versions < 1.0.
+libraries with versions < 1.0.  Version 1.0.0 was released in
+March 1998.
 
 ****
 Note that some of the changes to the png_info structure render this
@@ -28,8 +29,8 @@
 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.
+the new APIs (as shown in example.c and pngtest.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
@@ -50,7 +51,8 @@
 
 The changes made to the library, and bugs fixed are based on discussions
 on the PNG implementation mailing list <png-implement@ccrc.wustl.edu>
-and not on material submitted to Guy.
+and not on material submitted privately to Guy, Andreas, or Glenn.  They will
+forward any good suggestions to the list.
 
 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
@@ -163,9 +165,9 @@
        makefile.sgi     =>  Silicon Graphics IRIX makefile
        makefile.sunos   =>  Sun makefile
        makefile.solaris =>  Solaris 2.X makefile
-                            (gcc, creates libpng.so.2.1.0.4e)
+                            (gcc, creates libpng.so.2.1.0.4f)
        makefile.linux   =>  Linux/ELF makefile
-                            (gcc, creates libpng.so.2.1.0.4e)
+                            (gcc, creates libpng.so.2.1.0.4f)
        makefile.sco     =>  For SCO OSr5  ELF and Unixware 7 with Native cc
        makefile.mips    =>  MIPS makefile
        makefile.acorn   =>  Acorn makefile
diff --git a/Y2KINFO b/Y2KINFO
index ba7be80..8e9ba84 100644
--- a/Y2KINFO
+++ b/Y2KINFO
@@ -1,13 +1,13 @@
    Y2K compliance in libpng:
    =========================
       
-      October 10, 1999
+      October 12, 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.71 and
-      upward through 1.0.4e are Y2K compliant.  It is my belief that earlier
+      upward through 1.0.4f 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
diff --git a/contrib/gregbook/README b/contrib/gregbook/README
new file mode 100644
index 0000000..fa3871a
--- /dev/null
+++ b/contrib/gregbook/README
@@ -0,0 +1,52 @@
+PNG: The Definitive Guide: Source Code
+
+Chapters 13, 14 and 15 of PNG: The Definitive Guide discuss three
+cross-platform demo programs that show how to use the libpng reference
+library: rpng, rpng2 and wpng. rpng and rpng2 are viewers; the first is a
+very simple example that that shows how a standard file-viewer might use
+libpng, while the second is designed to process streaming data and shows how
+a web browser might be written. wpng is a simple command-line program that
+reads binary PPM files (the ``raw'' RGB subset of NetPBM) and converts them
+to PNG.
+
+The source code for all three demo programs currently compiles only under
+Unix and 32-bit Windows. It has been tested with gcc 2.7.2.3 under Linux and
+Solaris and with Microsoft Visual C++ 5.0 under Windows 95. Brief
+instructions for compiling the programs are included at the top of the
+makefiles; makefile.unx is the Unix version, and makefile.w32 is (you
+guessed it!) the version for 32-bit Windows. libpng and zlib are required.
+
+----------------------------------------------------------------------------
+
+License
+
+The source code to the demo programs may be used and distributed freely
+(even if you didn't buy the book--but feel free to do so at any time),
+subject to the terms of the following BSD-like license:
+
+     Copyright (c) 1998-1999 Greg Roelofs. All rights reserved.
+
+     This software is provided "as is," without warranty of any kind,
+     express or implied. In no event shall the author or contributors
+     be held liable for any damages arising in any way from the use of
+     this software.
+
+     Permission is granted to anyone to use this software for any
+     purpose, including commercial applications, and to alter it and
+     redistribute it freely, subject to the following restrictions:
+
+       1. Redistributions of source code must retain the above
+          copyright notice, disclaimer, and this list of conditions.
+       2. Redistributions in binary form must reproduce the above
+          copyright notice, disclaimer, and this list of conditions in
+          the documentation and/or other materials provided with the
+          distribution.
+       3. All advertising materials mentioning features or use of this
+          software must display the following acknowledgment:
+
+               This product includes software developed by Greg Roelofs
+               and contributors for the book, "PNG: The Definitive
+               Guide," published by O'Reilly and Associates.
+
+----------------------------------------------------------
+http://www.cdrom.com/pub/png/book/sources.html
diff --git a/contrib/gregbook/makefile.unx b/contrib/gregbook/makefile.unx
new file mode 100644
index 0000000..93d46d1
--- /dev/null
+++ b/contrib/gregbook/makefile.unx
@@ -0,0 +1,95 @@
+# Sample makefile for rpng-x / rpng2-x / wpng using gcc and make.
+# Greg Roelofs
+# Last modified:  16 February 1999
+#
+#	The programs built by this makefile are described in the book,
+#	"PNG:  The Definitive Guide," by Greg Roelofs (O'Reilly and
+#	Associates, 1999).  Go buy a copy, eh?  Buy some for friends
+#	and family, too.  (Not that this is a blatant plug or anything.)
+#
+# Invoke this makefile from a shell prompt in the usual way; for example:
+#
+#	make -f makefile.unx
+#
+# This makefile assumes libpng and zlib have already been built or downloaded
+# and are both installed in /usr/local/{include,lib} (as indicated by the
+# PNGPATH and ZPATH macros below).  Edit as appropriate.
+#
+# This makefile builds statically linked executables (against libpng and zlib,
+# that is), but that can be changed by uncommenting the appropriate PNGLIB and
+# ZLIB lines.
+
+
+# macros --------------------------------------------------------------------
+
+PNGPATH = /usr/local
+PNGINC = -I$(PNGPATH)/include
+#PNGLIB = -L$(PNGPATH)/lib -lpng
+PNGLIB = $(PNGPATH)/lib/libpng.a
+
+ZPATH = /usr/local
+ZINC = -I$(ZPATH)/include
+#ZLIB = -L$(ZPATH)/lib -lz
+ZLIB = $(ZPATH)/lib/libz.a
+
+#XPATH = /usr/X11
+XPATH = /usr/X11R6
+XINC = -I$(XPATH)/include
+XLIB = -L$(XPATH)/lib -lX11
+
+INCS = $(PNGINC) $(ZINC) $(XINC)
+RLIBS = $(PNGLIB) $(ZLIB) $(XLIB) -lm
+WLIBS = $(PNGLIB) $(ZLIB) -lm
+
+CC = gcc
+LD = gcc
+RM = rm -f
+CFLAGS = -O -Wall $(INCS)
+# [note that -Wall is a gcc-specific compilation flag ("all warnings on")]
+LDFLAGS =
+O = .o
+E =
+
+RPNG  = rpng-x
+RPNG2 = rpng2-x
+WPNG  = wpng
+
+ROBJS  = $(RPNG)$(O) readpng$(O)
+ROBJS2 = $(RPNG2)$(O) readpng2$(O)
+WOBJS  = $(WPNG)$(O) writepng$(O)
+
+EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E)
+
+
+# implicit make rules -------------------------------------------------------
+
+.c$(O):
+	$(CC) -c $(CFLAGS) $<
+
+
+# dependencies --------------------------------------------------------------
+
+all:  $(EXES)
+
+$(RPNG)$(E): $(ROBJS)
+	$(LD) $(LDFLAGS) -o $@ $(ROBJS) $(RLIBS)
+
+$(RPNG2)$(E): $(ROBJS2)
+	$(LD) $(LDFLAGS) -o $@ $(ROBJS2) $(RLIBS)
+
+$(WPNG)$(E): $(WOBJS)
+	$(LD) $(LDFLAGS) -o $@ $(WOBJS) $(WLIBS)
+
+$(RPNG)$(O):	$(RPNG).c readpng.h
+$(RPNG2)$(O):	$(RPNG2).c readpng2.h
+$(WPNG)$(O):	$(WPNG).c writepng.h
+
+readpng$(O):	readpng.c readpng.h
+readpng2$(O):	readpng2.c readpng2.h
+writepng$(O):	writepng.c writepng.h
+
+
+# maintenance ---------------------------------------------------------------
+
+clean:
+	$(RM) $(EXES) $(ROBJS) $(ROBJS2) $(WOBJS)
diff --git a/contrib/gregbook/makefile.w32 b/contrib/gregbook/makefile.w32
new file mode 100644
index 0000000..dbcec3b
--- /dev/null
+++ b/contrib/gregbook/makefile.w32
@@ -0,0 +1,112 @@
+# Sample makefile for rpng-win / rpng2-win / wpng using MSVC and NMAKE.
+# Greg Roelofs
+# Last modified:  16 February 1999
+#
+#	The programs built by this makefile are described in the book,
+#	"PNG:  The Definitive Guide," by Greg Roelofs (O'Reilly and
+#	Associates, 1999).  Go buy a copy, eh?  Buy some for friends
+#	and family, too.  (Not that this is a blatant plug or anything.)
+#
+# Invoke this makefile from a DOS prompt window via:
+#
+#	%devstudio%\vc\bin\vcvars32.bat
+#	nmake -nologo -f makefile.w32
+#
+# where %devstudio% is the installation directory for MSVC / DevStudio.  If
+# you get "environment out of space" errors, create a desktop shortcut with
+# "c:\windows\command.com /e:4096" as the program command line and set the
+# working directory to this directory.  Then double-click to open the new
+# DOS-prompt window with a bigger environment and retry the commands above.
+# 
+# This makefile assumes libpng and zlib have already been built or downloaded
+# and are in subdirectories at the same level as the current subdirectory
+# (as indicated by the PNGPATH and ZPATH macros below).  Edit as appropriate.
+#
+# Note that the names of the dynamic and static libpng and zlib libraries
+# used below may change in later releases of the libraries.  This makefile
+# builds statically linked executables, but that can be changed by uncom-
+# menting the appropriate PNGLIB and ZLIB lines.
+
+!include <ntwin32.mak>
+
+
+# macros --------------------------------------------------------------------
+
+PNGPATH = ../libpng
+PNGINC = -I$(PNGPATH)
+#PNGLIB = $(PNGPATH)/pngdll.lib
+PNGLIB = $(PNGPATH)/libpng.lib
+
+ZPATH = ../zlib
+ZINC = -I$(ZPATH)
+#ZLIB = $(ZPATH)/zlibdll.lib
+ZLIB = $(ZPATH)/zlibstat.lib
+
+WINLIBS = -defaultlib:user32.lib gdi32.lib
+# ["real" apps may also need comctl32.lib, comdlg32.lib, winmm.lib, etc.]
+
+INCS = $(PNGINC) $(ZINC)
+RLIBS = $(PNGLIB) $(ZLIB) $(WINLIBS)
+WLIBS = $(PNGLIB) $(ZLIB)
+
+CC = cl
+LD = link
+RM = del
+CFLAGS = -nologo -O -W3 $(INCS) $(cvars)
+# [note that -Wall is an MSVC-specific compilation flag ("all warnings on")]
+# [see %devstudio%\vc\include\win32.mak for cvars macro definition]
+O = .obj
+E = .exe
+
+RLDFLAGS = -nologo -subsystem:windows
+WLDFLAGS = -nologo
+
+RPNG  = rpng-win
+RPNG2 = rpng2-win
+WPNG  = wpng
+
+ROBJS  = $(RPNG)$(O) readpng$(O)
+ROBJS2 = $(RPNG2)$(O) readpng2$(O)
+WOBJS  = $(WPNG)$(O) writepng$(O)
+
+EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E)
+
+
+# implicit make rules -------------------------------------------------------
+
+.c$(O):
+	$(CC) -c $(CFLAGS) $<
+
+
+# dependencies --------------------------------------------------------------
+
+all:  $(EXES)
+
+$(RPNG)$(E): $(ROBJS)
+	$(LD) $(RLDFLAGS) -out:$@ $(ROBJS) $(RLIBS)
+
+$(RPNG2)$(E): $(ROBJS2)
+	$(LD) $(RLDFLAGS) -out:$@ $(ROBJS2) $(RLIBS)
+
+$(WPNG)$(E): $(WOBJS)
+	$(LD) $(WLDFLAGS) -out:$@ $(WOBJS) $(WLIBS)
+
+$(RPNG)$(O):	$(RPNG).c readpng.h
+$(RPNG2)$(O):	$(RPNG2).c readpng2.h
+$(WPNG)$(O):	$(WPNG).c writepng.h
+
+readpng$(O):	readpng.c readpng.h
+readpng2$(O):	readpng2.c readpng2.h
+writepng$(O):	writepng.c writepng.h
+
+
+# maintenance ---------------------------------------------------------------
+
+clean:
+#	ideally we could just do this:
+#	$(RM) $(EXES) $(ROBJS) $(ROBJS2) $(WOBJS)
+#	...but the Windows "DEL" command is none too bright, so:
+	$(RM) r*$(E)
+	$(RM) w*$(E)
+	$(RM) r*$(O)
+	$(RM) w*$(O)
diff --git a/contrib/gregbook/readpng.c b/contrib/gregbook/readpng.c
new file mode 100644
index 0000000..2d584fd
--- /dev/null
+++ b/contrib/gregbook/readpng.c
@@ -0,0 +1,275 @@
+/*---------------------------------------------------------------------------
+
+   rpng - simple PNG display program                              readpng.c
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-1999 Greg Roelofs.  All rights reserved.
+
+      This software is provided "as is," without warranty of any kind,
+      express or implied.  In no event shall the author or contributors
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+  ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "png.h"	/* libpng header; includes zlib.h */
+#include "readpng.h"	/* typedefs, common macros, public prototypes */
+
+
+static png_structp png_ptr = NULL;
+static png_infop info_ptr = NULL;
+
+png_uint_32  width, height;
+int  bit_depth, color_type;
+uch  *image_data = NULL;
+
+
+void readpng_version_info()
+{
+    fprintf(stderr, "   Compiled with libpng %s; using libpng %s.\n",
+      PNG_LIBPNG_VER_STRING, png_libpng_ver);
+    fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
+      ZLIB_VERSION, zlib_version);
+} 
+
+
+/* return value = 0 for success, 1 for bad sig, 2 for bad IHDR, 4 for no mem */
+
+int readpng_init(FILE *infile, long *pWidth, long *pHeight)
+{
+    uch sig[8];
+
+
+    /* first do a quick check that the file really is a PNG image; could
+     * have used slightly more general png_sig_cmp() function instead */
+
+    fread(sig, 1, 8, infile);
+    if (!png_check_sig(sig, 8))
+        return 1;   /* bad signature */
+
+
+    /* could pass pointers to user-defined error handlers instead of NULLs: */
+
+    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    if (!png_ptr)
+        return 4;   /* out of memory */
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_read_struct(&png_ptr, NULL, NULL);
+        return 4;   /* out of memory */
+    }
+
+
+    /* we could create a second info struct here (end_info), but it's only
+     * useful if we want to keep pre- and post-IDAT chunk info separated
+     * (mainly for PNG-aware image editors and converters) */
+
+
+    /* setjmp() must be called in every function that calls a PNG-reading
+     * libpng function */
+
+    if (setjmp(png_ptr->jmpbuf)) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return 2;
+    }
+
+
+    png_init_io(png_ptr, infile);
+    png_set_sig_bytes(png_ptr, 8);  /* we already read the 8 signature bytes */
+
+    png_read_info(png_ptr, info_ptr);  /* read all PNG info up to image data */
+
+
+    /* alternatively, could make separate calls to png_get_image_width(),
+     * etc., but want bit_depth and color_type for later [don't care about
+     * compression_type and filter_type => NULLs] */
+
+    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+      NULL, NULL, NULL);
+    *pWidth = width;
+    *pHeight = height;
+
+
+    /* OK, that's all we need for now; return happy */
+
+    return 0;
+}
+
+
+
+
+/* returns 0 if succeeds, 1 if fails due to no bKGD chunk, 2 if libpng error;
+ * scales values to 8-bit if necessary */
+
+int readpng_get_bgcolor(uch *red, uch *green, uch *blue)
+{
+    png_color_16p pBackground;
+
+
+    /* setjmp() must be called in every function that calls a PNG-reading
+     * libpng function */
+
+    if (setjmp(png_ptr->jmpbuf)) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return 2;
+    }
+
+
+    if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
+        return 1;
+
+    /* it is not obvious from the libpng documentation, but this function
+     * takes a pointer to a pointer, and it always returns valid red, green
+     * and blue values, regardless of color_type: */
+
+    png_get_bKGD(png_ptr, info_ptr, &pBackground);
+
+
+    /* however, it always returns the raw bKGD data, regardless of any
+     * bit-depth transformations, so check depth and adjust if necessary */
+
+    if (bit_depth == 16) {
+        *red   = pBackground->red   >> 8;
+        *green = pBackground->green >> 8;
+        *blue  = pBackground->blue  >> 8;
+    } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+        if (bit_depth == 1)
+            *red = *green = *blue = pBackground->gray? 255 : 0;
+        else if (bit_depth == 2)
+            *red = *green = *blue = (255/3) * pBackground->gray;
+        else /* bit_depth == 4 */
+            *red = *green = *blue = (255/15) * pBackground->gray;
+    } else {
+        *red   = (uch)pBackground->red;
+        *green = (uch)pBackground->green;
+        *blue  = (uch)pBackground->blue;
+    }
+
+    return 0;
+}
+
+
+
+
+/* display_exponent == LUT_exponent * CRT_exponent */
+
+uch *readpng_get_image(double display_exponent, int *pChannels, ulg *pRowbytes)
+{
+    double  gamma;
+    png_uint_32  i, rowbytes;
+    png_bytepp  row_pointers = NULL;
+
+
+    /* setjmp() must be called in every function that calls a PNG-reading
+     * libpng function */
+
+    if (setjmp(png_ptr->jmpbuf)) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return NULL;
+    }
+
+
+    /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
+     * transparency chunks to full alpha channel; strip 16-bit-per-sample
+     * images to 8 bits per sample; and convert grayscale to RGB[A] */
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE)
+        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);
+    if (bit_depth == 16)
+        png_set_strip_16(png_ptr);
+    if (color_type == PNG_COLOR_TYPE_GRAY ||
+        color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+        png_set_gray_to_rgb(png_ptr);
+
+
+    /* unlike the example in the libpng documentation, we have *no* idea where
+     * this file may have come from--so if it doesn't have a file gamma, don't
+     * do any correction ("do no harm") */
+
+    if (png_get_gAMA(png_ptr, info_ptr, &gamma))
+        png_set_gamma(png_ptr, display_exponent, gamma);
+
+
+    /* all transformations have been registered; now update info_ptr data,
+     * get rowbytes and channels, and allocate image memory */
+
+    png_read_update_info(png_ptr, info_ptr);
+
+    *pRowbytes = rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+    *pChannels = (int)png_get_channels(png_ptr, info_ptr);
+
+    if ((image_data = (uch *)malloc(rowbytes*height)) == NULL) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return NULL;
+    }
+    if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        free(image_data);
+        image_data = NULL;
+        return NULL;
+    }
+
+    Trace((stderr, "readpng_get_image:  rowbytes = %ld, height = %ld\n", rowbytes, height));
+
+
+    /* set the individual row_pointers to point at the correct offsets */
+
+    for (i = 0;  i < height;  ++i)
+        row_pointers[i] = image_data + i*rowbytes;
+
+
+    /* now we can go ahead and just read the whole image */
+
+    png_read_image(png_ptr, row_pointers);
+
+
+    /* and we're done!  (png_read_end() can be omitted if no processing of
+     * post-IDAT text/time/etc. is desired) */
+
+    free(row_pointers);
+    row_pointers = NULL;
+
+    png_read_end(png_ptr, NULL);
+
+    return image_data;
+}
+
+
+void readpng_cleanup(int free_image_data)
+{
+    if (free_image_data && image_data) {
+        free(image_data);
+        image_data = NULL;
+    }
+
+    if (png_ptr && info_ptr) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        png_ptr = NULL;
+        info_ptr = NULL;
+    }
+}
diff --git a/contrib/gregbook/readpng.h b/contrib/gregbook/readpng.h
new file mode 100644
index 0000000..31780c5
--- /dev/null
+++ b/contrib/gregbook/readpng.h
@@ -0,0 +1,64 @@
+/*---------------------------------------------------------------------------
+
+   rpng - simple PNG display program                              readpng.h
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-1999 Greg Roelofs.  All rights reserved.
+
+      This software is provided "as is," without warranty of any kind,
+      express or implied.  In no event shall the author or contributors
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+  ---------------------------------------------------------------------------*/
+
+#ifndef TRUE
+#  define TRUE 1
+#  define FALSE 0
+#endif
+
+#ifndef MAX
+#  define MAX(a,b)  ((a) > (b)? (a) : (b))
+#  define MIN(a,b)  ((a) < (b)? (a) : (b))
+#endif
+
+#ifdef DEBUG
+#  define Trace(x)  {fprintf x ; fflush(stderr); fflush(stdout);}
+#else
+#  define Trace(x)  ;
+#endif
+
+typedef unsigned char   uch;
+typedef unsigned short  ush;
+typedef unsigned long   ulg;
+
+
+/* prototypes for public functions in readpng.c */
+
+void readpng_version_info(void);
+
+int readpng_init(FILE *infile, long *pWidth, long *pHeight);
+
+int readpng_get_bgcolor(uch *bg_red, uch *bg_green, uch *bg_blue);
+
+uch *readpng_get_image(double display_exponent, int *pChannels,
+                       ulg *pRowbytes);
+
+void readpng_cleanup(int free_image_data);
diff --git a/contrib/gregbook/readpng2.c b/contrib/gregbook/readpng2.c
new file mode 100644
index 0000000..fe7b0fa
--- /dev/null
+++ b/contrib/gregbook/readpng2.c
@@ -0,0 +1,419 @@
+/*---------------------------------------------------------------------------
+
+   rpng2 - progressive-model PNG display program                 readpng2.c
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-1999 Greg Roelofs.  All rights reserved.
+
+      This software is provided "as is," without warranty of any kind,
+      express or implied.  In no event shall the author or contributors
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+  ---------------------------------------------------------------------------*/
+
+
+#include <stdlib.h>	/* for exit() prototype */
+
+#include "png.h"	/* libpng header; includes zlib.h and setjmp.h */
+#include "readpng2.h"	/* typedefs, common macros, public prototypes */
+
+
+/* local prototypes */
+
+static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr);
+static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row,
+                                 png_uint_32 row_num, int pass);
+static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr);
+static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg);
+
+
+
+
+void readpng2_version_info()
+{
+    fprintf(stderr, "   Compiled with libpng %s; using libpng %s.\n",
+      PNG_LIBPNG_VER_STRING, png_libpng_ver);
+    fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
+      ZLIB_VERSION, zlib_version);
+} 
+
+
+
+
+int readpng2_check_sig(uch *sig, int num)
+{
+    return png_check_sig(sig, num);
+}
+
+
+
+
+/* returns 0 for success, 2 for libpng problem, 4 for out of memory */
+
+int readpng2_init(mainprog_info *mainprog_ptr)
+{
+    png_structp  png_ptr;	/* note:  temporary variables! */
+    png_infop  info_ptr;
+
+
+    /* could also replace libpng warning-handler (final NULL), but no need: */
+
+    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
+      readpng2_error_handler, NULL);
+    if (!png_ptr)
+        return 4;   /* out of memory */
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_read_struct(&png_ptr, NULL, NULL);
+        return 4;   /* out of memory */
+    }
+
+
+    /* we could create a second info struct here (end_info), but it's only
+     * useful if we want to keep pre- and post-IDAT chunk info separated
+     * (mainly for PNG-aware image editors and converters) */
+
+
+    /* setjmp() must be called in every function that calls a PNG-reading
+     * libpng function, unless an alternate error handler was installed--
+     * but compatible error handlers must either use longjmp() themselves
+     * (as in this program) or exit immediately, so here we are: */
+
+    if (setjmp(mainprog_ptr->jmpbuf)) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return 2;
+    }
+
+
+    /* instead of doing png_init_io() here, now we set up our callback
+     * functions for progressive decoding */
+
+    png_set_progressive_read_fn(png_ptr, mainprog_ptr,
+      readpng2_info_callback, readpng2_row_callback, readpng2_end_callback);
+
+
+    /* make sure we save our pointers for use in readpng2_decode_data() */
+
+    mainprog_ptr->png_ptr = png_ptr;
+    mainprog_ptr->info_ptr = info_ptr;
+
+
+    /* and that's all there is to initialization */
+
+    return 0;
+}
+
+
+
+
+/* returns 0 for success, 2 for libpng (longjmp) problem */
+
+int readpng2_decode_data(mainprog_info *mainprog_ptr, uch *rawbuf, ulg length)
+{
+    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
+    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
+
+
+    /* setjmp() must be called in every function that calls a PNG-reading
+     * libpng function */
+
+    if (setjmp(mainprog_ptr->jmpbuf)) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        mainprog_ptr->png_ptr = NULL;
+        mainprog_ptr->info_ptr = NULL;
+        return 2;
+    }
+
+
+    /* hand off the next chunk of input data to libpng for decoding */
+
+    png_process_data(png_ptr, info_ptr, rawbuf, length);
+
+    return 0;
+}
+
+
+
+
+static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr)
+{
+    mainprog_info  *mainprog_ptr;
+    int  color_type, bit_depth;
+    double  gamma;
+
+
+    /* setjmp() doesn't make sense here, because we'd either have to exit(),
+     * longjmp() ourselves, or return control to libpng, which doesn't want
+     * to see us again.  By not doing anything here, libpng will instead jump
+     * to readpng2_decode_data(), which can return an error value to the main
+     * program. */
+
+
+    /* retrieve the pointer to our special-purpose struct, using the png_ptr
+     * that libpng passed back to us (i.e., not a global this time--there's
+     * no real difference for a single image, but for a multithreaded browser
+     * decoding several PNG images at the same time, one needs to avoid mixing
+     * up different images' structs) */
+
+    mainprog_ptr = png_get_progressive_ptr(png_ptr);
+
+    if (mainprog_ptr == NULL) {		/* we be hosed */
+        fprintf(stderr,
+          "readpng2 error:  main struct not recoverable in info_callback.\n");
+        fflush(stderr);
+        return;
+        /*
+         * Alternatively, we could call our error-handler just like libpng
+         * does, which would effectively terminate the program.  Since this
+         * can only happen if png_ptr gets redirected somewhere odd or the
+         * main PNG struct gets wiped, we're probably toast anyway.  (If
+         * png_ptr itself is NULL, we would not have been called.)
+         */
+    }
+
+
+    /* this is just like in the non-progressive case */
+
+    png_get_IHDR(png_ptr, info_ptr, &mainprog_ptr->width,
+      &mainprog_ptr->height, &bit_depth, &color_type, NULL, NULL, NULL);
+
+
+    /* since we know we've read all of the PNG file's "header" (i.e., up
+     * to IDAT), we can check for a background color here */
+
+    if (mainprog_ptr->need_bgcolor &&
+        png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
+    {
+        png_color_16p pBackground;
+
+        /* it is not obvious from the libpng documentation, but this function
+         * takes a pointer to a pointer, and it always returns valid red,
+         * green and blue values, regardless of color_type: */
+        png_get_bKGD(png_ptr, info_ptr, &pBackground);
+
+        /* however, it always returns the raw bKGD data, regardless of any
+         * bit-depth transformations, so check depth and adjust if necessary */
+        if (bit_depth == 16) {
+            mainprog_ptr->bg_red   = pBackground->red   >> 8;
+            mainprog_ptr->bg_green = pBackground->green >> 8;
+            mainprog_ptr->bg_blue  = pBackground->blue  >> 8;
+        } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+            if (bit_depth == 1)
+                mainprog_ptr->bg_red = mainprog_ptr->bg_green =
+                  mainprog_ptr->bg_blue = pBackground->gray? 255 : 0;
+            else if (bit_depth == 2)
+                mainprog_ptr->bg_red = mainprog_ptr->bg_green =
+                  mainprog_ptr->bg_blue = (255/3) * pBackground->gray;
+            else /* bit_depth == 4 */
+                mainprog_ptr->bg_red = mainprog_ptr->bg_green =
+                  mainprog_ptr->bg_blue = (255/15) * pBackground->gray;
+        } else {
+            mainprog_ptr->bg_red   = (uch)pBackground->red;
+            mainprog_ptr->bg_green = (uch)pBackground->green;
+            mainprog_ptr->bg_blue  = (uch)pBackground->blue;
+        }
+    }
+
+
+    /* as before, let libpng expand palette images to RGB, low-bit-depth
+     * grayscale images to 8 bits, transparency chunks to full alpha channel;
+     * strip 16-bit-per-sample images to 8 bits per sample; and convert
+     * grayscale to RGB[A] */
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE)
+        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);
+    if (bit_depth == 16)
+        png_set_strip_16(png_ptr);
+    if (color_type == PNG_COLOR_TYPE_GRAY ||
+        color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+        png_set_gray_to_rgb(png_ptr);
+
+
+    /* Unlike the basic viewer, which was designed to operate on local files,
+     * this program is intended to simulate a web browser--even though we
+     * actually read from a local file, too.  But because we are pretending
+     * that most of the images originate on the Internet, we follow the recom-
+     * mendation of the sRGB proposal and treat unlabelled images (no gAMA
+     * chunk) as existing in the sRGB color space.  That is, we assume that
+     * such images have a file gamma of 0.45455, which corresponds to a PC-like
+     * display system.  This change in assumptions will have no effect on a
+     * PC-like system, but on a Mac, SGI, NeXT or other system with a non-
+     * identity lookup table, it will darken unlabelled images, which effec-
+     * tively favors images from PC-like systems over those originating on
+     * the local platform.  Note that mainprog_ptr->display_exponent is the
+     * "gamma" value for the entire display system, i.e., the product of
+     * LUT_exponent and CRT_exponent. */
+
+    if (png_get_gAMA(png_ptr, info_ptr, &gamma))
+        png_set_gamma(png_ptr, mainprog_ptr->display_exponent, gamma);
+    else
+        png_set_gamma(png_ptr, mainprog_ptr->display_exponent, 0.45455);
+
+
+    /* we'll let libpng expand interlaced images, too */
+
+    mainprog_ptr->passes = png_set_interlace_handling(png_ptr);
+
+
+    /* all transformations have been registered; now update info_ptr data and
+     * then get rowbytes and channels */
+
+    png_read_update_info(png_ptr, info_ptr);
+
+    mainprog_ptr->rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+    mainprog_ptr->channels = png_get_channels(png_ptr, info_ptr);
+
+
+    /* Call the main program to allocate memory for the image buffer and
+     * initialize windows and whatnot.  (The old-style function-pointer
+     * invocation is used for compatibility with a few supposedly ANSI
+     * compilers that nevertheless barf on "fn_ptr()"-style syntax.) */
+
+    (*mainprog_ptr->mainprog_init)();
+
+
+    /* and that takes care of initialization */
+
+    return;
+}
+
+
+
+
+
+static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row,
+                                  png_uint_32 row_num, int pass)
+{
+    mainprog_info  *mainprog_ptr;
+
+
+    /* first check whether the row differs from the previous pass; if not,
+     * nothing to combine or display */
+
+    if (!new_row)
+        return;
+
+
+    /* retrieve the pointer to our special-purpose struct so we can access
+     * the old rows and image-display callback function */
+
+    mainprog_ptr = png_get_progressive_ptr(png_ptr);
+
+
+    /* have libpng either combine the new row data with the existing row data
+     * from previous passes (if interlaced) or else just copy the new row
+     * into the main program's image buffer */
+
+    png_progressive_combine_row(png_ptr, mainprog_ptr->row_pointers[row_num],
+      new_row);
+
+
+    /* finally, call the display routine in the main program with the number
+     * of the row we just updated */
+
+    (*mainprog_ptr->mainprog_display_row)(row_num);
+
+
+    /* and we're ready for more */
+
+    return;
+}
+
+
+
+
+
+static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr)
+{
+    mainprog_info  *mainprog_ptr;
+
+
+    /* retrieve the pointer to our special-purpose struct */
+
+    mainprog_ptr = png_get_progressive_ptr(png_ptr);
+
+
+    /* let the main program know that it should flush any buffered image
+     * data to the display now and set a "done" flag or whatever, but note
+     * that it SHOULD NOT DESTROY THE PNG STRUCTS YET--in other words, do
+     * NOT call readpng2_cleanup() either here or in the finish_display()
+     * routine; wait until control returns to the main program via
+     * readpng2_decode_data() */
+
+    (*mainprog_ptr->mainprog_finish_display)();
+
+
+    /* all done */
+
+    return;
+}
+
+
+
+
+
+void readpng2_cleanup(mainprog_info *mainprog_ptr)
+{
+    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
+    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
+
+    if (png_ptr && info_ptr)
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+    mainprog_ptr->png_ptr = NULL;
+    mainprog_ptr->info_ptr = NULL;
+}
+
+
+
+
+
+static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg)
+{
+    mainprog_info  *mainprog_ptr;
+
+    /* This function, aside from the extra step of retrieving the "error
+     * pointer" (below) and the fact that it exists within the application
+     * rather than within libpng, is essentially identical to libpng's
+     * default error handler.  The second point is critical:  since both
+     * setjmp() and longjmp() are called from the same code, they are
+     * guaranteed to have compatible notions of how big a jmp_buf is,
+     * regardless of whether _BSD_SOURCE or anything else has (or has not)
+     * been defined. */
+
+    fprintf(stderr, "readpng2 libpng error: %s\n", msg);
+    fflush(stderr);
+
+    mainprog_ptr = png_get_error_ptr(png_ptr);
+    if (mainprog_ptr == NULL) {		/* we are completely hosed now */
+        fprintf(stderr,
+          "readpng2 severe error:  jmpbuf not recoverable; terminating.\n");
+        fflush(stderr);
+        exit(99);
+    }
+
+    longjmp(mainprog_ptr->jmpbuf, 1);
+}
diff --git a/contrib/gregbook/readpng2.h b/contrib/gregbook/readpng2.h
new file mode 100644
index 0000000..9d22444
--- /dev/null
+++ b/contrib/gregbook/readpng2.h
@@ -0,0 +1,85 @@
+/*---------------------------------------------------------------------------
+
+   rpng2 - progressive-model PNG display program                 readpng2.h
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-1999 Greg Roelofs.  All rights reserved.
+
+      This software is provided "as is," without warranty of any kind,
+      express or implied.  In no event shall the author or contributors
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+  ---------------------------------------------------------------------------*/
+
+#ifndef TRUE
+#  define TRUE 1
+#  define FALSE 0
+#endif
+
+#ifndef MAX
+#  define MAX(a,b)  ((a) > (b)? (a) : (b))
+#  define MIN(a,b)  ((a) < (b)? (a) : (b))
+#endif
+
+#ifdef DEBUG
+#  define Trace(x)  {fprintf x ; fflush(stderr); fflush(stdout);}
+#else
+#  define Trace(x)  ;
+#endif
+
+typedef unsigned char   uch;
+typedef unsigned short  ush;
+typedef unsigned long   ulg;
+
+typedef struct _mainprog_info {
+    double display_exponent;
+    ulg width;
+    ulg height;
+    void *png_ptr;
+    void *info_ptr;
+    void (*mainprog_init)(void);
+    void (*mainprog_display_row)(ulg row_num);
+    void (*mainprog_finish_display)(void);
+    uch *image_data;
+    uch **row_pointers;
+    jmp_buf jmpbuf;
+    int passes;			/* not used */
+    int rowbytes;
+    int channels;
+    int need_bgcolor;
+    int done;
+    uch bg_red;
+    uch bg_green;
+    uch bg_blue;
+} mainprog_info;
+
+
+/* prototypes for public functions in readpng2.c */
+
+void readpng2_version_info(void);
+
+int readpng2_check_sig(uch *sig, int num);
+
+int readpng2_init(mainprog_info *mainprog_ptr);
+
+int readpng2_decode_data(mainprog_info *mainprog_ptr, uch *rawbuf, ulg length);
+
+void readpng2_cleanup(mainprog_info *mainprog_ptr);
diff --git a/contrib/gregbook/rpng-win.c b/contrib/gregbook/rpng-win.c
new file mode 100644
index 0000000..3460132
--- /dev/null
+++ b/contrib/gregbook/rpng-win.c
@@ -0,0 +1,600 @@
+/*---------------------------------------------------------------------------
+
+   rpng - simple PNG display program                             rpng-win.c
+
+   This program decodes and displays PNG images, with gamma correction and
+   optionally with a user-specified background color (in case the image has
+   transparency).  It is very nearly the most basic PNG viewer possible.
+   This version is for 32-bit Windows; it may compile under 16-bit Windows
+   with a little tweaking (or maybe not).
+
+   to do:
+    - stdout/stderr don't work!  need message window (maybe scrollable?)
+    - handle quoted command-line args (especially filenames with spaces)
+    - have minimum window width:  oh well
+    - use %.1023s to simplify truncation of title-bar string?
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-1999 Greg Roelofs.  All rights reserved.
+
+      This software is provided "as is," without warranty of any kind,
+      express or implied.  In no event shall the author or contributors
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+  ---------------------------------------------------------------------------*/
+
+#define PROGNAME  "rpng-win"
+#define LONGNAME  "Simple PNG Viewer for Windows"
+#define VERSION   "1.0 of 20 February 1999"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <windows.h>
+
+/* #define DEBUG  :  this enables the Trace() macros */
+
+#include "readpng.h"	/* typedefs, common macros, readpng prototypes */
+
+
+/* could just include png.h, but this macro is the only thing we need
+ * (name and typedefs changed to local versions); note that side effects
+ * only happen with alpha (which could easily be avoided with
+ * "ush acopy = (alpha);") */
+
+#define alpha_composite(composite, fg, alpha, bg) {			\
+    ush temp = ((ush)(fg)*(ush)(alpha) +				\
+                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);	\
+    (composite) = (uch)((temp + (temp >> 8)) >> 8);			\
+}
+
+
+/* local prototypes */
+static int        rpng_win_create_window(HINSTANCE hInst, int showmode);
+static int        rpng_win_display_image(void);
+static void       rpng_win_cleanup(void);
+LRESULT CALLBACK  rpng_win_wndproc(HWND, UINT, WPARAM, LPARAM);
+
+
+static char titlebar[1024], *window_name = titlebar;
+static char *progname = PROGNAME;
+static char *appname = LONGNAME;
+static char *icon_name = PROGNAME;	/* GRR:  not (yet) used */
+static char *filename;
+static FILE *infile;
+
+static char *bgstr;
+static uch bg_red=0, bg_green=0, bg_blue=0;
+
+static double display_exponent;
+
+static ulg image_width, image_height, image_rowbytes;
+static int image_channels;
+static uch *image_data;
+
+/* Windows-specific variables */
+static ulg wimage_rowbytes;
+static uch *dib;
+static uch *wimage_data;
+static BITMAPINFOHEADER *bmih;
+
+static HWND global_hwnd;
+
+
+
+
+int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
+{
+    char *args[1024];		/* arbitrary limit, but should suffice */
+    char *p, *q, **argv = args;
+    int argc = 0;
+    int rc, alen, flen;
+    int error = 0;
+    int have_bg = FALSE;
+    double LUT_exponent;		/* just the lookup table */
+    double CRT_exponent = 2.2;		/* just the monitor */
+    double default_display_exponent;	/* whole display system */
+    MSG msg;
+
+
+    filename = (char *)NULL;
+
+
+    /* First set the default value for our display-system exponent, i.e.,
+     * the product of the CRT exponent and the exponent corresponding to
+     * the frame-buffer's lookup table (LUT), if any.  This is not an
+     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
+     * ones), but it should cover 99% of the current possibilities.  And
+     * yes, these ifdefs are completely wasted in a Windows program... */
+
+#if defined(NeXT)
+    LUT_exponent = 1.0 / 2.2;
+    /*
+    if (some_next_function_that_returns_gamma(&next_gamma))
+        LUT_exponent = 1.0 / next_gamma;
+     */
+#elif defined(sgi)
+    LUT_exponent = 1.0 / 1.7;
+    /* there doesn't seem to be any documented function to get the
+     * "gamma" value, so we do it the hard way */
+    infile = fopen("/etc/config/system.glGammaVal", "r");
+    if (infile) {
+        double sgi_gamma;
+
+        fgets(tmpline, 80, infile);
+        fclose(infile);
+        sgi_gamma = atof(tmpline);
+        if (sgi_gamma > 0.0)
+            LUT_exponent = 1.0 / sgi_gamma;
+    }
+#elif defined(Macintosh)
+    LUT_exponent = 1.8 / 2.61;
+    /*
+    if (some_mac_function_that_returns_gamma(&mac_gamma))
+        LUT_exponent = mac_gamma / 2.61;                 
+     */
+#else
+    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
+#endif
+
+    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
+    default_display_exponent = LUT_exponent * CRT_exponent;
+
+
+    /* If the user has set the SCREEN_GAMMA environment variable as suggested
+     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
+     * use the default value we just calculated.  Either way, the user may
+     * override this via a command-line option. */
+
+    if ((p = getenv("SCREEN_GAMMA")) != NULL)
+        display_exponent = atof(p);
+    else
+        display_exponent = default_display_exponent;
+
+
+    /* Windows really hates command lines, so we have to set up our own argv.
+     * Note that we do NOT bother with quoted arguments here, so don't use
+     * filenames with spaces in 'em! */
+
+    argv[argc++] = PROGNAME;
+    p = cmd;
+    for (;;) {
+        if (*p == ' ')
+            while (*++p == ' ')
+                ;
+        /* now p points at the first non-space after some spaces */
+        if (*p == '\0')
+            break;    /* nothing after the spaces:  done */
+        argv[argc++] = q = p;
+        while (*q && *q != ' ')
+            ++q;
+        /* now q points at a space or the end of the string */
+        if (*q == '\0')
+            break;    /* last argv already terminated; quit */
+        *q = '\0';    /* change space to terminator */
+        p = q + 1;
+    }
+    argv[argc] = NULL;   /* terminate the argv array itself */
+
+
+    /* Now parse the command line for options and the PNG filename. */
+
+    while (*++argv && !error) {
+        if (!strcmp(*argv, "-gamma")) {
+            if (!*++argv)
+                ++error;
+            display_exponent = atof(*argv);
+            if (display_exponent <= 0.0)
+                ++error;
+        } else if (!strcmp(*argv, "-bgcolor")) {
+            if (!*++argv)
+                ++error;
+            bgstr = *argv;
+            if (strlen(bgstr) != 7 || bgstr[0] != '#')
+                ++error;
+            else
+                have_bg = TRUE;
+        } else {
+            if (**argv != '-') {
+                filename = *argv;
+                if (argv[1])   /* shouldn't be any more args after filename */
+                    ++error;
+            } else
+                ++error;   /* not expecting any other options */
+        }
+    }
+
+    if (!filename) {
+        ++error;
+    } else if (!(infile = fopen(filename, "rb"))) {
+        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
+        ++error;
+    } else {
+        if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) {
+            switch (rc) {
+                case 1:
+                    fprintf(stderr, PROGNAME
+                      ":  [%s] is not a PNG file: incorrect signature\n",
+                      filename);
+                    break;
+                case 2:
+                    fprintf(stderr, PROGNAME
+                      ":  [%s] has bad IHDR (libpng longjmp)\n",
+                      filename);
+                    break;
+                case 4:
+                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
+                    break;
+                default:
+                    fprintf(stderr, PROGNAME
+                      ":  unknown readpng_init() error\n");
+                    break;
+            }
+            ++error;
+        }
+        if (error)
+            fclose(infile);
+    }
+
+    if (error) {
+        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
+        readpng_version_info();
+        fprintf(stderr, "\n"
+         "Usage:  %s [-gamma exp] [-bgcolor bg] file.png\n"
+         "    exp \ttransfer-function exponent (``gamma'') of the display\n"
+         "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
+         "\t\t  to the product of the lookup-table exponent (varies)\n"
+         "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
+         "    bg  \tdesired background color in 7-character hex RGB format\n"
+         "\t\t  (e.g., ``#ff7f00'' for orange:  same as HTML colors);\n"
+         "\t\t  used with transparent images\n"
+         "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
+         "\n", PROGNAME, default_display_exponent);
+        exit(1);
+    }
+
+
+    /* set the title-bar string, but make sure buffer doesn't overflow */
+
+    alen = strlen(appname);
+    flen = strlen(filename);
+    if (alen + flen + 3 > 1023)
+        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
+    else
+        sprintf(titlebar, "%s:  %s", appname, filename);
+
+
+    /* if the user didn't specify a background color on the command line,
+     * check for one in the PNG file--if not, the initialized values of 0
+     * (black) will be used */
+
+    if (have_bg)
+        sscanf(bgstr+1, "%2x%2x%2x", &bg_red, &bg_green, &bg_blue);
+    else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) {
+        readpng_cleanup(TRUE);
+        fprintf(stderr, PROGNAME
+          ":  libpng error while checking for background color\n");
+        exit(2);
+    }
+
+
+    /* do the basic Windows initialization stuff, make the window and fill it
+     * with the background color */
+
+    if (rpng_win_create_window(hInst, showmode))
+        exit(2);
+
+
+    /* decode the image, all at once */
+
+    Trace((stderr, "calling readpng_get_image()\n"))
+    image_data = readpng_get_image(display_exponent, &image_channels,
+      &image_rowbytes);
+    Trace((stderr, "done with readpng_get_image()\n"))
+
+
+    /* done with PNG file, so clean up to minimize memory usage (but do NOT
+     * nuke image_data!) */
+
+    readpng_cleanup(FALSE);
+    fclose(infile);
+
+    if (!image_data) {
+        fprintf(stderr, PROGNAME ":  unable to decode PNG image\n");
+        exit(3);
+    }
+
+
+    /* display image (composite with background if requested) */
+
+    Trace((stderr, "calling rpng_win_display_image()\n"))
+    if (rpng_win_display_image()) {
+        free(image_data);
+        exit(4);
+    }
+    Trace((stderr, "done with rpng_win_display_image()\n"))
+
+
+    /* wait for the user to tell us when to quit */
+
+    while (GetMessage(&msg, NULL, 0, 0)) {
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+    }
+
+
+    /* OK, we're done:  clean up all image and Windows resources and go away */
+
+    rpng_win_cleanup();
+
+    return msg.wParam;
+}
+
+
+
+
+
+static int rpng_win_create_window(HINSTANCE hInst, int showmode)
+{
+    uch *dest;
+    int extra_width, extra_height;
+    ulg i, j;
+    WNDCLASSEX wndclass;
+
+
+/*---------------------------------------------------------------------------
+    Allocate memory for the display-specific version of the image (round up
+    to multiple of 4 for Windows DIB).
+  ---------------------------------------------------------------------------*/
+
+    wimage_rowbytes = ((3*image_width + 3L) >> 2) << 2;
+
+    if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
+                              wimage_rowbytes*image_height)))
+    {
+        return 4;	/* fail */
+    }
+
+/*---------------------------------------------------------------------------
+    Initialize the DIB.  Negative height means to use top-down BMP ordering
+    (must be uncompressed, but that's what we want).  Bit count of 1, 4 or 8
+    implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values
+    directly => wimage_data begins immediately after BMP header.
+  ---------------------------------------------------------------------------*/
+
+    memset(dib, 0, sizeof(BITMAPINFOHEADER));
+    bmih = (BITMAPINFOHEADER *)dib;
+    bmih->biSize = sizeof(BITMAPINFOHEADER);
+    bmih->biWidth = image_width;
+    bmih->biHeight = -((long)image_height);
+    bmih->biPlanes = 1;
+    bmih->biBitCount = 24;
+    bmih->biCompression = 0;
+    wimage_data = dib + sizeof(BITMAPINFOHEADER);
+
+/*---------------------------------------------------------------------------
+    Fill in background color (black by default); data are in BGR order.
+  ---------------------------------------------------------------------------*/
+
+    for (j = 0;  j < image_height;  ++j) {
+        dest = wimage_data + j*wimage_rowbytes;
+        for (i = image_width;  i > 0;  --i) {
+            *dest++ = bg_blue;
+            *dest++ = bg_green;
+            *dest++ = bg_red;
+        }
+    }
+
+/*---------------------------------------------------------------------------
+    Set the window parameters.
+  ---------------------------------------------------------------------------*/
+
+    memset(&wndclass, 0, sizeof(wndclass));
+
+    wndclass.cbSize = sizeof(wndclass);
+    wndclass.style = CS_HREDRAW | CS_VREDRAW;
+    wndclass.lpfnWndProc = rpng_win_wndproc;
+    wndclass.hInstance = hInst;
+    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+    wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
+    wndclass.lpszMenuName = NULL;
+    wndclass.lpszClassName = progname;
+    wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
+
+    RegisterClassEx(&wndclass);
+
+/*---------------------------------------------------------------------------
+    Finally, create the window.
+  ---------------------------------------------------------------------------*/
+
+    extra_width  = 2*(GetSystemMetrics(SM_CXBORDER) +
+                      GetSystemMetrics(SM_CXDLGFRAME));
+    extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
+                      GetSystemMetrics(SM_CYDLGFRAME)) +
+                      GetSystemMetrics(SM_CYCAPTION);
+
+    global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW,
+      CW_USEDEFAULT, CW_USEDEFAULT, image_width+extra_width,
+      image_height+extra_height, NULL, NULL, hInst, NULL);
+
+    ShowWindow(global_hwnd, showmode);
+    UpdateWindow(global_hwnd);
+
+    return 0;
+
+} /* end function rpng_win_create_window() */
+
+
+
+
+
+static int rpng_win_display_image()
+{
+    uch *src, *dest;
+    uch r, g, b, a;
+    ulg i, row, lastrow;
+    RECT rect;
+
+
+    Trace((stderr, "beginning display loop (image_channels == %d)\n",
+      image_channels))
+    Trace((stderr, "(width = %ld, rowbytes = %ld, wimage_rowbytes = %d)\n",
+      image_width, image_rowbytes, wimage_rowbytes))
+
+
+/*---------------------------------------------------------------------------
+    Blast image data to buffer.  This whole routine takes place before the
+    message loop begins, so there's no real point in any pseudo-progressive
+    display...
+  ---------------------------------------------------------------------------*/
+
+    for (lastrow = row = 0;  row < image_height;  ++row) {
+        src = image_data + row*image_rowbytes;
+        dest = wimage_data + row*wimage_rowbytes;
+        if (image_channels == 3) {
+            for (i = image_width;  i > 0;  --i) {
+                r = *src++;
+                g = *src++;
+                b = *src++;
+                *dest++ = b;
+                *dest++ = g;	/* note reverse order */
+                *dest++ = r;
+            }
+        } else /* if (image_channels == 4) */ {
+            for (i = image_width;  i > 0;  --i) {
+                r = *src++;
+                g = *src++;
+                b = *src++;
+                a = *src++;
+                if (a == 255) {
+                    *dest++ = b;
+                    *dest++ = g;
+                    *dest++ = r;
+                } else if (a == 0) {
+                    *dest++ = bg_blue;
+                    *dest++ = bg_green;
+                    *dest++ = bg_red;
+                } else {
+                    /* this macro (copied from png.h) composites the
+                     * foreground and background values and puts the
+                     * result into the first argument; there are no
+                     * side effects with the first argument */
+                    alpha_composite(*dest++, b, a, bg_blue);
+                    alpha_composite(*dest++, g, a, bg_green);
+                    alpha_composite(*dest++, r, a, bg_red);
+                }
+            }
+        }
+        /* display after every 16 lines */
+        if (((row+1) & 0xf) == 0) {
+            rect.left = 0L;
+            rect.top = (LONG)lastrow;
+            rect.right = (LONG)image_width;      /* possibly off by one? */
+            rect.bottom = (LONG)lastrow + 16L;   /* possibly off by one? */
+            InvalidateRect(global_hwnd, &rect, FALSE);
+            UpdateWindow(global_hwnd);     /* similar to XFlush() */
+            lastrow = row + 1;
+        }
+    }
+
+    Trace((stderr, "calling final image-flush routine\n"))
+    if (lastrow < image_height) {
+        rect.left = 0L;
+        rect.top = (LONG)lastrow;
+        rect.right = (LONG)image_width;      /* possibly off by one? */
+        rect.bottom = (LONG)image_height;    /* possibly off by one? */
+        InvalidateRect(global_hwnd, &rect, FALSE);
+        UpdateWindow(global_hwnd);     /* similar to XFlush() */
+    }
+
+/*
+    last param determines whether or not background is wiped before paint
+    InvalidateRect(global_hwnd, NULL, TRUE);
+    UpdateWindow(global_hwnd);
+ */
+
+    return 0;
+}
+
+
+
+
+
+static void rpng_win_cleanup()
+{
+    if (image_data) {
+        free(image_data);
+        image_data = NULL;
+    }
+
+    if (dib) {
+        free(dib);
+        dib = NULL;
+    }
+}
+
+
+
+
+
+LRESULT CALLBACK rpng_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
+{
+    HDC         hdc;
+    PAINTSTRUCT ps;
+    int rc;
+
+    switch (iMsg) {
+        case WM_CREATE:
+            /* one-time processing here, if any */
+            return 0;
+
+        case WM_PAINT:
+            hdc = BeginPaint(hwnd, &ps);
+                    /*                    dest                          */
+            rc = StretchDIBits(hdc, 0, 0, image_width, image_height,
+                    /*                    source                        */
+                                    0, 0, image_width, image_height,
+                                    wimage_data, (BITMAPINFO *)bmih,
+                    /*              iUsage: no clue                     */
+                                    0, SRCCOPY);
+            EndPaint(hwnd, &ps);
+            return 0;
+
+        /* wait for the user to tell us when to quit */
+        case WM_CHAR:
+            switch (wP) {	/* only need one, so ignore repeat count */
+                case 'q':
+                case 'Q':
+                case 0x1B:	/* Esc key */
+                    PostQuitMessage(0);
+            }
+            return 0;
+
+        case WM_LBUTTONDOWN:	/* another way of quitting */
+        case WM_DESTROY:
+            PostQuitMessage(0);
+            return 0;
+    }
+
+    return DefWindowProc(hwnd, iMsg, wP, lP);
+}
diff --git a/contrib/gregbook/rpng-x.c b/contrib/gregbook/rpng-x.c
new file mode 100644
index 0000000..70a19e5
--- /dev/null
+++ b/contrib/gregbook/rpng-x.c
@@ -0,0 +1,739 @@
+/*---------------------------------------------------------------------------
+
+   rpng - simple PNG display program                               rpng-x.c
+
+   This program decodes and displays PNG images, with gamma correction and
+   optionally with a user-specified background color (in case the image has
+   transparency).  It is very nearly the most basic PNG viewer possible.
+   This version is for the X Window System (tested under Unix, but may work
+   under VMS or OS/2 with a little tweaking).
+
+   to do:
+    - 8-bit support
+    - use %.1023s to simplify truncation of title-bar string?
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-1999 Greg Roelofs.  All rights reserved.
+
+      This software is provided "as is," without warranty of any kind,
+      express or implied.  In no event shall the author or contributors
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+  ---------------------------------------------------------------------------*/
+
+#define PROGNAME  "rpng-x"
+#define LONGNAME  "Simple PNG Viewer for X"
+#define VERSION   "1.01 of 31 March 1999"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#include <X11/keysym.h>
+
+/* #define DEBUG  :  this enables the Trace() macros */
+
+#include "readpng.h"	/* typedefs, common macros, readpng prototypes */
+
+
+/* could just include png.h, but this macro is the only thing we need
+ * (name and typedefs changed to local versions); note that side effects 
+ * only happen with alpha (which could easily be avoided with 
+ * "ush acopy = (alpha);") */
+
+#define alpha_composite(composite, fg, alpha, bg) {			\
+    ush temp = ((ush)(fg)*(ush)(alpha) +				\
+                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);	\
+    (composite) = (uch)((temp + (temp >> 8)) >> 8);			\
+}
+
+
+/* local prototypes */
+static int  rpng_x_create_window(void);
+static int  rpng_x_display_image(void);
+static void rpng_x_cleanup(void);
+static int  rpng_x_msb(ulg u32val);
+
+
+static char titlebar[1024], *window_name = titlebar;
+static char *appname = LONGNAME;
+static char *icon_name = PROGNAME;
+static char *filename;
+static FILE *infile;
+
+static char *bgstr;
+static uch bg_red=0, bg_green=0, bg_blue=0;
+
+static double display_exponent;
+
+static ulg image_width, image_height, image_rowbytes;
+static int image_channels;
+static uch *image_data;
+
+/* X-specific variables */
+static char *displayname;
+static XImage *ximage;
+static Display *display;
+static int bitmap_order;
+static int depth;
+static Visual *visual;
+static int RPixelShift, GPixelShift, BPixelShift;
+static ulg RedMask, GreenMask, BlueMask;
+static Window window;
+static GC gc;
+static Colormap colormap;
+
+static int have_colormap = FALSE;
+static int have_window = FALSE;
+/*
+ulg numcolors=0, pixels[256];
+ush reds[256], greens[256], blues[256];
+ */
+
+
+
+
+int main(int argc, char **argv)
+{
+#ifdef sgi
+    char tmpline[80];
+#endif
+    char *p;
+    int rc, alen, flen;
+    int error = 0;
+    int have_bg = FALSE;
+    double LUT_exponent;		/* just the lookup table */
+    double CRT_exponent = 2.2;		/* just the monitor */
+    double default_display_exponent;	/* whole display system */
+    XEvent e;
+    KeySym k;
+
+
+    displayname = (char *)NULL;
+    filename = (char *)NULL;
+
+
+    /* First set the default value for our display-system exponent, i.e.,
+     * the product of the CRT exponent and the exponent corresponding to
+     * the frame-buffer's lookup table (LUT), if any.  This is not an
+     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
+     * ones), but it should cover 99% of the current possibilities. */
+
+#if defined(NeXT)
+    LUT_exponent = 1.0 / 2.2;
+    /*
+    if (some_next_function_that_returns_gamma(&next_gamma))
+        LUT_exponent = 1.0 / next_gamma;
+     */
+#elif defined(sgi)
+    LUT_exponent = 1.0 / 1.7;
+    /* there doesn't seem to be any documented function to get the
+     * "gamma" value, so we do it the hard way */
+    infile = fopen("/etc/config/system.glGammaVal", "r");
+    if (infile) {
+        double sgi_gamma;
+
+        fgets(tmpline, 80, infile);
+        fclose(infile);
+        sgi_gamma = atof(tmpline);
+        if (sgi_gamma > 0.0)
+            LUT_exponent = 1.0 / sgi_gamma;
+    }
+#elif defined(Macintosh)
+    LUT_exponent = 1.8 / 2.61;
+    /*
+    if (some_mac_function_that_returns_gamma(&mac_gamma))
+        LUT_exponent = mac_gamma / 2.61;
+     */
+#else
+    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
+#endif
+
+    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
+    default_display_exponent = LUT_exponent * CRT_exponent;
+
+
+    /* If the user has set the SCREEN_GAMMA environment variable as suggested
+     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
+     * use the default value we just calculated.  Either way, the user may
+     * override this via a command-line option. */
+
+    if ((p = getenv("SCREEN_GAMMA")) != NULL)
+        display_exponent = atof(p);
+    else
+        display_exponent = default_display_exponent;
+
+
+    /* Now parse the command line for options and the PNG filename. */
+
+    while (*++argv && !error) {
+        if (!strcmp(*argv, "-display")) {
+            if (!*++argv)
+                ++error;
+            displayname = *argv;
+        } else if (!strcmp(*argv, "-gamma")) {
+            if (!*++argv)
+                ++error;
+            display_exponent = atof(*argv);
+            if (display_exponent <= 0.0)
+                ++error;
+        } else if (!strcmp(*argv, "-bgcolor")) {
+            if (!*++argv)
+                ++error;
+            bgstr = *argv;
+            if (strlen(bgstr) != 7 || bgstr[0] != '#')
+                ++error;
+            else
+                have_bg = TRUE;
+        } else {
+            if (**argv != '-') {
+                filename = *argv;
+                if (argv[1])   /* shouldn't be any more args after filename */
+                    ++error;
+            } else
+                ++error;   /* not expecting any other options */
+        }
+    }
+
+    if (!filename) {
+        ++error;
+    } else if (!(infile = fopen(filename, "rb"))) {
+        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
+        ++error;
+    } else {
+        if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) {
+            switch (rc) {
+                case 1:
+                    fprintf(stderr, PROGNAME
+                      ":  [%s] is not a PNG file: incorrect signature\n",
+                      filename);
+                    break;
+                case 2:
+                    fprintf(stderr, PROGNAME
+                      ":  [%s] has bad IHDR (libpng longjmp)\n",
+                      filename);
+                    break;
+                case 4:
+                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
+                    break;
+                default:
+                    fprintf(stderr, PROGNAME
+                      ":  unknown readpng_init() error\n");
+                    break;
+            }
+            ++error;
+        } else {
+            display = XOpenDisplay(displayname);
+            if (!display) {
+                readpng_cleanup(TRUE);
+                fprintf(stderr, PROGNAME ":  can't open X display [%s]\n",
+                  displayname? displayname : "default");
+                ++error;
+            }
+        }
+        if (error)
+            fclose(infile);
+    }
+
+    if (error) {
+        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
+        readpng_version_info();
+        fprintf(stderr, "\n"
+         "Usage:  %s [-display xdpy] [-gamma exp] [-bgcolor bg] file.png\n"
+         "    xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
+         "    exp \ttransfer-function exponent (``gamma'') of the display\n"
+         "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
+         "\t\t  to the product of the lookup-table exponent (varies)\n"
+         "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
+         "    bg  \tdesired background color in 7-character hex RGB format\n"
+         "\t\t  (e.g., ``#ff7f00'' for orange:  same as HTML colors);\n"
+         "\t\t  used with transparent images\n"
+         "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
+         "\n", PROGNAME, default_display_exponent);
+        exit(1);
+    }
+
+
+    /* set the title-bar string, but make sure buffer doesn't overflow */
+
+    alen = strlen(appname);
+    flen = strlen(filename);
+    if (alen + flen + 3 > 1023)
+        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
+    else
+        sprintf(titlebar, "%s:  %s", appname, filename);
+
+
+    /* if the user didn't specify a background color on the command line,
+     * check for one in the PNG file--if not, the initialized values of 0
+     * (black) will be used */
+
+    if (have_bg) {
+        unsigned r, g, b;   /* this approach quiets compiler warnings */
+
+        sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
+        bg_red   = (uch)r;
+        bg_green = (uch)g;
+        bg_blue  = (uch)b;
+    } else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) {
+        readpng_cleanup(TRUE);
+        fprintf(stderr, PROGNAME
+          ":  libpng error while checking for background color\n");
+        exit(2);
+    }
+
+
+    /* do the basic X initialization stuff, make the window and fill it
+     * with the background color */
+
+    if (rpng_x_create_window())
+        exit(2);
+
+
+    /* decode the image, all at once */
+
+    Trace((stderr, "calling readpng_get_image()\n"))
+    image_data = readpng_get_image(display_exponent, &image_channels,
+      &image_rowbytes);
+    Trace((stderr, "done with readpng_get_image()\n"))
+
+
+    /* done with PNG file, so clean up to minimize memory usage (but do NOT
+     * nuke image_data!) */
+
+    readpng_cleanup(FALSE);
+    fclose(infile);
+
+    if (!image_data) {
+        fprintf(stderr, PROGNAME ":  unable to decode PNG image\n");
+        exit(3);
+    }
+
+
+    /* display image (composite with background if requested) */
+
+    Trace((stderr, "calling rpng_x_display_image()\n"))
+    if (rpng_x_display_image()) {
+        free(image_data);
+        exit(4);
+    }
+    Trace((stderr, "done with rpng_x_display_image()\n"))
+
+
+    /* wait for the user to tell us when to quit */
+
+    do
+        XNextEvent(display, &e);
+    while (!(e.type == ButtonPress && e.xbutton.button == Button1) &&
+           !(e.type == KeyPress &&    /*  v--- or 1 for shifted keys */
+             ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape) ));
+
+
+    /* OK, we're done:  clean up all image and X resources and go away */
+
+    rpng_x_cleanup();
+
+    return 0;
+}
+
+
+
+
+
+static int rpng_x_create_window()
+{
+    uch *xdata;
+    int screen, pad;
+    ulg bg_pixel = 0L;
+    Window root;
+    XEvent e;
+    XGCValues gcvalues;
+    XSetWindowAttributes attr;
+    XSizeHints *size_hints;
+    XTextProperty windowName, *pWindowName = &windowName;
+    XTextProperty iconName, *pIconName = &iconName;
+    XVisualInfo visual_info;
+    XWMHints *wm_hints;
+
+
+    bitmap_order = BitmapBitOrder(display);
+    screen = DefaultScreen(display);
+    depth = DisplayPlanes(display, screen);
+    root = RootWindow(display, screen);
+
+/* GRR:  add 8-bit support */
+    if (/* depth != 8 && */ depth != 16 && depth != 24 && depth != 32) {
+        fprintf(stderr,
+          "screen depth %d not supported (only 16-, 24- or 32-bit TrueColor)\n",
+          depth);
+        return 2;
+    }
+
+    XMatchVisualInfo(display, screen, depth,
+      (depth == 8)? PseudoColor : TrueColor, &visual_info);
+    visual = visual_info.visual;
+
+    RedMask   = visual->red_mask;
+    GreenMask = visual->green_mask;
+    BlueMask  = visual->blue_mask;
+
+/* GRR:  add/check 8-bit support */
+    if (depth == 8) {
+        colormap = XCreateColormap(display, root, visual, AllocNone);
+        if (!colormap) {
+            fprintf(stderr, "XCreateColormap() failed\n");
+            return 2;
+        }
+        have_colormap = TRUE;
+    } else if (depth == 16) {
+        RPixelShift = 15 - rpng_x_msb(RedMask);	/* these are right-shifts */
+        GPixelShift = 15 - rpng_x_msb(GreenMask);
+        BPixelShift = 15 - rpng_x_msb(BlueMask);
+    } else /* if (depth > 16) */ {
+        RPixelShift = rpng_x_msb(RedMask) - 7;	/* these are left-shifts */
+        GPixelShift = rpng_x_msb(GreenMask) - 7;
+        BPixelShift = rpng_x_msb(BlueMask) - 7;
+    }
+
+/*---------------------------------------------------------------------------
+    Finally, create the window.
+  ---------------------------------------------------------------------------*/
+
+    attr.backing_store = Always;
+    attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
+
+    window = XCreateWindow(display, root, 0, 0, image_width, image_height,
+      0, depth, InputOutput, visual, CWBackingStore | CWEventMask, &attr);
+
+    if (window == None) {
+        fprintf(stderr, "XCreateWindow() failed\n");
+        return 2;
+    } else
+        have_window = TRUE;
+
+    if (depth == 8)
+        XSetWindowColormap(display, window, colormap);
+
+    if (!XStringListToTextProperty(&window_name, 1, pWindowName))
+        pWindowName = NULL;
+    if (!XStringListToTextProperty(&icon_name, 1, pIconName))
+        pIconName = NULL;
+
+    /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */
+
+    if ((size_hints = XAllocSizeHints()) != NULL) {
+        /* window will not be resizable */
+        size_hints->flags = PMinSize | PMaxSize;
+        size_hints->min_width = size_hints->max_width = image_width;
+        size_hints->min_height = size_hints->max_height = image_height;
+    }
+
+    if ((wm_hints = XAllocWMHints()) != NULL) {
+        wm_hints->initial_state = NormalState;
+        wm_hints->input = True;
+     /* wm_hints->icon_pixmap = icon_pixmap; */
+        wm_hints->flags = StateHint | InputHint  /* | IconPixmapHint */ ;
+    }
+
+    XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
+      size_hints, wm_hints, NULL);
+
+    XMapWindow(display, window);
+
+    gc = XCreateGC(display, window, 0, &gcvalues);
+
+/*---------------------------------------------------------------------------
+    Fill window with the specified background color.
+  ---------------------------------------------------------------------------*/
+
+    if (depth == 24 || depth == 32) {
+        bg_pixel = ((ulg)bg_red   << RPixelShift) |
+                   ((ulg)bg_green << GPixelShift) |
+                   ((ulg)bg_blue  << BPixelShift);
+    } else if (depth == 16) {
+        bg_pixel = ((((ulg)bg_red   << 8) >> RPixelShift) & RedMask)   |
+                   ((((ulg)bg_green << 8) >> GPixelShift) & GreenMask) |
+                   ((((ulg)bg_blue  << 8) >> BPixelShift) & BlueMask);
+    } else /* depth == 8 */ {
+
+        /* GRR:  add 8-bit support */
+
+    }
+
+    XSetForeground(display, gc, bg_pixel);
+    XFillRectangle(display, window, gc, 0, 0, image_width, image_height);
+
+/*---------------------------------------------------------------------------
+    Wait for first Expose event to do any drawing, then flush.
+  ---------------------------------------------------------------------------*/
+
+    do
+        XNextEvent(display, &e);
+    while (e.type != Expose || e.xexpose.count);
+
+    XFlush(display);
+
+/*---------------------------------------------------------------------------
+    Allocate memory for the X- and display-specific version of the image.
+  ---------------------------------------------------------------------------*/
+
+    if (depth == 24 || depth == 32) {
+        xdata = (uch *)malloc(4*image_width*image_height);
+        pad = 32;
+    } else if (depth == 16) {
+        xdata = (uch *)malloc(2*image_width*image_height);
+        pad = 16;
+    } else /* depth == 8 */ {
+        xdata = (uch *)malloc(image_width*image_height);
+        pad = 8;
+    }
+
+    if (!xdata) {
+        fprintf(stderr, PROGNAME ":  unable to allocate image memory\n");
+        return 4;
+    }
+
+    ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
+      (char *)xdata, image_width, image_height, pad, 0);
+
+    if (!ximage) {
+        fprintf(stderr, PROGNAME ":  XCreateImage() failed\n");
+        free(xdata);
+        return 3;
+    }
+
+    /* to avoid testing the bitmap_order every pixel (or doubling the size of
+     * the drawing routine with a giant if-test), we arbitrarily set the byte
+     * order to MSBFirst and let Xlib worry about inverting things on little-
+     * endian machines (like Linux/x86, old VAXen, etc.)--this is not the most
+     * efficient approach (the giant if-test would be better), but in the
+     * interest of clarity, we take the easy way out... */
+
+    ximage->byte_order = MSBFirst;
+
+    return 0;
+
+} /* end function rpng_x_create_window() */
+
+
+
+
+
+static int rpng_x_display_image()
+{
+    uch *src, *dest;
+    uch r, g, b, a;
+    int ximage_rowbytes = ximage->bytes_per_line;
+    ulg i, row, lastrow = 0;
+    ulg pixel;
+
+
+    Trace((stderr, "beginning display loop (image_channels == %d)\n",
+      image_channels))
+    Trace((stderr, "(width = %ld, rowbytes = %ld, ximage_rowbytes = %d)\n",
+      image_width, image_rowbytes, ximage_rowbytes))
+
+    if (depth == 24 || depth == 32) {
+        ulg red, green, blue;
+
+        for (lastrow = row = 0;  row < image_height;  ++row) {
+            src = image_data + row*image_rowbytes;
+            dest = ximage->data + row*ximage_rowbytes;
+            if (image_channels == 3) {
+                for (i = image_width;  i > 0;  --i) {
+                    red   = *src++;
+                    green = *src++;
+                    blue  = *src++;
+                    pixel = (red   << RPixelShift) |
+                            (green << GPixelShift) |
+                            (blue  << BPixelShift);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    *dest++ = ((uch *)&pixel)[3];
+                    *dest++ = ((uch *)&pixel)[2];
+                    *dest++ = ((uch *)&pixel)[1];
+                    *dest++ = ((uch *)&pixel)[0];
+                }
+            } else /* if (image_channels == 4) */ {
+                for (i = image_width;  i > 0;  --i) {
+                    r = *src++;
+                    g = *src++;
+                    b = *src++;
+                    a = *src++;
+                    if (a == 255) {
+                        red   = r;
+                        green = g;
+                        blue  = b;
+                    } else if (a == 0) {
+                        red   = bg_red;
+                        green = bg_green;
+                        blue  = bg_blue;
+                    } else {
+                        /* this macro (from png.h) composites the foreground
+                         * and background values and puts the result into the
+                         * first argument */
+                        alpha_composite(red,   r, a, bg_red);
+                        alpha_composite(green, g, a, bg_green);
+                        alpha_composite(blue,  b, a, bg_blue);
+                    }
+                    pixel = (red   << RPixelShift) |
+                            (green << GPixelShift) |
+                            (blue  << BPixelShift);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    *dest++ = ((uch *)&pixel)[3];
+                    *dest++ = ((uch *)&pixel)[2];
+                    *dest++ = ((uch *)&pixel)[1];
+                    *dest++ = ((uch *)&pixel)[0];
+                }
+            }
+            /* display after every 16 lines */
+            if (((row+1) & 0xf) == 0) {
+                XPutImage(display, window, gc, ximage, 0, lastrow, 0, lastrow,
+                  image_width, 16);
+                XFlush(display);
+                lastrow = row + 1;
+            }
+        }
+
+    } else if (depth == 16) {
+        ush red, green, blue;
+
+        for (lastrow = row = 0;  row < image_height;  ++row) {
+            src = image_data + row*image_rowbytes;
+            dest = ximage->data + row*ximage_rowbytes;
+            if (image_channels == 3) {
+                for (i = image_width;  i > 0;  --i) {
+                    red   = ((ush)(*src) << 8);
+                    ++src;
+                    green = ((ush)(*src) << 8);
+                    ++src;
+                    blue  = ((ush)(*src) << 8);
+                    ++src;
+                    pixel = ((red   >> RPixelShift) & RedMask)   |
+                            ((green >> GPixelShift) & GreenMask) |
+                            ((blue  >> BPixelShift) & BlueMask);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    *dest++ = ((uch *)&pixel)[1];
+                    *dest++ = ((uch *)&pixel)[0];
+                }
+            } else /* if (image_channels == 4) */ {
+                for (i = image_width;  i > 0;  --i) {
+                    r = *src++;
+                    g = *src++;
+                    b = *src++;
+                    a = *src++;
+                    if (a == 255) {
+                        red   = ((ush)r << 8);
+                        green = ((ush)g << 8);
+                        blue  = ((ush)b << 8);
+                    } else if (a == 0) {
+                        red   = ((ush)bg_red   << 8);
+                        green = ((ush)bg_green << 8);
+                        blue  = ((ush)bg_blue  << 8);
+                    } else {
+                        /* this macro (from png.h) composites the foreground
+                         * and background values and puts the result back into
+                         * the first argument (== fg byte here:  safe) */
+                        alpha_composite(r, r, a, bg_red);
+                        alpha_composite(g, g, a, bg_green);
+                        alpha_composite(b, b, a, bg_blue);
+                        red   = ((ush)r << 8);
+                        green = ((ush)g << 8);
+                        blue  = ((ush)b << 8);
+                    }
+                    pixel = ((red   >> RPixelShift) & RedMask)   |
+                            ((green >> GPixelShift) & GreenMask) |
+                            ((blue  >> BPixelShift) & BlueMask);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    *dest++ = ((uch *)&pixel)[1];
+                    *dest++ = ((uch *)&pixel)[0];
+                }
+            }
+            /* display after every 16 lines */
+            if (((row+1) & 0xf) == 0) {
+                XPutImage(display, window, gc, ximage, 0, lastrow, 0, lastrow,
+                  image_width, 16);
+                XFlush(display);
+                lastrow = row + 1;
+            }
+        }
+
+    } else /* depth == 8 */ {
+
+        /* GRR:  add 8-bit support */
+
+    }
+
+    Trace((stderr, "calling final XPutImage()\n"))
+    if (lastrow < image_height) {
+        XPutImage(display, window, gc, ximage, 0, lastrow, 0, lastrow,
+          image_width, image_height-lastrow);
+        XFlush(display);
+    }
+
+    return 0;
+}
+
+
+
+
+static void rpng_x_cleanup()
+{
+    if (image_data) {
+        free(image_data);
+        image_data = NULL;
+    }
+
+    if (ximage) {
+        if (ximage->data) {
+            free(ximage->data);           /* we allocated it, so we free it */
+            ximage->data = (char *)NULL;  /*  instead of XDestroyImage() */
+        }
+        XDestroyImage(ximage);
+        ximage = NULL;
+    }
+
+    XFreeGC(display, gc);
+
+    if (have_window)
+        XDestroyWindow(display, window);
+
+    if (have_colormap)
+        XFreeColormap(display, colormap);
+}
+
+
+
+
+
+static int rpng_x_msb(ulg u32val)
+{
+    int i;
+
+    for (i = 31;  i >= 0;  --i) {
+        if (u32val & 0x80000000L)
+            break;
+        u32val <<= 1;
+    }
+    return i;
+}
diff --git a/contrib/gregbook/rpng2-win.c b/contrib/gregbook/rpng2-win.c
new file mode 100644
index 0000000..fd32ec4
--- /dev/null
+++ b/contrib/gregbook/rpng2-win.c
@@ -0,0 +1,1102 @@
+/*---------------------------------------------------------------------------
+
+   rpng2 - progressive-model PNG display program                rpng2-win.c 
+
+   This program decodes and displays PNG files progressively, as if it were
+   a web browser (though the front end is only set up to read from files).
+   It supports gamma correction, user-specified background colors, and user-
+   specified background patterns (for transparent images).  This version is
+   for 32-bit Windows; it may compile under 16-bit Windows with a little
+   tweaking (or maybe not).  Thanks to Adam Costello and Pieter S. van der
+   Meulen for the "diamond" and "radial waves" patterns, respectively.
+
+   to do:
+    - stdout/stderr don't work!  need message window (maybe scrollable?)
+    - handle quoted command-line args (especially filenames with spaces)
+    - finish resizable checkerboard-gradient (sizes 4-128?)
+    - use %.1023s to simplify truncation of title-bar string?
+    - have minimum window width:  oh well
+
+  ---------------------------------------------------------------------------
+
+   Changelog:
+    - 1.01:  initial public release
+    - 1.02:  fixed cut-and-paste error in usage screen (oops...)
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-1999 Greg Roelofs.  All rights reserved.
+
+      This software is provided "as is," without warranty of any kind,
+      express or implied.  In no event shall the author or contributors
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+  ---------------------------------------------------------------------------*/
+
+#define PROGNAME  "rpng2-win"
+#define LONGNAME  "Progressive PNG Viewer for Windows"
+#define VERSION   "1.02 of 22 September 1999"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>	/* for jmpbuf declaration in readpng2.h */
+#include <time.h>
+#include <math.h>	/* only for PvdM background code */
+#include <windows.h>
+
+/* all for PvdM background code: */
+#ifndef PI
+#  define PI             3.141592653589793238
+#endif
+#define PI_2             (PI*0.5)
+#define INV_PI_360       (360.0 / PI)
+#define MAX(a,b)         (a>b?a:b)
+#define MIN(a,b)         (a<b?a:b)
+#define CLIP(a,min,max)  MAX(min,MIN((a),max))
+#define ABS(a)           ((a)<0?-(a):(a))
+#define CLIP8P(c)        MAX(0,(MIN((c),255)))   /* 8-bit pos. integer (uch) */
+#define ROUNDF(f)        ((int)(f + 0.5))
+
+#define rgb1_max   bg_freq
+#define rgb1_min   bg_gray
+#define rgb2_max   bg_bsat
+#define rgb2_min   bg_brot
+
+/* #define DEBUG */	/* this enables the Trace() macros */
+
+#include "readpng2.h"	/* typedefs, common macros, readpng2 prototypes */
+
+
+/* could just include png.h, but this macro is the only thing we need
+ * (name and typedefs changed to local versions); note that side effects
+ * only happen with alpha (which could easily be avoided with
+ * "ush acopy = (alpha);") */
+
+#define alpha_composite(composite, fg, alpha, bg) {			\
+    ush temp = ((ush)(fg)*(ush)(alpha) +				\
+                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);	\
+    (composite) = (uch)((temp + (temp >> 8)) >> 8);			\
+}
+
+
+#define INBUFSIZE 4096	/* with pseudo-timing on (1 sec delay/block), this
+			 *  block size corresponds roughly to a download
+			 *  speed 10% faster than theoretical 33.6K maximum
+			 *  (assuming 8 data bits, 1 stop bit and no other
+			 *  overhead) */
+
+/* local prototypes */
+static void       rpng2_win_init(void);
+static int        rpng2_win_create_window(void);
+static int        rpng2_win_load_bg_image(void);
+static void       rpng2_win_display_row(ulg row);
+static void       rpng2_win_finish_display(void);
+static void       rpng2_win_cleanup(void);
+LRESULT CALLBACK  rpng2_win_wndproc(HWND, UINT, WPARAM, LPARAM);
+
+
+static char titlebar[1024], *window_name = titlebar;
+static char *progname = PROGNAME;
+static char *appname = LONGNAME;
+static char *icon_name = PROGNAME;	/* GRR:  not (yet) used */
+static char *filename;
+static FILE *infile;
+
+static mainprog_info rpng2_info;
+
+static uch inbuf[INBUFSIZE];
+static int incount;
+
+static int pat = 6;		/* must be less than num_bgpat */
+static int bg_image = 0;
+static int bgscale = 16;
+static ulg bg_rowbytes;
+static uch *bg_data;
+
+static struct rgb_color {
+    uch r, g, b;
+} rgb[] = {
+    {  0,   0,   0},	/*  0:  black */
+    {255, 255, 255},	/*  1:  white */
+    {173, 132,  57},	/*  2:  tan */
+    { 64, 132,   0},	/*  3:  medium green */
+    {189, 117,   1},	/*  4:  gold */
+    {253, 249,   1},	/*  5:  yellow */
+    {  0,   0, 255},	/*  6:  blue */
+    {  0,   0, 120},	/*  7:  medium blue */
+    {255,   0, 255},	/*  8:  magenta */
+    { 64,   0,  64},	/*  9:  dark magenta */
+    {255,   0,   0},	/* 10:  red */
+    { 64,   0,   0},	/* 11:  dark red */
+    {255, 127,   0},	/* 12:  orange */
+    {192,  96,   0},	/* 13:  darker orange */
+    { 24,  60,   0},	/* 14:  dark green-yellow */
+    { 85, 125, 200} 	/* 15:  ice blue */
+};
+/* not used for now, but should be for error-checking:
+static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
+ */
+
+/*
+    This whole struct is a fairly cheesy way to keep the number of
+    command-line options to a minimum.  The radial-waves background
+    type is a particularly poor fit to the integer elements of the
+    struct...but a few macros and a little fixed-point math will do
+    wonders for ya.
+
+    type bits:
+       F E D C B A 9 8 7 6 5 4 3 2 1 0
+                             | | | | |
+                             | | +-+-+-- 0 = sharp-edged checkerboard
+                             | |         1 = soft diamonds
+                             | |         2 = radial waves
+                             | |       3-7 = undefined
+                             | +-- gradient #2 inverted?
+                             +-- alternating columns inverted?
+ */
+static struct background_pattern {
+    ush type;
+    int rgb1_max, rgb1_min;	/* or bg_freq, bg_gray */
+    int rgb2_max, rgb2_min;	/* or bg_bsat, bg_brot (both scaled by 10)*/
+} bg[] = {
+    {0+8,   2,0,  1,15},  	/* checkered:  tan/black vs. white/ice blue */
+    {0+24,  2,0,  1,0},  	/* checkered:  tan/black vs. white/black */
+    {0+8,   4,5,  0,2},  	/* checkered:  gold/yellow vs. black/tan */
+    {0+8,   4,5,  0,6},  	/* checkered:  gold/yellow vs. black/blue */
+    {0,     7,0,  8,9},  	/* checkered:  deep blue/black vs. magenta */
+    {0+8,  13,0,  5,14},  	/* checkered:  orange/black vs. yellow */
+    {0+8,  12,0, 10,11},  	/* checkered:  orange/black vs. red */
+    {1,     7,0,  8,0},  	/* diamonds:  deep blue/black vs. magenta */
+    {1,    12,0, 11,0},  	/* diamonds:  orange vs. dark red */
+    {1,    10,0,  7,0},  	/* diamonds:  red vs. medium blue */
+    {1,     4,0,  5,0},  	/* diamonds:  gold vs. yellow */
+    {1,     3,0,  0,0},  	/* diamonds:  medium green vs. black */
+    {2,    16, 100,  20,   0},  /* radial:  ~hard radial color-beams */
+    {2,    18, 100,  10,   2},  /* radial:  soft, curved radial color-beams */
+    {2,    16, 256, 100, 250},  /* radial:  very tight spiral */
+    {2, 10000, 256,  11,   0}   /* radial:  dipole-moire' (almost fractal) */
+};
+static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
+
+
+/* Windows-specific global variables (could go in struct, but messy...) */
+static ulg wimage_rowbytes;
+static uch *dib;
+static uch *wimage_data;
+static BITMAPINFOHEADER *bmih;
+
+static HWND global_hwnd;
+static HINSTANCE global_hInst;
+static int global_showmode;
+
+
+
+
+int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
+{
+    char *args[1024];		/* arbitrary limit, but should suffice */
+    char *p, *q, *bgstr = NULL, **argv = args;
+    int argc = 0;
+    int rc, alen, flen;
+    int error = 0;
+    int timing = FALSE;
+    int have_bg = FALSE;
+    double LUT_exponent;		/* just the lookup table */
+    double CRT_exponent = 2.2;		/* just the monitor */
+    double default_display_exponent;	/* whole display system */
+    MSG msg;
+
+
+    /* First initialize a few things, just to be sure--memset takes care of
+     * default background color (black), booleans (FALSE), pointers (NULL),
+     * etc. */
+
+    global_hInst = hInst;
+    global_showmode = showmode;
+    filename = (char *)NULL;
+    memset(&rpng2_info, 0, sizeof(mainprog_info));
+
+
+    /* Set the default value for our display-system exponent, i.e., the
+     * product of the CRT exponent and the exponent corresponding to
+     * the frame-buffer's lookup table (LUT), if any.  This is not an
+     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
+     * ones), but it should cover 99% of the current possibilities.  And
+     * yes, these ifdefs are completely wasted in a Windows program... */
+
+#if defined(NeXT)
+    /* third-party utilities can modify the default LUT exponent */
+    LUT_exponent = 1.0 / 2.2;
+    /*
+    if (some_next_function_that_returns_gamma(&next_gamma))
+        LUT_exponent = 1.0 / next_gamma;
+     */
+#elif defined(sgi)
+    LUT_exponent = 1.0 / 1.7;
+    /* there doesn't seem to be any documented function to
+     * get the "gamma" value, so we do it the hard way */
+    infile = fopen("/etc/config/system.glGammaVal", "r");
+    if (infile) {
+        double sgi_gamma;
+
+        fgets(tmpline, 80, infile);
+        fclose(infile);
+        sgi_gamma = atof(tmpline);
+        if (sgi_gamma > 0.0)
+            LUT_exponent = 1.0 / sgi_gamma;
+    }
+#elif defined(Macintosh)
+    LUT_exponent = 1.8 / 2.61;
+    /*
+    if (some_mac_function_that_returns_gamma(&mac_gamma))
+        LUT_exponent = mac_gamma / 2.61;                 
+     */
+#else
+    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
+#endif
+
+    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
+    default_display_exponent = LUT_exponent * CRT_exponent;
+
+
+    /* If the user has set the SCREEN_GAMMA environment variable as suggested
+     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
+     * use the default value we just calculated.  Either way, the user may
+     * override this via a command-line option. */
+
+    if ((p = getenv("SCREEN_GAMMA")) != NULL)
+        rpng2_info.display_exponent = atof(p);
+    else
+        rpng2_info.display_exponent = default_display_exponent;
+
+
+    /* Windows really hates command lines, so we have to set up our own argv.
+     * Note that we do NOT bother with quoted arguments here, so don't use
+     * filenames with spaces in 'em! */
+
+    argv[argc++] = PROGNAME;
+    p = cmd;
+    for (;;) {
+        if (*p == ' ')
+            while (*++p == ' ')
+                ;
+        /* now p points at the first non-space after some spaces */
+        if (*p == '\0')
+            break;    /* nothing after the spaces:  done */
+        argv[argc++] = q = p;
+        while (*q && *q != ' ')
+            ++q;
+        /* now q points at a space or the end of the string */
+        if (*q == '\0')
+            break;    /* last argv already terminated; quit */
+        *q = '\0';    /* change space to terminator */
+        p = q + 1;
+    }
+    argv[argc] = NULL;   /* terminate the argv array itself */
+
+
+    /* Now parse the command line for options and the PNG filename. */
+
+    while (*++argv && !error) {
+        if (!strcmp(*argv, "-gamma")) {
+            if (!*++argv)
+                ++error;
+            rpng2_info.display_exponent = atof(*argv);
+            if (rpng2_info.display_exponent <= 0.0)
+                ++error;
+        } else if (!strcmp(*argv, "-bgcolor")) {
+            if (!*++argv)
+                ++error;
+            bgstr = *argv;
+            if (strlen(bgstr) != 7 || bgstr[0] != '#')
+                ++error;
+            else {
+                have_bg = TRUE;
+                bg_image = FALSE;
+            }
+        } else if (!strcmp(*argv, "-bgpat")) {
+            if (!*++argv)
+                ++error;
+            pat = atoi(*argv) - 1;
+            if (pat < 0 || pat >= num_bgpat)
+                ++error;
+            else {
+                bg_image = TRUE;
+                have_bg = FALSE;
+            }
+        } else if (!strcmp(*argv, "-timing")) {
+            timing = TRUE;
+        } else {
+            if (**argv != '-') {
+                filename = *argv;
+                if (argv[1])   /* shouldn't be any more args after filename */
+                    ++error;
+            } else
+                ++error;   /* not expecting any other options */
+        }
+    }
+
+    if (!filename) {
+        ++error;
+    } else if (!(infile = fopen(filename, "rb"))) {
+        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
+        ++error;
+    } else {
+        incount = fread(inbuf, 1, INBUFSIZE, infile);
+        if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
+            fprintf(stderr, PROGNAME
+              ":  [%s] is not a PNG file: incorrect signature\n",
+              filename);
+            ++error;
+        } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
+            switch (rc) {
+                case 2:
+                    fprintf(stderr, PROGNAME
+                      ":  [%s] has bad IHDR (libpng longjmp)\n",
+                      filename);
+                    break;
+                case 4:
+                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
+                    break;
+                default:
+                    fprintf(stderr, PROGNAME
+                      ":  unknown readpng2_init() error\n");
+                    break;
+            }
+            ++error;
+        }
+        if (error)
+            fclose(infile);
+    }
+
+    if (error) {
+        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
+        readpng2_version_info();
+        fprintf(stderr, "\n"
+          "Usage:  %s [-gamma exp] [-bgcolor bg | -bgpat pat] [-timing]"
+          " file.png\n\n"
+          "    exp \ttransfer-function exponent (``gamma'') of the display\n"
+          "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
+          "\t\t  to the product of the lookup-table exponent (varies)\n"
+          "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
+          "    bg  \tdesired background color in 7-character hex RGB format\n"
+          "\t\t  (e.g., ``#ff7f00'' for orange:  same as HTML colors);\n"
+          "\t\t  used with transparent images; overrides -bgpat\n"
+          "    pat \tdesired background pattern number (1-%d); used with\n"
+          "\t\t  transparent images; overrides -bgcolor\n"
+          "    -timing\tenables delay for every block read, to simulate modem\n"
+          "\t\t  download of image (~36 Kbps)\n"
+          "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
+          "\n", PROGNAME, " ", default_display_exponent, num_bgpat);
+        exit(1);
+    }
+
+
+    /* set the title-bar string, but make sure buffer doesn't overflow */
+
+    alen = strlen(appname);
+    flen = strlen(filename);
+    if (alen + flen + 3 > 1023)
+        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
+    else
+        sprintf(titlebar, "%s:  %s", appname, filename);
+
+
+    /* set some final rpng2_info variables before entering main data loop */
+
+    if (have_bg) {
+        unsigned r, g, b;   /* this approach quiets compiler warnings */
+
+        sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
+        rpng2_info.bg_red   = (uch)r;
+        rpng2_info.bg_green = (uch)g;
+        rpng2_info.bg_blue  = (uch)b;
+    } else
+        rpng2_info.need_bgcolor = TRUE;
+
+    rpng2_info.done = FALSE;
+    rpng2_info.mainprog_init = rpng2_win_init;
+    rpng2_info.mainprog_display_row = rpng2_win_display_row;
+    rpng2_info.mainprog_finish_display = rpng2_win_finish_display;
+
+
+    /* OK, this is the fun part:  call readpng2_decode_data() at the start of
+     * the loop to deal with our first buffer of data (read in above to verify
+     * that the file is a PNG image), then loop through the file and continue
+     * calling the same routine to handle each chunk of data.  It in turn
+     * passes the data to libpng, which will invoke one or more of our call-
+     * backs as decoded data become available.  We optionally call Sleep() for
+     * one second per iteration to simulate downloading the image via an analog
+     * modem. */
+
+    for (;;) {
+        Trace((stderr, "about to call readpng2_decode_data()\n"))
+        if (readpng2_decode_data(&rpng2_info, inbuf, incount))
+            ++error;
+        Trace((stderr, "done with readpng2_decode_data()\n"))
+        if (error || feof(infile) || rpng2_info.done)
+            break;
+        if (timing)
+            Sleep(1000L);
+        incount = fread(inbuf, 1, INBUFSIZE, infile);
+    }
+
+
+    /* clean up PNG stuff and report any decoding errors */
+
+    fclose(infile);
+    Trace((stderr, "about to call readpng2_cleanup()\n"))
+    readpng2_cleanup(&rpng2_info);
+
+    if (error) {
+        fprintf(stderr, PROGNAME ":  libpng error while decoding PNG image\n");
+        exit(3);
+    }
+
+
+    /* wait for the user to tell us when to quit */
+
+    while (GetMessage(&msg, NULL, 0, 0)) {
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+    }
+
+
+    /* we're done:  clean up all image and Windows resources and go away */
+
+    Trace((stderr, "about to call rpng2_win_cleanup()\n"))
+    rpng2_win_cleanup();
+
+    return msg.wParam;
+}
+
+
+
+
+
+/* this function is called by readpng2_info_callback() in readpng2.c, which
+ * in turn is called by libpng after all of the pre-IDAT chunks have been
+ * read and processed--i.e., we now have enough info to finish initializing */
+
+static void rpng2_win_init()
+{
+    ulg i;
+    ulg rowbytes = rpng2_info.rowbytes;
+
+    Trace((stderr, "beginning rpng2_win_init()\n"))
+    Trace((stderr, "  rowbytes = %ld\n", rpng2_info.rowbytes))
+    Trace((stderr, "  width  = %ld\n", rpng2_info.width))
+    Trace((stderr, "  height = %ld\n", rpng2_info.height))
+
+    rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
+    if (!rpng2_info.image_data) {
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+
+    rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
+    if (!rpng2_info.row_pointers) {
+        free(rpng2_info.image_data);
+        rpng2_info.image_data = NULL;
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+
+    for (i = 0;  i < rpng2_info.height;  ++i)
+        rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
+
+/*---------------------------------------------------------------------------
+    Do the basic Windows initialization stuff, make the window, and fill it
+    with the user-specified, file-specified or default background color.
+  ---------------------------------------------------------------------------*/
+
+    if (rpng2_win_create_window()) {
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+}
+
+
+
+
+
+static int rpng2_win_create_window()
+{
+    uch bg_red   = rpng2_info.bg_red;
+    uch bg_green = rpng2_info.bg_green;
+    uch bg_blue  = rpng2_info.bg_blue;
+    uch *dest;
+    int extra_width, extra_height;
+    ulg i, j;
+    WNDCLASSEX wndclass;
+    RECT rect;
+
+
+/*---------------------------------------------------------------------------
+    Allocate memory for the display-specific version of the image (round up
+    to multiple of 4 for Windows DIB).
+  ---------------------------------------------------------------------------*/
+
+    wimage_rowbytes = ((3*rpng2_info.width + 3L) >> 2) << 2;
+
+    if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
+                              wimage_rowbytes*rpng2_info.height)))
+    {
+        return 4;	/* fail */
+    }
+
+/*---------------------------------------------------------------------------
+    Initialize the DIB.  Negative height means to use top-down BMP ordering
+    (must be uncompressed, but that's what we want).  Bit count of 1, 4 or 8
+    implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values
+    directly => wimage_data begins immediately after BMP header.
+  ---------------------------------------------------------------------------*/
+
+    memset(dib, 0, sizeof(BITMAPINFOHEADER));
+    bmih = (BITMAPINFOHEADER *)dib;
+    bmih->biSize = sizeof(BITMAPINFOHEADER);
+    bmih->biWidth = rpng2_info.width;
+    bmih->biHeight = -((long)rpng2_info.height);
+    bmih->biPlanes = 1;
+    bmih->biBitCount = 24;
+    bmih->biCompression = 0;
+    wimage_data = dib + sizeof(BITMAPINFOHEADER);
+
+/*---------------------------------------------------------------------------
+    Fill window with the specified background color (default is black), but
+    defer loading faked "background image" until window is displayed (may be
+    slow to compute).  Data are in BGR order.
+  ---------------------------------------------------------------------------*/
+
+    if (bg_image) {   /* just fill with black for now */
+        memset(wimage_data, 0, wimage_rowbytes*rpng2_info.height);
+    } else {
+        for (j = 0;  j < rpng2_info.height;  ++j) {
+            dest = wimage_data + j*wimage_rowbytes;
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                *dest++ = bg_blue;
+                *dest++ = bg_green;
+                *dest++ = bg_red;
+            }
+        }
+    }
+
+/*---------------------------------------------------------------------------
+    Set the window parameters.
+  ---------------------------------------------------------------------------*/
+
+    memset(&wndclass, 0, sizeof(wndclass));
+
+    wndclass.cbSize = sizeof(wndclass);
+    wndclass.style = CS_HREDRAW | CS_VREDRAW;
+    wndclass.lpfnWndProc = rpng2_win_wndproc;
+    wndclass.hInstance = global_hInst;
+    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+    wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
+    wndclass.lpszMenuName = NULL;
+    wndclass.lpszClassName = progname;
+    wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
+
+    RegisterClassEx(&wndclass);
+
+/*---------------------------------------------------------------------------
+    Finally, create the window.
+  ---------------------------------------------------------------------------*/
+
+    extra_width  = 2*(GetSystemMetrics(SM_CXBORDER) +
+                      GetSystemMetrics(SM_CXDLGFRAME));
+    extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
+                      GetSystemMetrics(SM_CYDLGFRAME)) +
+                      GetSystemMetrics(SM_CYCAPTION);
+
+    global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW,
+      CW_USEDEFAULT, CW_USEDEFAULT, rpng2_info.width+extra_width,
+      rpng2_info.height+extra_height, NULL, NULL, global_hInst, NULL);
+
+    ShowWindow(global_hwnd, global_showmode);
+    UpdateWindow(global_hwnd);
+
+/*---------------------------------------------------------------------------
+    Now compute the background image and display it.  If it fails (memory
+    allocation), revert to a plain background color.
+  ---------------------------------------------------------------------------*/
+
+    if (bg_image) {
+        static const char *msg = "Computing background image...";
+        int x, y, len = strlen(msg);
+        HDC hdc = GetDC(global_hwnd);
+        TEXTMETRIC tm;
+
+        GetTextMetrics(hdc, &tm);
+        x = (rpng2_info.width - len*tm.tmAveCharWidth)/2;
+        y = (rpng2_info.height - tm.tmHeight)/2;
+        SetBkMode(hdc, TRANSPARENT);
+        SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+        /* this can still begin out of bounds even if x is positive (???): */
+        TextOut(hdc, ((x < 0)? 0 : x), ((y < 0)? 0 : y), msg, len);
+        ReleaseDC(global_hwnd, hdc);
+
+        rpng2_win_load_bg_image();	/* resets bg_image if fails */
+    }
+
+    if (!bg_image) {
+        for (j = 0;  j < rpng2_info.height;  ++j) {
+            dest = wimage_data + j*wimage_rowbytes;
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                *dest++ = bg_blue;
+                *dest++ = bg_green;
+                *dest++ = bg_red;
+            }
+        }
+    }
+
+    rect.left = 0L;
+    rect.top = 0L;
+    rect.right = (LONG)rpng2_info.width;       /* possibly off by one? */
+    rect.bottom = (LONG)rpng2_info.height;     /* possibly off by one? */
+    InvalidateRect(global_hwnd, &rect, FALSE);
+    UpdateWindow(global_hwnd);                 /* similar to XFlush() */
+
+    return 0;
+
+} /* end function rpng2_win_create_window() */
+
+
+
+
+
+static int rpng2_win_load_bg_image()
+{
+    uch *src, *dest;
+    uch r1, r2, g1, g2, b1, b2;
+    uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
+    int k, hmax, max;
+    int xidx, yidx, yidx_max = (bgscale-1);
+    int even_odd_vert, even_odd_horiz, even_odd;
+    int invert_gradient2 = (bg[pat].type & 0x08);
+    int invert_column;
+    ulg i, row;
+
+/*---------------------------------------------------------------------------
+    Allocate buffer for fake background image to be used with transparent
+    images; if this fails, revert to plain background color.
+  ---------------------------------------------------------------------------*/
+
+    bg_rowbytes = 3 * rpng2_info.width;
+    bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
+    if (!bg_data) {
+        fprintf(stderr, PROGNAME
+          ":  unable to allocate memory for background image\n");
+        bg_image = 0;
+        return 1;
+    }
+
+/*---------------------------------------------------------------------------
+    Vertical gradients (ramps) in NxN squares, alternating direction and
+    colors (N == bgscale).
+  ---------------------------------------------------------------------------*/
+
+    if ((bg[pat].type & 0x07) == 0) {
+        uch r1_min  = rgb[bg[pat].rgb1_min].r;
+        uch g1_min  = rgb[bg[pat].rgb1_min].g;
+        uch b1_min  = rgb[bg[pat].rgb1_min].b;
+        uch r2_min  = rgb[bg[pat].rgb2_min].r;
+        uch g2_min  = rgb[bg[pat].rgb2_min].g;
+        uch b2_min  = rgb[bg[pat].rgb2_min].b;
+        int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
+        int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
+        int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
+        int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
+        int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
+        int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            yidx = row % bgscale;
+            even_odd_vert = (row / bgscale) & 1;
+
+            r1 = r1_min + (r1_diff * yidx) / yidx_max;
+            g1 = g1_min + (g1_diff * yidx) / yidx_max;
+            b1 = b1_min + (b1_diff * yidx) / yidx_max;
+            r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
+            g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
+            b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
+
+            r2 = r2_min + (r2_diff * yidx) / yidx_max;
+            g2 = g2_min + (g2_diff * yidx) / yidx_max;
+            b2 = b2_min + (b2_diff * yidx) / yidx_max;
+            r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
+            g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
+            b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
+
+            dest = bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                even_odd_horiz = (i / bgscale) & 1;
+                even_odd = even_odd_vert ^ even_odd_horiz;
+                invert_column =
+                  (even_odd_horiz && (bg[pat].type & 0x10));
+                if (even_odd == 0) {		/* gradient #1 */
+                    if (invert_column) {
+                        *dest++ = r1_inv;
+                        *dest++ = g1_inv;
+                        *dest++ = b1_inv;
+                    } else {
+                        *dest++ = r1;
+                        *dest++ = g1;
+                        *dest++ = b1;
+                    }
+                } else {			/* gradient #2 */
+                    if ((invert_column && invert_gradient2) ||
+                        (!invert_column && !invert_gradient2))
+                    {
+                        *dest++ = r2;      /* not inverted or */
+                        *dest++ = g2;      /*  doubly inverted */
+                        *dest++ = b2;
+                    } else {
+                        *dest++ = r2_inv;
+                        *dest++ = g2_inv;  /* singly inverted */
+                        *dest++ = b2_inv;
+                    }
+                }
+            }
+        }
+
+/*---------------------------------------------------------------------------
+    Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
+    M. Costello.
+  ---------------------------------------------------------------------------*/
+
+    } else if ((bg[pat].type & 0x07) == 1) {
+
+        hmax = (bgscale-1)/2;	/* half the max weight of a color */
+        max = 2*hmax;		/* the max weight of a color */
+
+        r1 = rgb[bg[pat].rgb1_max].r;
+        g1 = rgb[bg[pat].rgb1_max].g;
+        b1 = rgb[bg[pat].rgb1_max].b;
+        r2 = rgb[bg[pat].rgb2_max].r;
+        g2 = rgb[bg[pat].rgb2_max].g;
+        b2 = rgb[bg[pat].rgb2_max].b;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            yidx = row % bgscale;
+            if (yidx > hmax)
+                yidx = bgscale-1 - yidx;
+            dest = bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                xidx = i % bgscale;
+                if (xidx > hmax)
+                    xidx = bgscale-1 - xidx;
+                k = xidx + yidx;
+                *dest++ = (k*r1 + (max-k)*r2) / max;
+                *dest++ = (k*g1 + (max-k)*g2) / max;
+                *dest++ = (k*b1 + (max-k)*b2) / max;
+            }
+        }
+
+/*---------------------------------------------------------------------------
+    Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
+    soids will equal bgscale?].  This one is slow but very cool.  Code con-
+    tributed by Pieter S. van der Meulen (originally in Smalltalk).
+  ---------------------------------------------------------------------------*/
+
+    } else if ((bg[pat].type & 0x07) == 2) {
+        uch ch;
+        int ii, x, y, hw, hh, grayspot;
+        double freq, rotate, saturate, gray, intensity;
+        double angle=0.0, aoffset=0.0, maxDist, dist;
+        double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
+
+        fprintf(stderr, "%s:  computing radial background...",
+          PROGNAME);
+        fflush(stderr);
+
+        hh = rpng2_info.height / 2;
+        hw = rpng2_info.width / 2;
+
+        /* variables for radial waves:
+         *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
+         *   freq:  number of color beams originating from the center
+         *   grayspot:  size of the graying center area (anti-alias)
+         *   rotate:  rotation of the beams as a function of radius
+         *   saturate:  saturation of beams' shape azimuthally
+         */
+        angle = CLIP(angle, 0.0, 360.0);
+        grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
+        freq = MAX((double)bg[pat].bg_freq, 0.0);
+        saturate = (double)bg[pat].bg_bsat * 0.1;
+        rotate = (double)bg[pat].bg_brot * 0.1;
+        gray = 0.0;
+        intensity = 0.0;
+        maxDist = (double)((hw*hw) + (hh*hh));
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            y = row - hh;
+            dest = bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                x = i - hw;
+                angle = (x == 0)? PI_2 : atan((double)y / (double)x);
+                gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
+                gray = MIN(1.0, gray);
+                dist = (double)((x*x) + (y*y)) / maxDist;
+                intensity = cos((angle+(rotate*dist*PI)) * freq) *
+                  gray * saturate;
+                intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
+                hue = (angle + PI) * INV_PI_360 + aoffset;
+                s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
+                s = MIN(MAX(s,0.0), 1.0);
+                v = MIN(MAX(intensity,0.0), 1.0);
+
+                if (s == 0.0) {
+                    ch = (uch)(v * 255.0);
+                    *dest++ = ch;
+                    *dest++ = ch;
+                    *dest++ = ch;
+                } else {
+                    if ((hue < 0.0) || (hue >= 360.0))
+                        hue -= (((int)(hue / 360.0)) * 360.0);
+                    hue /= 60.0;
+                    ii = (int)hue;
+                    f = hue - (double)ii;
+                    p = (1.0 - s) * v;
+                    q = (1.0 - (s * f)) * v;
+                    t = (1.0 - (s * (1.0 - f))) * v;
+                    if      (ii == 0) { red = v; green = t; blue = p; }
+                    else if (ii == 1) { red = q; green = v; blue = p; }
+                    else if (ii == 2) { red = p; green = v; blue = t; }
+                    else if (ii == 3) { red = p; green = q; blue = v; }
+                    else if (ii == 4) { red = t; green = p; blue = v; }
+                    else if (ii == 5) { red = v; green = p; blue = q; }
+                    *dest++ = (uch)(red * 255.0);
+                    *dest++ = (uch)(green * 255.0);
+                    *dest++ = (uch)(blue * 255.0);
+                }
+            }
+        }
+        fprintf(stderr, "done.\n");
+        fflush(stderr);
+    }
+
+/*---------------------------------------------------------------------------
+    Blast background image to display buffer before beginning PNG decode;
+    calling function will handle invalidation and UpdateWindow() call.
+  ---------------------------------------------------------------------------*/
+
+    for (row = 0;  row < rpng2_info.height;  ++row) {
+        src = bg_data + row*bg_rowbytes;
+        dest = wimage_data + row*wimage_rowbytes;
+        for (i = rpng2_info.width;  i > 0;  --i) {
+            r1 = *src++;
+            g1 = *src++;
+            b1 = *src++;
+            *dest++ = b1;
+            *dest++ = g1;	/* note reverse order */
+            *dest++ = r1;
+        }
+    }
+
+    return 0;
+
+} /* end function rpng2_win_load_bg_image() */
+
+
+
+
+
+static void rpng2_win_display_row(ulg row)
+{
+    uch bg_red   = rpng2_info.bg_red;
+    uch bg_green = rpng2_info.bg_green;
+    uch bg_blue  = rpng2_info.bg_blue;
+    uch *src, *src2=NULL, *dest;
+    uch r, g, b, a;
+    ulg i;
+    static int rows=0;
+    static ulg firstrow;
+
+/*---------------------------------------------------------------------------
+    rows and firstrow simply track how many rows (and which ones) have not
+    yet been displayed; alternatively, we could call InvalidateRect() for
+    every row and not bother with the records-keeping.
+  ---------------------------------------------------------------------------*/
+
+    Trace((stderr, "beginning rpng2_win_display_row()\n"))
+
+    if (rows == 0)
+        firstrow = row;   /* first row not yet displayed */
+
+    ++rows;   /* count of rows received but not yet displayed */
+
+/*---------------------------------------------------------------------------
+    Aside from the use of the rpng2_info struct and the lack of an outer
+    loop (over rows), this routine is identical to rpng_win_display_image()
+    in the non-progressive version of the program.
+  ---------------------------------------------------------------------------*/
+
+    src = rpng2_info.image_data + row*rpng2_info.rowbytes;
+    if (bg_image)
+        src2 = bg_data + row*bg_rowbytes;
+    dest = wimage_data + row*wimage_rowbytes;
+
+    if (rpng2_info.channels == 3) {
+        for (i = rpng2_info.width;  i > 0;  --i) {
+            r = *src++;
+            g = *src++;
+            b = *src++;
+            *dest++ = b;
+            *dest++ = g;	/* note reverse order */
+            *dest++ = r;
+        }
+    } else /* if (rpng2_info.channels == 4) */ {
+        for (i = rpng2_info.width;  i > 0;  --i) {
+            r = *src++;
+            g = *src++;
+            b = *src++;
+            a = *src++;
+            if (bg_image) {
+                bg_red   = *src2++;
+                bg_green = *src2++;
+                bg_blue  = *src2++;
+            }
+            if (a == 255) {
+                *dest++ = b;
+                *dest++ = g;
+                *dest++ = r;
+            } else if (a == 0) {
+                *dest++ = bg_blue;
+                *dest++ = bg_green;
+                *dest++ = bg_red;
+            } else {
+                /* this macro (copied from png.h) composites the
+                 * foreground and background values and puts the
+                 * result into the first argument; there are no
+                 * side effects with the first argument */
+                alpha_composite(*dest++, b, a, bg_blue);
+                alpha_composite(*dest++, g, a, bg_green);
+                alpha_composite(*dest++, r, a, bg_red);
+            }
+        }
+    }
+
+/*---------------------------------------------------------------------------
+    Display after every 16 rows or when on last row.  (Region may include
+    previously displayed lines due to interlacing--i.e., not contiguous.)
+  ---------------------------------------------------------------------------*/
+
+    if ((rows & 0xf) == 0 || row == rpng2_info.height-1) {
+        RECT rect;
+
+        rect.left = 0L;
+        rect.top = (LONG)firstrow;
+        rect.right = (LONG)rpng2_info.width;       /* possibly off by one? */
+        rect.bottom = (LONG)row + 1L;              /* possibly off by one? */
+        InvalidateRect(global_hwnd, &rect, FALSE);
+        UpdateWindow(global_hwnd);                 /* similar to XFlush() */
+        rows = 0;
+    }
+
+} /* end function rpng2_win_display_row() */
+
+
+
+
+
+static void rpng2_win_finish_display()
+{
+    Trace((stderr, "beginning rpng2_win_finish_display()\n"))
+
+    /* last row has already been displayed by rpng2_win_display_row(), so
+     * we have nothing to do here except set a flag and let the user know
+     * that the image is done */
+
+    rpng2_info.done = TRUE;
+    printf("Done.  Press Q, Esc or mouse button 1 to quit.\n");
+}
+
+
+
+
+
+static void rpng2_win_cleanup()
+{
+    if (bg_image && bg_data) {
+        free(bg_data);
+        bg_data = NULL;
+    }
+
+    if (rpng2_info.image_data) {
+        free(rpng2_info.image_data);
+        rpng2_info.image_data = NULL;
+    }
+
+    if (rpng2_info.row_pointers) {
+        free(rpng2_info.row_pointers);
+        rpng2_info.row_pointers = NULL;
+    }
+
+    if (dib) {
+        free(dib);
+        dib = NULL;
+    }
+}
+
+
+
+
+
+LRESULT CALLBACK rpng2_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
+{
+    HDC         hdc;
+    PAINTSTRUCT ps;
+    int rc;
+
+    switch (iMsg) {
+        case WM_CREATE:
+            /* one-time processing here, if any */
+            return 0;
+
+        case WM_PAINT:
+            hdc = BeginPaint(hwnd, &ps);
+            rc = StretchDIBits(hdc, 0, 0, rpng2_info.width, rpng2_info.height,
+                                    0, 0, rpng2_info.width, rpng2_info.height,
+                                    wimage_data, (BITMAPINFO *)bmih,
+                                    0, SRCCOPY);
+            EndPaint(hwnd, &ps);
+            return 0;
+
+        /* wait for the user to tell us when to quit */
+        case WM_CHAR:
+            switch (wP) {	/* only need one, so ignore repeat count */
+                case 'q':
+                case 'Q':
+                case 0x1B:	/* Esc key */
+                    PostQuitMessage(0);
+            }
+            return 0;
+
+        case WM_LBUTTONDOWN:	/* another way of quitting */
+        case WM_DESTROY:
+            PostQuitMessage(0);
+            return 0;
+    }
+
+    return DefWindowProc(hwnd, iMsg, wP, lP);
+}
diff --git a/contrib/gregbook/rpng2-x.c b/contrib/gregbook/rpng2-x.c
new file mode 100644
index 0000000..bfa83e6
--- /dev/null
+++ b/contrib/gregbook/rpng2-x.c
@@ -0,0 +1,1241 @@
+/*---------------------------------------------------------------------------
+
+   rpng2 - progressive-model PNG display program                  rpng2-x.c 
+
+   This program decodes and displays PNG files progressively, as if it were
+   a web browser (though the front end is only set up to read from files).
+   It supports gamma correction, user-specified background colors, and user-
+   specified background patterns (for transparent images).  This version is
+   for the X Window System (tested under Unix, but may work under VMS or OS/2
+   with a little tweaking).  Thanks to Adam Costello and Pieter S. van der
+   Meulen for the "diamond" and "radial waves" patterns, respectively.
+
+   to do:
+    - 8-bit support
+    - finish resizable checkerboard-gradient (sizes 4-128?)
+    - use %.1023s to simplify truncation of title-bar string?
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-1999 Greg Roelofs.  All rights reserved.
+
+      This software is provided "as is," without warranty of any kind,
+      express or implied.  In no event shall the author or contributors
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+  ---------------------------------------------------------------------------*/
+
+#define PROGNAME  "rpng2-x"
+#define LONGNAME  "Progressive PNG Viewer for X"
+#define VERSION   "1.01 of 31 March 1999"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>	/* for jmpbuf declaration in readpng2.h */
+#include <time.h>
+#include <math.h>	/* only for PvdM background code */
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#include <X11/keysym.h>	/* defines XK_* macros */
+
+/* all for PvdM background code: */
+#ifndef PI
+#  define PI             3.141592653589793238
+#endif
+#define PI_2             (PI*0.5)
+#define INV_PI_360       (360.0 / PI)
+#define MAX(a,b)         (a>b?a:b)
+#define MIN(a,b)         (a<b?a:b)
+#define CLIP(a,min,max)  MAX(min,MIN((a),max))
+#define ABS(a)           ((a)<0?-(a):(a))
+#define CLIP8P(c)        MAX(0,(MIN((c),255)))   /* 8-bit pos. integer (uch) */
+#define ROUNDF(f)        ((int)(f + 0.5))
+
+#define rgb1_max   bg_freq
+#define rgb1_min   bg_gray
+#define rgb2_max   bg_bsat
+#define rgb2_min   bg_brot
+
+/* #define DEBUG */	/* this enables the Trace() macros */
+
+#include "readpng2.h"	/* typedefs, common macros, readpng2 prototypes */
+
+
+/* could just include png.h, but this macro is the only thing we need
+ * (name and typedefs changed to local versions); note that side effects
+ * only happen with alpha (which could easily be avoided with
+ * "ush acopy = (alpha);") */
+
+#define alpha_composite(composite, fg, alpha, bg) {			\
+    ush temp = ((ush)(fg)*(ush)(alpha) +				\
+                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);	\
+    (composite) = (uch)((temp + (temp >> 8)) >> 8);			\
+}
+
+
+#define INBUFSIZE 4096	/* with pseudo-timing on (1 sec delay/block), this
+			 *  block size corresponds roughly to a download
+			 *  speed 10% faster than theoretical 33.6K maximum
+			 *  (assuming 8 data bits, 1 stop bit and no other
+			 *  overhead) */
+
+/* local prototypes */
+static void rpng2_x_init(void);
+static int  rpng2_x_create_window(void);
+static int  rpng2_x_load_bg_image(void);
+static void rpng2_x_display_row(ulg row);
+static void rpng2_x_finish_display(void);
+static void rpng2_x_cleanup(void);
+static int  rpng2_x_msb(ulg u32val);
+
+
+static char titlebar[1024], *window_name = titlebar;
+static char *appname = LONGNAME;
+static char *icon_name = PROGNAME;
+static char *filename;
+static FILE *infile;
+
+static mainprog_info rpng2_info;
+
+static uch inbuf[INBUFSIZE];
+static int incount;
+
+static int pat = 6;		/* must be less than num_bgpat */
+static int bg_image = 0;
+static int bgscale = 16;
+static ulg bg_rowbytes;
+static uch *bg_data;
+
+static struct rgb_color {
+    uch r, g, b;
+} rgb[] = {
+    {  0,   0,   0},	/*  0:  black */
+    {255, 255, 255},	/*  1:  white */
+    {173, 132,  57},	/*  2:  tan */
+    { 64, 132,   0},	/*  3:  medium green */
+    {189, 117,   1},	/*  4:  gold */
+    {253, 249,   1},	/*  5:  yellow */
+    {  0,   0, 255},	/*  6:  blue */
+    {  0,   0, 120},	/*  7:  medium blue */
+    {255,   0, 255},	/*  8:  magenta */
+    { 64,   0,  64},	/*  9:  dark magenta */
+    {255,   0,   0},	/* 10:  red */
+    { 64,   0,   0},	/* 11:  dark red */
+    {255, 127,   0},	/* 12:  orange */
+    {192,  96,   0},	/* 13:  darker orange */
+    { 24,  60,   0},	/* 14:  dark green-yellow */
+    { 85, 125, 200} 	/* 15:  ice blue */
+};
+/* not used for now, but should be for error-checking:
+static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
+ */
+
+/*
+    This whole struct is a fairly cheesy way to keep the number of
+    command-line options to a minimum.  The radial-waves background
+    type is a particularly poor fit to the integer elements of the
+    struct...but a few macros and a little fixed-point math will do
+    wonders for ya.
+
+    type bits:
+       F E D C B A 9 8 7 6 5 4 3 2 1 0
+                             | | | | |
+                             | | +-+-+-- 0 = sharp-edged checkerboard
+                             | |         1 = soft diamonds
+                             | |         2 = radial waves
+                             | |       3-7 = undefined
+                             | +-- gradient #2 inverted?
+                             +-- alternating columns inverted?
+ */
+static struct background_pattern {
+    ush type;
+    int rgb1_max, rgb1_min;	/* or bg_freq, bg_gray */
+    int rgb2_max, rgb2_min;	/* or bg_bsat, bg_brot (both scaled by 10)*/
+} bg[] = {
+    {0+8,   2,0,  1,15},  	/* checkered:  tan/black vs. white/ice blue */
+    {0+24,  2,0,  1,0},  	/* checkered:  tan/black vs. white/black */
+    {0+8,   4,5,  0,2},  	/* checkered:  gold/yellow vs. black/tan */
+    {0+8,   4,5,  0,6},  	/* checkered:  gold/yellow vs. black/blue */
+    {0,     7,0,  8,9},  	/* checkered:  deep blue/black vs. magenta */
+    {0+8,  13,0,  5,14},  	/* checkered:  orange/black vs. yellow */
+    {0+8,  12,0, 10,11},  	/* checkered:  orange/black vs. red */
+    {1,     7,0,  8,0},  	/* diamonds:  deep blue/black vs. magenta */
+    {1,    12,0, 11,0},  	/* diamonds:  orange vs. dark red */
+    {1,    10,0,  7,0},  	/* diamonds:  red vs. medium blue */
+    {1,     4,0,  5,0},  	/* diamonds:  gold vs. yellow */
+    {1,     3,0,  0,0},  	/* diamonds:  medium green vs. black */
+    {2,    16, 100,  20,   0},  /* radial:  ~hard radial color-beams */
+    {2,    18, 100,  10,   2},  /* radial:  soft, curved radial color-beams */
+    {2,    16, 256, 100, 250},  /* radial:  very tight spiral */
+    {2, 10000, 256,  11,   0}   /* radial:  dipole-moire' (almost fractal) */
+};
+static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
+
+
+/* X-specific variables */
+static char *displayname;
+static XImage *ximage;
+static Display *display;
+static int depth;
+static Visual *visual;
+static int RPixelShift, GPixelShift, BPixelShift;
+static ulg RedMask, GreenMask, BlueMask;
+static Window window;
+static GC gc;
+static Colormap colormap;
+
+static int have_colormap = FALSE;
+static int have_window = FALSE;
+
+
+
+
+int main(int argc, char **argv)
+{
+#ifdef sgi
+    char tmpline[80];
+#endif
+    char *p, *bgstr = NULL;
+    int rc, alen, flen;
+    int error = 0;
+    int timing = FALSE;
+    int have_bg = FALSE;
+    double LUT_exponent;                /* just the lookup table */
+    double CRT_exponent = 2.2;          /* just the monitor */
+    double default_display_exponent;    /* whole display system */
+    XEvent e;
+    KeySym k;
+
+
+    /* First initialize a few things, just to be sure--memset takes care of
+     * default background color (black), booleans (FALSE), pointers (NULL),
+     * etc. */
+
+    displayname = (char *)NULL;
+    filename = (char *)NULL;
+    memset(&rpng2_info, 0, sizeof(mainprog_info));
+
+
+    /* Set the default value for our display-system exponent, i.e., the
+     * product of the CRT exponent and the exponent corresponding to
+     * the frame-buffer's lookup table (LUT), if any.  This is not an
+     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
+     * ones), but it should cover 99% of the current possibilities. */
+
+#if defined(NeXT)
+    /* third-party utilities can modify the default LUT exponent */
+    LUT_exponent = 1.0 / 2.2;
+    /*
+    if (some_next_function_that_returns_gamma(&next_gamma))
+        LUT_exponent = 1.0 / next_gamma;
+     */
+#elif defined(sgi)
+    LUT_exponent = 1.0 / 1.7;
+    /* there doesn't seem to be any documented function to
+     * get the "gamma" value, so we do it the hard way */
+    infile = fopen("/etc/config/system.glGammaVal", "r");
+    if (infile) {
+        double sgi_gamma;
+
+        fgets(tmpline, 80, infile);
+        fclose(infile);
+        sgi_gamma = atof(tmpline);
+        if (sgi_gamma > 0.0)
+            LUT_exponent = 1.0 / sgi_gamma;
+    }
+#elif defined(Macintosh)
+    LUT_exponent = 1.8 / 2.61;
+    /*
+    if (some_mac_function_that_returns_gamma(&mac_gamma))
+        LUT_exponent = mac_gamma / 2.61;
+     */
+#else
+    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
+#endif
+
+    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
+    default_display_exponent = LUT_exponent * CRT_exponent;
+
+
+    /* If the user has set the SCREEN_GAMMA environment variable as suggested
+     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
+     * use the default value we just calculated.  Either way, the user may
+     * override this via a command-line option. */
+
+    if ((p = getenv("SCREEN_GAMMA")) != NULL)
+        rpng2_info.display_exponent = atof(p);
+    else
+        rpng2_info.display_exponent = default_display_exponent;
+
+
+    /* Now parse the command line for options and the PNG filename. */
+
+    while (*++argv && !error) {
+        if (!strcmp(*argv, "-display")) {
+            if (!*++argv)
+                ++error;
+            displayname = *argv;
+        } else if (!strcmp(*argv, "-gamma")) {
+            if (!*++argv)
+                ++error;
+            rpng2_info.display_exponent = atof(*argv);
+            if (rpng2_info.display_exponent <= 0.0)
+                ++error;
+        } else if (!strcmp(*argv, "-bgcolor")) {
+            if (!*++argv)
+                ++error;
+            bgstr = *argv;
+            if (strlen(bgstr) != 7 || bgstr[0] != '#')
+                ++error;
+            else {
+                have_bg = TRUE;
+                bg_image = FALSE;
+            }
+        } else if (!strcmp(*argv, "-bgpat")) {
+            if (!*++argv)
+                ++error;
+            pat = atoi(*argv) - 1;
+            if (pat < 0 || pat >= num_bgpat)
+                ++error;
+            else {
+                bg_image = TRUE;
+                have_bg = FALSE;
+            }
+        } else if (!strcmp(*argv, "-timing")) {
+            timing = TRUE;
+        } else {
+            if (**argv != '-') {
+                filename = *argv;
+                if (argv[1])   /* shouldn't be any more args after filename */
+                    ++error;
+            } else
+                ++error;   /* not expecting any other options */
+        }
+    }
+
+    if (!filename) {
+        ++error;
+    } else if (!(infile = fopen(filename, "rb"))) {
+        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
+        ++error;
+    } else {
+        incount = fread(inbuf, 1, INBUFSIZE, infile);
+        if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
+            fprintf(stderr, PROGNAME
+              ":  [%s] is not a PNG file: incorrect signature\n",
+              filename);
+            ++error;
+        } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
+            switch (rc) {
+                case 2:
+                    fprintf(stderr, PROGNAME
+                      ":  [%s] has bad IHDR (libpng longjmp)\n",
+                      filename);
+                    break;
+                case 4:
+                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
+                    break;
+                default:
+                    fprintf(stderr, PROGNAME
+                      ":  unknown readpng2_init() error\n");
+                    break;
+            }
+            ++error;
+        } else {
+            display = XOpenDisplay(displayname);
+            if (!display) {
+                readpng2_cleanup(&rpng2_info);
+                fprintf(stderr, PROGNAME ":  can't open X display [%s]\n",
+                  displayname? displayname : "default");
+                ++error;
+            }
+        }
+        if (error)
+            fclose(infile);
+    }
+
+    if (error) {
+        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
+        readpng2_version_info();
+        fprintf(stderr, "\n"
+          "Usage:  %s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
+          "        %*s [-timing] file.png\n\n"
+          "    xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
+          "    exp \ttransfer-function exponent (``gamma'') of the display\n"
+          "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
+          "\t\t  to the product of the lookup-table exponent (varies)\n"
+          "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
+          "    bg  \tdesired background color in 7-character hex RGB format\n"
+          "\t\t  (e.g., ``#ff7f00'' for orange:  same as HTML colors);\n"
+          "\t\t  used with transparent images; overrides -bgpat\n"
+          "    pat \tdesired background pattern number (1-%d); used with\n"
+          "\t\t  transparent images; overrides -bgcolor\n"
+          "    -timing\tenables delay for every block read, to simulate modem\n"
+          "\t\t  download of image (~36 Kbps)\n"
+          "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
+          "\n", PROGNAME, strlen(PROGNAME), " ", default_display_exponent,
+          num_bgpat);
+        exit(1);
+    }
+
+
+    /* set the title-bar string, but make sure buffer doesn't overflow */
+
+    alen = strlen(appname);
+    flen = strlen(filename);
+    if (alen + flen + 3 > 1023)
+        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
+    else
+        sprintf(titlebar, "%s:  %s", appname, filename);
+
+
+    /* set some final rpng2_info variables before entering main data loop */
+
+    if (have_bg) {
+        unsigned r, g, b;   /* this approach quiets compiler warnings */
+
+        sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
+        rpng2_info.bg_red   = (uch)r;
+        rpng2_info.bg_green = (uch)g;
+        rpng2_info.bg_blue  = (uch)b;
+    } else
+        rpng2_info.need_bgcolor = TRUE;
+
+    rpng2_info.done = FALSE;
+    rpng2_info.mainprog_init = rpng2_x_init;
+    rpng2_info.mainprog_display_row = rpng2_x_display_row;
+    rpng2_info.mainprog_finish_display = rpng2_x_finish_display;
+
+
+    /* OK, this is the fun part:  call readpng2_decode_data() at the start of
+     * the loop to deal with our first buffer of data (read in above to verify
+     * that the file is a PNG image), then loop through the file and continue
+     * calling the same routine to handle each chunk of data.  It in turn
+     * passes the data to libpng, which will invoke one or more of our call-
+     * backs as decoded data become available.  We optionally call sleep() for
+     * one second per iteration to simulate downloading the image via an analog
+     * modem. */
+
+    for (;;) {
+        Trace((stderr, "about to call readpng2_decode_data()\n"))
+        if (readpng2_decode_data(&rpng2_info, inbuf, incount))
+            ++error;
+        Trace((stderr, "done with readpng2_decode_data()\n"))
+        if (error || feof(infile) || rpng2_info.done)
+            break;
+        if (timing)
+            sleep(1);
+        incount = fread(inbuf, 1, INBUFSIZE, infile);
+    }
+
+
+    /* clean up PNG stuff and report any decoding errors */
+
+    fclose(infile);
+    Trace((stderr, "about to call readpng2_cleanup()\n"))
+    readpng2_cleanup(&rpng2_info);
+
+    if (error) {
+        fprintf(stderr, PROGNAME ":  libpng error while decoding PNG image\n");
+        exit(3);
+    }
+
+
+    /* wait for the user to tell us when to quit */
+
+    do
+        XNextEvent(display, &e);
+    while (!(e.type == ButtonPress && e.xbutton.button == Button1) &&
+           !(e.type == KeyPress &&    /*  v--- or 1 for shifted keys */
+             ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape) ));
+
+
+    /* we're done:  clean up all image and X resources and go away */
+
+    Trace((stderr, "about to call rpng2_x_cleanup()\n"))
+    rpng2_x_cleanup();
+
+    return 0;
+}
+
+
+
+
+
+/* this function is called by readpng2_info_callback() in readpng2.c, which
+ * in turn is called by libpng after all of the pre-IDAT chunks have been
+ * read and processed--i.e., we now have enough info to finish initializing */
+
+static void rpng2_x_init()
+{
+    ulg i;
+    ulg rowbytes = rpng2_info.rowbytes;
+
+    Trace((stderr, "beginning rpng2_x_init()\n"))
+    Trace((stderr, "  rowbytes = %ld\n", rpng2_info.rowbytes))
+    Trace((stderr, "  width  = %ld\n", rpng2_info.width))
+    Trace((stderr, "  height = %ld\n", rpng2_info.height))
+
+    rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
+    if (!rpng2_info.image_data) {
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+
+    rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
+    if (!rpng2_info.row_pointers) {
+        free(rpng2_info.image_data);
+        rpng2_info.image_data = NULL;
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+
+    for (i = 0;  i < rpng2_info.height;  ++i)
+        rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
+
+
+    /* do the basic X initialization stuff, make the window, and fill it with
+     * the user-specified, file-specified or default background color or
+     * pattern */
+
+    if (rpng2_x_create_window()) {
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+}
+
+
+
+
+
+static int rpng2_x_create_window()
+{
+    ulg bg_red   = rpng2_info.bg_red;
+    ulg bg_green = rpng2_info.bg_green;
+    ulg bg_blue  = rpng2_info.bg_blue;
+    ulg bg_pixel = 0L;
+    int screen, pad;
+    uch *xdata;
+    Window root;
+    XEvent e;
+    XGCValues gcvalues;
+    XSetWindowAttributes attr;
+    XSizeHints *size_hints;
+    XTextProperty windowName, *pWindowName = &windowName;
+    XTextProperty iconName, *pIconName = &iconName;
+    XVisualInfo visual_info;
+    XWMHints *wm_hints;
+
+
+    Trace((stderr, "beginning rpng2_x_create_window()\n"))
+
+    screen = DefaultScreen(display);
+    depth = DisplayPlanes(display, screen);
+    root = RootWindow(display, screen);
+
+/* GRR:  add 8-bit support */
+    if (/* depth != 8 && */ depth != 16 && depth != 24 && depth != 32) {
+        fprintf(stderr,
+          "screen depth %d not supported (only 16-, 24- or 32-bit TrueColor)\n",
+          depth);
+        return 2;
+    }
+
+    XMatchVisualInfo(display, screen, depth,
+      (depth == 8)? PseudoColor : TrueColor, &visual_info);
+    visual = visual_info.visual;
+
+    RedMask   = visual->red_mask;
+    GreenMask = visual->green_mask;
+    BlueMask  = visual->blue_mask;
+
+/* GRR:  add/check 8-bit support */
+    if (depth == 8) {
+        colormap = XCreateColormap(display, root, visual, AllocNone);
+        if (!colormap) {
+            fprintf(stderr, "XCreateColormap() failed\n");
+            return 2;
+        }
+        have_colormap = TRUE;
+        bg_image = FALSE;	/* gradient just wastes palette entries */
+    } else if (depth == 16) {
+        RPixelShift = 15 - rpng2_x_msb(RedMask);   /* these are right-shifts */
+        GPixelShift = 15 - rpng2_x_msb(GreenMask);
+        BPixelShift = 15 - rpng2_x_msb(BlueMask);
+    } else /* if (depth > 16) */ {
+        RPixelShift = rpng2_x_msb(RedMask) - 7;    /* these are left-shifts */
+        GPixelShift = rpng2_x_msb(GreenMask) - 7;
+        BPixelShift = rpng2_x_msb(BlueMask) - 7;
+    }
+
+/*---------------------------------------------------------------------------
+    Finally, create the window.
+  ---------------------------------------------------------------------------*/
+
+    attr.backing_store = Always;
+    attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
+
+    window = XCreateWindow(display, root, 0, 0, rpng2_info.width,
+      rpng2_info.height, 0, depth, InputOutput, visual,
+      CWBackingStore | CWEventMask, &attr);
+
+    if (window == None) {
+        fprintf(stderr, "XCreateWindow() failed\n");
+        return 2;
+    } else
+        have_window = TRUE;
+
+    if (depth == 8)
+        XSetWindowColormap(display, window, colormap);
+
+    if (!XStringListToTextProperty(&window_name, 1, pWindowName))
+        pWindowName = NULL;
+    if (!XStringListToTextProperty(&icon_name, 1, pIconName))
+        pIconName = NULL;
+
+    /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */
+
+    if ((size_hints = XAllocSizeHints()) != NULL) {
+        /* window will not be resizable */
+        size_hints->flags = PMinSize | PMaxSize;
+        size_hints->min_width = size_hints->max_width = rpng2_info.width;
+        size_hints->min_height = size_hints->max_height = rpng2_info.height;
+    }
+
+    if ((wm_hints = XAllocWMHints()) != NULL) {
+        wm_hints->initial_state = NormalState;
+        wm_hints->input = True;
+     /* wm_hints->icon_pixmap = icon_pixmap; */
+        wm_hints->flags = StateHint | InputHint  /* | IconPixmapHint */ ;
+    }
+
+    XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
+      size_hints, wm_hints, NULL);
+
+    XMapWindow(display, window);
+
+    gc = XCreateGC(display, window, 0, &gcvalues);
+
+/*---------------------------------------------------------------------------
+    Allocate memory for the X- and display-specific version of the image.
+  ---------------------------------------------------------------------------*/
+
+    if (depth == 24 || depth == 32) {
+        xdata = (uch *)malloc(4*rpng2_info.width*rpng2_info.height);
+        pad = 32;
+    } else if (depth == 16) {
+        xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height);
+        pad = 16;
+    } else /* depth == 8 */ {
+        xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height);
+        pad = 8;
+    }
+
+    if (!xdata) {
+        fprintf(stderr, PROGNAME ":  unable to allocate image memory\n");
+        return 4;
+    }
+
+    ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
+      (char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0);
+
+    if (!ximage) {
+        fprintf(stderr, PROGNAME ":  XCreateImage() failed\n");
+        free(xdata);
+        return 3;
+    }
+
+    /* to avoid testing the bitmap order every pixel (or doubling the size of
+     * the drawing routine with a giant if-test), we arbitrarily set the byte
+     * order to MSBFirst and let Xlib worry about inverting things on little-
+     * endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the
+     * most efficient approach (the giant if-test would be better), but in
+     * the interest of clarity, we'll take the easy way out... */
+
+    ximage->byte_order = MSBFirst;
+
+/*---------------------------------------------------------------------------
+    Fill window with the specified background color (default is black) or
+    faked "background image" (but latter is disabled if 8-bit; gradients
+    just waste palette entries).
+  ---------------------------------------------------------------------------*/
+
+    if (bg_image)
+        rpng2_x_load_bg_image();	/* resets bg_image if fails */
+
+    if (!bg_image) {
+        if (depth == 24 || depth == 32) {
+            bg_pixel = (bg_red   << RPixelShift) |
+                       (bg_green << GPixelShift) |
+                       (bg_blue  << BPixelShift);
+        } else if (depth == 16) {
+            bg_pixel = (((bg_red   << 8) >> RPixelShift) & RedMask)   |
+                       (((bg_green << 8) >> GPixelShift) & GreenMask) |
+                       (((bg_blue  << 8) >> BPixelShift) & BlueMask);
+        } else /* depth == 8 */ {
+
+            /* GRR:  add 8-bit support */
+
+        }
+        XSetForeground(display, gc, bg_pixel);
+        XFillRectangle(display, window, gc, 0, 0, rpng2_info.width,
+          rpng2_info.height);
+    }
+
+/*---------------------------------------------------------------------------
+    Wait for first Expose event to do any drawing, then flush and return.
+  ---------------------------------------------------------------------------*/
+
+    do
+        XNextEvent(display, &e);
+    while (e.type != Expose || e.xexpose.count);
+
+    XFlush(display);
+
+    return 0;
+
+} /* end function rpng2_x_create_window() */
+
+
+
+
+
+static int rpng2_x_load_bg_image()
+{
+    uch *src, *dest;
+    uch r1, r2, g1, g2, b1, b2;
+    uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
+    int k, hmax, max;
+    int xidx, yidx, yidx_max = (bgscale-1);
+    int even_odd_vert, even_odd_horiz, even_odd;
+    int invert_gradient2 = (bg[pat].type & 0x08);
+    int invert_column;
+    int ximage_rowbytes = ximage->bytes_per_line;
+    ulg i, row;
+    ulg pixel;
+
+/*---------------------------------------------------------------------------
+    Allocate buffer for fake background image to be used with transparent
+    images; if this fails, revert to plain background color.
+  ---------------------------------------------------------------------------*/
+
+    bg_rowbytes = 3 * rpng2_info.width;
+    bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
+    if (!bg_data) {
+        fprintf(stderr, PROGNAME
+          ":  unable to allocate memory for background image\n");
+        bg_image = 0;
+        return 1;
+    }
+
+/*---------------------------------------------------------------------------
+    Vertical gradients (ramps) in NxN squares, alternating direction and
+    colors (N == bgscale).
+  ---------------------------------------------------------------------------*/
+
+    if ((bg[pat].type & 0x07) == 0) {
+        uch r1_min  = rgb[bg[pat].rgb1_min].r;
+        uch g1_min  = rgb[bg[pat].rgb1_min].g;
+        uch b1_min  = rgb[bg[pat].rgb1_min].b;
+        uch r2_min  = rgb[bg[pat].rgb2_min].r;
+        uch g2_min  = rgb[bg[pat].rgb2_min].g;
+        uch b2_min  = rgb[bg[pat].rgb2_min].b;
+        int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
+        int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
+        int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
+        int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
+        int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
+        int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            yidx = row % bgscale;
+            even_odd_vert = (row / bgscale) & 1;
+
+            r1 = r1_min + (r1_diff * yidx) / yidx_max;
+            g1 = g1_min + (g1_diff * yidx) / yidx_max;
+            b1 = b1_min + (b1_diff * yidx) / yidx_max;
+            r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
+            g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
+            b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
+
+            r2 = r2_min + (r2_diff * yidx) / yidx_max;
+            g2 = g2_min + (g2_diff * yidx) / yidx_max;
+            b2 = b2_min + (b2_diff * yidx) / yidx_max;
+            r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
+            g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
+            b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
+
+            dest = bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                even_odd_horiz = (i / bgscale) & 1;
+                even_odd = even_odd_vert ^ even_odd_horiz;
+                invert_column =
+                  (even_odd_horiz && (bg[pat].type & 0x10));
+                if (even_odd == 0) {		/* gradient #1 */
+                    if (invert_column) {
+                        *dest++ = r1_inv;
+                        *dest++ = g1_inv;
+                        *dest++ = b1_inv;
+                    } else {
+                        *dest++ = r1;
+                        *dest++ = g1;
+                        *dest++ = b1;
+                    }
+                } else {			/* gradient #2 */
+                    if ((invert_column && invert_gradient2) ||
+                        (!invert_column && !invert_gradient2))
+                    {
+                        *dest++ = r2;      /* not inverted or */
+                        *dest++ = g2;      /*  doubly inverted */
+                        *dest++ = b2;
+                    } else {
+                        *dest++ = r2_inv;
+                        *dest++ = g2_inv;  /* singly inverted */
+                        *dest++ = b2_inv;
+                    }
+                }
+            }
+        }
+
+/*---------------------------------------------------------------------------
+    Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
+    M. Costello.
+  ---------------------------------------------------------------------------*/
+
+    } else if ((bg[pat].type & 0x07) == 1) {
+
+        hmax = (bgscale-1)/2;	/* half the max weight of a color */
+        max = 2*hmax;		/* the max weight of a color */
+
+        r1 = rgb[bg[pat].rgb1_max].r;
+        g1 = rgb[bg[pat].rgb1_max].g;
+        b1 = rgb[bg[pat].rgb1_max].b;
+        r2 = rgb[bg[pat].rgb2_max].r;
+        g2 = rgb[bg[pat].rgb2_max].g;
+        b2 = rgb[bg[pat].rgb2_max].b;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            yidx = row % bgscale;
+            if (yidx > hmax)
+                yidx = bgscale-1 - yidx;
+            dest = bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                xidx = i % bgscale;
+                if (xidx > hmax)
+                    xidx = bgscale-1 - xidx;
+                k = xidx + yidx;
+                *dest++ = (k*r1 + (max-k)*r2) / max;
+                *dest++ = (k*g1 + (max-k)*g2) / max;
+                *dest++ = (k*b1 + (max-k)*b2) / max;
+            }
+        }
+
+/*---------------------------------------------------------------------------
+    Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
+    soids will equal bgscale?].  This one is slow but very cool.  Code con-
+    tributed by Pieter S. van der Meulen (originally in Smalltalk).
+  ---------------------------------------------------------------------------*/
+
+    } else if ((bg[pat].type & 0x07) == 2) {
+        uch ch;
+        int ii, x, y, hw, hh, grayspot;
+        double freq, rotate, saturate, gray, intensity;
+        double angle=0.0, aoffset=0.0, maxDist, dist;
+        double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
+
+        fprintf(stderr, "%s:  computing radial background...",
+          PROGNAME);
+        fflush(stderr);
+
+        hh = rpng2_info.height / 2;
+        hw = rpng2_info.width / 2;
+
+        /* variables for radial waves:
+         *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
+         *   freq:  number of color beams originating from the center
+         *   grayspot:  size of the graying center area (anti-alias)
+         *   rotate:  rotation of the beams as a function of radius
+         *   saturate:  saturation of beams' shape azimuthally
+         */
+        angle = CLIP(angle, 0.0, 360.0);
+        grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
+        freq = MAX((double)bg[pat].bg_freq, 0.0);
+        saturate = (double)bg[pat].bg_bsat * 0.1;
+        rotate = (double)bg[pat].bg_brot * 0.1;
+        gray = 0.0;
+        intensity = 0.0;
+        maxDist = (double)((hw*hw) + (hh*hh));
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            y = row - hh;
+            dest = bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                x = i - hw;
+                angle = (x == 0)? PI_2 : atan((double)y / (double)x);
+                gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
+                gray = MIN(1.0, gray);
+                dist = (double)((x*x) + (y*y)) / maxDist;
+                intensity = cos((angle+(rotate*dist*PI)) * freq) *
+                  gray * saturate;
+                intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
+                hue = (angle + PI) * INV_PI_360 + aoffset;
+                s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
+                s = MIN(MAX(s,0.0), 1.0);
+                v = MIN(MAX(intensity,0.0), 1.0);
+
+                if (s == 0.0) {
+                    ch = (uch)(v * 255.0);
+                    *dest++ = ch;
+                    *dest++ = ch;
+                    *dest++ = ch;
+                } else {
+                    if ((hue < 0.0) || (hue >= 360.0))
+                        hue -= (((int)(hue / 360.0)) * 360.0);
+                    hue /= 60.0;
+                    ii = (int)hue;
+                    f = hue - (double)ii;
+                    p = (1.0 - s) * v;
+                    q = (1.0 - (s * f)) * v;
+                    t = (1.0 - (s * (1.0 - f))) * v;
+                    if      (ii == 0) { red = v; green = t; blue = p; }
+                    else if (ii == 1) { red = q; green = v; blue = p; }
+                    else if (ii == 2) { red = p; green = v; blue = t; }
+                    else if (ii == 3) { red = p; green = q; blue = v; }
+                    else if (ii == 4) { red = t; green = p; blue = v; }
+                    else if (ii == 5) { red = v; green = p; blue = q; }
+                    *dest++ = (uch)(red * 255.0);
+                    *dest++ = (uch)(green * 255.0);
+                    *dest++ = (uch)(blue * 255.0);
+                }
+            }
+        }
+        fprintf(stderr, "done.\n");
+        fflush(stderr);
+    }
+
+/*---------------------------------------------------------------------------
+    Blast background image to display buffer before beginning PNG decode.
+  ---------------------------------------------------------------------------*/
+
+    if (depth == 24 || depth == 32) {
+        ulg red, green, blue;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            src = bg_data + row*bg_rowbytes;
+            dest = ximage->data + row*ximage_rowbytes;
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                red   = *src++;
+                green = *src++;
+                blue  = *src++;
+                pixel = (red   << RPixelShift) |
+                        (green << GPixelShift) |
+                        (blue  << BPixelShift);
+                /* recall that we set ximage->byte_order = MSBFirst above */
+                *dest++ = ((uch *)&pixel)[3];
+                *dest++ = ((uch *)&pixel)[2];
+                *dest++ = ((uch *)&pixel)[1];
+                *dest++ = ((uch *)&pixel)[0];
+            }
+        }
+
+    } else if (depth == 16) {
+        ush red, green, blue;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            src = bg_data + row*bg_rowbytes;
+            dest = ximage->data + row*ximage_rowbytes;
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                red   = ((ush)(*src) << 8);
+                ++src;
+                green = ((ush)(*src) << 8);
+                ++src;
+                blue  = ((ush)(*src) << 8);
+                ++src;
+                pixel = ((red   >> RPixelShift) & RedMask)   |
+                        ((green >> GPixelShift) & GreenMask) |
+                        ((blue  >> BPixelShift) & BlueMask);
+                /* recall that we set ximage->byte_order = MSBFirst above */
+                *dest++ = ((uch *)&pixel)[1];
+                *dest++ = ((uch *)&pixel)[0];
+            }
+        }
+
+    } else /* depth == 8 */ {
+
+        /* GRR:  add 8-bit support */
+
+    }
+
+    XPutImage(display, window, gc, ximage, 0, 0, 0, 0, rpng2_info.width,
+      rpng2_info.height);
+
+    return 0;
+
+} /* end function rpng2_x_load_bg_image() */
+
+
+
+
+
+static void rpng2_x_display_row(ulg row)
+{
+    uch bg_red   = rpng2_info.bg_red;
+    uch bg_green = rpng2_info.bg_green;
+    uch bg_blue  = rpng2_info.bg_blue;
+    uch *src, *src2=NULL, *dest;
+    uch r, g, b, a;
+    int ximage_rowbytes = ximage->bytes_per_line;
+    ulg i, pixel;
+    static int rows=0;
+    static ulg firstrow;
+
+/*---------------------------------------------------------------------------
+    rows and firstrow simply track how many rows (and which ones) have not
+    yet been displayed; alternatively, we could call XPutImage() for every
+    row and not bother with the records-keeping.
+  ---------------------------------------------------------------------------*/
+
+    Trace((stderr, "beginning rpng2_x_display_row()\n"))
+
+    if (rows == 0)
+        firstrow = row;   /* first row not yet displayed */
+
+    ++rows;   /* count of rows received but not yet displayed */
+
+/*---------------------------------------------------------------------------
+    Aside from the use of the rpng2_info struct, the lack of an outer loop
+    (over rows) and moving the XPutImage() call outside the "if (depth)"
+    tests, this routine is identical to rpng_x_display_image() in the non-
+    progressive version of the program.
+  ---------------------------------------------------------------------------*/
+
+    if (depth == 24 || depth == 32) {
+        ulg red, green, blue;
+
+        src = rpng2_info.image_data + row*rpng2_info.rowbytes;
+        if (bg_image)
+            src2 = bg_data + row*bg_rowbytes;
+        dest = ximage->data + row*ximage_rowbytes;
+        if (rpng2_info.channels == 3) {
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                red   = *src++;
+                green = *src++;
+                blue  = *src++;
+                pixel = (red   << RPixelShift) |
+                        (green << GPixelShift) |
+                        (blue  << BPixelShift);
+                /* recall that we set ximage->byte_order = MSBFirst */
+                *dest++ = ((uch *)&pixel)[3];
+                *dest++ = ((uch *)&pixel)[2];
+                *dest++ = ((uch *)&pixel)[1];
+                *dest++ = ((uch *)&pixel)[0];
+            }
+        } else /* if (rpng2_info.channels == 4) */ {
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                r = *src++;
+                g = *src++;
+                b = *src++;
+                a = *src++;
+                if (bg_image) {
+                    bg_red   = *src2++;
+                    bg_green = *src2++;
+                    bg_blue  = *src2++;
+                }
+                if (a == 255) {
+                    red   = r;
+                    green = g;
+                    blue  = b;
+                } else if (a == 0) {
+                    red   = bg_red;
+                    green = bg_green;
+                    blue  = bg_blue;
+                } else {
+                    /* this macro (from png.h) composites the foreground
+                     * and background values and puts the result into the
+                     * first argument */
+                    alpha_composite(red,   r, a, bg_red);
+                    alpha_composite(green, g, a, bg_green);
+                    alpha_composite(blue,  b, a, bg_blue);
+                }
+                pixel = (red   << RPixelShift) |
+                        (green << GPixelShift) |
+                        (blue  << BPixelShift);
+                /* recall that we set ximage->byte_order = MSBFirst */
+                *dest++ = ((uch *)&pixel)[3];
+                *dest++ = ((uch *)&pixel)[2];
+                *dest++ = ((uch *)&pixel)[1];
+                *dest++ = ((uch *)&pixel)[0];
+            }
+        }
+
+    } else if (depth == 16) {
+        ush red, green, blue;
+
+        src = rpng2_info.row_pointers[row];
+        if (bg_image)
+            src2 = bg_data + row*bg_rowbytes;
+        dest = ximage->data + row*ximage_rowbytes;
+        if (rpng2_info.channels == 3) {
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                red   = ((ush)(*src) << 8);
+                ++src;
+                green = ((ush)(*src) << 8);
+                ++src;
+                blue  = ((ush)(*src) << 8);
+                ++src;
+                pixel = ((red   >> RPixelShift) & RedMask)   |
+                        ((green >> GPixelShift) & GreenMask) |
+                        ((blue  >> BPixelShift) & BlueMask);
+                /* recall that we set ximage->byte_order = MSBFirst */
+                *dest++ = ((uch *)&pixel)[1];
+                *dest++ = ((uch *)&pixel)[0];
+            }
+        } else /* if (rpng2_info.channels == 4) */ {
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                r = *src++;
+                g = *src++;
+                b = *src++;
+                a = *src++;
+                if (bg_image) {
+                    bg_red   = *src2++;
+                    bg_green = *src2++;
+                    bg_blue  = *src2++;
+                }
+                if (a == 255) {
+                    red   = ((ush)r << 8);
+                    green = ((ush)g << 8);
+                    blue  = ((ush)b << 8);
+                } else if (a == 0) {
+                    red   = ((ush)bg_red   << 8);
+                    green = ((ush)bg_green << 8);
+                    blue  = ((ush)bg_blue  << 8);
+                } else {
+                    /* this macro (from png.h) composites the foreground
+                     * and background values and puts the result back into
+                     * the first argument (== fg byte here:  safe) */
+                    alpha_composite(r, r, a, bg_red);
+                    alpha_composite(g, g, a, bg_green);
+                    alpha_composite(b, b, a, bg_blue);
+                    red   = ((ush)r << 8);
+                    green = ((ush)g << 8);
+                    blue  = ((ush)b << 8);
+                }
+                pixel = ((red   >> RPixelShift) & RedMask)   |
+                        ((green >> GPixelShift) & GreenMask) |
+                        ((blue  >> BPixelShift) & BlueMask);
+                /* recall that we set ximage->byte_order = MSBFirst */
+                *dest++ = ((uch *)&pixel)[1];
+                *dest++ = ((uch *)&pixel)[0];
+            }
+        }
+
+    } else /* depth == 8 */ {
+
+        /* GRR:  add 8-bit support */
+
+    }
+
+
+/*---------------------------------------------------------------------------
+    Display after every 16 rows or when on last row.  (Region may include
+    previously displayed lines due to interlacing--i.e., not contiguous.)
+  ---------------------------------------------------------------------------*/
+
+    if ((rows & 0xf) == 0 || row == rpng2_info.height-1) {
+        XPutImage(display, window, gc, ximage, 0, firstrow, 0, firstrow,
+          rpng2_info.width, row - firstrow + 1);
+        XFlush(display);
+        rows = 0;
+    }
+
+}
+
+
+
+
+
+static void rpng2_x_finish_display()
+{
+    Trace((stderr, "beginning rpng2_x_finish_display()\n"))
+
+    /* last row has already been displayed by rpng2_x_display_row(), so we
+     * have nothing to do here except set a flag and let the user know that
+     * the image is done */
+
+    rpng2_info.done = TRUE;
+    printf("Done.  Press Q, Esc or mouse button 1 to quit.\n");
+}
+
+
+
+
+
+static void rpng2_x_cleanup()
+{
+    if (bg_image && bg_data) {
+        free(bg_data);
+        bg_data = NULL;
+    }
+
+    if (rpng2_info.image_data) {
+        free(rpng2_info.image_data);
+        rpng2_info.image_data = NULL;
+    }
+
+    if (rpng2_info.row_pointers) {
+        free(rpng2_info.row_pointers);
+        rpng2_info.row_pointers = NULL;
+    }
+
+    if (ximage) {
+        if (ximage->data) {
+            free(ximage->data);           /* we allocated it, so we free it */
+            ximage->data = (char *)NULL;  /*  instead of XDestroyImage() */
+        }
+        XDestroyImage(ximage);
+        ximage = NULL;
+    }
+
+    XFreeGC(display, gc);
+
+    if (have_window)
+        XDestroyWindow(display, window);
+
+    if (have_colormap)
+        XFreeColormap(display, colormap);
+}
+
+
+
+
+
+static int rpng2_x_msb(ulg u32val)
+{
+    int i;
+
+    for (i = 31;  i >= 0;  --i) {
+        if (u32val & 0x80000000L)
+            break;
+        u32val <<= 1;
+    }
+    return i;
+}
diff --git a/contrib/gregbook/wpng.c b/contrib/gregbook/wpng.c
new file mode 100644
index 0000000..38f16f8
--- /dev/null
+++ b/contrib/gregbook/wpng.c
@@ -0,0 +1,804 @@
+/*---------------------------------------------------------------------------
+
+   wpng - simple PNG-writing program                                 wpng.c
+
+   This program converts certain NetPBM binary files (grayscale and RGB,
+   maxval = 255) to PNG.  Non-interlaced PNGs are written progressively;
+   interlaced PNGs are read and written in one memory-intensive blast.
+   Thanks to Jean-loup Gailly for providing the necessary trick to read
+   interactive text from the keyboard while stdin is redirected.
+
+   NOTE:  includes provisional support for PNM type "8" (portable alphamap)
+          images, presumed to be a 32-bit interleaved RGBA format; no pro-
+          vision for possible interleaved grayscale+alpha (16-bit) format.
+          THIS IS UNLIKELY TO BECOME AN OFFICIAL NETPBM ALPHA FORMAT!
+
+   to do:
+    - delete output file if quit before calling any writepng routines
+    - process backspace with -text option under DOS/Win? (currently get ^H)
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-1999 Greg Roelofs.  All rights reserved.
+
+      This software is provided "as is," without warranty of any kind,
+      express or implied.  In no event shall the author or contributors
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+  ---------------------------------------------------------------------------*/
+
+#define PROGNAME  "wpng"
+#define VERSION   "1.01 of 31 March 1999"
+#define APPNAME   "Simple PGM/PPM/PAM to PNG Converter"
+
+#if defined(__MSDOS__) || defined(__OS2__)
+#  define DOS_OS2_W32
+#elif defined(_WIN32) || defined(__WIN32__)
+#  define DOS_OS2_W32
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>	/* for jmpbuf declaration in writepng.h */
+#include <time.h>
+
+#ifdef DOS_OS2_W32
+#  include <io.h>	/* for isatty(), setmode() prototypes */
+#  include <fcntl.h>	/* O_BINARY for fdopen() without text translation */
+#  ifdef __EMX__
+#    ifndef getch
+#      define getch() _read_kbd(0, 1, 0)	/* need getche() */
+#    endif
+#  else /* !__EMX__ */
+#    ifdef __GO32__
+#      include <pc.h>
+#      define getch() getkey()	/* GRR:  need getche() */
+#    else
+#      include <conio.h>	/* for getche() console input */
+#    endif
+#  endif /* ?__EMX__ */
+#  define FGETS(buf,len,stream)  dos_kbd_gets(buf,len)
+#else
+#  include <unistd.h>		/* for isatty() prototype */
+#  define FGETS fgets
+#endif
+
+/* #define DEBUG  :  this enables the Trace() macros */
+
+/* #define FORBID_LATIN1_CTRL  :  this requires the user to re-enter any
+   text that includes control characters discouraged by the PNG spec; text
+   that includes an escape character (27) must be re-entered regardless */
+
+#include "writepng.h"	/* typedefs, common macros, writepng prototypes */
+
+
+
+/* local prototypes */
+
+static int  wpng_isvalid_latin1(uch *p, int len);
+static void wpng_cleanup(void);
+
+#ifdef DOS_OS2_W32
+   static char *dos_kbd_gets(char *buf, int len);
+#endif
+
+
+
+static mainprog_info wpng_info;   /* lone global */
+
+
+
+int main(int argc, char **argv)
+{
+#ifndef DOS_OS2_W32
+    FILE *keybd;
+#endif
+#ifdef sgi
+    FILE *tmpfile;	/* or we could just use keybd, since no overlap */
+    char tmpline[80];
+#endif
+    char *inname = NULL, outname[256];
+    char *p, pnmchar, pnmline[256];
+    char *bgstr, *textbuf = NULL;
+    ulg rowbytes;
+    int rc, len = 0;
+    int error = 0;
+    int text = FALSE;
+    int maxval;
+    double LUT_exponent;                /* just the lookup table */
+    double CRT_exponent = 2.2;          /* just the monitor */
+    double default_display_exponent;    /* whole display system */
+    double default_gamma = 0.0;
+
+
+    wpng_info.infile = NULL;
+    wpng_info.outfile = NULL;
+    wpng_info.image_data = NULL;
+    wpng_info.row_pointers = NULL;
+    wpng_info.filter = FALSE;
+    wpng_info.interlaced = FALSE;
+    wpng_info.have_bg = FALSE;
+    wpng_info.have_time = FALSE;
+    wpng_info.have_text = 0;
+    wpng_info.gamma = 0.0;
+
+
+    /* First get the default value for our display-system exponent, i.e.,
+     * the product of the CRT exponent and the exponent corresponding to
+     * the frame-buffer's lookup table (LUT), if any.  If the PNM image
+     * looks correct on the user's display system, its file gamma is the
+     * inverse of this value.  (Note that this is not an exhaustive list
+     * of LUT values--e.g., OpenStep has a lot of weird ones--but it should
+     * cover 99% of the current possibilities.  This section must ensure
+     * that default_display_exponent is positive.) */
+
+#if defined(NeXT)
+    /* third-party utilities can modify the default LUT exponent */
+    LUT_exponent = 1.0 / 2.2;
+    /*
+    if (some_next_function_that_returns_gamma(&next_gamma))
+        LUT_exponent = 1.0 / next_gamma;
+     */
+#elif defined(sgi)
+    LUT_exponent = 1.0 / 1.7;
+    /* there doesn't seem to be any documented function to
+     * get the "gamma" value, so we do it the hard way */
+    tmpfile = fopen("/etc/config/system.glGammaVal", "r");
+    if (tmpfile) {
+        double sgi_gamma;
+
+        fgets(tmpline, 80, tmpfile);
+        fclose(tmpfile);
+        sgi_gamma = atof(tmpline);
+        if (sgi_gamma > 0.0)
+            LUT_exponent = 1.0 / sgi_gamma;
+    }
+#elif defined(Macintosh)
+    LUT_exponent = 1.8 / 2.61;
+    /*
+    if (some_mac_function_that_returns_gamma(&mac_gamma))
+        LUT_exponent = mac_gamma / 2.61;
+     */
+#else
+    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
+#endif
+
+    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
+    default_display_exponent = LUT_exponent * CRT_exponent;
+
+
+    /* If the user has set the SCREEN_GAMMA environment variable as suggested
+     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
+     * use the default value we just calculated.  Either way, the user may
+     * override this via a command-line option. */
+
+    if ((p = getenv("SCREEN_GAMMA")) != NULL) {
+        double exponent = atof(p);
+
+        if (exponent > 0.0)
+            default_gamma = 1.0 / exponent;
+    }
+
+    if (default_gamma == 0.0)
+        default_gamma = 1.0 / default_display_exponent;
+
+
+    /* Now parse the command line for options and the PNM filename. */
+
+    while (*++argv && !error) {
+        if (!strcmp(*argv, "-interlaced")) {
+            wpng_info.interlaced = TRUE;
+        } else if (!strcmp(*argv, "-time")) {
+            wpng_info.modtime = time(NULL);
+            wpng_info.have_time = TRUE;
+        } else if (!strcmp(*argv, "-text")) {
+            text = TRUE;
+        } else if (!strcmp(*argv, "-gamma")) {
+            if (!*++argv)
+                ++error;
+            wpng_info.gamma = atof(*argv);
+            if (wpng_info.gamma <= 0.0)
+                ++error;
+            else if (wpng_info.gamma > 1.01)
+                fprintf(stderr, PROGNAME
+                  " warning:  file gammas are usually less than 1.0\n");
+        } else if (!strcmp(*argv, "-bgcolor")) {
+            if (!*++argv)
+                ++error;
+            bgstr = *argv;
+            if (strlen(bgstr) != 7 || bgstr[0] != '#')
+                ++error;
+            else {
+                unsigned r, g, b;  /* this approach quiets compiler warnings */
+
+                sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
+                wpng_info.bg_red   = (uch)r;
+                wpng_info.bg_green = (uch)g;
+                wpng_info.bg_blue  = (uch)b;
+                wpng_info.have_bg = TRUE;
+            }
+        } else {
+            if (**argv != '-') {
+                inname = *argv;
+                if (argv[1])   /* shouldn't be any more args after filename */
+                    ++error;
+            } else
+                ++error;   /* not expecting any other options */
+        }
+    }
+
+
+    /* open the input and output files, or register an error and abort */
+
+    if (!inname) {
+        if (isatty(0)) {
+            fprintf(stderr, PROGNAME
+              ":  must give input filename or provide image data via stdin\n");
+            ++error;
+        } else {
+#ifdef DOS_OS2_W32
+            /* some buggy C libraries require BOTH setmode() and fdopen(bin) */
+            setmode(fileno(stdin), O_BINARY);
+            setmode(fileno(stdout), O_BINARY);
+#endif
+            if ((wpng_info.infile = fdopen(fileno(stdin), "rb")) == NULL) {
+                fprintf(stderr, PROGNAME
+                  ":  unable to reopen stdin in binary mode\n");
+                ++error;
+            } else
+            if ((wpng_info.outfile = fdopen(fileno(stdout), "wb")) == NULL) {
+                fprintf(stderr, PROGNAME
+                  ":  unable to reopen stdout in binary mode\n");
+                fclose(wpng_info.infile);
+                ++error;
+            } else 
+                wpng_info.filter = TRUE;
+        }
+    } else if ((len = strlen(inname)) > 250) {
+        fprintf(stderr, PROGNAME ":  input filename is too long [%d chars]\n",
+          len);
+        ++error;
+    } else if (!(wpng_info.infile = fopen(inname, "rb"))) {
+        fprintf(stderr, PROGNAME ":  can't open input file [%s]\n", inname);
+        ++error;
+    }
+
+    if (!error) {
+        fgets(pnmline, 256, wpng_info.infile);
+        if (pnmline[0] != 'P' || ((pnmchar = pnmline[1]) != '5' &&
+            pnmchar != '6' && pnmchar != '8'))
+        {
+            fprintf(stderr, PROGNAME
+              ":  input file [%s] is not a binary PGM, PPM or PAM file\n",
+              inname);
+            ++error;
+        } else {
+            wpng_info.pnmtype = (int)(pnmchar - '0');
+            if (wpng_info.pnmtype != 8)
+                wpng_info.have_bg = FALSE;  /* no need for bg if opaque */
+            do {
+                fgets(pnmline, 256, wpng_info.infile);  /* lose any comments */
+            } while (pnmline[0] == '#');
+            sscanf(pnmline, "%ld %ld", &wpng_info.width, &wpng_info.height);
+            do {
+                fgets(pnmline, 256, wpng_info.infile);  /* more comment lines */
+            } while (pnmline[0] == '#');
+            sscanf(pnmline, "%d", &maxval);
+            if (wpng_info.width <= 0L || wpng_info.height <= 0L ||
+                maxval != 255)
+            {
+                fprintf(stderr, PROGNAME
+                  ":  only positive width/height, maxval == 255 allowed \n");
+                ++error;
+            }
+            wpng_info.sample_depth = 8;  /* <==> maxval 255 */
+
+            if (!wpng_info.filter) {
+                /* make outname from inname */
+                if ((p = strrchr(inname, '.')) == NULL ||
+                    (p - inname) != (len - 4))
+                {
+                    strcpy(outname, inname);
+                    strcpy(outname+len, ".png");
+                } else {
+                    len -= 4;
+                    strncpy(outname, inname, len);
+                    strcpy(outname+len, ".png");
+                }
+                /* check if outname already exists; if not, open */
+                if ((wpng_info.outfile = fopen(outname, "rb")) != NULL) {
+                    fprintf(stderr, PROGNAME ":  output file exists [%s]\n",
+                      outname);
+                    fclose(wpng_info.outfile);
+                    ++error;
+                } else if (!(wpng_info.outfile = fopen(outname, "wb"))) {
+                    fprintf(stderr, PROGNAME ":  can't open output file [%s]\n",
+                      outname);
+                    ++error;
+                }
+            }
+        }
+        if (error) {
+            fclose(wpng_info.infile);
+            wpng_info.infile = NULL;
+            if (wpng_info.filter) {
+                fclose(wpng_info.outfile);
+                wpng_info.outfile = NULL;
+            }
+        }
+    }
+
+
+    /* if we had any errors, print usage and die horrible death...arrr! */
+
+    if (error) {
+        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, APPNAME);
+        writepng_version_info();
+        fprintf(stderr, "\n"
+"Usage:  %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] pnmfile\n"
+"or: ... | %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] | ...\n"
+         "    exp \ttransfer-function exponent (``gamma'') of the image in\n"
+         "\t\t  floating-point format (e.g., ``%.5f''); if image looks\n"
+         "\t\t  correct on given display system, image gamma is equal to\n"
+         "\t\t  inverse of display-system exponent, i.e., 1 / (LUT * CRT)\n"
+         "\t\t  (where LUT = lookup-table exponent and CRT = CRT exponent;\n"
+         "\t\t  first varies, second is usually 2.2, all are positive)\n"
+         "    bg  \tdesired background color for alpha-channel images, in\n"
+         "\t\t  7-character hex RGB format (e.g., ``#ff7f00'' for orange:\n"
+         "\t\t  same as HTML colors)\n"
+         "    -text\tprompt interactively for text info (tEXt chunks)\n"
+         "    -time\tinclude a tIME chunk (last modification time)\n"
+         "    -interlace\twrite interlaced PNG image\n"
+         "\n"
+"pnmfile or stdin must be a binary PGM (`P5'), PPM (`P6') or (extremely\n"
+"unofficial and unsupported!) PAM (`P8') file.  Currently it is required\n"
+"to have maxval == 255 (i.e., no scaling).  If pnmfile is specified, it\n"
+"is converted to the corresponding PNG file with the same base name but a\n"
+"``.png'' extension; files read from stdin are converted and sent to stdout.\n"
+"The conversion is progressive (low memory usage) unless interlacing is\n"
+"requested; in that case the whole image will be buffered in memory and\n"
+"written in one call.\n"
+         "\n", PROGNAME, PROGNAME, default_gamma);
+        exit(1);
+    }
+
+
+    /* prepare the text buffers for libpng's use; note that even though
+     * PNG's png_text struct includes a length field, we don't have to fill
+     * it out */
+
+    if (text &&
+#ifndef DOS_OS2_W32
+        (keybd = fdopen(fileno(stderr), "r")) != NULL &&
+#endif
+        (textbuf = (char *)malloc((5 + 9)*75)) != NULL)
+    {
+        int i, valid, result;
+
+        fprintf(stderr,
+          "Enter text info (no more than 72 characters per line);\n");
+        fprintf(stderr, "to skip a field, hit the <Enter> key.\n");
+        /* note:  just <Enter> leaves len == 1 */
+
+        do {
+            valid = TRUE;
+            p = textbuf + TEXT_TITLE_OFFSET;
+            fprintf(stderr, "  Title: ");
+            fflush(stderr);
+            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
+                if (p[len-1] == '\n')
+                    p[--len] = '\0';
+                wpng_info.title = p;
+                wpng_info.have_text |= TEXT_TITLE;
+                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
+                    fprintf(stderr, "    " PROGNAME " warning:  character code"
+                      " %u is %sdiscouraged by the PNG\n    specification "
+                      "[first occurrence was at character position #%d]\n",
+                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
+                      result+1);
+                    fflush(stderr);
+#ifdef FORBID_LATIN1_CTRL
+                    wpng_info.have_text &= ~TEXT_TITLE;
+                    valid = FALSE;
+#else
+                    if (p[result] == 27) {	/* escape character */
+                        wpng_info.have_text &= ~TEXT_TITLE;
+                        valid = FALSE;
+                    }
+#endif
+                }
+            }
+        } while (!valid);
+
+        do {
+            valid = TRUE;
+            p = textbuf + TEXT_AUTHOR_OFFSET;
+            fprintf(stderr, "  Author: ");
+            fflush(stderr);
+            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
+                if (p[len-1] == '\n')
+                    p[--len] = '\0';
+                wpng_info.author = p;
+                wpng_info.have_text |= TEXT_AUTHOR;
+                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
+                    fprintf(stderr, "    " PROGNAME " warning:  character code"
+                      " %u is %sdiscouraged by the PNG\n    specification "
+                      "[first occurrence was at character position #%d]\n",
+                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
+                      result+1);
+                    fflush(stderr);
+#ifdef FORBID_LATIN1_CTRL
+                    wpng_info.have_text &= ~TEXT_AUTHOR;
+                    valid = FALSE;
+#else
+                    if (p[result] == 27) {	/* escape character */
+                        wpng_info.have_text &= ~TEXT_AUTHOR;
+                        valid = FALSE;
+                    }
+#endif
+                }
+            }
+        } while (!valid);
+
+        do {
+            valid = TRUE;
+            p = textbuf + TEXT_DESC_OFFSET;
+            fprintf(stderr, "  Description (up to 9 lines):\n");
+            for (i = 1;  i < 10;  ++i) {
+                fprintf(stderr, "    [%d] ", i);
+                fflush(stderr);
+                if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1)
+                    p += len;   /* now points at NULL; char before is newline */
+                else
+                    break;
+            }
+            if ((len = p - (textbuf + TEXT_DESC_OFFSET)) > 1) {
+                if (p[-1] == '\n') {
+                    p[-1] = '\0';
+                    --len;
+                }
+                wpng_info.desc = textbuf + TEXT_DESC_OFFSET;
+                wpng_info.have_text |= TEXT_DESC;
+                p = textbuf + TEXT_DESC_OFFSET;
+                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
+                    fprintf(stderr, "    " PROGNAME " warning:  character code"
+                      " %u is %sdiscouraged by the PNG\n    specification "
+                      "[first occurrence was at character position #%d]\n",
+                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
+                      result+1);
+                    fflush(stderr);
+#ifdef FORBID_LATIN1_CTRL
+                    wpng_info.have_text &= ~TEXT_DESC;
+                    valid = FALSE;
+#else
+                    if (p[result] == 27) {	/* escape character */
+                        wpng_info.have_text &= ~TEXT_DESC;
+                        valid = FALSE;
+                    }
+#endif
+                }
+            }
+        } while (!valid);
+
+        do {
+            valid = TRUE;
+            p = textbuf + TEXT_COPY_OFFSET;
+            fprintf(stderr, "  Copyright: ");
+            fflush(stderr);
+            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
+                if (p[len-1] == '\n')
+                    p[--len] = '\0';
+                wpng_info.copyright = p;
+                wpng_info.have_text |= TEXT_COPY;
+                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
+                    fprintf(stderr, "    " PROGNAME " warning:  character code"
+                      " %u is %sdiscouraged by the PNG\n    specification "
+                      "[first occurrence was at character position #%d]\n",
+                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
+                      result+1);
+                    fflush(stderr);
+#ifdef FORBID_LATIN1_CTRL
+                    wpng_info.have_text &= ~TEXT_COPY;
+                    valid = FALSE;
+#else
+                    if (p[result] == 27) {	/* escape character */
+                        wpng_info.have_text &= ~TEXT_COPY;
+                        valid = FALSE;
+                    }
+#endif
+                }
+            }
+        } while (!valid);
+
+        do {
+            valid = TRUE;
+            p = textbuf + TEXT_EMAIL_OFFSET;
+            fprintf(stderr, "  E-mail: ");
+            fflush(stderr);
+            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
+                if (p[len-1] == '\n')
+                    p[--len] = '\0';
+                wpng_info.email = p;
+                wpng_info.have_text |= TEXT_EMAIL;
+                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
+                    fprintf(stderr, "    " PROGNAME " warning:  character code"
+                      " %u is %sdiscouraged by the PNG\n    specification "
+                      "[first occurrence was at character position #%d]\n",
+                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
+                      result+1);
+                    fflush(stderr);
+#ifdef FORBID_LATIN1_CTRL
+                    wpng_info.have_text &= ~TEXT_EMAIL;
+                    valid = FALSE;
+#else
+                    if (p[result] == 27) {	/* escape character */
+                        wpng_info.have_text &= ~TEXT_EMAIL;
+                        valid = FALSE;
+                    }
+#endif
+                }
+            }
+        } while (!valid);
+
+        do {
+            valid = TRUE;
+            p = textbuf + TEXT_URL_OFFSET;
+            fprintf(stderr, "  URL: ");
+            fflush(stderr);
+            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
+                if (p[len-1] == '\n')
+                    p[--len] = '\0';
+                wpng_info.url = p;
+                wpng_info.have_text |= TEXT_URL;
+                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
+                    fprintf(stderr, "    " PROGNAME " warning:  character code"
+                      " %u is %sdiscouraged by the PNG\n    specification "
+                      "[first occurrence was at character position #%d]\n",
+                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
+                      result+1);
+                    fflush(stderr);
+#ifdef FORBID_LATIN1_CTRL
+                    wpng_info.have_text &= ~TEXT_URL;
+                    valid = FALSE;
+#else
+                    if (p[result] == 27) {	/* escape character */
+                        wpng_info.have_text &= ~TEXT_URL;
+                        valid = FALSE;
+                    }
+#endif
+                }
+            }
+        } while (!valid);
+
+#ifndef DOS_OS2_W32
+        fclose(keybd);
+#endif
+
+    } else if (text) {
+        fprintf(stderr, PROGNAME ":  unable to allocate memory for text\n");
+        text = FALSE;
+        wpng_info.have_text = 0;
+    }
+
+
+    /* allocate libpng stuff, initialize transformations, write pre-IDAT data */
+
+    if ((rc = writepng_init(&wpng_info)) != 0) {
+        switch (rc) {
+            case 2:
+                fprintf(stderr, PROGNAME
+                  ":  libpng initialization problem (longjmp)\n");
+                break;
+            case 4:
+                fprintf(stderr, PROGNAME ":  insufficient memory\n");
+                break;
+            case 11:
+                fprintf(stderr, PROGNAME
+                  ":  internal logic error (unexpected PNM type)\n");
+                break;
+            default:
+                fprintf(stderr, PROGNAME
+                  ":  unknown writepng_init() error\n");
+                break;
+        }
+        exit(rc);
+    }
+
+
+    /* free textbuf, since it's a completely local variable and all text info
+     * has just been written to the PNG file */
+
+    if (text && textbuf) {
+        free(textbuf);
+        textbuf = NULL;
+    }
+
+
+    /* calculate rowbytes on basis of image type; note that this becomes much
+     * more complicated if we choose to support PBM type, ASCII PNM types, or
+     * 16-bit-per-sample binary data [currently not an official NetPBM type] */
+
+    if (wpng_info.pnmtype == 5)
+        rowbytes = wpng_info.width;
+    else if (wpng_info.pnmtype == 6)
+        rowbytes = wpng_info.width * 3;
+    else /* if (wpng_info.pnmtype == 8) */
+        rowbytes = wpng_info.width * 4;
+
+
+    /* read and write the image, either in its entirety (if writing interlaced
+     * PNG) or row by row (if non-interlaced) */
+
+    fprintf(stderr, "Encoding image data...\n");
+    fflush(stderr);
+
+    if (wpng_info.interlaced) {
+        long i;
+        ulg bytes;
+        ulg image_bytes = rowbytes * wpng_info.height;   /* overflow? */
+
+        wpng_info.image_data = (uch *)malloc(image_bytes);
+        wpng_info.row_pointers = (uch **)malloc(wpng_info.height*sizeof(uch *));
+        if (wpng_info.image_data == NULL || wpng_info.row_pointers == NULL) {
+            fprintf(stderr, PROGNAME ":  insufficient memory for image data\n");
+            writepng_cleanup(&wpng_info);
+            wpng_cleanup();
+            exit(5);
+        }
+        for (i = 0;  i < wpng_info.height;  ++i)
+            wpng_info.row_pointers[i] = wpng_info.image_data + i*rowbytes;
+        bytes = fread(wpng_info.image_data, 1, image_bytes, wpng_info.infile);
+        if (bytes != image_bytes) {
+            fprintf(stderr, PROGNAME ":  expected %lu bytes, got %lu bytes\n",
+              image_bytes, bytes);
+            fprintf(stderr, "  (continuing anyway)\n");
+        }
+        if (writepng_encode_image(&wpng_info) != 0) {
+            fprintf(stderr, PROGNAME
+              ":  libpng problem (longjmp) while writing image data\n");
+            writepng_cleanup(&wpng_info);
+            wpng_cleanup();
+            exit(2);
+        }
+
+    } else /* not interlaced:  write progressively (row by row) */ {
+        long j;
+        ulg bytes;
+
+        wpng_info.image_data = (uch *)malloc(rowbytes);
+        if (wpng_info.image_data == NULL) {
+            fprintf(stderr, PROGNAME ":  insufficient memory for row data\n");
+            writepng_cleanup(&wpng_info);
+            wpng_cleanup();
+            exit(5);
+        }
+        error = 0;
+        for (j = wpng_info.height;  j > 0L;  --j) {
+            bytes = fread(wpng_info.image_data, 1, rowbytes, wpng_info.infile);
+            if (bytes != rowbytes) {
+                fprintf(stderr, PROGNAME
+                  ":  expected %lu bytes, got %lu bytes (row %ld)\n", rowbytes,
+                  bytes, wpng_info.height-j);
+                ++error;
+                break;
+            }
+            if (writepng_encode_row(&wpng_info) != 0) {
+                fprintf(stderr, PROGNAME
+                  ":  libpng problem (longjmp) while writing row %ld\n",
+                  wpng_info.height-j);
+                ++error;
+                break;
+            }
+        }
+        if (error) {
+            writepng_cleanup(&wpng_info);
+            wpng_cleanup();
+            exit(2);
+        }
+        if (writepng_encode_finish(&wpng_info) != 0) {
+            fprintf(stderr, PROGNAME ":  error on final libpng call\n");
+            writepng_cleanup(&wpng_info);
+            wpng_cleanup();
+            exit(2);
+        }
+    }
+
+
+    /* OK, we're done (successfully):  clean up all resources and quit */
+
+    fprintf(stderr, "Done.\n");
+    fflush(stderr);
+
+    writepng_cleanup(&wpng_info);
+    wpng_cleanup();
+
+    return 0;
+}
+
+
+
+
+
+static int wpng_isvalid_latin1(uch *p, int len)
+{
+    int i, result = -1;
+
+    for (i = 0;  i < len;  ++i) {
+        if (p[i] == 10 || (p[i] > 31 && p[i] < 127) || p[i] > 160)
+            continue;		/* character is completely OK */
+        if (result < 0 || (p[result] != 27 && p[i] == 27))
+            result = i;		/* mark location of first questionable one */
+    }                  		/*  or of first escape character (bad) */
+
+    return result;
+}
+
+
+
+
+
+static void wpng_cleanup()
+{
+    if (wpng_info.outfile) {
+        fclose(wpng_info.outfile);
+        wpng_info.outfile = NULL;
+    }
+
+    if (wpng_info.infile) {
+        fclose(wpng_info.infile);
+        wpng_info.infile = NULL;
+    }
+
+    if (wpng_info.image_data) {
+        free(wpng_info.image_data);
+        wpng_info.image_data = NULL;
+    }
+
+    if (wpng_info.row_pointers) {
+        free(wpng_info.row_pointers);
+        wpng_info.row_pointers = NULL;
+    }
+}
+
+
+
+
+#ifdef DOS_OS2_W32
+
+static char *dos_kbd_gets(char *buf, int len)
+{
+    int ch, count=0;
+
+    do {
+        buf[count++] = ch = getche();
+    } while (ch != '\r' && count < len-1);
+
+    buf[count--] = '\0';	/* terminate string */
+    if (buf[count] == '\r')	/* Enter key makes CR, so change to newline */
+        buf[count] = '\n';
+
+    fprintf(stderr, "\n");	/* Enter key does *not* cause a newline */
+    fflush(stderr);
+
+    return buf;
+}
+
+#endif /* DOS_OS2_W32 */
diff --git a/contrib/gregbook/writepng.c b/contrib/gregbook/writepng.c
new file mode 100644
index 0000000..1bfe447
--- /dev/null
+++ b/contrib/gregbook/writepng.c
@@ -0,0 +1,368 @@
+/*---------------------------------------------------------------------------
+
+   wpng - simple PNG-writing program                             writepng.c
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-1999 Greg Roelofs.  All rights reserved.
+
+      This software is provided "as is," without warranty of any kind,
+      express or implied.  In no event shall the author or contributors
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+  ---------------------------------------------------------------------------*/
+
+
+#include <stdlib.h>	/* for exit() prototype */
+
+#include "png.h"	/* libpng header; includes zlib.h and setjmp.h */
+#include "writepng.h"	/* typedefs, common macros, public prototypes */
+
+
+/* local prototype */
+
+static void writepng_error_handler(png_structp png_ptr, png_const_charp msg);
+
+
+
+void writepng_version_info()
+{
+  fprintf(stderr, "   Compiled with libpng %s; using libpng %s.\n",
+    PNG_LIBPNG_VER_STRING, png_libpng_ver);
+  fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
+    ZLIB_VERSION, zlib_version);
+} 
+
+
+
+
+/* returns 0 for success, 2 for libpng problem, 4 for out of memory, 11 for
+ *  unexpected pnmtype; note that outfile might be stdout */
+
+int writepng_init(mainprog_info *mainprog_ptr)
+{
+    png_structp  png_ptr;       /* note:  temporary variables! */
+    png_infop  info_ptr;
+    int color_type, interlace_type;
+
+
+    /* could also replace libpng warning-handler (final NULL), but no need: */
+
+    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
+      writepng_error_handler, NULL);
+    if (!png_ptr)
+        return 4;   /* out of memory */
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_write_struct(&png_ptr, NULL);
+        return 4;   /* out of memory */
+    }
+
+
+    /* setjmp() must be called in every function that calls a PNG-writing
+     * libpng function, unless an alternate error handler was installed--
+     * but compatible error handlers must either use longjmp() themselves
+     * (as in this program) or exit immediately, so here we go: */
+
+    if (setjmp(mainprog_ptr->jmpbuf)) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        return 2;
+    }
+
+
+    /* make sure outfile is (re)opened in BINARY mode */
+
+    png_init_io(png_ptr, mainprog_ptr->outfile);
+
+
+    /* set the compression levels--in general, always want to leave filtering
+     * turned on (except for palette images) and allow all of the filters,
+     * which is the default; want 32K zlib window, unless entire image buffer
+     * is 16K or smaller (unknown here)--also the default; usually want max
+     * compression (NOT the default); and remaining compression flags should
+     * be left alone */
+
+    png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
+/*
+    >> this is default for no filtering; Z_FILTERED is default otherwise:
+    png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
+    >> these are all defaults:
+    png_set_compression_mem_level(png_ptr, 8);
+    png_set_compression_window_bits(png_ptr, 15);
+    png_set_compression_method(png_ptr, 8);
+ */
+
+
+    /* set the image parameters appropriately */
+
+    if (mainprog_ptr->pnmtype == 5)
+        color_type = PNG_COLOR_TYPE_GRAY;
+    else if (mainprog_ptr->pnmtype == 6)
+        color_type = PNG_COLOR_TYPE_RGB;
+    else if (mainprog_ptr->pnmtype == 8)
+        color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+    else {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        return 11;
+    }
+
+    interlace_type = mainprog_ptr->interlaced? PNG_INTERLACE_ADAM7 :
+                                               PNG_INTERLACE_NONE;
+
+    png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height,
+      mainprog_ptr->sample_depth, color_type, interlace_type,
+      PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+    if (mainprog_ptr->gamma > 0.0)
+        png_set_gAMA(png_ptr, info_ptr, mainprog_ptr->gamma);
+
+    if (mainprog_ptr->have_bg) {   /* we know it's RGBA, not gray+alpha */
+        png_color_16  background;
+
+        background.red = mainprog_ptr->bg_red;
+        background.green = mainprog_ptr->bg_green;
+        background.blue = mainprog_ptr->bg_blue;
+        png_set_bKGD(png_ptr, info_ptr, &background);
+    }
+
+    if (mainprog_ptr->have_time) {
+        png_time  modtime;
+
+        png_convert_from_time_t(&modtime, mainprog_ptr->modtime);
+        png_set_tIME(png_ptr, info_ptr, &modtime);
+    }
+
+    if (mainprog_ptr->have_text) {
+        png_text  text[6];
+        int  num_text = 0;
+
+        if (mainprog_ptr->have_text & TEXT_TITLE) {
+            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
+            text[num_text].key = "Title";
+            text[num_text].text = mainprog_ptr->title;
+            ++num_text;
+        }
+        if (mainprog_ptr->have_text & TEXT_AUTHOR) {
+            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
+            text[num_text].key = "Author";
+            text[num_text].text = mainprog_ptr->author;
+            ++num_text;
+        }
+        if (mainprog_ptr->have_text & TEXT_DESC) {
+            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
+            text[num_text].key = "Description";
+            text[num_text].text = mainprog_ptr->desc;
+            ++num_text;
+        }
+        if (mainprog_ptr->have_text & TEXT_COPY) {
+            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
+            text[num_text].key = "Copyright";
+            text[num_text].text = mainprog_ptr->copyright;
+            ++num_text;
+        }
+        if (mainprog_ptr->have_text & TEXT_EMAIL) {
+            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
+            text[num_text].key = "E-mail";
+            text[num_text].text = mainprog_ptr->email;
+            ++num_text;
+        }
+        if (mainprog_ptr->have_text & TEXT_URL) {
+            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
+            text[num_text].key = "URL";
+            text[num_text].text = mainprog_ptr->url;
+            ++num_text;
+        }
+        png_set_text(png_ptr, info_ptr, text, num_text);
+    }
+
+
+    /* write all chunks up to (but not including) first IDAT */
+
+    png_write_info(png_ptr, info_ptr);
+
+
+    /* if we wanted to write any more text info *after* the image data, we
+     * would set up text struct(s) here and call png_set_text() again, with
+     * just the new data; png_set_tIME() could also go here, but it would
+     * have no effect since we already called it above (only one tIME chunk
+     * allowed) */
+
+
+    /* set up the transformations:  for now, just pack low-bit-depth pixels
+     * into bytes (one, two or four pixels per byte) */
+
+    png_set_packing(png_ptr);
+/*  png_set_shift(png_ptr, &sig_bit);  to scale low-bit-depth values */
+
+
+    /* make sure we save our pointers for use in writepng_encode_image() */
+
+    mainprog_ptr->png_ptr = png_ptr;
+    mainprog_ptr->info_ptr = info_ptr;
+
+
+    /* OK, that's all we need to do for now; return happy */
+
+    return 0;
+}
+
+
+
+
+
+/* returns 0 for success, 2 for libpng (longjmp) problem */
+
+int writepng_encode_image(mainprog_info *mainprog_ptr)
+{
+    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
+    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
+
+
+    /* as always, setjmp() must be called in every function that calls a
+     * PNG-writing libpng function */
+
+    if (setjmp(mainprog_ptr->jmpbuf)) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        mainprog_ptr->png_ptr = NULL;
+        mainprog_ptr->info_ptr = NULL;
+        return 2;
+    }
+
+
+    /* and now we just write the whole image; libpng takes care of interlacing
+     * for us */
+
+    png_write_image(png_ptr, mainprog_ptr->row_pointers);
+
+
+    /* since that's it, we also close out the end of the PNG file now--if we
+     * had any text or time info to write after the IDATs, second argument
+     * would be info_ptr, but we optimize slightly by sending NULL pointer: */
+
+    png_write_end(png_ptr, NULL);
+
+    return 0;
+}
+
+
+
+
+
+/* returns 0 if succeeds, 2 if libpng problem */
+
+int writepng_encode_row(mainprog_info *mainprog_ptr)  /* NON-interlaced only! */
+{
+    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
+    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
+
+
+    /* as always, setjmp() must be called in every function that calls a
+     * PNG-writing libpng function */
+
+    if (setjmp(mainprog_ptr->jmpbuf)) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        mainprog_ptr->png_ptr = NULL;
+        mainprog_ptr->info_ptr = NULL;
+        return 2;
+    }
+
+
+    /* image_data points at our one row of image data */
+
+    png_write_row(png_ptr, mainprog_ptr->image_data);
+
+    return 0;
+}
+
+
+
+
+
+/* returns 0 if succeeds, 2 if libpng problem */
+
+int writepng_encode_finish(mainprog_info *mainprog_ptr)   /* NON-interlaced! */
+{
+    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
+    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
+
+
+    /* as always, setjmp() must be called in every function that calls a
+     * PNG-writing libpng function */
+
+    if (setjmp(mainprog_ptr->jmpbuf)) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        mainprog_ptr->png_ptr = NULL;
+        mainprog_ptr->info_ptr = NULL;
+        return 2;
+    }
+
+
+    /* close out PNG file; if we had any text or time info to write after
+     * the IDATs, second argument would be info_ptr: */
+
+    png_write_end(png_ptr, NULL);
+
+    return 0;
+}
+
+
+
+
+
+void writepng_cleanup(mainprog_info *mainprog_ptr)
+{
+    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
+    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
+
+    if (png_ptr && info_ptr)
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+}
+
+
+
+
+
+static void writepng_error_handler(png_structp png_ptr, png_const_charp msg)
+{
+    mainprog_info  *mainprog_ptr;
+
+    /* This function, aside from the extra step of retrieving the "error
+     * pointer" (below) and the fact that it exists within the application
+     * rather than within libpng, is essentially identical to libpng's
+     * default error handler.  The second point is critical:  since both
+     * setjmp() and longjmp() are called from the same code, they are
+     * guaranteed to have compatible notions of how big a jmp_buf is,
+     * regardless of whether _BSD_SOURCE or anything else has (or has not)
+     * been defined. */
+
+    fprintf(stderr, "writepng libpng error: %s\n", msg);
+    fflush(stderr);
+
+    mainprog_ptr = png_get_error_ptr(png_ptr);
+    if (mainprog_ptr == NULL) {         /* we are completely hosed now */
+        fprintf(stderr,
+          "writepng severe error:  jmpbuf not recoverable; terminating.\n");
+        fflush(stderr);
+        exit(99);
+    }
+
+    longjmp(mainprog_ptr->jmpbuf, 1);
+}
diff --git a/contrib/gregbook/writepng.h b/contrib/gregbook/writepng.h
new file mode 100644
index 0000000..123d2d6
--- /dev/null
+++ b/contrib/gregbook/writepng.h
@@ -0,0 +1,109 @@
+/*---------------------------------------------------------------------------
+
+   wpng - simple PNG-writing program                             writepng.h
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-1999 Greg Roelofs.  All rights reserved.
+
+      This software is provided "as is," without warranty of any kind,
+      express or implied.  In no event shall the author or contributors
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+  ---------------------------------------------------------------------------*/
+
+#ifndef TRUE
+#  define TRUE 1
+#  define FALSE 0
+#endif
+
+#ifndef MAX
+#  define MAX(a,b)  ((a) > (b)? (a) : (b))
+#  define MIN(a,b)  ((a) < (b)? (a) : (b))
+#endif
+
+#ifdef DEBUG
+#  define Trace(x)  {fprintf x ; fflush(stderr); fflush(stdout);}
+#else
+#  define Trace(x)  ;
+#endif
+
+#define TEXT_TITLE    0x01
+#define TEXT_AUTHOR   0x02
+#define TEXT_DESC     0x04
+#define TEXT_COPY     0x08
+#define TEXT_EMAIL    0x10
+#define TEXT_URL      0x20
+
+#define TEXT_TITLE_OFFSET        0
+#define TEXT_AUTHOR_OFFSET      72
+#define TEXT_COPY_OFFSET     (2*72)
+#define TEXT_EMAIL_OFFSET    (3*72)
+#define TEXT_URL_OFFSET      (4*72)
+#define TEXT_DESC_OFFSET     (5*72)
+
+typedef unsigned char   uch;
+typedef unsigned short  ush;
+typedef unsigned long   ulg;
+
+typedef struct _mainprog_info {
+    double gamma;
+    long width;
+    long height;
+    time_t modtime;
+    FILE *infile;
+    FILE *outfile;
+    void *png_ptr;
+    void *info_ptr;
+    uch *image_data;
+    uch **row_pointers;
+    char *title;
+    char *author;
+    char *desc;
+    char *copyright;
+    char *email;
+    char *url;
+    int filter;    /* command-line-filter flag, not PNG row filter! */
+    int pnmtype;
+    int sample_depth;
+    int interlaced;
+    int have_bg;
+    int have_time;
+    int have_text;
+    jmp_buf jmpbuf;
+    uch bg_red;
+    uch bg_green;
+    uch bg_blue;
+} mainprog_info;
+
+
+/* prototypes for public functions in writepng.c */
+
+void writepng_version_info(void);
+
+int writepng_init(mainprog_info *mainprog_ptr);
+
+int writepng_encode_image(mainprog_info *mainprog_ptr);
+
+int writepng_encode_row(mainprog_info *mainprog_ptr);
+
+int writepng_encode_finish(mainprog_info *mainprog_ptr);
+
+void writepng_cleanup(mainprog_info *mainprog_ptr);
diff --git a/example.c b/example.c
index 52afbdf..b78f787 100644
--- a/example.c
+++ b/example.c
@@ -1,4 +1,6 @@
 
+#if 0 /* in case someone actually tries to compile this */
+
 /* example.c - an example of using libpng */
 
 /* This is an example of how to use libpng to read and write PNG files.
@@ -701,3 +703,4 @@
    return;
 }
 
+#endif /* if 0 */
diff --git a/libpng.3 b/libpng.3
index 7f9cf97..b4bbc1b 100644
--- a/libpng.3
+++ b/libpng.3
@@ -1,6 +1,6 @@
-.TH LIBPNG 3 "October 10, 1999"
+.TH LIBPNG 3 "October 12, 1999"
 .SH NAME
-libpng \- Portable Network Graphics (PNG) Reference Library 1.0.4e - October 10, 1999
+libpng \- Portable Network Graphics (PNG) Reference Library 1.0.4f - October 12, 1999
 .SH SYNOPSIS
 \fI\fB
 
@@ -617,7 +617,7 @@
 .SH LIBPNG.TXT
 libpng.txt - A description on how to use and modify libpng
 
- libpng version 1.0.4e - October 10, 1999
+ libpng version 1.0.4f - October 12, 1999
  Updated and distributed by Glenn Randers-Pehrson
  <randeg@alum.rpi.edu>
  Copyright (c) 1998, 1999 Glenn Randers-Pehrson
@@ -2675,13 +2675,13 @@
 
 .SH VII. Y2K Compliance in libpng
 
-October 10, 1999
+October 12, 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.71 and
-upward through 1.0.4e are Y2K compliant.  It is my belief that earlier
+upward through 1.0.4f 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
@@ -2807,7 +2807,7 @@
 
 Thanks to Frank J. T. Wojcik for helping with the documentation.
   
-Libpng version 1.0.4e - October 10, 1999:
+Libpng version 1.0.4f - October 12, 1999:
 Initially created in 1995 by Guy Eric Schalnat, then of Group 42, Inc.
 Currently maintained by Glenn Randers-Pehrson (randeg@alum.rpi.edu).
 
@@ -2822,7 +2822,7 @@
 Copyright (c) 1996, 1997 Andreas Dilger
 (libpng versions 0.90, December 1996, through 0.96, May 1997)
 Copyright (c) 1998, 1999 Glenn Randers-Pehrson
-(libpng versions 0.97, January 1998, through 1.0.4e, October 10, 1999)
+(libpng versions 0.97, January 1998, through 1.0.4f, October 12, 1999)
 
 For the purposes of this copyright and license, "Contributing Authors"
 is defined as the following set of individuals:
diff --git a/libpng.txt b/libpng.txt
index b81c1b4..df55ce5 100644
--- a/libpng.txt
+++ b/libpng.txt
@@ -1,6 +1,6 @@
 libpng.txt - A description on how to use and modify libpng
 
- libpng version 1.0.4e - October 10, 1999
+ libpng version 1.0.4f - October 12, 1999
  Updated and distributed by Glenn Randers-Pehrson
  <randeg@alum.rpi.edu>
  Copyright (c) 1998, 1999 Glenn Randers-Pehrson
@@ -2058,13 +2058,13 @@
 
 VII. Y2K Compliance in libpng
 
-October 10, 1999
+October 12, 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.71 and
-upward through 1.0.4e are Y2K compliant.  It is my belief that earlier
+upward through 1.0.4f 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
diff --git a/libpngpf.3 b/libpngpf.3
index 57345eb..bbb9625 100644
--- a/libpngpf.3
+++ b/libpngpf.3
@@ -1,6 +1,6 @@
-.TH LIBPNGPF 3 October 10, 1999
+.TH LIBPNGPF 3 October 12, 1999
 .SH NAME
-libpng \- Portable Network Graphics (PNG) Reference Library 1.0.4e - October 10, 1999
+libpng \- Portable Network Graphics (PNG) Reference Library 1.0.4f - October 12, 1999
 (private functions)
 .SH SYNOPSIS
 \fB#include <png.h>\fP
diff --git a/png.5 b/png.5
index 3cd6ff1..538672b 100644
--- a/png.5
+++ b/png.5
@@ -1,4 +1,4 @@
-.TH PNG 5 "October 10, 1999"
+.TH PNG 5 "October 12, 1999"
 .SH NAME
 png \- Portable Network Graphics (PNG) format
 .SH DESCRIPTION
diff --git a/png.c b/png.c
index e2eeb3b..9000e05 100644
--- a/png.c
+++ b/png.c
@@ -1,7 +1,7 @@
 
 /* png.c - location for general purpose libpng functions
  *
- * libpng version 1.0.4e - October 10, 1999
+ * libpng version 1.0.4f - October 12, 1999
  * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
  * Copyright (c) 1996, 1997 Andreas Dilger
  * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
@@ -12,11 +12,13 @@
 #define PNG_NO_EXTERN
 #include "png.h"
 
+PNG_GET_HEADER
+
 /* Version information for C files.  This had better match the version
  * string defined in png.h.
  */
 
-char png_libpng_ver[12] = "1.0.4e";
+char png_libpng_ver[12] = "1.0.4f";
 
 /* Place to hold the signature string for a PNG file. */
 png_byte FARDATA png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10};
@@ -86,7 +88,7 @@
    if (num_bytes > 8)
       png_error(png_ptr, "Too many bytes for PNG signature.");
 
-   png_ptr->sig_bytes = num_bytes < 0 ? 0 : num_bytes;
+   png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes);
 }
 
 /* Checks whether the supplied bytes match the PNG signature.  We allow
@@ -350,19 +352,19 @@
 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.4e - October 10, 1999\n\
+   if (png_ptr != NULL || png_ptr == NULL)  /* silence compiler warning */
+   return ("\n libpng version 1.0.4f - October 12, 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");
+   return ("");
 }
 
 /* Generate a compiler error if there is an old png.h in the search path. */
 void
 png_check_version
-   (version_1_0_4e png_h_is_not_version_1_0_4e)
+   (version_1_0_4f png_h_is_not_version_1_0_4f)
 {
-   if(png_h_is_not_version_1_0_4e == NULL)
-     /* silence compiler warning about unused parameter */ ;
+   if(png_h_is_not_version_1_0_4f == NULL)
+     return;
 }
diff --git a/png.h b/png.h
index bd86eef..d3ab30e 100644
--- a/png.h
+++ b/png.h
@@ -1,7 +1,7 @@
 
 /* png.h - header file for PNG reference library
  *
- * libpng version 1.0.4e - October 10, 1999
+ * libpng version 1.0.4f - October 12, 1999
  * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
  * Copyright (c) 1996, 1997 Andreas Dilger
  * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
@@ -9,19 +9,19 @@
  * Authors and maintainers:
  *  libpng versions 0.71, May 1995, through 0.89c, May 1996: Guy Schalnat
  *  libpng versions 0.90, December 1996, through 0.96, May 1997: Andreas Dilger
- *  libpng versions 0.97, January 1998, through 1.0.4e - October 10, 1999: Glenn
+ *  libpng versions 0.97, January 1998, through 1.0.4f - October 12, 1999: Glenn
  *  See also "Contributing Authors", below.
  *
  * Y2K compliance in libpng:
  * =========================
  *    
- *    October 10, 1999
+ *    October 12, 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.71 and
- *    upward through 1.0.4e are Y2K compliant.  It is my belief that earlier
+ *    upward through 1.0.4f 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
@@ -95,7 +95,7 @@
  *    1.0.3                    1.0.3    10003  2.1.0.3
  *    1.0.3a-d                 1.0.3a-d 10004  2.1.0.3a-d
  *    1.0.4                    1.0.4    10004  2.1.0.4
- *    1.0.4a-e                 1.0.4a-e 10005  2.1.0.4a-e
+ *    1.0.4a-f                 1.0.4a-f 10005  2.1.0.4a-f
  *    1.0.5                    1.0.5    10005  2.1.0.5
  *
  *    Henceforth the source version will match the shared-library minor
@@ -109,7 +109,9 @@
  * 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>
- *
+ */
+
+/*
  * COPYRIGHT NOTICE:
  *
  * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
@@ -117,7 +119,7 @@
  * Copyright (c) 1996, 1997 Andreas Dilger
  * (libpng versions 0.90, December 1996, through 0.96, May 1997)
  * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
- * (libpng versions 0.97, January 1998, through 1.0.4e, October 10, 1999)
+ * (libpng versions 0.97, January 1998, through 1.0.4f, October 12, 1999)
  *
  * For the purposes of this copyright and license, "Contributing Authors"
  * is defined as the following set of individuals:
@@ -168,7 +170,16 @@
  */
 
 /*
- *
+ * A "png_get_copyright" function is available, for convenient use in "about"
+ * boxes and the like:
+ * 
+ * printf("%s",png_get_copyright(NULL));
+ * 
+ * Also, the PNG logo (in PNG format, of course) is supplied in the
+ * file "pngnow.png".
+ */
+
+/*
  * Libpng is OSI Certified Open Source Software.  OSI Certified is a
  * certification mark of the Open Source Initiative.
  */
@@ -208,7 +219,7 @@
  */
 
 /* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.0.4e"
+#define PNG_LIBPNG_VER_STRING "1.0.4f"
 
 /* Careful here.  At one time, Guy wanted to use 082, but that would be octal.
  * We must not include leading zeros.
@@ -806,9 +817,9 @@
 };
 
 /* This prevents a compiler error in png_get_copyright() in png.c if png.c
-and png.h are both at * version 1.0.4e
+and png.h are both at * version 1.0.4f
  */
-typedef png_structp version_1_0_4e;
+typedef png_structp version_1_0_4f;
 
 typedef png_struct FAR * FAR * png_structpp;
 
@@ -1624,12 +1635,14 @@
 
 #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.4e - October 10, 1999 (header)\n");
+
+#define PNG_GET_HEADER \
+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.4f - October 12, 1999 (header)\n"); \
+   return("\n libpng version 1.0.4f - October 12, 1999 (header)\n"); \
 }
 #endif
 
@@ -1649,14 +1662,14 @@
  /* 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 temp = (png_uint_16)((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 temp = (png_uint_32)((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); }
 
diff --git a/pngasmrd.h b/pngasmrd.h
index 02c3282..c57e275 100644
--- a/pngasmrd.h
+++ b/pngasmrd.h
@@ -1,6 +1,6 @@
 /* pngasmrd.h - assembler version of utilities to read a PNG file
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 1999
  * For conditions of distribution and use, see copyright notice in png.h
  * Copyright (c) 1999 Glenn Randers-Pehrson
  *
@@ -21,7 +21,7 @@
 /* Set this in the makefile for gcc on Pentium, not in pngconf.h */
 #ifdef PNG_USE_PNGGCCRD
 /* Platform must be Pentium.  Makefile must assemble and load pnggccrd.c
- * (not available in libpng 1.0.4e).
+ * (not available in libpng 1.0.4f).
  * MMX will be detected at run time and used if present.
  */
 #define PNG_HAVE_ASSEMBLER_COMBINE_ROW
diff --git a/pngconf.h b/pngconf.h
index c81bb41..7ba2d3e 100644
--- a/pngconf.h
+++ b/pngconf.h
@@ -1,7 +1,7 @@
 
 /* pngconf.h - machine configurable file for libpng
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
@@ -17,7 +17,6 @@
 #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
diff --git a/pngerror.c b/pngerror.c
index 9f0fcda..28617b5 100644
--- a/pngerror.c
+++ b/pngerror.c
@@ -1,7 +1,7 @@
 
 /* pngerror.c - stub functions for i/o and memory allocation
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
@@ -75,7 +75,7 @@
          buffer[iout++] = png_digit[c & 0xf];
          buffer[iout++] = ']';
       } else {
-         buffer[iout++] = c;
+         buffer[iout++] = (png_byte)c;
       }
    }
 
diff --git a/pngget.c b/pngget.c
index d786a72..9cc502a 100644
--- a/pngget.c
+++ b/pngget.c
@@ -1,7 +1,7 @@
 
 /* pngget.c - retrieval of values from info struct
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
diff --git a/pngmem.c b/pngmem.c
index 307c901..c6fec69 100644
--- a/pngmem.c
+++ b/pngmem.c
@@ -1,7 +1,7 @@
 
 /* pngmem.c - stub functions for memory allocation
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
@@ -82,12 +82,10 @@
          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;
    }
 }
 
@@ -236,7 +234,6 @@
    if (png_ptr->free_fn != NULL)
    {
       (*(png_ptr->free_fn))(png_ptr, ptr);
-      ptr = NULL;
       return;
    }
    else png_free_default(png_ptr, ptr);
@@ -272,7 +269,6 @@
    if (ptr != NULL)
    {
       farfree(ptr);
-      ptr = NULL;
    }
 }
 
@@ -352,20 +348,16 @@
          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
    }
@@ -434,7 +426,6 @@
    if (png_ptr->free_fn != NULL)
    {
       (*(png_ptr->free_fn))(png_ptr, ptr);
-      ptr = NULL;
       return;
    }
    else png_free_default(png_ptr, ptr);
@@ -446,14 +437,11 @@
 
 #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
 }
diff --git a/pngpread.c b/pngpread.c
index 7e6be06..7e471ec 100644
--- a/pngpread.c
+++ b/pngpread.c
@@ -1,7 +1,7 @@
 
 /* pngpread.c - read a png file in push mode
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
@@ -94,7 +94,7 @@
 
    png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]),
       num_to_check);
-   png_ptr->sig_bytes += num_to_check;
+   png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes+num_to_check);
 
    if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
    {
diff --git a/pngread.c b/pngread.c
index 8c08c48..ce6e8c7 100644
--- a/pngread.c
+++ b/pngread.c
@@ -1,7 +1,7 @@
 
 /* pngread.c - read a PNG file
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
@@ -529,7 +529,7 @@
  * 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.4e.
+ * [*] png_handle_alpha() does not exist yet, as of libpng version 1.0.4f.
  */
 
 void
@@ -578,7 +578,7 @@
  * 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.4e.
+ * [*] png_handle_alpha() does not exist yet, as of libpng version 1.0.4f.
  */
 void
 png_read_image(png_structp png_ptr, png_bytepp image)
diff --git a/pngrio.c b/pngrio.c
index 28c709c..0f2e4a0 100644
--- a/pngrio.c
+++ b/pngrio.c
@@ -1,7 +1,7 @@
 
 /* pngrio.c - functions for data input
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
diff --git a/pngrtran.c b/pngrtran.c
index 2a44f81..8af06f9 100644
--- a/pngrtran.c
+++ b/pngrtran.c
@@ -1,7 +1,7 @@
 
 /* pngrtran.c - transforms the data in a row for PNG readers
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
@@ -623,7 +623,7 @@
       }
       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;
+      png_ptr->rgb_to_gray_blue_coeff  = (png_byte)(255-red_byte-green_byte);
    }
 }
 #endif
@@ -706,7 +706,7 @@
               int i,istop;
               istop=(int)png_ptr->num_trans;
               for (i=0; i<istop; i++)
-                 png_ptr->trans[i] = 255 - png_ptr->trans[i];
+                 png_ptr->trans[i] = (png_byte)(255 - png_ptr->trans[i]);
            }
         }
 #endif
@@ -1053,7 +1053,7 @@
    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;
+      info_ptr->channels++;
 #endif
 
 #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
@@ -1456,8 +1456,8 @@
             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]);
+            png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) |
+               (png_byte)((int)0xf >> shift[0]));
 
             for (i = 0; i < istop; i++)
             {
@@ -1666,7 +1666,7 @@
 
             for (i = 0; i < row_width; i++)
             {
-               *(--dp) = 255 - *(--sp);
+               *(--dp) = (png_byte)(255 - *(--sp));
                *(--dp) = *(--sp);
                *(--dp) = *(--sp);
                *(--dp) = *(--sp);
@@ -1681,8 +1681,8 @@
 
             for (i = 0; i < row_width; i++)
             {
-               *(--dp) = 255 - *(--sp);
-               *(--dp) = 255 - *(--sp);
+               *(--dp) = (png_byte)(255 - *(--sp));
+               *(--dp) = (png_byte)(255 - *(--sp));
                *(--dp) = *(--sp);
                *(--dp) = *(--sp);
                *(--dp) = *(--sp);
@@ -1703,7 +1703,7 @@
 
             for (i = 0; i < row_width; i++)
             {
-               *(--dp) = 255 - *(--sp);
+               *(--dp) = (png_byte)(255 - *(--sp));
                *(--dp) = *(--sp);
             }
          }
@@ -1716,8 +1716,8 @@
 
             for (i = 0; i < row_width; i++)
             {
-               *(--dp) = 255 - *(--sp);
-               *(--dp) = 255 - *(--sp);
+               *(--dp) = (png_byte)(255 - *(--sp));
+               *(--dp) = (png_byte)(255 - *(--sp));
                *(--dp) = *(--sp);
                *(--dp) = *(--sp);
             }
@@ -2065,7 +2065,7 @@
                   if(red != green || red != blue)
                   {
                      rgb_error |= 1;
-                     *(dp++) = (rc*red+gc*green+bc*blue)>>8;
+                     *(dp++) = (png_byte)((rc*red+gc*green+bc*blue)>>8);
                   }
                   else
                      *(dp++) = *(sp-1);
@@ -2085,9 +2085,9 @@
                {
                   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;
+                  red   = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  blue  = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
 
                   if(red == green && red == blue)
                      w = red;
@@ -2099,15 +2099,15 @@
                                   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;
+                     png_uint_16 gray16  = (png_uint_16)((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++) = (png_byte)((w>>8) & 0xff);
+                  *(dp++) = (png_byte)(w & 0xff);
                }
             }
             else
@@ -2119,15 +2119,15 @@
                {
                   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;
+                  red   = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  blue  = (png_uint_16)(((*(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;
+                  gray16  = (png_uint_16)((rc*red + gc*green + bc*blue)>>8);
+                  *(dp++) = (png_byte)((gray16>>8) & 0xff);
+                  *(dp++) = (png_byte)(gray16 & 0xff);
                }
             }
          }
@@ -2165,7 +2165,7 @@
                   png_byte blue  = *(sp++);
                   if(red != green || red != blue)
                      rgb_error |= 1;
-                  *(dp++) =  (gc*red + gc*green + bc*blue)>>8;
+                  *(dp++) =  (png_byte)((gc*red + gc*green + bc*blue)>>8);
                   *(dp++) = *(sp++);  /* alpha */
                }
             }
@@ -2182,9 +2182,9 @@
                {
                   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;
+                  red   = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  blue  = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
 
                   if(red == green && red == blue)
                      w = red;
@@ -2196,15 +2196,15 @@
                                   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;
+                     png_uint_16 gray16  = (png_uint_16)((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++) = (png_byte)((w>>8) & 0xff);
+                  *(dp++) = (png_byte)(w & 0xff);
                   *(dp++) = *(sp++);  /* alpha */
                   *(dp++) = *(sp++);
                }
@@ -2217,14 +2217,14 @@
                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;
+                  red   = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2;
+                  green = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2;
+                  blue  = (png_uint_16)((*(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;
+                  gray16  = (png_uint_16)((rc*red + gc*green + bc*blue)>>8);
+                  *(dp++) = (png_byte)((gray16>>8) & 0xff);
+                  *(dp++) = (png_byte)(gray16 & 0xff);
                   *(dp++) = *(sp++);  /* alpha */
                   *(dp++) = *(sp++);
                }
@@ -2543,9 +2543,9 @@
                         }
                         else
                         {
-                           png_byte p = (*sp >> shift) & 0x3;
-                           png_byte g = (gamma_table [p | (p << 2) | (p << 4) |
-                               (p << 6)] >> 6) & 0x3;
+                           png_byte p = (png_byte)((*sp >> shift) & 0x3);
+                           png_byte g = (png_byte)((gamma_table [p | (p << 2) |
+                               (p << 4) | (p << 6)] >> 6) & 0x3);
                            *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
                            *sp |= (png_byte)(g << shift);
                         }
@@ -2599,8 +2599,9 @@
                         }
                         else
                         {
-                           png_byte p = (*sp >> shift) & 0xf;
-                           png_byte g = (gamma_table[p | (p << 4)] >> 4) & 0xf;
+                           png_byte p = (png_byte)((*sp >> shift) & 0xf);
+                           png_byte g = (png_byte)((gamma_table[p |
+                             (p << 4)] >> 4) & 0xf);
                            *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
                            *sp |= (png_byte)(g << shift);
                         }
@@ -2679,7 +2680,7 @@
                      {
                         png_uint_16 v;
 
-                        v = ((png_uint_16)(*sp) << 8) + *(sp + 1);
+                        v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
                         if (v == trans_values->gray)
                         {
                            /* background is already in screen gamma */
@@ -2702,7 +2703,7 @@
                      {
                         png_uint_16 v;
 
-                        v = ((png_uint_16)(*sp) << 8) + *(sp + 1);
+                        v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
                         if (v == trans_values->gray)
                         {
                            *sp = (png_byte)((background->gray >> 8) & 0xff);
@@ -2766,9 +2767,9 @@
                   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);
+                     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)
                      {
@@ -2800,9 +2801,9 @@
                   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);
+                     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)
@@ -2886,7 +2887,7 @@
                   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);
+                     png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3));
 
                      if (a == (png_uint_16)0xffff)
                      {
@@ -2921,7 +2922,7 @@
                   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);
+                     png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3));
                      if (a == (png_uint_16)0xffff)
                      {
                         png_memcpy(dp, sp, 2);
@@ -2935,7 +2936,7 @@
                      {
                         png_uint_16 g, v;
 
-                        g = ((png_uint_16)(*sp) << 8) + *(sp + 1);
+                        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);
@@ -3104,11 +3105,11 @@
                      {
                         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_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);
@@ -3277,10 +3278,11 @@
                   int c = *sp & 0x0c;
                   int d = *sp & 0x03;
 
-                  *sp = ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)])   ) & 0xc0)|
+                  *sp = (png_byte)(
+                        ((((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)       );
+                        ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) ));
                   sp++;
                }
             }
@@ -3292,8 +3294,8 @@
                   int msb = *sp & 0xf0;
                   int lsb = *sp & 0x0f;
 
-                  *sp = (((int)gamma_table[msb | (msb >> 4)]) & 0xf0) |
-                        (((int)gamma_table[(lsb << 4) | lsb]) >> 4);
+                  *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0)
+                          | (((int)gamma_table[(lsb << 4) | lsb]) >> 4));
                   sp++;
                }
             }
@@ -3487,7 +3489,7 @@
    {
       if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
       {
-         png_uint_16 gray = trans_value ? trans_value->gray : 0;
+         png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0);
 
          if (row_info->bit_depth < 8)
          {
@@ -3495,7 +3497,7 @@
             {
                case 1:
                {
-                  gray *= 0xff;
+                  gray = (png_uint_16)(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);
@@ -3519,7 +3521,7 @@
                }
                case 2:
                {
-                  gray *= 0x55;
+                  gray = (png_uint_16)(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);
@@ -3542,7 +3544,7 @@
                }
                case 4:
                {
-                  gray *= 0x11;
+                  gray = (png_uint_16)(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);
diff --git a/pngrutil.c b/pngrutil.c
index ce982ef..cd46620 100644
--- a/pngrutil.c
+++ b/pngrutil.c
@@ -1,7 +1,7 @@
 
 /* pngrutil.c - utilities to read a PNG file
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
diff --git a/pngset.c b/pngset.c
index 037333b..0a17268 100644
--- a/pngset.c
+++ b/pngset.c
@@ -1,7 +1,7 @@
 
 /* pngset.c - storage of image information into info struct
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
diff --git a/pngtest.c b/pngtest.c
index 3796c63..30f1f90 100644
--- a/pngtest.c
+++ b/pngtest.c
@@ -1,7 +1,7 @@
 
 /* pngtest.c - a simple test program to test libpng
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
@@ -1172,7 +1172,7 @@
 /* Generate a compiler error if there is an old png.h in the search path. */
 void
 png_check_pngtest_version
-   (version_1_0_4e png_h_is_not_version_1_0_4e)
+   (version_1_0_4f png_h_is_not_version_1_0_4f)
 {
-   if(png_h_is_not_version_1_0_4e == NULL) return;
+   if(png_h_is_not_version_1_0_4f == NULL) return;
 }
diff --git a/pngtrans.c b/pngtrans.c
index cbe13c3..37dc5b8 100644
--- a/pngtrans.c
+++ b/pngtrans.c
@@ -1,7 +1,7 @@
 
 /* pngtrans.c - transforms the data in a row (used by both readers and writers)
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
diff --git a/pngvcrd.c b/pngvcrd.c
index d26b72f..504477e 100644
--- a/pngvcrd.c
+++ b/pngvcrd.c
@@ -2,7 +2,7 @@
  *
  * For Intel x86 CPU and Microsoft Visual C++ compiler
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 1999
  * For conditions of distribution and use, see copyright notice in png.h
  * Copyright (c) 1998, Intel Corporation
  * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
@@ -1163,7 +1163,7 @@
          default:         // This is the place where the routine is modified
          {
             __int64 const4 = 0x0000000000FFFFFF;
-            __int64 const5 = 0x000000FFFFFF0000;
+            // __int64 const5 = 0x000000FFFFFF0000;  // unused...
             __int64 const6 = 0x00000000000000FF;
             png_bytep sptr, dp;
             png_uint_32 i;
@@ -3643,7 +3643,7 @@
 #ifdef PNG_DEBUG
    char filnm[6];
 #endif
-   #define UseMMX (1)
+   #define UseMMX 1
 
    if (mmx_supported == 2)
        mmx_supported = mmxsupport();
@@ -3656,7 +3656,11 @@
 
 #ifdef PNG_DEBUG
    png_debug(1, "in png_read_filter_row\n");
-   png_debug1(0,"%s, ", (UseMMX?"MMX":"x86"));
+#if (UseMMX == 1)
+   png_debug1(0,"%s, ", "MMX");
+#else
+   png_debug1(0,"%s, ", "x86");
+#endif
    switch (filter)
    {
       case 0: sprintf(filnm, "None ");
@@ -3684,12 +3688,14 @@
          break;
       case PNG_FILTER_VALUE_SUB:
       {
-         if ( UseMMX && (row_info->pixel_depth > 8) &&
+#if (UseMMX == 1)
+         if ((row_info->pixel_depth > 8) &&
             (row_info->rowbytes >= 128) )
          {
             png_read_filter_row_mmx_sub(row_info, row);
-         }  //end if UseMMX
+         }
          else
+#endif
          {
             png_uint_32 i;
             png_uint_32 istop = row_info->rowbytes;
@@ -3707,12 +3713,14 @@
       }
       case PNG_FILTER_VALUE_UP:
       {
-         if ( UseMMX && (row_info->pixel_depth > 8) &&
+#if (UseMMX == 1)
+         if ((row_info->pixel_depth > 8) &&
              (row_info->rowbytes >= 128) )
          {
             png_read_filter_row_mmx_up(row_info, row, prev_row);
          }  //end if UseMMX
          else
+#endif
          {
             png_bytep rp;
             png_bytep pp;
@@ -3727,12 +3735,14 @@
       }
       case PNG_FILTER_VALUE_AVG:
       {
-         if ( UseMMX && (row_info->pixel_depth > 8) &&
+#if (UseMMX == 1)
+         if ((row_info->pixel_depth > 8) &&
              (row_info->rowbytes >= 128) )
          {
             png_read_filter_row_mmx_avg(row_info, row, prev_row);
          }  //end if UseMMX
          else
+#endif
          {
             png_uint_32 i;
             png_bytep rp = row;
@@ -3759,12 +3769,14 @@
       }
       case PNG_FILTER_VALUE_PAETH:
       {
-         if ( UseMMX && (row_info->pixel_depth > 8) &&
+#if (UseMMX == 1)
+         if ((row_info->pixel_depth > 8) &&
              (row_info->rowbytes >= 128) )
          {
             png_read_filter_row_mmx_paeth(row_info, row, prev_row);
          }  //end if UseMMX
          else
+#endif
          {
             png_uint_32 i;
             png_bytep rp = row;
diff --git a/pngwio.c b/pngwio.c
index 5c2f0c2..85000be 100644
--- a/pngwio.c
+++ b/pngwio.c
@@ -1,7 +1,7 @@
 
 /* pngwio.c - functions for data output
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
diff --git a/pngwrite.c b/pngwrite.c
index f8f654b..445d14f 100644
--- a/pngwrite.c
+++ b/pngwrite.c
@@ -1,7 +1,7 @@
 
 /* pngwrite.c - general routines to write a PNG file
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
@@ -77,7 +77,7 @@
          {
             int j;
             for (j=0; j<(int)info_ptr->num_trans; j++)
-               info_ptr->trans[j] = 255 - info_ptr->trans[j];
+               info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]);
          }
 #endif
       png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values),
@@ -799,7 +799,7 @@
             if (png_ptr->prev_row == NULL)
             {
                png_warning(png_ptr, "Can't add Paeth filter after starting");
-               png_ptr->do_filter &= ~PNG_FILTER_PAETH;
+               png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);
             }
             else
             {
@@ -850,8 +850,8 @@
       num_weights = 0;
    }
 
-   png_ptr->num_prev_filters = num_weights;
-   png_ptr->heuristic_method = heuristic_method;
+   png_ptr->num_prev_filters = (png_byte)num_weights;
+   png_ptr->heuristic_method = (png_byte)heuristic_method;
 
    if (num_weights > 0)
    {
diff --git a/pngwtran.c b/pngwtran.c
index e85b428..cac5f28 100644
--- a/pngwtran.c
+++ b/pngwtran.c
@@ -1,7 +1,7 @@
 
 /* pngwtran.c - transforms the data in a row for PNG writers
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
@@ -311,7 +311,7 @@
             png_uint_16 value, v;
             int j;
 
-            v = ((png_uint_16)(*bp) << 8) + *(bp + 1);
+            v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1));
             value = 0;
             for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
             {
@@ -438,7 +438,7 @@
                *(dp++) = *(sp++);
                *(dp++) = *(sp++);
                *(dp++) = *(sp++);
-               *(dp++) = 255 - *(sp++);
+               *(dp++) = (png_byte)(255 - *(sp++));
             }
          }
          /* This inverts the alpha channel in RRGGBBAA */
@@ -456,8 +456,8 @@
                *(dp++) = *(sp++);
                *(dp++) = *(sp++);
                *(dp++) = *(sp++);
-               *(dp++) = 255 - *(sp++);
-               *(dp++) = 255 - *(sp++);
+               *(dp++) = (png_byte)(255 - *(sp++));
+               *(dp++) = (png_byte)(255 - *(sp++));
             }
          }
       }
@@ -473,7 +473,7 @@
             for (i = 0, sp = dp = row; i < row_width; i++)
             {
                *(dp++) = *(sp++);
-               *(dp++) = 255 - *(sp++);
+               *(dp++) = (png_byte)(255 - *(sp++));
             }
          }
          /* This inverts the alpha channel in GGAA */
@@ -487,8 +487,8 @@
             {
                *(dp++) = *(sp++);
                *(dp++) = *(sp++);
-               *(dp++) = 255 - *(sp++);
-               *(dp++) = 255 - *(sp++);
+               *(dp++) = (png_byte)(255 - *(sp++));
+               *(dp++) = (png_byte)(255 - *(sp++));
             }
          }
       }
diff --git a/pngwutil.c b/pngwutil.c
index 2582547..7aed90e 100644
--- a/pngwutil.c
+++ b/pngwutil.c
@@ -1,7 +1,7 @@
 
 /* pngwutil.c - utilities to write a PNG file
  *
- * libpng 1.0.4e - October 10, 1999
+ * libpng 1.0.4f - October 12, 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
@@ -380,7 +380,8 @@
    {
       png_byte maxbits;
 
-      maxbits = color_type==PNG_COLOR_TYPE_PALETTE ? 8 : png_ptr->usr_bit_depth;
+      maxbits = (png_byte)(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)
diff --git a/scripts/makefile.beos b/scripts/makefile.beos
index b91051e..30ef1a7 100644
--- a/scripts/makefile.beos
+++ b/scripts/makefile.beos
@@ -31,7 +31,7 @@
 # read libpng.txt or png.h to see why PNGMAJ is 2.  You should not
 # have to change it.
 PNGMAJ = 2
-PNGMIN = 1.0.4e
+PNGMIN = 1.0.4f
 PNGVER = $(PNGMAJ).$(PNGMIN)
 
 # where make install puts libpng.a, libpng.so*, and png.h
diff --git a/scripts/makefile.dec b/scripts/makefile.dec
index 93bde55..35bce99 100644
--- a/scripts/makefile.dec
+++ b/scripts/makefile.dec
@@ -14,7 +14,7 @@
 # read libpng.txt or png.h to see why PNGMAJ is 2.  You should not
 # have to change it.
 PNGMAJ = 2
-PNGMIN = 1.0.4e
+PNGMIN = 1.0.4f
 PNGVER = $(PNGMAJ).$(PNGMIN)
 
 CC=cc
diff --git a/scripts/makefile.knr b/scripts/makefile.knr
index b85f435..09cb0a2 100644
--- a/scripts/makefile.knr
+++ b/scripts/makefile.knr
@@ -2,7 +2,7 @@
 # Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc.
 # For conditions of distribution and use, see copyright notice in png.h
 
-# This makefile requires file file ansi2knr.c, which you can get
+# This makefile requires the file ansi2knr.c, which you can get
 # from the Ghostscript ftp site at ftp://ftp.cs.wisc.edu/ghost/
 # If you have libjpeg, you probably already have ansi2knr.c in the jpeg
 # source distribution.
diff --git a/scripts/makefile.linux b/scripts/makefile.linux
index 8d04e64..4a4ef54 100644
--- a/scripts/makefile.linux
+++ b/scripts/makefile.linux
@@ -34,7 +34,7 @@
 # read libpng.txt or png.h to see why PNGMAJ is 2.  You should not
 # have to change it.
 PNGMAJ = 2
-PNGMIN = 1.0.4e
+PNGMIN = 1.0.4f
 PNGVER = $(PNGMAJ).$(PNGMIN)
 
 INCPATH=$(prefix)/include
diff --git a/scripts/makefile.sco b/scripts/makefile.sco
index c292b6f..7d9c801 100644
--- a/scripts/makefile.sco
+++ b/scripts/makefile.sco
@@ -25,7 +25,7 @@
 # read libpng.txt or png.h to see why PNGMAJ is 2.  You should not
 # have to change it.
 PNGMAJ = 2
-PNGMIN = 1.0.4e
+PNGMIN = 1.0.4f
 PNGVER = $(PNGMAJ).$(PNGMIN)
 
 INCPATH=$(prefix)/include
diff --git a/scripts/makefile.solaris b/scripts/makefile.solaris
index ab9969a..27e1935 100644
--- a/scripts/makefile.solaris
+++ b/scripts/makefile.solaris
@@ -36,7 +36,7 @@
 # read libpng.txt or png.h to see why PNGMAJ is 2.  You should not
 # have to change it.
 PNGMAJ = 2
-PNGMIN = 1.0.4e
+PNGMIN = 1.0.4f
 PNGVER = $(PNGMAJ).$(PNGMIN)
 
 INCPATH=$(prefix)/include
diff --git a/scripts/pngdef.pas b/scripts/pngdef.pas
index 67784f6..60f5ea4 100644
--- a/scripts/pngdef.pas
+++ b/scripts/pngdef.pas
@@ -3,7 +3,7 @@
 interface
 
 const
-  PNG_LIBPNG_VER_STRING = '1.0.4e';
+  PNG_LIBPNG_VER_STRING = '1.0.4f';
   PNG_LIBPNG_VER        =  10005;
 
 type