Imported from libpng-0.71.tar
diff --git a/ansi2knr.c b/ansi2knr.c
new file mode 100644
index 0000000..3924215
--- /dev/null
+++ b/ansi2knr.c
@@ -0,0 +1,488 @@
+/* Copyright (C) 1989, 1991, 1993 Aladdin Enterprises. All rights reserved. */
+
+/* ansi2knr.c */
+/* Convert ANSI function declarations to K&R syntax */
+
+/*
+ansi2knr is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
+to anyone for the consequences of using it or for whether it serves any
+particular purpose or works at all, unless he says so in writing.  Refer
+to the GNU General Public License for full details.
+
+Everyone is granted permission to copy, modify and redistribute
+ansi2knr, but only under the conditions described in the GNU
+General Public License.  A copy of this license is supposed to have been
+given to you along with ansi2knr so you can know your rights and
+responsibilities.  It should be in a file named COPYING.  Among other
+things, the copyright notice and this notice must be preserved on all
+copies.
+*/
+
+/*
+---------- Here is the GNU GPL file COPYING, referred to above ----------
+----- These terms do NOT apply to the JPEG software itself; see README ------
+
+		    GHOSTSCRIPT GENERAL PUBLIC LICENSE
+		    (Clarified 11 Feb 1988)
+
+ Copyright (C) 1988 Richard M. Stallman
+ Everyone is permitted to copy and distribute verbatim copies of this
+ license, but changing it is not allowed.  You can also use this wording
+ to make the terms for other programs.
+
+  The license agreements of most software companies keep you at the
+mercy of those companies.  By contrast, our general public license is
+intended to give everyone the right to share Ghostscript.  To make sure
+that you get the rights we want you to have, we need to make
+restrictions that forbid anyone to deny you these rights or to ask you
+to surrender the rights.  Hence this license agreement.
+
+  Specifically, we want to make sure that you have the right to give
+away copies of Ghostscript, that you receive source code or else can get
+it if you want it, that you can change Ghostscript or use pieces of it
+in new free programs, and that you know you can do these things.
+
+  To make sure that everyone has such rights, we have to forbid you to
+deprive anyone else of these rights.  For example, if you distribute
+copies of Ghostscript, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must tell them their rights.
+
+  Also, for our own protection, we must make certain that everyone finds
+out that there is no warranty for Ghostscript.  If Ghostscript is
+modified by someone else and passed on, we want its recipients to know
+that what they have is not what we distributed, so that any problems
+introduced by others will not reflect on our reputation.
+
+  Therefore we (Richard M. Stallman and the Free Software Foundation,
+Inc.) make the following terms which say what you must do to be allowed
+to distribute or change Ghostscript.
+
+
+			COPYING POLICIES
+
+  1. You may copy and distribute verbatim copies of Ghostscript source
+code as you receive it, in any medium, provided that you conspicuously
+and appropriately publish on each copy a valid copyright and license
+notice "Copyright (C) 1989 Aladdin Enterprises.  All rights reserved.
+Distributed by Free Software Foundation, Inc." (or with whatever year is
+appropriate); keep intact the notices on all files that refer to this
+License Agreement and to the absence of any warranty; and give any other
+recipients of the Ghostscript program a copy of this License Agreement
+along with the program.  You may charge a distribution fee for the
+physical act of transferring a copy.
+
+  2. You may modify your copy or copies of Ghostscript or any portion of
+it, and copy and distribute such modifications under the terms of
+Paragraph 1 above, provided that you also do the following:
+
+    a) cause the modified files to carry prominent notices stating
+    that you changed the files and the date of any change; and
+
+    b) cause the whole of any work that you distribute or publish,
+    that in whole or in part contains or is a derivative of Ghostscript
+    or any part thereof, to be licensed at no charge to all third
+    parties on terms identical to those contained in this License
+    Agreement (except that you may choose to grant more extensive
+    warranty protection to some or all third parties, at your option).
+
+    c) You may charge a distribution fee for the physical act of
+    transferring a copy, and you may at your option offer warranty
+    protection in exchange for a fee.
+
+Mere aggregation of another unrelated program with this program (or its
+derivative) on a volume of a storage or distribution medium does not bring
+the other program under the scope of these terms.
+
+  3. You may copy and distribute Ghostscript (or a portion or derivative
+of it, under Paragraph 2) in object code or executable form under the
+terms of Paragraphs 1 and 2 above provided that you also do one of the
+following:
+
+    a) accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of
+    Paragraphs 1 and 2 above; or,
+
+    b) accompany it with a written offer, valid for at least three
+    years, to give any third party free (except for a nominal
+    shipping charge) a complete machine-readable copy of the
+    corresponding source code, to be distributed under the terms of
+    Paragraphs 1 and 2 above; or,
+
+    c) accompany it with the information you received as to where the
+    corresponding source code may be obtained.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form alone.)
+
+For an executable file, complete source code means all the source code for
+all modules it contains; but, as a special exception, it need not include
+source code for modules which are standard libraries that accompany the
+operating system on which the executable file runs.
+
+  4. You may not copy, sublicense, distribute or transfer Ghostscript
+except as expressly provided under this License Agreement.  Any attempt
+otherwise to copy, sublicense, distribute or transfer Ghostscript is
+void and your rights to use the program under this License agreement
+shall be automatically terminated.  However, parties who have received
+computer software programs from you with this License Agreement will not
+have their licenses terminated so long as such parties remain in full
+compliance.
+
+  5. If you wish to incorporate parts of Ghostscript into other free
+programs whose distribution conditions are different, write to the Free
+Software Foundation at 675 Mass Ave, Cambridge, MA 02139.  We have not
+yet worked out a simple rule that can be stated here, but we will often
+permit this.  We will be guided by the two goals of preserving the free
+status of all derivatives of our free software and of promoting the
+sharing and reuse of software.
+
+Your comments and suggestions about our licensing policies and our
+software are welcome!  Please contact the Free Software Foundation,
+Inc., 675 Mass Ave, Cambridge, MA 02139, or call (617) 876-3296.
+
+		       NO WARRANTY
+
+  BECAUSE GHOSTSCRIPT IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
+NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
+WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC, RICHARD
+M. STALLMAN, ALADDIN ENTERPRISES, L. PETER DEUTSCH, AND/OR OTHER PARTIES
+PROVIDE GHOSTSCRIPT "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF GHOSTSCRIPT IS WITH
+YOU.  SHOULD GHOSTSCRIPT PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
+STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., L. PETER DEUTSCH, ALADDIN
+ENTERPRISES, AND/OR ANY OTHER PARTY WHO MAY MODIFY AND REDISTRIBUTE
+GHOSTSCRIPT AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING
+ANY LOST PROFITS, LOST MONIES, OR OTHER SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A FAILURE OF THE
+PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) GHOSTSCRIPT, EVEN IF YOU
+HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM
+BY ANY OTHER PARTY.
+
+-------------------- End of file COPYING ------------------------------
+*/
+
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef BSD
+#include <strings.h>
+#else
+#ifdef VMS
+	extern int strlen(), strncmp();
+#else
+#include <string.h>
+#endif
+#endif
+
+/* malloc and free should be declared in stdlib.h, */
+/* but if you've got a K&R compiler, they probably aren't. */
+#ifdef MSDOS
+#include <malloc.h>
+#else
+#ifdef VMS
+     extern char *malloc();
+     extern void free();
+#else
+     extern char *malloc();
+     extern int free();
+#endif
+#endif
+
+/* Usage:
+	ansi2knr input_file [output_file]
+ * If no output_file is supplied, output goes to stdout.
+ * There are no error messages.
+ *
+ * ansi2knr recognizes functions by seeing a non-keyword identifier
+ * at the left margin, followed by a left parenthesis,
+ * with a right parenthesis as the last character on the line.
+ * It will recognize a multi-line header provided that the last character
+ * of the last line of the header is a right parenthesis,
+ * and no intervening line ends with a left brace or a semicolon.
+ * These algorithms ignore whitespace and comments, except that
+ * the function name must be the first thing on the line.
+ * The following constructs will confuse it:
+ *	- Any other construct that starts at the left margin and
+ *	    follows the above syntax (such as a macro or function call).
+ *	- Macros that tinker with the syntax of the function header.
+ */
+
+/* Scanning macros */
+#define isidchar(ch) (isalnum(ch) || (ch) == '_')
+#define isidfirstchar(ch) (isalpha(ch) || (ch) == '_')
+
+/* Forward references */
+char *skipspace();
+int writeblanks();
+int test1();
+int convert1();
+
+/* The main program */
+main(argc, argv)
+    int argc;
+    char *argv[];
+{	FILE *in, *out;
+#define bufsize 5000			/* arbitrary size */
+	char *buf;
+	char *line;
+	switch ( argc )
+	   {
+	default:
+		printf("Usage: ansi2knr input_file [output_file]\n");
+		exit(0);
+	case 2:
+		out = stdout; break;
+	case 3:
+		out = fopen(argv[2], "w");
+		if ( out == NULL )
+		   {	fprintf(stderr, "Cannot open %s\n", argv[2]);
+			exit(1);
+		   }
+	   }
+	in = fopen(argv[1], "r");
+	if ( in == NULL )
+	   {	fprintf(stderr, "Cannot open %s\n", argv[1]);
+		exit(1);
+	   }
+	fprintf(out, "#line 1 \"%s\"\n", argv[1]);
+	buf = malloc(bufsize);
+	line = buf;
+	while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL )
+	   {	switch ( test1(buf) )
+		   {
+		case 1:			/* a function */
+			convert1(buf, out);
+			break;
+		case -1:		/* maybe the start of a function */
+			line = buf + strlen(buf);
+			if ( line != buf + (bufsize - 1) ) /* overflow check */
+				continue;
+			/* falls through */
+		default:		/* not a function */
+			fputs(buf, out);
+			break;
+		   }
+		line = buf;
+	   }
+	if ( line != buf ) fputs(buf, out);
+	free(buf);
+	fclose(out);
+	fclose(in);
+	return 0;
+}
+
+/* Skip over space and comments, in either direction. */
+char *
+skipspace(p, dir)
+    register char *p;
+    register int dir;			/* 1 for forward, -1 for backward */
+{	for ( ; ; )
+	   {	while ( isspace(*p) ) p += dir;
+		if ( !(*p == '/' && p[dir] == '*') ) break;
+		p += dir;  p += dir;
+		while ( !(*p == '*' && p[dir] == '/') )
+		   {	if ( *p == 0 ) return p;	/* multi-line comment?? */
+			p += dir;
+		   }
+		p += dir;  p += dir;
+	   }
+	return p;
+}
+
+/*
+ * Write blanks over part of a string.
+ */
+int
+writeblanks(start, end)
+    char *start;
+    char *end;
+{	char *p;
+	for ( p = start; p < end; p++ ) *p = ' ';
+	return 0;
+}
+
+/*
+ * Test whether the string in buf is a function definition.
+ * The string may contain and/or end with a newline.
+ * Return as follows:
+ *	0 - definitely not a function definition;
+ *	1 - definitely a function definition;
+ *	-1 - may be the beginning of a function definition,
+ *		append another line and look again.
+ */
+int
+test1(buf)
+    char *buf;
+{	register char *p = buf;
+	char *bend;
+	char *endfn;
+	int contin;
+	if ( !isidfirstchar(*p) )
+		return 0;		/* no name at left margin */
+	bend = skipspace(buf + strlen(buf) - 1, -1);
+	switch ( *bend )
+	   {
+	case ')': contin = 1; break;
+	case '{':
+	case ';': return 0;		/* not a function */
+	default: contin = -1;
+	   }
+	while ( isidchar(*p) ) p++;
+	endfn = p;
+	p = skipspace(p, 1);
+	if ( *p++ != '(' )
+		return 0;		/* not a function */
+	p = skipspace(p, 1);
+	if ( *p == ')' )
+		return 0;		/* no parameters */
+	/* Check that the apparent function name isn't a keyword. */
+	/* We only need to check for keywords that could be followed */
+	/* by a left parenthesis (which, unfortunately, is most of them). */
+	   {	static char *words[] =
+		   {	"asm", "auto", "case", "char", "const", "double",
+			"extern", "float", "for", "if", "int", "long",
+			"register", "return", "short", "signed", "sizeof",
+			"static", "switch", "typedef", "unsigned",
+			"void", "volatile", "while", 0
+		   };
+		char **key = words;
+		char *kp;
+		int len = endfn - buf;
+		while ( (kp = *key) != 0 )
+		   {	if ( strlen(kp) == len && !strncmp(kp, buf, len) )
+				return 0;	/* name is a keyword */
+			key++;
+		   }
+	   }
+	return contin;
+}
+
+int
+convert1(buf, out)
+    char *buf;
+    FILE *out;
+{	char *endfn;
+	register char *p;
+	char **breaks;
+	unsigned num_breaks = 2;	/* for testing */
+	char **btop;
+	char **bp;
+	char **ap;
+	/* Pre-ANSI implementations don't agree on whether strchr */
+	/* is called strchr or index, so we open-code it here. */
+	for ( endfn = buf; *(endfn++) != '('; ) ;
+top:	p = endfn;
+	breaks = (char **)malloc(sizeof(char *) * num_breaks * 2);
+	if ( breaks == 0 )
+	   {	/* Couldn't allocate break table, give up */
+		fprintf(stderr, "Unable to allocate break table!\n");
+		fputs(buf, out);
+		return -1;
+	   }
+	btop = breaks + num_breaks * 2 - 2;
+	bp = breaks;
+	/* Parse the argument list */
+	do
+	   {	int level = 0;
+		char *end = NULL;
+		if ( bp >= btop )
+		   {	/* Filled up break table. */
+			/* Allocate a bigger one and start over. */
+			free((char *)breaks);
+			num_breaks <<= 1;
+			goto top;
+		   }
+		*bp++ = p;
+		/* Find the end of the argument */
+		for ( ; end == NULL; p++ )
+		   {	switch(*p)
+			   {
+			case ',': if ( !level ) end = p; break;
+			case '(': level++; break;
+			case ')': if ( --level < 0 ) end = p; break;
+			case '/': p = skipspace(p, 1) - 1; break;
+			default: ;
+			   }
+		   }
+		p--;			/* back up over terminator */
+		/* Find the name being declared. */
+		/* This is complicated because of procedure and */
+		/* array modifiers. */
+		for ( ; ; )
+		   {	p = skipspace(p - 1, -1);
+			switch ( *p )
+			   {
+			case ']':	/* skip array dimension(s) */
+			case ')':	/* skip procedure args OR name */
+			   {	int level = 1;
+				while ( level )
+				 switch ( *--p )
+				   {
+				case ']': case ')': level++; break;
+				case '[': case '(': level--; break;
+				case '/': p = skipspace(p, -1) + 1; break;
+				default: ;
+				   }
+			   }
+				if ( *p == '(' && *skipspace(p + 1, 1) == '*' )
+				   {	/* We found the name being declared */
+					while ( !isidfirstchar(*p) )
+						p = skipspace(p, 1) + 1;
+					goto found;
+				   }
+				break;
+			default: goto found;
+			   }
+		   }
+found:		if ( *p == '.' && p[-1] == '.' && p[-2] == '.' )
+		   {	p++;
+			if ( bp == breaks + 1 )	/* sole argument */
+				writeblanks(breaks[0], p);
+			else
+				writeblanks(bp[-1] - 1, p);
+			bp--;
+		   }
+		else
+		   {	while ( isidchar(*p) ) p--;
+			*bp++ = p+1;
+		   }
+		p = end;
+	   }
+	while ( *p++ == ',' );
+	*bp = p;
+	/* Make a special check for 'void' arglist */
+	if ( bp == breaks+2 )
+	   {	p = skipspace(breaks[0], 1);
+		if ( !strncmp(p, "void", 4) )
+		   {	p = skipspace(p+4, 1);
+			if ( p == breaks[2] - 1 )
+			   {	bp = breaks;	/* yup, pretend arglist is empty */
+				writeblanks(breaks[0], p + 1);
+			   }
+		   }
+	   }
+	/* Put out the function name */
+	p = buf;
+	while ( p != endfn ) putc(*p, out), p++;
+	/* Put out the declaration */
+	for ( ap = breaks+1; ap < bp; ap += 2 )
+	   {	p = *ap;
+		while ( isidchar(*p) ) putc(*p, out), p++;
+		if ( ap < bp - 1 ) fputs(", ", out);
+	   }
+	fputs(")  ", out);
+	/* Put out the argument declarations */
+	for ( ap = breaks+2; ap <= bp; ap += 2 ) (*ap)[-1] = ';';
+	fputs(breaks[0], out);
+	free((char *)breaks);
+	return 0;
+}
diff --git a/example.c b/example.c
new file mode 100644
index 0000000..830f26d
--- /dev/null
+++ b/example.c
@@ -0,0 +1,360 @@
+/* example.c - an example of using libpng */
+
+/* this is an example of how to use libpng to read and write
+   png files.  The file libpng.txt is much more verbose then
+   this.  If you have not read it, do so first.  This was
+   designed to be a starting point of an implementation.
+   This is not officially part of libpng, and therefore
+   does not require a copyright notice.
+   */
+
+#include <png.h>
+
+/* check to see if a file is a png file using png_check_sig() */
+int check_png(char *file_name)
+{
+   FILE *fp;
+   char buf[8];
+   int ret;
+
+   fp = fopen(file_name, "rb");
+   if (!fp)
+      return 0;
+   ret = fread(buf, 1, 8, fp);
+   fclose(fp);
+
+   if (ret != 8)
+      return 0;
+
+   ret = png_check_sig(buf, 8);
+
+   return (ret);
+}
+
+/* read a png file.  You may want to return an error code if the read
+   fails (depending upon the failure). */
+void read_png(char *file_name)
+{
+   FILE *fp;
+   png_struct *png_ptr;
+   png_info *info_ptr;
+
+   /* open the file */
+   fp = fopen(file_name, "rb");
+   if (!fp)
+      return;
+
+   /* allocate the necessary structures */
+   png_ptr = malloc(sizeof (png_struct));
+   if (!png_ptr)
+   {
+      fclose(fp);
+      return;
+   }
+
+   info_ptr = malloc(sizeof (png_info));
+   if (!info_ptr)
+   {
+      fclose(fp);
+      free(png_ptr);
+      return;
+   }
+
+   /* set error handling */
+   if (setjmp(png_ptr->jmpbuf))
+   {
+      png_read_destroy(png_ptr, info_ptr, (png_info *)0);
+      fclose(fp);
+      free(png_ptr);
+      free(info_ptr);
+      /* If we get here, we had a problem reading the file */
+      return;
+   }
+
+   /* initialize the structures, info first for error handling */
+   png_info_init(info_ptr);
+   png_read_init(png_ptr);
+
+   /* set up the input control */
+   png_init_io(png_ptr, fp);
+
+   /* read the file information */
+   png_read_info(png_ptr, info_ptr);
+
+   /* allocate the memory to hold the image using the fields
+      of png_info. */
+
+   /* set up the transformations you want.  Note that these are
+      all optional.  Only call them if you want them */
+
+   /* expand paletted colors into true rgb */
+   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+      info_ptr->bit_depth < 8)
+      png_set_expand(png_ptr);
+
+   /* expand grayscale images to the full 8 bits */
+   if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY &&
+      info_ptr->bit_depth < 8)
+      png_set_expand(png_ptr);
+
+   /* expand images with transparency to full alpha channels */
+   if (info_ptr->valid & PNG_INFO_tRNS)
+      png_set_expand(png_ptr);
+
+   /* Set the background color to draw transparent and alpha
+      images over */
+   png_color_16 my_background;
+
+   if (info_ptr->valid & PNG_INFO_bKGD)
+      png_set_background(png_ptr, &(info_ptr->background),
+         PNG_GAMMA_FILE, 1, 1.0);
+   else
+      png_set_background(png_ptr, &my_background,
+         PNG_GAMMA_SCREEN, 0, 1.0);
+
+   /* tell libpng to handle the gamma conversion for you */
+   if (info_ptr->valid & PNG_INFO_gAMA)
+      png_set_gamma(png_ptr, screen_gamma, info_ptr->gamma);
+   else
+      png_set_gamma(png_ptr, screen_gamma, 0.45);
+
+   /* tell libpng to strip 16 bit depth files down to 8 bits */
+   if (info_ptr->bit_depth == 16)
+      png_set_strip_16(png_ptr);
+
+   /* dither rgb files down to 8 bit palettes & reduce palettes
+      to the number of colors available on your screen */
+   if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
+   {
+      if (info_ptr->valid & PNG_INFO_PLTE)
+         png_set_dither(png_ptr, info_ptr->palette,
+            info_ptr->num_palette, max_screen_colors,
+               info_ptr->histogram);
+      else
+      {
+         png_color std_color_cube[MAX_SCREEN_COLORS] =
+            {/* ... colors ... */};
+
+         png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS,
+            MAX_SCREEN_COLORS, NULL);
+      }
+   }
+
+   /* invert monocrome files */
+   if (info_ptr->bit_depth == 1 &&
+      info_ptr->color_type == PNG_COLOR_GRAY)
+      png_set_invert(png_ptr);
+
+   /* shift the pixels down to their true bit depth */
+   if (info_ptr->valid & PNG_INFO_sBIT &&
+      info_ptr->bit_depth > info_ptr->sig_bit)
+      png_set_shift(png_ptr, &(info_ptr->sig_bit));
+
+   /* pack pixels into bytes */
+   if (info_ptr->bit_depth < 8)
+      png_set_packing(png_ptr);
+
+   /* flip the rgb pixels to bgr */
+   if (info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
+      info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      png_set_bgr(png_ptr);
+
+   /* swap bytes of 16 bit files to least significant bit first */
+   if (info_ptr->bit_depth == 16)
+      png_set_swap(png_ptr);
+
+   /* add a filler byte to store rgb files as rgbx */
+   if (info_ptr->bit_depth == 8 &&
+      info_ptr->color_type == PNG_COLOR_TYPE_RGB)
+      png_set_rgbx(png_ptr);
+
+   /* optional call to update palette with transformations */
+   png_start_read_image(png_ptr);
+
+   /* the easiest way to read the image */
+   void *row_pointers[height];
+   png_read_image(png_ptr, row_pointers);
+
+   /* the other way to read images - deal with interlacing */
+
+   /* turn on interlace handling */
+   if (info_ptr->interlace_type)
+      number_passes = png_set_interlace_handling(png_ptr);
+   else
+      number_passes = 1;
+
+   for (pass = 0; pass < number_passes; pass++)
+   {
+      /* Read the image using the "sparkle" effect. */
+      png_read_rows(png_ptr, row_pointers, NULL, number_of_rows);
+
+      /* If you are only reading on row at a time, this works */
+      for (y = 0; y < height; y++)
+      {
+         char *row_pointers = row[y];
+         png_read_rows(png_ptr, &row_pointers, NULL, 1);
+      }
+
+      /* to get the rectangle effect, use the third parameter */
+      png_read_rows(png_ptr, NULL, row_pointers, number_of_rows);
+
+      /* if you want to display the image after every pass, do
+         so here */
+   }
+
+   /* read the rest of the file, getting any additional chunks
+      in info_ptr */
+   png_read_end(png_ptr, info_ptr);
+
+   /* clean up after the read, and free any memory allocated */
+   png_read_destroy(png_ptr, info_ptr, (png_info *)0);
+
+   /* free the structures */
+   free(png_ptr);
+   free(info_ptr);
+
+   /* close the file */
+   fclose(fp);
+
+   /* that's it */
+   return;
+}
+
+/* write a png file */
+void write_png(char *file_name, ... other image information ...)
+{
+   FILE *fp;
+   png_struct *png_ptr;
+   png_info *info_ptr;
+
+   /* open the file */
+   fp = fopen(file_name, "wb");
+   if (!fp)
+      return;
+
+   /* allocate the necessary structures */
+   png_ptr = malloc(sizeof (png_struct));
+   if (!png_ptr)
+   {
+      fclose(fp);
+      return;
+   }
+
+   info_ptr = malloc(sizeof (png_info));
+   if (!info_ptr)
+   {
+      fclose(fp);
+      free(png_ptr);
+      return;
+   }
+
+   /* set error handling */
+   if (setjmp(png_ptr->jmpbuf))
+   {
+      png_write_destroy(png_ptr);
+      fclose(fp);
+      free(png_ptr);
+      free(info_ptr);
+      /* If we get here, we had a problem reading the file */
+      return;
+   }
+
+   /* initialize the structures */
+   png_info_init(info_ptr);
+   png_write_init(png_ptr);
+
+   /* set up the output control */
+   png_init_io(png_ptr, fp);
+
+   /* set the file information here */
+   info_ptr->width = ;
+   info_ptr->height = ;
+   etc.
+
+   /* set the palette if there is one */
+   info_ptr->valid |= PNG_INFO_PLTE;
+   info_ptr->palette = malloc(256 * sizeof (png_color));
+   info_ptr->num_palette = 256;
+   ... set palette colors ...
+
+   /* optional significant bit chunk */
+   info_ptr->valid |= PNG_INFO_sBIT;
+   info_ptr->sig_bit = true_bit_depth;
+
+   /* optional gamma chunk */
+   info_ptr->valid |= PNG_INFO_gAMA;
+   info_ptr->gamma = gamma;
+
+   /* other optional chunks */
+
+   /* write the file information */
+   png_write_info(png_ptr, info_ptr);
+
+   /* set up the transformations you want.  Note that these are
+      all optional.  Only call them if you want them */
+
+   /* invert monocrome pixels */
+   png_set_invert(png_ptr);
+
+   /* shift the pixels up to a legal bit depth and fill in
+      as appropriate to correctly scale the image */
+   png_set_shift(png_ptr, &(info_ptr->sig_bit));
+
+   /* pack pixels into bytes */
+   png_set_packing(png_ptr);
+
+   /* flip bgr pixels to rgb */
+   png_set_bgr(png_ptr);
+
+   /* swap bytes of 16 bit files to most significant bit first */
+   png_set_swap(png_ptr);
+
+   /* get rid of filler bytes, pack rgb into 3 bytes */
+   png_set_rgbx(png_ptr);
+
+   /* the easiest way to write the image */
+   void *row_pointers[height];
+   png_write_image(png_ptr, row_pointers);
+
+   /* the other way to write the image - deal with interlacing */
+
+   /* turn on interlace handling */
+   if (interlacing)
+      number_passes = png_set_interlace_handling(png_ptr);
+   else
+      number_passes = 1;
+
+   for (pass = 0; pass < number_passes; pass++)
+   {
+      /* Write a few rows at a time. */
+      png_write_rows(png_ptr, row_pointers, number_of_rows);
+
+      /* If you are only writing one row at a time, this works */
+      for (y = 0; y < height; y++)
+      {
+         char *row_pointers = row[y];
+         png_write_rows(png_ptr, &row_pointers, 1);
+      }
+   }
+
+   /* write the rest of the file */
+   png_write_end(png_ptr, info_ptr);
+
+   /* clean up after the write, and free any memory allocated */
+   png_write_destroy(png_ptr);
+
+   /* if you malloced the palette, free it here */
+   if (info_ptr->palette)
+      free(info_ptr->palette);
+
+   /* free the structures */
+   free(png_ptr);
+   free(info_ptr);
+
+   /* close the file */
+   fclose(fp);
+
+   /* that's it */
+   return;
+}
+
diff --git a/libpng.txt b/libpng.txt
new file mode 100644
index 0000000..4ddd1c1
--- /dev/null
+++ b/libpng.txt
@@ -0,0 +1,821 @@
+libpng.txt - a description on how to use and modify libpng
+
+   libpng 1.0 beta 1 - version 0.71
+   For conditions of distribution and use, see copyright notice in png.h
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   June 26, 1995
+
+This file describes how to use and modify the PNG reference library
+(known as libpng) for your own use.  There are four sections to this
+file: reading, writing, modifying, and configuration notes for various
+special platforms.  Other then this file, the file example.c is a good
+starting point for using the library, as it is heavily commented and
+should include everything most people will need.
+
+Libpng was written as a companion to the PNG specification, as a
+way to reduce the amount of time and effort it takes to support
+the PNG file format in application programs.  Most users will not
+have to modify the library significantly; advanced users may want
+to modify it more.  The library was coded for both users.  All
+attempts were made to make it as complete as possible, while
+keeping the code easy to understand.  Currently, this library
+only supports C.  Support for other languages is being considered.
+
+Libpng has been designed to handle multiple sessions at one time,
+to be easily modifiable, to be portable to the vast majority of
+machines (ANSI, K&R, 16 bit, 32 bit) available, and to be easy to
+use.  The ultimate goal of libpng is to promote the acceptance of
+the PNG file format in whatever way possible.  While there is still
+work to be done (see the todo.txt file), libpng should cover the
+majority of the needs of it's users.
+
+Libpng uses zlib for its compression and decompression of PNG files.
+The zlib compression utility is a general purpose utility that is
+useful for more then PNG files, and can be used without libpng for
+whatever use you want.  See the documentation delivered with zlib for
+more details.
+
+Those people who do not need to modify libpng should still read at
+least part of the PNG specification.  The most important parts are
+the data formats and the chunk descriptions.  Those who will be
+making changes to libpng should read the whole specification.
+
+The structures:
+
+There are two main structures that are important to libpng, png_struct
+and png_info.  The first, png_struct, is an internal structure that
+will not, for the most part, be used by the general user except as
+the first variable passed to every png function call.
+
+The png_info structure is designed to provide information about the
+png file.  All of it's fields are intended to be examined or modified
+by the user.  See png.h for a good description of the png_info fields.
+
+And while I'm on the topic, make sure you include the png header file:
+
+#include <png.h>
+
+Checking PNG files:
+
+Libpng provides a simple check to see if a file is a png file.  To
+use it, pass in the first 1 to 8 bytes of the file, and it will return
+true or false (1 or 0) depending on whether the bytes could be part
+of a png file.  Of course, the more bytes you pass in, the greater
+the accuracy of the prediction.
+
+   fread(header, 1, number, fp);
+   is_png = png_check_sig(header, number);
+
+Reading PNG files:
+
+The first thing you need to do while reading a PNG file is to allocate
+and initialize png_struct and png_info.  As these are both large, you
+may not want to store these on the stack, unless you have stack space
+to spare.  Of course, you will want to check if malloc returns NULL.
+
+   png_struct *png_ptr = malloc(sizeof (png_struct));
+   if (!png_ptr)
+      return;
+   png_info *info_ptr = malloc(sizeof (png_info));
+   if (!info_ptr)
+   {
+      free(png_ptr);
+      return;
+   }
+
+You may also want to do any i/o initialization here, before
+you get into libpng, so if it doesn't work, you don't have
+much to undo.
+
+   FILE *fp = fopen(file_name, "rb");
+   if (!fp)
+   {
+      free(png_ptr);
+      free(info_ptr);
+      return;
+   }
+
+After you have these structures, you will need to set up the
+error handling.  When libpng encounters an error, it expects to
+longjmp back to your routine.  Therefore, you will need to call
+setjmp and pass the jmpbuf field of your png_struct.  If you
+read the file from different routines, you will need to update
+the jmpbuf field every time you enter a new routine that will
+call a png_ function.  See your documentation of setjmp/longjmp
+for your compiler for more information on setjmp/longjmp.  See
+the discussion on png error handling in the Customizing Libpng
+section below for more information on the png error handling.
+If an error occurs, and libpng longjmp's back to your setjmp,
+you will want to call png_read_destroy() to free any memory.
+
+   if (setjmp(png_ptr->jmpbuf))
+   {
+      png_read_destroy(png_ptr, info_ptr, (png_info *)0);
+      /* free pointers before returning, if necessary */
+      free(png_ptr);
+      free(info_ptr);
+      fclose(fp);
+      return;
+   }
+
+Next, you will need to call png_read_init() and png_info_init().
+These functions make sure all the fields are initialized to useful
+values, and, in the case of png_read_init(), and allocate any memory
+needed for internal uses.  You must call png_info_init() first, as
+png_read_init() could do a longjmp, and if the info is not initialized,
+the png_read_destroy() could try to png_free() random addresses, which
+would be bad.
+
+   png_info_init(info_ptr);
+   png_read_init(png_ptr);
+
+Now you need to set up the input code.  The default for libpng is
+to use the C function fread().  If you use this, you will need to
+pass a valid FILE * in the function png_init_io().  Be sure that
+the file is opened in binary mode.  If you wish to handle reading
+data in another way, see the discussion on png i/o handling in the
+Customizing Libpng section below.
+
+   png_init_io(png_ptr, fp);
+
+You are now ready to read all the file information up to the actual
+image data.  You do this with a call to png_read_info().
+
+   png_read_info(png_ptr, info_ptr);
+
+The png_info structure is now filled in with all the data necessary
+to read the file.  Some of the more important parts of the png_info are:
+   width - holds the width of the file
+   height - holds the height of the file
+   bit_depth - holds the bit depth of one of the image channels
+   color_type - describes the channels and what they mean
+      see the PNG_COLOR_TYPE_ macros for more information
+   channels - number of channels of info for the color type
+   pixel_depth - bits per pixel
+   rowbytes - number of bytes needed to hold a row
+   interlace_type - currently 0 for none, 1 for interlaced
+   valid - this details which optional chunks were found in the file
+      to see if a chunk was present, OR valid with the appropriate
+      PNG_INFO_<chunk name> define.
+   palette and num_palette - the palette for the file
+   gamma - the gamma the file is written at
+   sig_bit and sig_bit_number - the number of significant bits
+   trans, trans_values, and number_trans - transparency info
+   hist - histogram of palette
+   text and num_text - text comments in the file.
+for more information, see the png_info definition in png.h and the
+PNG specification for chunk contents.  Be careful with trusting
+rowbytes, as some of the transformations could increase the space
+needed to hold a row (expand, rgbx, xrgb, graph_to_rgb, etc.).
+
+A quick word about text and num_text.  PNG stores comments in
+keyword/text pairs, one pair per chunk.  While there are
+suggested keywords, there is no requirement to restrict the use
+to these strings.  There is a requirement to have at least one
+character for a keyword.  It is strongly suggested that keywords
+be sensible to humans (that's the point), so don't use abbreviations.
+See the png specification for more details.  There is no requirement
+to have text after the keyword on tEXt chunks.  However, you must
+have text after the keyword on zTXt chunks, as only the text gets
+compressed, and compressing nothing will result in an error.
+
+There is no maximum length on the keyword, and nothing
+prevents you from duplicating the keyword.  The text field is an
+array of png_text structures, each holding pointer to a keyword
+and a pointer to a text string.  Only the text string may be null.
+The keyword/text pairs are put into the array in the order that
+they are received.  However, some or all of the text chunks may be
+after the image, so to make sure you have read all the text chunks,
+don't mess with these until after you read the stuff after the image.
+This will be mentioned again below in the discussion that goes with
+png_read_end().
+
+After you've read the file information, you can set up the library to
+handle any special transformations of the image data.  The various
+ways to transform the data will be described in the order that they
+occur.  This is important, as some of these change the color type
+and bit depth of the data, and some others only work on certain
+color types and bit depths.  Even though each transformation should
+check to see if it has data that it can do somthing with, you should
+make sure to only enable a transformation if it will be valid for
+the data.  For example, don't swap red and blue on grayscale data.
+
+This transforms bit depths of less then 8 to 8 bits, changes paletted
+images to rgb, and adds an alpha channel if there is transparency
+information in a tRNS chunk.  This is probably most useful on grayscale
+images with bit depths of 2 or 4 and tRNS chunks.
+
+   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+      info_ptr->bit_depth < 8)
+      png_set_expand(png_ptr);
+
+   if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY &&
+      info_ptr->bit_depth < 8)
+      png_set_expand(png_ptr);
+
+   if (info_ptr->valid & PNG_INFO_tRNS)
+      png_set_expand(png_ptr);
+
+This handles alpha and transparency by replacing it with a background
+value.  If there was a valid one in the file, you can use it if you
+want.  However, you can replace it with your own if you want also.  If
+there wasn't one in the file, you must supply a color.  If libpng is
+doing gamma correction, you will need to tell libpng where the
+background came from so it can do the appropriate gamma correction.
+If you are modifying the color data with png_set_expand(), you must
+indicate whether the background needs to be expanded.  See the
+function definition in png.h for more details.
+
+   png_color_16 my_background;
+
+   if (info_ptr->valid & PNG_INFO_bKGD)
+      png_set_backgrond(png_ptr, &(info_ptr->background),
+         PNG_GAMMA_FILE, 1, 1.0);
+   else
+      png_set_background(png_ptr, &my_background,
+         PNG_GAMMA_SCREEN, 0, 1.0);
+
+This handles gamma transformations of the data.  Pass both the file
+gamma and the desired screen gamma.  If the file does not have a
+gamma value, you can pass one anyway if you wish.  Note that file
+gammas are inverted from screen gammas.  See the discussions on
+gamma in the PNG specification for more information.
+
+   if (info_ptr->valid & PNG_INFO_gAMA)
+      png_set_gamma(png_ptr, screen_gamma, info_ptr->gamma);
+   else
+      png_set_gamma(png_ptr, screen_gamma, 0.45);
+
+PNG can have files with 16 bits per channel.  If you only can handle
+8 bits per channel, this will strip the pixels down to 8 bit.
+
+   if (info_ptr->bit_depth == 16)
+      png_set_strip_16(png_ptr);
+
+If you need to reduce an rgb file to a paletted file, or if a
+paletted file has more entries then will fit on your screen, this
+function will do that.  Note that this is a simple match dither, that
+merely finds the closest color available.  This should work fairly
+well with optimized palettes, and fairly badly with linear color
+cubes.  If you pass a palette that is larger then maximum_colors,
+the file will reduce the number of colors in the palette so it
+will fit into maximum_colors.  If there is an histogram, it will
+use it to make intelligent choises when reducing the palette.  If
+there is no histogram, it may not do a good job.
+
+   if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
+   {
+      if (info_ptr->valid & PNG_INFO_PLTE)
+         png_set_dither(png_ptr, info_ptr->palette,
+            info_ptr->num_palette, max_screen_colors,
+               info_ptr->histogram);
+      else
+      {
+         png_color std_color_cube[MAX_SCREEN_COLORS] =
+            { ... colors ... };
+
+         png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS,
+            MAX_SCREEN_COLORS, NULL);
+      }
+   }
+
+PNG files describe monocrome as black is zero and white is one.  If you
+want this reversed (black is one and white is zero), call this:
+
+   if (info_ptr->bit_depth == 1 &&
+      info_ptr->color_type == PNG_COLOR_GRAY)
+      png_set_invert(png_ptr);
+
+PNG files reduce possible bit depths to 1, 2, 4, 8, and 16.  However,
+they also provide a way to describe the true bit depth of the image.
+Then they require bits to be scaled to full range for the bit depth
+used in the file.  If you want to reduce your pixels back down to
+the true bit depth, call this:
+
+   if (info_ptr->valid & PNG_INFO_sBIT)
+      png_set_shift(png_ptr, &(info_ptr->sig_bit));
+
+PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as
+they can, resulting in, for example, 8 pixels per byte for 1 bit files.
+If you would rather these were expanded to 1 pixel per byte without
+changing the values of the pixels, call this:
+
+   if (info_ptr->bit_depth < 8)
+      png_set_packing(png_ptr);
+
+PNG files store 3 color pixels in red, green, blue order.  If you would
+rather have the pixels as blue, green, red, call this.
+
+   if (info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
+      info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      png_set_bgr(png_ptr);
+
+For some uses, you may want a grayscale image to be represented as
+rgb.  If you need this, call this:
+
+   if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
+      info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      png_set_gray_to_rgb(png_ptr);
+
+PNG files store 16 bit pixels in network byte order (most significant
+bit first).  If you would rather store them the other way, (the way
+PC's store them, for example), call this:
+
+   if (info_ptr->bit_depth == 16)
+      png_set_swap(png_ptr);
+
+PNG files store rgb pixels packed into 3 bytes.  If you would rather
+pack them into 4 bytes, with the filler byte last, call this:
+
+   if (info_ptr->bit_depth == 8 &&
+      info_ptr->color_type == PNG_COLOR_TYPE_RGB)
+      png_set_rgbx(png_ptr);
+
+If you need the filler byte first, call this:
+
+   if (info_ptr->bit_depth == 8 &&
+      info_ptr->color_type == PNG_COLOR_TYPE_RGB)
+      png_set_xrgb(png_ptr);
+
+After setting the transformations, you can update your palette by
+calling png_start_read_image().  This function is provided for those
+who need an updated palette before they read the image data.  If you
+don't call this function, the library will automatically call it
+before it reads the first row.
+
+   png_start_read_image(png_ptr);
+
+That's it for the transformations.  Now you can read the image data.
+The simplest way to do this is in one function call.  If you are
+allocating enough memory to hold the whole image, you can just
+call png_read_image() and libpng will read in all the image data
+and put it in the memory area supplied.  You will need to pass in
+an array of pointers to each row.
+
+This function automatically handles interlacing, so you don't need
+to call png_set_interlace_handling() or call this function multiple
+times, or any of that other stuff necessary with png_read_rows().
+
+   png_read_image(png_ptr, row_pointers);
+
+where row_pointers is:
+
+   void *row_pointers[height];
+
+You can point to void or char or whatever you use for pixels.
+
+If you don't want to read the whole image in at once, you can
+use png_read_rows() instead.  If there is no interlacing (check
+info_ptr->interlace_type), this is simple:
+
+   png_read_rows(png_ptr, row_pointers, NULL, number_of_rows);
+
+row_pointers is the same as in the png_read_image() call.
+
+If you are just calling one row at a time, you can do this for
+row_pointers:
+
+   char *row_pointers = row;
+
+   png_read_rows(png_ptr, &row_pointers, NULL, 1);
+
+When the file is interlaced (info_ptr->interlace_type == 1), things
+get a good deal harder.  PNG files have a complicated interlace scheme
+that breaks down an image into seven smaller images of varying size.
+Libpng will fill out those images if you want, or it will give them
+to you "as is".  If you want to fill them out, there is two ways
+to do that.  The one mentioned in the PNG specification is to expand
+each pixel to cover those pixels that have not been read yet.  This
+results in a blocky image for the first pass, which gradually smooths
+out as more pixels are read.  The other method is the "sparkle" method,
+where pixels are draw only in their final locations, with the rest of
+the image remaining whatever colors they were initialized to before
+the start of the read.  The first method usually looks better, but
+tends to be slower, as there are more pixels to put in the rows.  Some
+examples to help clear this up:
+
+If you don't want libpng to handle the interlacing details, just
+call png_read_rows() the correct number of times to read in all
+seven images.  See the PNG specification for more details on the
+interlacing scheme.
+
+If you want libpng to expand the images, call this:
+
+   if (info_ptr->interlace_type)
+      number_passes = png_set_interlace_handling(png_ptr);
+
+This will return the number of passes needed.  Currently, this
+is seven, but may change if another interlace type is added.
+This function can be called even if the file is not interlaced,
+when it will return one.
+
+If you are not going to display the image after each pass, but are
+going to wait until the entire image is read in, use the sparkle
+effect.  This effect is faster and the end result of either method
+is exactly the same.  If you are planning on displaying the image
+after each pass, the rectangle effect is generally considered the
+better looking one.
+
+If you only want the "sparkle" effect, just call png_read_rows() as
+normal, with the third parameter NULL.  Make sure you make pass over
+the image number_passes times, and you don't change the data in the
+rows between calls.  You can change the locations of the data, just
+not the data.  Each pass only writes the pixels appropriate for that
+pass, and assumes the data from previous passes is still valid.
+
+   png_read_rows(png_ptr, row_pointers, NULL, number_of_rows);
+
+If you only want the first effect (the rectangles), do the same as
+before except pass the row buffer in the third parameter, and leave
+the second parameter NULL.
+
+   png_read_rows(png_ptr, NULL, row_pointers, number_of_rows);
+
+After you are finished reading the image, you can finish reading
+the file.  If you are interested in comments or time, you should
+pass the png_info pointer from the png_read_info() call.  If you
+are not interested, you can pass NULL.
+
+   png_read_end(png_ptr, info_ptr);
+
+When you are done, you can free all memory used by libpng like this:
+
+   png_read_destroy(png_ptr, info_ptr, (png_info *)0);
+
+After that, you can discard the structures, or reuse them another
+read or write.  For a more compact example of reading a PNG image,
+see the file example.c.
+
+
+Writing PNG files:
+
+Much of this is very similar to reading.  However, everything of
+importance is repeated here, so you don't have to constantly look
+back up in the Reading PNG files section to understand writing.
+
+The first thing you need to do while writing a PNG file is to allocate
+and initialize png_struct and png_info.  As these are both large, you
+may not want to store these on the stack, unless you have stack space
+to spare.
+
+   png_struct *png_ptr = malloc(sizeof (png_struct));
+   if (!png_ptr)
+      return;
+   png_info *info_ptr = malloc(sizeof (png_info));
+   if (!info_ptr)
+   {
+      free(png_ptr);
+      return;
+   }
+
+You may also want to do any i/o initialization here, before
+you get into libpng, so if it doesn't work, you don't have
+much to undo.
+
+   FILE *fp = fopen(file_name, "wb");
+   if (!fp)
+   {
+      free(png_ptr);
+      free(info_ptr);
+      return;
+   }
+
+After you have these structures, you will need to set up the
+error handling.  When libpng encounters an error, it expects to
+longjmp back to your routine.  Therefore, you will need to call
+setjmp and pass the jmpbuf field of your png_struct.  If you
+write the file from different routines, you will need to update
+the jmpbuf field every time you enter a new routine that will
+call a png_ function.  See your documentation of setjmp/longjmp
+for your compiler for more information on setjmp/longjmp.  See
+the discussion on png error handling in the Customizing Libpng
+section below for more information on the png error handling.
+
+   if (setjmp(png_ptr->jmpbuf))
+   {
+      png_write_destroy(png_ptr);
+      /* free pointers before returning.  Make sure you clean up
+         anything else you've done. */
+      free(png_ptr);
+      free(info_ptr);
+      fclose(fp);
+      return;
+   }
+
+Next, you will need to call png_write_init() and png_info_init().
+These functions make sure all the fields are initialized to useful
+values, and, in the case of png_write_init(), allocate any memory
+needed for internal uses.  Do png_info_init() first, so if
+png_write_init() longjmps, you know info_ptr is valid, so you
+don't free random memory pointers, which would be bad.
+
+   png_info_init(info_ptr);
+   png_write_init(png_ptr);
+
+Now you need to set up the input code.  The default for libpng is
+to use the C function fwrite().  If you use this, you will need to
+pass a valid FILE * in the function png_init_io().  Be sure that
+the file is opened in binary mode.  If you wish to handle writing
+data in another way, see the discussion on png i/o handling in the
+Customizing Libpng section below.
+
+   png_init_io(png_ptr, fp);
+
+You now need to fill in the png_info structure with all the data
+you wish to write before the actual image.  Note that the only thing
+you are allowed to write after the image is the text chunks and the
+time chunk.  See png_write_end() for more information on that.  If you
+wish to write them before the image, fill them in now.  If you want to
+wait until after the data, don't fill them until png_write_end().  For
+all the fields in png_info, see png.h.  For explinations of what the
+fields contain, see the PNG specification.  Some of the more important
+parts of the png_info are:
+   width - holds the width of the file
+   height - holds the height of the file
+   bit_depth - holds the bit depth of one of the image channels
+   color_type - describes the channels and what they mean
+      see the PNG_COLOR_TYPE_ defines for more information
+   interlace_type - currently 0 for none, 1 for interlaced
+   valid - this describes which optional chunks to write to the
+      file.  Note that if you are writing a PNG_COLOR_TYPE_PALETTE
+      file, the PLTE chunk is not optional, but must still be marked
+      for writing.  To mark chunks for writing, OR valid with the
+      appropriate PNG_INFO_<chunk name> define.
+   palette and num_palette - the palette for the file
+   gamma - the gamma the file is written at
+   sig_bit and sig_bit_number - the number of significant bits
+   trans, trans_values, and number_trans - transparency info
+   hist - histogram of palette
+   text and num_text - text comments in the file.
+
+A quick word about text and num_text.  text is an array of png_text
+structures.  num_text is the number of valid structures in the array.
+If you want, you can use max_text to hold the size of the array, but
+libpng ignores it for writing (it does use it for reading).  Each
+png_text structure holds a keyword-text value, and a compression type.
+The compression types have the same valid numbers as the compression
+types of the image data.  Currently, the only valid number is zero.
+However, you can store text either compressed or uncompressed, unlike
+images which always have to be compressed.  So if you don't want the
+text compressed, set the compression type to -1.  Until text gets
+arount 1000 bytes, it is not worth compressing it.
+
+The keyword-text pairs work like this.  Keywords should be short
+simple descriptions of what the comment is about.  Some typical
+keywords are found in the PNG specification, as is some recomendations
+on keywords.  You can repeat keywords in a file.  You can even write
+some text before the image and some after.  For example, you may want
+to put a description of the image before the image, but leave the
+disclaimer until after, so viewers working over modem connections
+don't have to wait for the disclaimer to go over the modem before
+they start seeing the image.  Finally, keywords should be full
+words, not abbreviations.  Keywords can not contain NUL characters,
+and should not contain control characters.  Text in general should
+not contain control characters.  The keyword must be present, but
+you can leave off the text string on non-compressed pairs.
+Compressed pairs must have a text string, as only the text string
+is compressed anyway, so the compression would be meaningless.
+
+PNG supports modification time via the png_time structure.  Two
+conversion routines are proved, png_convert_from_time_t() for
+time_t and png_convert_from_struct_tm() for struct tm.  The
+time_t routine uses gmtime().  You don't have to use either of
+these, but if you wish to fill in the png_time structure directly,
+you should provide the time in universal time (GMT) if possible
+instead of your local time.
+
+You are now ready to write all the file information up to the actual
+image data.  You do this with a call to png_write_info().
+
+   png_write_info(png_ptr, info_ptr);
+
+After you've read the file information, you can set up the library to
+handle any special transformations of the image data.  The various
+ways to transform the data will be described in the order that they
+occur.  This is important, as some of these change the color type
+and bit depth of the data, and some others only work on certain
+color types and bit depths.  Even though each transformation should
+check to see if it has data that it can do somthing with, you should
+make sure to only enable a transformation if it will be valid for
+the data.  For example, don't swap red and blue on grayscale data.
+
+PNG files store rgb pixels packed into 3 bytes.  If you would rather
+supply the pixels as 4 bytes per pixel, with the filler byte last,
+call this:
+
+   png_set_rgbx(png_ptr);
+
+If your filler byte goes first, call this:
+
+   png_set_xrgb(png_ptr);
+
+PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as
+they can, resulting in, for example, 8 pixels per byte for 1 bit files.
+If you would rather supply the data 1 pixel per byte, but with the
+values limited to the correct number of bits, call this:
+
+   png_set_packing(png_ptr);
+
+PNG files reduce possible bit depths to 1, 2, 4, 8, and 16.  If your
+data is of another bit depth, but is packed into the bytes correctly,
+this will scale the values to appear to be the correct bit depth.
+Make sure you write a sBIT chunk when you do this, so others, if
+they want, can reduce the values down to their true depth.
+
+   /* do this before png_write_info() */
+   info_ptr->valid |= PNG_INFO_sBIT;
+
+   /* note that you can cheat and set all the values of
+      sig_bit to true_bit_depth if you want */
+   if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
+   {
+      info_ptr->sig_bit.red = true_bit_depth;
+      info_ptr->sig_bit.green = true_bit_depth;
+      info_ptr->sig_bit.blue = true_bit_depth;
+   }
+   else
+   {
+      info_ptr->sig_bit.gray = true_bit_depth;
+   }
+
+   if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+   {
+      info_ptr->sig_bit.alpha = true_bit_depth;
+   }
+
+   png_set_shift(png_ptr, &(info_ptr->sig_bit));
+
+PNG files store 16 bit pixels in network byte order (most significant
+bit first).  If you would rather supply them the other way, (the way
+PC's store them, for example), call this:
+
+   png_set_swap(png_ptr);
+
+PNG files store 3 color pixels in red, green, blue order.  If you would
+rather supply the pixels as blue, green, red, call this.
+
+   png_set_bgr(png_ptr);
+
+PNG files describe moncrome as black is zero and white is one.  If you
+would rather supply the pixels with this reversed (black is one and
+white is zero), call this:
+
+   png_set_invert(png_ptr);
+
+That's it for the transformations.  Now you can write the image data.
+The simplest way to do this is in one function call.  If have the
+whole image in memory, you can just call png_write_image() and libpng
+will write the image.  You will need to pass in an array of pointers to
+each row.  This function automatically handles interlacing, so you don't
+need to call png_set_interlace_handling() or call this function multiple
+times, or any of that other stuff necessary with png_write_rows().
+
+   png_write_image(png_ptr, row_pointers);
+
+where row_pointers is:
+
+   void *row_pointers[height];
+
+You can point to void or char or whatever you use for pixels.
+
+If you can't want to write the whole image at once, you can
+use png_write_rows() instead.  If the file is not interlaced,
+this is simple:
+
+   png_write_rows(png_ptr, row_pointers, number_of_rows);
+
+row_pointers is the same as in the png_write_image() call.
+
+If you are just calling one row at a time, you can do this for
+row_pointers:
+
+   char *row_pointers = row;
+
+   png_write_rows(png_ptr, &row_pointers, 1);
+
+When the file is interlaced, things can get a good deal harder.
+PNG files have a complicated interlace scheme that breaks down an
+image into seven smaller images of varying size.  Libpng will
+build these images if you want, or you can do them yourself.  If
+you want to build them yourself, see the PNG specification for
+details of which pixels to write when.
+
+If you don't want libpng to handle the interlacing details, just
+call png_write_rows() the correct number of times to write all
+seven sub-images.
+
+If you want libpng to build the sub-images, call this:
+
+   number_passes = png_set_interlace_handling(png_ptr);
+
+This will return the number of passes needed.  Currently, this
+is seven, but may change if another interlace type is added.
+
+Then write the image number_passes times.
+
+   png_write_rows(png_ptr, row_pointers, number_of_rows);
+
+As some of these rows are not used, and thus return immediately,
+you may want to read about interlacing in the PNG specification,
+and only update the rows that are actually used.
+
+After you are finished writing the image, you should finish writing
+the file.  If you are interested in writing comments or time, you should
+pass the an appropriately filled png_info pointer.  If you
+are not interested, you can pass NULL.  Be careful that you don't
+write the same text or time chunks here as you did in png_write_info().
+
+   png_write_end(png_ptr, info_ptr);
+
+When you are done, you can free all memory used by libpng like this:
+
+   png_write_destroy(png_ptr);
+
+Any data you allocated for png_info, you must free yourself.
+
+After that, you can discard the structures, or reuse them another
+read or write.  For a more compact example of writing a PNG image,
+see the file example.c.
+
+
+Customizing libpng:
+
+There are two issues here.  The first is changing how libpng does
+standard things like memory allocation, input/output, and error handling.
+The second deals with more complicated things like adding new chunks,
+adding new transformations, and generally changing how libpng works.
+
+All of the memory allocation, input/output, and error handling in libpng
+goes through the routines in pngstub.c.  The file as plenty of comments
+describing each function and how it expects to work, so I will just
+summarize here.  See pngstub.c for more details.
+
+Memory allocation is done through the functions png_large_malloc(),
+png_malloc(), png_realloc(), png_large_free(), and png_free().
+These currently just call the standard C functions.  The large
+functions must handle exactly 64K, but they don't have to handle
+more then that.  If your pointers can't access more then 64K at a
+time, you will want to set MAXSEG_64K in zlib.h.
+
+Input/Output in libpng is done throught png_read() and png_write(), which
+currently just call fread() and fwrite().  The FILE * is stored in
+png_struct, and is initialized via png_init_io().  If you wish to change
+this, make the appropriate changes in pngstub.c and png.h.  Make sure you
+change the function prototype for png_init_io() if you are no longer
+using a FILE *.
+
+Error handling in libpng is done through png_error() and png_warning().
+Errors handled through png_error() are fatal, meaning that png_error()
+should never return to it's caller.  Currently, this is handled via
+setjmp() and longjmp(), but you could change this to do things like
+exit() if you should wish.  Similarly, both png_error() and png_warning()
+print a message on stderr, but that can also be changed.  The motivation
+behind using setjmp() and longjmp() is the C++ throw and catch exception
+handling methods.  This makes the code much easier to write, as there
+is no need to check every return code of every function call.  However,
+there are some uncertainties about the status of local variables after
+a longjmp, so the user may want to be careful about doing anything after
+setjmp returns non zero besides returning itself.  Consult your compiler
+documentation for more details.
+
+If you need to read or write custom chunks, you will need to get deeper
+into the libpng code.  First, read the PNG specification, and have
+a first level of understanding of how it works.  Pay particular
+attention to the sections that describe chunk names, and look
+at how other chunks were designed, so you can do things similar.
+Second, check out the sections of libpng that read and write chunks.
+Try to find a chunk that is similar to yours, and copy off of it.
+More details can be found in the comments inside the code.
+
+If you wish to write your own transformation for the data, look
+through the part of the code that does the transformations, and check
+out some of the more simple ones to get an idea of how they work.  Try
+to find a similar transformation to the one you want to add, and copy
+off of it.  More details can be found in the comments inside the code
+itself.
+
+Configuring for 16 bit platforms:
+
+You will probably need to change the png__large_malloc() and
+png_large_free() routines in pngstub.c, as these are requred
+to allocate 64K.  Also, you will want to look into zconf.h to tell
+zlib (and thus libpng) that it cannot allocate more then 64K at a
+time.  Even if you can, the memory won't be accessable.  So limit zlib
+and libpng to 64K by defining MAXSEG_64K.
+
+Configuring for gui/windowing platforms:
+
+You will need to change the error message display in png_error() and
+png_warning() to display a message instead of fprinting it to stderr.
+You may want to write a single function to do this and call it something
+like png_message().  On some compliers, you may have to change the
+memory allocators (png_malloc, etc.).
+
+Configuring for compiler xxx:
+
+All includes for libpng are in png.h.  If you need to add/change/delete
+an include, this is the place to do it.  The includes that are not
+needed outside libpng are protected by the PNG_INTERNAL definition,
+which is only defined for those routines inside libpng itself.  The
+files in libpng proper only include png.h.
+
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..5d3da0f
--- /dev/null
+++ b/makefile
@@ -0,0 +1,49 @@
+# makefile for libpng
+# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc.
+# For conditions of distribution and use, see copyright notice in png.h
+
+CC=gcc
+CFLAGS=-I../zlib -O3
+LDFLAGS=-L. -L../zlib/ -lpng -lgz -lm
+
+RANLIB=ranlib
+#RANLIB=echo
+
+# where make install puts libpng.a and png.h
+prefix=/usr/local
+
+OBJS = png.o pngrcb.o pngrutil.o pngtrans.o pngwutil.o \
+	pngread.o pngstub.o pngwrite.o pngrtran.o pngwtran.o
+
+all: libpng.a pngtest
+
+libpng.a: $(OBJS)
+	ar rc $@  $(OBJS)
+	$(RANLIB) $@
+
+pngtest: pngtest.o libpng.a
+	cc -o pngtest $(CCFLAGS) pngtest.o $(LDFLAGS)
+
+install: libpng.a
+	-@mkdir $(prefix)/include
+	-@mkdir $(prefix)/lib
+	cp png.h $(prefix)/include
+	chmod 644 $(prefix)/include/png.h
+	cp libpng.a $(prefix)/lib
+	chmod 644 $(prefix)/lib/libpng.a
+
+clean:
+	rm -f *.o libpng.a pngtest pngout.png
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+pngrcb.o: png.h
+pngread.o: png.h
+pngrtran.o: png.h
+pngrutil.o: png.h
+pngstub.o: png.h
+pngtest.o: png.h
+pngtrans.o: png.h
+pngwrite.o: png.h
+pngwtran.o: png.h
+pngwutil.o: png.h
diff --git a/makefile.knr b/makefile.knr
new file mode 100644
index 0000000..5eb0b79
--- /dev/null
+++ b/makefile.knr
@@ -0,0 +1,61 @@
+# makefile for libpng
+# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc.
+# For conditions of distribution and use, see copyright notice in png.h
+
+CC=cc
+CFLAGS=-I../zlib -O
+LDFLAGS=-L. -L../zlib/ -lpng -lgz -lm
+# flags for ansi2knr
+ANSI2KNRFLAGS=
+
+RANLIB=ranlib
+#RANLIB=echo
+
+# where make install puts libpng.a and png.h
+prefix=/usr/local
+
+OBJS = png.o pngrcb.o pngrutil.o pngtrans.o pngwutil.o \
+	pngread.o pngstub.o pngwrite.o pngrtran.o pngwtran.o
+
+all: ansi2knr libpng.a pngtest
+
+# general rule to allow ansi2knr to work
+.c.o:
+	./ansi2knr $*.c T$*.c
+	$(CC) $(CFLAGS) -c T$*.c
+	rm -f T$*.c $*.o
+	mv T$*.o $*.o
+
+ansi2knr: ansi2knr.c
+	$(CC) $(CFLAGS) $(ANSI2KNRFLAGS) -o ansi2knr ansi2knr.c
+
+libpng.a: ansi2knr $(OBJS)
+	ar rc $@  $(OBJS)
+	$(RANLIB) $@
+
+pngtest: pngtest.o libpng.a ansi2knr
+	cc -o pngtest $(CCFLAGS) pngtest.o $(LDFLAGS)
+
+install: libpng.a
+	-@mkdir $(prefix)/include
+	-@mkdir $(prefix)/lib
+	cp png.h $(prefix)/include
+	chmod 644 $(prefix)/include/png.h
+	cp libpng.a $(prefix)/lib
+	chmod 644 $(prefix)/lib/libpng.a
+
+clean:
+	rm -f *.o libpng.a pngtest pngout.png ansi2knr
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+pngrcb.o: png.h
+pngread.o: png.h
+pngrtran.o: png.h
+pngrutil.o: png.h
+pngstub.o: png.h
+pngtest.o: png.h
+pngtrans.o: png.h
+pngwrite.o: png.h
+pngwtran.o: png.h
+pngwutil.o: png.h
diff --git a/makefile.mip b/makefile.mip
new file mode 100644
index 0000000..ea41c61
--- /dev/null
+++ b/makefile.mip
@@ -0,0 +1,50 @@
+# makefile for libpng
+# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc.
+# For conditions of distribution and use, see copyright notice in png.h
+
+CC=cc
+CFLAGS=-I../zlib -O -systype sysv -DSYSV -w -Dmips
+#CFLAGS=-O
+LDFLAGS=-L. -L../zlib/ -lpng -lgz -lm
+
+#RANLIB=ranlib
+RANLIB=echo
+
+# where make install puts libpng.a and png.h
+prefix=/usr/local
+
+OBJS = png.o pngrcb.o pngrutil.o pngtrans.o pngwutil.o \
+	pngread.o pngstub.o pngwrite.o pngrtran.o pngwtran.o
+
+all: libpng.a pngtest
+
+libpng.a: $(OBJS)
+	ar rc $@  $(OBJS)
+	$(RANLIB) $@
+
+pngtest: pngtest.o libpng.a
+	cc -o pngtest $(CCFLAGS) pngtest.o $(LDFLAGS)
+
+install: libpng.a
+	-@mkdir $(prefix)/include
+	-@mkdir $(prefix)/lib
+	cp png.h $(prefix)/include
+	chmod 644 $(prefix)/include/png.h
+	cp libpng.a $(prefix)/lib
+	chmod 644 $(prefix)/lib/libpng.a
+
+clean:
+	rm -f *.o libpng.a pngtest pngout.png
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+pngrcb.o: png.h
+pngread.o: png.h
+pngrtran.o: png.h
+pngrutil.o: png.h
+pngstub.o: png.h
+pngtest.o: png.h
+pngtrans.o: png.h
+pngwrite.o: png.h
+pngwtran.o: png.h
+pngwutil.o: png.h
diff --git a/makefile.std b/makefile.std
new file mode 100644
index 0000000..1613e90
--- /dev/null
+++ b/makefile.std
@@ -0,0 +1,49 @@
+# makefile for libpng
+# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc.
+# For conditions of distribution and use, see copyright notice in png.h
+
+CC=cc
+CFLAGS=-I../zlib -O
+LDFLAGS=-L. -L../zlib/ -lpng -lgz -lm
+
+#RANLIB=ranlib
+RANLIB=echo
+
+# where make install puts libpng.a and png.h
+prefix=/usr/local
+
+OBJS = png.o pngrcb.o pngrutil.o pngtrans.o pngwutil.o \
+	pngread.o pngstub.o pngwrite.o pngrtran.o pngwtran.o
+
+all: libpng.a pngtest
+
+libpng.a: $(OBJS)
+	ar rc $@  $(OBJS)
+	$(RANLIB) $@
+
+pngtest: pngtest.o libpng.a
+	cc -o pngtest $(CCFLAGS) pngtest.o $(LDFLAGS)
+
+install: libpng.a
+	-@mkdir $(prefix)/include
+	-@mkdir $(prefix)/lib
+	cp png.h $(prefix)/include
+	chmod 644 $(prefix)/include/png.h
+	cp libpng.a $(prefix)/lib
+	chmod 644 $(prefix)/lib/libpng.a
+
+clean:
+	rm -f *.o libpng.a pngtest pngout.png
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+pngrcb.o: png.h
+pngread.o: png.h
+pngrtran.o: png.h
+pngrutil.o: png.h
+pngstub.o: png.h
+pngtest.o: png.h
+pngtrans.o: png.h
+pngwrite.o: png.h
+pngwtran.o: png.h
+pngwutil.o: png.h
diff --git a/png.c b/png.c
new file mode 100644
index 0000000..d65d7c6
--- /dev/null
+++ b/png.c
@@ -0,0 +1,172 @@
+
+/* png.c - location for general purpose png functions
+
+   libpng 1.0 beta 1 - version 0.71
+   For conditions of distribution and use, see copyright notice in png.h
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   June 26, 1995
+   */
+
+#define PNG_INTERNAL
+#define PNG_NO_EXTERN
+#include "png.h"
+
+/* place to hold the signiture string for a png file. */
+png_byte png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+
+/* constant strings for known chunk types.  If you need to add a chunk,
+   add a string holding the name here.  If you want to make the code
+   portable to EBCDIC machines, use ASCII numbers, not characters. */
+png_byte png_IHDR[4] = { 73,  72,  68,  82};
+png_byte png_IDAT[4] = { 73,  68,  65,  84};
+png_byte png_IEND[4] = { 73,  69,  78,  68};
+png_byte png_PLTE[4] = { 80,  76,  84,  69};
+png_byte png_gAMA[4] = {103,  65,  77,  65};
+png_byte png_sBIT[4] = {115,  66,  73,  84};
+png_byte png_cHRM[4] = { 99,  72,  82,  77};
+png_byte png_tRNS[4] = {116,  82,  78,  83};
+png_byte png_bKGD[4] = { 98,  75,  71,  68};
+png_byte png_hIST[4] = {104,  73,  83,  84};
+png_byte png_tEXt[4] = {116,  69,  88, 116};
+png_byte png_zTXt[4] = {122,  84,  88, 116};
+png_byte png_pHYs[4] = {112,  72,  89, 115};
+png_byte png_oFFs[4] = {111,  70,  70, 115};
+png_byte png_tIME[4] = {116,  73,  77,  69};
+
+/* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+/* start of interlace block */
+int png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
+
+/* offset to next interlace block */
+int png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
+
+/* start of interlace block in the y direction */
+int png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
+
+/* offset to next interlace block in the y direction */
+int png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
+
+/* width of interlace block */
+/* this is not currently used - if you need it, uncomment it here and
+   in png.h
+int png_pass_width[] = {8, 4, 4, 2, 2, 1, 1};
+*/
+
+/* height of interlace block */
+/* this is not currently used - if you need it, uncomment it here and
+   in png.h
+int png_pass_height[] = {8, 8, 4, 4, 4, 2, 2, 1};
+*/
+
+/* mask to determine which pixels are valid in a pass */
+int png_pass_mask[] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff};
+
+/* mask to determine which pixels to overwrite while displaying */
+int png_pass_dsp_mask[] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff};
+
+
+int
+png_check_sig(png_byte *sig, int num)
+{
+   if (num > 8)
+      num = 8;
+   if (num < 1)
+      return 0;
+
+   return (!memcmp(sig, png_sig, num));
+}
+
+/* Function to allocate memory for zlib. */
+voidp
+png_zalloc(voidp png_ptr, uInt items, uInt size)
+{
+   return ((voidp)png_large_malloc((png_struct *)png_ptr,
+      (png_uint_32)items * (png_uint_32)size));
+}
+
+/* function to free memory for zlib */
+void
+png_zfree(voidp png_ptr, voidp ptr)
+{
+   png_large_free((png_struct *)png_ptr, (void *)ptr);
+}
+
+/* reset the crc variable to 32 bits of 1's.  Care must be taken
+   in case crc is > 32 bits to leave the top bits 0 */
+void
+png_reset_crc(png_struct *png_ptr)
+{
+   /* set crc to all 1's */
+   png_ptr->crc = 0xffffffffL;
+}
+
+/* Note: the crc code below was copied from the sample code in the
+   PNG spec, with appropriate modifications made to ensure the
+   variables are large enough */
+
+/* table of crc's of all 8-bit messages.  If you wish to png_malloc this
+   table, turn this into a pointer, and png_malloc it in make_crc_table().
+   You may then want to hook it into png_struct and free it with the
+   destroy functions. */
+static png_uint_32 crc_table[256];
+
+/* Flag: has the table been computed? Initially false. */
+static int crc_table_computed = 0;
+
+/* make the table for a fast crc */
+static void
+make_crc_table(void)
+{
+  png_uint_32 c;
+  int n, k;
+
+  for (n = 0; n < 256; n++)
+  {
+   c = (png_uint_32)n;
+   for (k = 0; k < 8; k++)
+     c = c & 1 ? 0xedb88320L ^ (c >> 1) : c >> 1;
+   crc_table[n] = c;
+  }
+  crc_table_computed = 1;
+}
+
+/* update a running crc with the bytes buf[0..len-1]--the crc should be
+   initialized to all 1's, and the transmitted value is the 1's complement
+   of the final running crc. */
+static png_uint_32
+update_crc(png_uint_32 crc, png_byte *buf, png_uint_32 len)
+{
+  png_uint_32 c;
+  png_byte *p;
+  png_uint_32 n;
+
+  c = crc;
+  p = buf;
+  n = len;
+
+  if (!crc_table_computed)
+  {
+   make_crc_table();
+  }
+
+  if (n > 0) do
+  {
+   c = crc_table[(png_byte)((c ^ (*p++)) & 0xff)] ^ (c >> 8);
+  } while (--n);
+
+  return c;
+}
+
+/* calculate the crc over a section of data.  Note that while we
+   are passing in a 32 bit value for length, on 16 bit machines, you
+   would need to use huge pointers to access all that data.  If you
+   need this, put huge here and above. */
+void
+png_calculate_crc(png_struct *png_ptr, png_byte *ptr,
+   png_uint_32 length)
+{
+   png_ptr->crc = update_crc(png_ptr->crc, ptr, length);
+}
+
+
diff --git a/png.h b/png.h
new file mode 100644
index 0000000..b0fc8e5
--- /dev/null
+++ b/png.h
@@ -0,0 +1,913 @@
+
+/* png.h - header file for png reference library
+   libpng 1.0 beta 1 - version 0.71
+   June 26, 1995
+
+   Note: This is a beta version.  It reads and writes valid files
+   on the platforms I have, but it has had limited portability
+   testing.  Furthermore, you will probably have to modify the
+   includes below to get it to work on your system, and you
+   may have to supply the correct compiler flags in the makefile.
+   Read the readme.txt for more information, and how to contact
+   me if you have any problems, or if you want your compiler/
+   platform to be supported in the next official libpng release.
+
+   See readme.txt for more information
+
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   Contributing Authors:
+      Guy Eric Schalnat
+
+   The PNG Reference Library is supplied "AS IS". The Contributing Authors
+   and Group 42, Inc. disclaim all warranties, expressed or implied,
+   including, without limitation, the warranties of merchantability and of
+   fitness for any purpose. The Contributing Authors and Group 42, Inc.
+   assume no liability for damages, direct or consequential, which may
+   result from the use of the PNG Reference Library.
+
+   Permission is hereby granted to use, copy, modify, and distribute this
+   source code, or portions hereof, for any purpose, without fee, subject
+   to the following restrictions:
+   1. The origin of this source code must not be misrepresented.
+   2. Altered versions must be plainly marked as such and must not be
+     misrepresented as being the original source.
+   3. This Copyright notice may not be removed or altered from any source or
+     altered source distribution.
+
+   The Contributing Authors and Group 42, Inc. specifically permit, without
+   fee, and encourage the use of this source code as a component to
+   supporting the PNG file format in commercial products. If you use this
+   source code in a product, acknowledgment is not required but would be
+   appreciated.
+   */
+
+#ifndef _PNG_H
+#define _PNG_H
+
+/* This is not the place to learn how to use libpng.  The file libpng.txt
+   describes how to use libpng, and the file example.c summarizes it
+   with some code to build around.  This file is useful for looking
+   at the actual function definitions and structure components. */
+
+/* This file is arranged in several sections.  The first section contains
+   all the definitions for libpng.  The second section details the functions
+   most users will use.  The third section describes the stub files that
+   users will most likely need to change.  The last section contains
+   functions used internally by the code.
+
+   Any machine specific code is near the front of this file, so if you
+   are configuring libpng for a machine, you may want to read the section
+   starting here down to where it starts to typedef png_color, png_text,
+   and png_info */
+
+/* this is the size of the compression buffer, and thus the size of
+   an IDAT chunk.  Make this whatever size you feel is best for your
+   machine.  One of these will be allocated per png_struct.  When this
+   is full, it writes the data to the disk, and does some other
+   calculations.  Making this an extreamly small size will slow
+   the library down, but you may want to experiment to determine
+   where it becomes significant, if you are concerned with memory
+   usage.  Note that zlib allocates at least 32Kb also.  For readers,
+   this describes the size of the buffer available to read the data in.
+   Unless this gets smaller then the size of a row (compressed),
+   it should not make much difference how big this is.  */
+
+#define PNG_ZBUF_SIZE 8192;
+
+/* include the compression library's header */
+#include "zlib.h"
+
+/* While libpng currently uses zlib for it's compression, it has been designed
+   to stand on it's own.  Towards this end, there are two defines that are
+   used to help portability between machines.  To make it simpler to
+   setup libpng on a machine, this currently uses zlib's definitions, so
+   any changes should be made in zlib.  Libpng will check zlib's settings
+   and adjust it's own accordingly. */
+
+/* if you are running on a machine where you cannot allocate more then
+   64K of memory, uncomment this.  While libpng will not normally need
+   that much memory in a chunk (unless you load up a very large file),
+   zlib needs to know how big of a chunk it can use, and libpng thus
+   makes sure to check any memory allocation to verify it will fit
+   into memory.
+#define PNG_MAX_ALLOC_64K
+*/
+#ifdef MAXSEG_64K
+#define PNG_MAX_ALLOC_64K
+#endif
+
+/* this macro protects us against machines that don't have function
+   prototypes.  If your compiler does not handle function prototypes,
+   define this macro.  I've always been able to use _NO_PROTO as the
+   indicator, but you may need to drag the empty declaration out in
+   front of here, or change the ifdef to suit your own needs. */
+#ifndef PNGARG
+
+#ifdef __P
+#define PNGARG(arglist) __P(arglist)
+#else
+
+#ifdef _NO_PROTO
+#define PNGARG(arglist)
+#else
+#define PNGARG(arglist) arglist
+#endif /* _NO_PROTO */
+
+#endif /* __P(arglist) */
+
+#endif /* PNGARG */
+
+/* enough people need this for various reasons to include it here */
+#include <sys/types.h>
+/* need the time information for reading tIME chunks */
+#include <time.h>
+
+/* for FILE.  If you are not using standard io, you don't need this */
+#include <stdio.h>
+
+/* include setjmp.h for error handling */
+#include <setjmp.h>
+
+/* other defines for things like memory and the like can go here.  These
+   are the only files included in libpng, so if you need to change them,
+   change them here.  They are only included if PNG_INTERNAL is defined. */
+#ifdef PNG_INTERNAL
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef BSD
+#include <strings.h>
+#else
+#include <string.h>
+#endif
+#include <math.h>
+
+/* other defines specific to compilers can go here.  Try to keep
+   them inside an appropriate ifdef/endif pair for portability */
+
+/* for some reason, Borland C++ defines memcmp, etc. in mem.h, not
+   stdlib.h like it should (I think).  Or perhaps this is a C++
+   feature */
+#ifdef __TURBOC__
+#include <mem.h>
+#include "alloc.h"
+#endif
+
+#ifdef _MSC_VER
+#include <malloc.h>
+#endif
+
+/* this controls how fine the dithering gets.  As this allocates
+   a largish chunk of memory (32K), those who are not as concerned
+   with dithering quality can decrease some or all of these */
+#define PNG_DITHER_RED_BITS 5
+#define PNG_DITHER_GREEN_BITS 5
+#define PNG_DITHER_BLUE_BITS 5
+
+/* this controls how fine the gamma correction becomes when you
+   are only interested in 8 bits anyway.  Increasing this value
+   results in more memory being used, and more pow() functions
+   being called to fill in the gamma tables.  Don't get this
+   value less then 8, and even that may not work (I haven't tested
+   it). */
+
+#define PNG_MAX_GAMMA_8 11
+
+#endif /* PNG_INTERNAL */
+
+/* some typedefs to get us started.  These should be safe on most of the
+   common platforms.  The typedefs should be at least as large
+   as the numbers suggest (a png_uint_32 must be at least 32 bits long),
+   but they don't have to be exactly that size. */
+
+typedef unsigned long png_uint_32;
+typedef long png_int_32;
+typedef unsigned short png_uint_16;
+typedef short png_int_16;
+typedef unsigned char png_byte;
+
+/* this is usually size_t. it is typedef'ed just in case you need it to
+   change (I'm not sure if you will or not, so I thought I'd be safe) */
+typedef size_t png_size_t;
+
+/* three color definitions.  The order of the red, green, and blue, (and the
+   exact size) is not important, although the size of the fields need to
+   be png_byte or png_uint_16 (as defined below).  While png_color_8 and
+   png_color_16 have more fields then they need, they are never used in
+   arrays, so the size isn't that important.  I thought about using
+   unions, but it looked too clumsy, so I left it. If you're using C++,
+   you can union red, index, and gray, if you want. */
+typedef struct png_color_struct
+{
+   png_byte red;
+   png_byte green;
+   png_byte blue;
+} png_color;
+
+typedef struct png_color_16_struct
+{
+   png_byte index; /* used for palette files */
+   png_uint_16 red; /* for use in red green blue files */
+   png_uint_16 green;
+   png_uint_16 blue;
+   png_uint_16 gray; /* for use in grayscale files */
+} png_color_16;
+
+typedef struct png_color_8_struct
+{
+   png_byte red; /* for use in red green blue files */
+   png_byte green;
+   png_byte blue;
+   png_byte gray; /* for use in grayscale files */
+   png_byte alpha; /* for alpha channel files */
+} png_color_8;
+
+/* png_text holds the text in a png file, and whether they are compressed
+   or not.  If compression is -1, the text is not compressed.  */
+typedef struct png_text_struct
+{
+   int compression; /* compression value, -1 if uncompressed */
+   char *key; /* keyword */
+   char *text; /* comment */
+   png_uint_32 text_length; /* length of text field */
+} png_text;
+
+/* png_time is a way to hold the time in an machine independent way.
+   Two conversions are provided, both from time_t and struct tm.  There
+   is no portable way to convert to either of these structures, as far
+   as I know.  If you know of a portable way, send it to me. */
+typedef struct png_time_struct
+{
+   png_uint_16 year; /* full year, as in, 1995 */
+   png_byte month; /* month of year, 1 - 12 */
+   png_byte day; /* day of month, 1 - 31 */
+   png_byte hour; /* hour of day, 0 - 23 */
+   png_byte minute; /* minute of hour, 0 - 59 */
+   png_byte second; /* second of minute, 0 - 60 (for leap seconds) */
+} png_time;
+
+/* png_info is a structure that holds the information in a png file.
+   If you are reading the file, This structure will tell you what is
+   in the png file.  If you are writing the file, fill in the information
+   you want to put into the png file, then call png_write_info().
+   The names chosen should be very close to the PNG
+   specification, so consult that document for information
+   about the meaning of each field. */
+typedef struct png_info_struct
+{
+   /* the following are necessary for every png file */
+   png_uint_32 width; /* with of file */
+   png_uint_32 height; /* height of file */
+   png_byte bit_depth; /* 1, 2, 4, 8, or 16 */
+   png_byte color_type; /* use the PNG_COLOR_TYPE_ defines */
+   png_byte compression_type; /* must be 0 */
+   png_byte filter_type; /* must be 0 */
+   png_byte interlace_type; /* 0 for non-interlaced, 1 for interlaced */
+   png_uint_32 valid; /* the PNG_INFO_ defines, OR'd together */
+   /* the following is informational only on read, and not used on
+      writes */
+   png_byte channels; /* number of channels of data per pixel */
+   png_byte pixel_depth; /* number of bits per pixel */
+   png_uint_32 rowbytes; /* bytes needed for untransformed row */
+   /* the rest are optional.  If you are reading, check the valid
+      field to see if the information in these are valid.  If you
+      are writing, set the valid field to those chunks you want
+      written, and initialize the appropriate fields below */
+   float gamma; /* gamma value of file, if gAMA chunk is valid */
+   png_color_8 sig_bit; /* significant bits */
+   float x_white; /* cHRM chunk values */
+   float y_white;
+   float x_red;
+   float y_red;
+   float x_green;
+   float y_green;
+   float x_blue;
+   float y_blue;
+   png_color *palette; /* palette of file */
+   png_uint_16 num_palette; /* number of values in palette */
+   png_byte *trans; /* tRNS values for palette image */
+    png_uint_16 num_trans; /* number of trans values */
+   png_color_16 trans_values; /* tRNS values for non-palette image */
+   png_color_16 background; /* background color of image */
+   png_uint_16 *hist; /* histogram of palette usage */
+   png_uint_32 x_pixels_per_unit; /* x resolution */
+   png_uint_32 y_pixels_per_unit; /* y resolution */
+   png_byte phys_unit_type; /* resolution type */
+   png_uint_32 x_offset; /* x offset on page */
+   png_uint_32 y_offset; /* y offset on page */
+   png_byte offset_unit_type; /* offset units type */
+   png_time mod_time; /* modification time */
+   int num_text; /* number of comments */
+   int max_text; /* size of text array */
+   png_text *text; /* array of comments */
+} png_info;
+
+#define PNG_RESOLUTION_UNKNOWN 0
+#define PNG_RESOLUTION_METER 1
+
+#define PNG_OFFSET_PIXEL 0
+#define PNG_OFFSET_MICROMETER 1
+
+/* these describe the color_type field in png_info */
+
+/* color type masks */
+#define PNG_COLOR_MASK_PALETTE 1
+#define PNG_COLOR_MASK_COLOR 2
+#define PNG_COLOR_MASK_ALPHA 4
+
+/* color types.  Note that not all combinations are legal */
+#define PNG_COLOR_TYPE_PALETTE \
+   (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
+#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR)
+#define PNG_COLOR_TYPE_GRAY 0
+#define PNG_COLOR_TYPE_RGB_ALPHA \
+   (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
+#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA)
+
+/* These determine if a chunks information is present in a read operation, or
+   if the chunk should be written in a write operation.  */
+#define PNG_INFO_gAMA 0x0001
+#define PNG_INFO_sBIT 0x0002
+#define PNG_INFO_cHRM 0x0004
+#define PNG_INFO_PLTE 0x0008
+#define PNG_INFO_tRNS 0x0010
+#define PNG_INFO_bKGD 0x0020
+#define PNG_INFO_hIST 0x0040
+#define PNG_INFO_pHYs 0x0080
+#define PNG_INFO_oFFs 0x0100
+#define PNG_INFO_tIME 0x0200
+
+/* this is used for the transformation routines, as some of them
+   change these values for the row.  It also should enable using
+   the routines for other uses. */
+typedef struct png_row_info_struct
+{
+   png_uint_32 width; /* width of row */
+   png_uint_32 rowbytes; /* number of bytes in row */
+   png_byte color_type; /* color type of row */
+   png_byte bit_depth; /* bit depth of row */
+   png_byte channels; /* number of channels (1, 2, 3, or 4) */
+   png_byte pixel_depth; /* bits per pixel (depth * channels) */
+} png_row_info;
+
+/* The structure that holds the information to read and write png files.
+   The only people who need to care about what is inside of this are the
+   people who will be modifying the library for their own special needs.
+   */
+typedef struct png_struct_def
+{
+   jmp_buf jmpbuf; /* used in png_error */
+   png_byte mode; /* used to determine where we are in the png file */
+   png_byte color_type; /* color type of file */
+   png_byte bit_depth; /* bit depth of file */
+   png_byte interlaced; /* interlace type of file */
+   png_byte compession; /* compression type of file */
+   png_byte filter; /* filter type */
+   png_byte channels; /* number of channels in file */
+   png_byte pixel_depth; /* number of bits per pixel */
+   png_byte usr_bit_depth; /* bit depth of users row */
+   png_byte usr_channels; /* channels at start of write */
+   png_byte gamma_shift; /* amount of shift for 16 bit gammas */
+   png_byte pass; /* current pass (0 - 6) */
+   png_byte row_init; /* 1 if png_read_start_row() has been called */
+   png_byte background_gamma_type;
+   png_byte background_expand;
+   png_byte zlib_finished;
+   png_byte user_palette;
+   png_uint_16 num_palette; /* number of entries in palette */
+   png_uint_16 num_trans; /* number of transparency values */
+   png_uint_32 transformations; /* which transformations to perform */
+   png_uint_32 crc; /* current crc value */
+   png_uint_32 width; /* width of file */
+   png_uint_32 height; /* height of file */
+   png_uint_32 num_rows; /* number of rows in current pass */
+   png_uint_32 rowbytes; /* size of row in bytes */
+   png_uint_32 usr_width; /* width of row at start of write */
+   png_uint_32 iwidth; /* interlaced width */
+   png_uint_32 irowbytes; /* interlaced rowbytes */
+   png_uint_32 row_number; /* current row in pass */
+   png_uint_32 idat_size; /* current idat size for read */
+   png_uint_32 zbuf_size; /* size of zbuf */
+   png_color *palette; /* files palette */
+   png_byte *palette_lookup; /* lookup table for dithering */
+   png_byte *gamma_table; /* gamma table for 8 bit depth files */
+   png_byte *gamma_from_1; /* converts from 1.0 to screen */
+   png_byte *gamma_to_1; /* converts from file to 1.0 */
+   png_byte *trans; /* transparency values for paletted files */
+   png_byte *dither_index; /* index translation for palette files */
+   png_uint_16 **gamma_16_table; /* gamma table for 16 bit depth files */
+   png_uint_16 **gamma_16_from_1; /* converts from 1.0 to screen */
+   png_uint_16 **gamma_16_to_1; /* converts from file to 1.0 */
+   png_uint_16 *hist; /* histogram */
+   png_byte *zbuf; /* buffer for zlib */
+   png_byte *row_buf; /* row buffer */
+   png_byte *prev_row; /* previous row */
+   png_byte *save_row; /* place to save row before filtering */
+   z_stream *zstream; /* pointer to decompression structure (below) */
+   float gamma; /* file gamma value */
+   float display_gamma; /* display gamma value */
+   float background_gamma;
+   png_color_8 shift; /* shift for significant bit tranformation */
+   png_color_8 sig_bit; /* significant bits in file */
+   png_color_16 trans_values; /* transparency values for non-paletted files */
+   png_color_16 background; /* background color, gamma corrected for screen */
+   png_color_16 background_1; /* background normalized to gamma 1.0 */
+   png_row_info row_info; /* used for transformation routines */
+   z_stream zstream_struct; /* decompression structure */
+   FILE *fp; /* used for png_read and png_write */
+} png_struct;
+
+
+/* Here are the function definitions most commonly used.  This is not
+   the place to find out how to use libpng.  See libpng.txt for the
+   full explanation, see example.c for the summary.  This just provides
+   a simple one line of the use of each function. */
+
+/* check the first 1 - 8 bytes to see if it is a png file */
+extern int png_check_sig PNGARG((png_byte *sig, int num));
+
+/* initialize png structure for reading, and allocate any memory needed */
+extern void png_read_init PNGARG((png_struct *png_ptr));
+
+/* initialize png structure for writing, and allocate any memory needed */
+extern void png_write_init PNGARG((png_struct *png_ptr));
+
+/* initialize the info structure */
+extern void png_info_init PNGARG((png_info *info));
+
+/* Writes all the png information before the image. */
+extern void png_write_info PNGARG((png_struct *png_ptr, png_info *info));
+
+/* read the information before the actual image data. */
+extern void png_read_info PNGARG((png_struct *png_ptr, png_info *info));
+
+/* convert from a struct tm to png_time */
+extern void png_convert_from_struct_tm PNGARG((png_time *ptime,
+   struct tm *ttime));
+
+/* convert from time_t to png_time.  Uses gmtime() */
+extern void png_convert_from_time_t PNGARG((png_time *ptime, time_t ttime));
+
+/* Expand the data to 24 bit RGB, or 8 bit Grayscale,
+   with alpha if necessary. */
+extern void png_set_expand PNGARG((png_struct *png_ptr));
+
+/* Use blue, green, red order for pixels. */
+extern void png_set_bgr PNGARG((png_struct *png_ptr));
+
+/* Add a filler byte to rgb images after the colors. */
+extern void png_set_rgbx PNGARG((png_struct *png_ptr));
+
+/* Add a filler byte to rgb images before the colors. */
+extern void png_set_xrgb PNGARG((png_struct *png_ptr));
+
+/* Swap bytes in 16 bit depth files. */
+extern void png_set_swap PNGARG((png_struct *png_ptr));
+
+/* Use 1 byte per pixel in 1, 2, or 4 bit depth files. */
+extern void png_set_packing PNGARG((png_struct *png_ptr));
+
+/* Converts files to legal bit depths. */
+extern void png_set_shift PNGARG((png_struct *png_ptr,
+   png_color_8 *true_bits));
+
+/* Have the code handle the interlacing.  Returns the number of passes. */
+extern int png_set_interlace_handling PNGARG((png_struct *png_ptr));
+
+/* Invert monocrome files */
+extern void png_set_invert_mono PNGARG((png_struct *png_ptr));
+
+/* Handle alpha and tRNS by replacing with a background color. */
+#define PNG_BACKGROUND_GAMMA_SCREEN 0
+#define PNG_BACKGROUND_GAMMA_FILE 1
+#define PNG_BACKGROUND_GAMMA_UNIQUE 2
+#define PNG_BACKGROUND_GAMMA_UNKNOWN 3
+extern void png_set_background PNGARG((png_struct *png_ptr,
+   png_color_16 *background_color, int background_gamma_code,
+   int need_expand, float background_gamma));
+
+/* strip the second byte of information from a 16 bit depth file. */
+extern void png_set_strip_16 PNGARG((png_struct *png_ptr));
+
+/* convert a grayscale file into rgb. */
+extern void png_set_gray_to_rgb PNGARG((png_struct *png_ptr));
+
+/* Turn on dithering, and reduce the palette to the number of colors available. */
+extern void png_set_dither PNGARG((png_struct *png_ptr, png_color *palette,
+   int num_palette, int maximum_colors, png_uint_16 *histogram, int full_dither));
+
+/* Handle gamma correction. */
+extern void png_set_gamma PNGARG((png_struct *png_ptr, float screen_gamma,
+   float default_file_gamma));
+
+/* optional update palette with requested transformations */
+void png_start_read_image PNGARG((png_struct *png_ptr));
+
+/* read a one or more rows of image data.*/
+extern void png_read_rows PNGARG((png_struct *png_ptr,
+   png_byte **row,
+   png_byte **display_row, png_uint_32 num_rows));
+
+/* read a row of data.*/
+extern void png_read_row PNGARG((png_struct *png_ptr,
+   png_byte *row,
+   png_byte *display_row));
+
+/* read the whole image into memory at once. */
+extern void png_read_image PNGARG((png_struct *png_ptr,
+   png_byte **image));
+
+/* write a row of image data */
+extern void png_write_row PNGARG((png_struct *png_ptr,
+   png_byte *row));
+
+/* write a few rows of image data */
+extern void png_write_rows PNGARG((png_struct *png_ptr,
+   png_byte **row,
+   png_uint_32 num_rows));
+
+/* write the image data */
+extern void png_write_image PNGARG((png_struct *png_ptr, png_byte **image));
+
+/* writes the end of the png file. */
+extern void png_write_end PNGARG((png_struct *png_ptr, png_info *info));
+
+/* read the end of the png file. */
+extern void png_read_end PNGARG((png_struct *png_ptr, png_info *info));
+
+/* free all memory used by the read */
+extern void png_read_destroy PNGARG((png_struct *png_ptr, png_info *info,
+   png_info *end_info));
+
+/* free any memory used in png struct */
+extern void png_write_destroy PNGARG((png_struct *png_ptr));
+
+
+/* These next functions are stubs of typical c functions for input/output,
+   memory, and error handling.  They are in the file pngstub.c, and are
+   set up to be easily modified for users that need to.  See the file
+   pngstub.c for more information */
+
+/* Write the data to whatever output you are using. */
+extern void png_write_data PNGARG((png_struct *png_ptr, png_byte *data,
+   png_uint_32 length));
+
+/* Read data from whatever input you are using */
+extern void png_read_data PNGARG((png_struct *png_ptr, png_byte *data,
+   png_uint_32 length));
+
+/* Initialize the input/output for the png file. */
+extern void png_init_io PNGARG((png_struct *png_ptr, FILE *fp));
+
+/* Allocate memory in larger chunks. */
+extern void *png_large_malloc PNGARG((png_struct *png_ptr, png_uint_32 size));
+
+/* free's a pointer allocated by png_large_malloc() */
+extern void png_large_free PNGARG((png_struct *png_ptr, void *ptr));
+
+/* Allocate memory. */
+extern void *png_malloc PNGARG((png_struct *png_ptr, png_uint_32 size));
+
+/* Reallocate memory. */
+extern void *png_realloc PNGARG((png_struct *png_ptr, void *ptr,
+   png_uint_32 size));
+
+/* free's a pointer allocated by png_malloc() */
+extern void png_free PNGARG((png_struct *png_ptr, void *ptr));
+
+/* Fatal error in libpng - can't continue */
+extern void png_error PNGARG((png_struct *png_ptr, char *error));
+
+/* Non-fatal error in libpng.  Can continue, but may have a problem. */
+extern void png_warning PNGARG((png_struct *png_ptr, char *message));
+
+
+/* These next functions are used internally in the code.  If you use
+   them, make sure you read and understand the png spec.  More information
+   about them can be found in the files where the functions are.
+   Feel free to move any of these outside the PNG_INTERNAL define if
+   you just need a few of them, but if you need access to more, you should
+   define PNG_INTERNAL inside your code, so everyone who includes png.h
+   won't get yet another definition the compiler has to deal with. */
+
+#ifdef PNG_INTERNAL
+
+/* various modes of operation.  Note that after an init, mode is set to
+   zero automatically */
+#define PNG_BEFORE_IHDR 0
+#define PNG_HAVE_IHDR 1
+#define PNG_HAVE_PLTE 2
+#define PNG_HAVE_IDAT 3
+#define PNG_AT_LAST_IDAT 4
+#define PNG_AFTER_IDAT 5
+#define PNG_AFTER_IEND 6
+
+/* defines for the transformations the png library does on the image data */
+#define PNG_BGR 0x0001
+#define PNG_INTERLACE 0x0002
+#define PNG_PACK 0x0004
+#define PNG_SHIFT 0x0008
+#define PNG_SWAP_BYTES 0x0010
+#define PNG_INVERT_MONO 0x0020
+#define PNG_DITHER 0x0040
+#define PNG_BACKGROUND 0x0080
+#define PNG_XRGB 0x0100
+#define PNG_16_TO_8 0x0200
+#define PNG_RGBA 0x0400
+#define PNG_EXPAND 0x0800
+#define PNG_GAMMA 0x1000
+#define PNG_GRAY_TO_RGB 0x2000
+
+/* save typing and make code easier to understand */
+#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \
+   abs((int)((c1).green) - (int)((c2).green)) + \
+   abs((int)((c1).blue) - (int)((c2).blue)))
+
+/* variables defined in png.c - only it needs to define PNG_NO_EXTERN */
+#ifndef PNG_NO_EXTERN
+/* place to hold the signiture string for a png file. */
+extern png_byte png_sig[];
+
+/* constant strings for known chunk types.  If you need to add a chunk,
+   add a string holding the name here.  See png.c for more details */
+extern png_byte png_IHDR[];
+extern png_byte png_IDAT[];
+extern png_byte png_IEND[];
+extern png_byte png_PLTE[];
+extern png_byte png_gAMA[];
+extern png_byte png_sBIT[];
+extern png_byte png_cHRM[];
+extern png_byte png_tRNS[];
+extern png_byte png_bKGD[];
+extern png_byte png_hIST[];
+extern png_byte png_tEXt[];
+extern png_byte png_zTXt[];
+extern png_byte png_pHYs[];
+extern png_byte png_oFFs[];
+extern png_byte png_tIME[];
+/* Structures to facilitate easy interlacing.  See png.c for more details */
+extern int png_pass_start[];
+extern int png_pass_inc[];
+extern int png_pass_ystart[];
+extern int png_pass_yinc[];
+/* these are not currently used.  If you need them, see png.c
+extern int png_pass_width[];
+extern int png_pass_height[];
+*/
+extern int png_pass_mask[];
+extern int png_pass_dsp_mask[];
+
+#endif /* PNG_NO_EXTERN */
+
+/* Function to allocate memory for zlib. */
+extern voidp png_zalloc PNGARG((voidp png_ptr, uInt items, uInt size));
+
+/* function to free memory for zlib */
+extern void png_zfree PNGARG((voidp png_ptr, voidp ptr));
+
+/* reset the crc variable */
+extern void png_reset_crc PNGARG((png_struct *png_ptr));
+
+/* calculate the crc over a section of data.  Note that while we
+   are passing in a 32 bit value for length, on 16 bit machines, you
+   would need to use huge pointers to access all that data.  See the
+   code in png.c for more information. */
+extern void png_calculate_crc PNGARG((png_struct *png_ptr, png_byte *ptr,
+   png_uint_32 length));
+
+/* place a 32 bit number into a buffer in png byte order.  We work
+   with unsigned numbers for convenience, you may have to cast
+   signed numbers (if you use any, most png data is unsigned). */
+extern void png_save_uint_32 PNGARG((png_byte *buf, png_uint_32 i));
+
+/* place a 16 bit number into a buffer in png byte order */
+extern void png_save_uint_16 PNGARG((png_byte *buf, png_uint_16 i));
+
+/* write a 32 bit number */
+extern void png_write_uint_32 PNGARG((png_struct *png_ptr, png_uint_32 i));
+
+/* write a 16 bit number */
+extern void png_write_uint_16 PNGARG((png_struct *png_ptr, png_uint_16 i));
+
+/* Write a png chunk.  */
+extern void png_write_chunk PNGARG((png_struct *png_ptr, png_byte *type,
+   png_byte *data, png_uint_32 length));
+
+/* Write the start of a png chunk. */
+extern void png_write_chunk_start PNGARG((png_struct *png_ptr, png_byte *type,
+   png_uint_32 total_length));
+
+/* write the data of a png chunk started with png_write_chunk_start(). */
+extern void png_write_chunk_data PNGARG((png_struct *png_ptr, png_byte *data,
+   png_uint_32 length));
+
+/* finish a chunk started with png_write_chunk_start() */
+extern void png_write_chunk_end PNGARG((png_struct *png_ptr));
+
+/* simple function to write the signiture */
+extern void png_write_sig PNGARG((png_struct *png_ptr));
+
+/* write various chunks */
+
+/* Write the IHDR chunk, and update the png_struct with the necessary
+   information. */
+extern void png_write_IHDR PNGARG((png_struct *png_ptr, png_uint_32 width,
+   png_uint_32 height,
+   int bit_depth, int color_type, int compression_type, int filter_type,
+   int interlace_type));
+
+extern void png_write_PLTE PNGARG((png_struct *png_ptr, png_color *palette,
+   int number));
+
+extern void png_write_IDAT PNGARG((png_struct *png_ptr, png_byte *data,
+   png_uint_32 length));
+
+extern void png_write_IEND PNGARG((png_struct *png_ptr));
+
+extern void png_write_gAMA PNGARG((png_struct *png_ptr, float gamma));
+
+extern void png_write_sBIT PNGARG((png_struct *png_ptr, png_color_8 *sbit,
+   int color_type));
+
+extern void png_write_cHRM PNGARG((png_struct *png_ptr,
+   float white_x, float white_y,
+   float red_x, float red_y, float green_x, float green_y,
+   float blue_x, float blue_y));
+
+extern void png_write_tRNS PNGARG((png_struct *png_ptr, png_byte *trans,
+   png_color_16 *values, int number, int color_type));
+
+extern void png_write_bKGD PNGARG((png_struct *png_ptr, png_color_16 *values,
+   int color_type));
+
+extern void png_write_hIST PNGARG((png_struct *png_ptr, png_uint_16 *hist,
+   int number));
+
+extern void png_write_tEXt PNGARG((png_struct *png_ptr, char *key,
+   char *text, png_uint_32 text_len));
+
+extern void png_write_zTXt PNGARG((png_struct *png_ptr, char *key,
+   char *text, png_uint_32 text_len, int compression));
+
+extern void png_write_pHYs PNGARG((png_struct *png_ptr,
+   png_uint_32 x_pixels_per_unit,
+   png_uint_32 y_pixels_per_unit,
+   int unit_type));
+
+extern void png_write_oFFs PNGARG((png_struct *png_ptr,
+   png_uint_32 x_offset,
+   png_uint_32 y_offset,
+   int unit_type));
+
+extern void png_write_tIME PNGARG((png_struct *png_ptr, png_time *mod_time));
+
+/* Internal use only.   Called when finished processing a row of data */
+extern void png_write_finish_row PNGARG((png_struct *png_ptr));
+
+/* Internal use only.   Called before first row of data */
+extern void png_write_start_row PNGARG((png_struct *png_ptr));
+
+/* callbacks for png chunks */
+extern void png_read_IHDR PNGARG((png_struct *png_ptr, png_info *info,
+   png_uint_32 width, png_uint_32 height, int bit_depth,
+   int color_type, int compression_type, int filter_type,
+   int interlace_type));
+extern void png_read_PLTE PNGARG((png_struct *png_ptr, png_info *info,
+   png_color *palette, int num));
+extern void png_read_gAMA PNGARG((png_struct *png_ptr, png_info *info,
+   float gamma));
+extern void png_read_sBIT PNGARG((png_struct *png_ptr, png_info *info,
+   png_color_8 *sig_bit));
+extern void png_read_cHRM PNGARG((png_struct *png_ptr, png_info *info,
+   float white_x, float white_y, float red_x, float red_y,
+   float green_x, float green_y, float blue_x, float blue_y));
+extern void png_read_tRNS PNGARG((png_struct *png_ptr, png_info *info,
+   png_byte *trans, int num_trans,   png_color_16 *trans_values));
+extern void png_read_bKGD PNGARG((png_struct *png_ptr, png_info *info,
+   png_color_16 *background));
+extern void png_read_hIST PNGARG((png_struct *png_ptr, png_info *info,
+   png_uint_16 *hist));
+extern void png_read_pHYs PNGARG((png_struct *png_ptr, png_info *info,
+   png_uint_32 res_x, png_uint_32 res_y, int unit_type));
+extern void png_read_oFFs PNGARG((png_struct *png_ptr, png_info *info,
+   png_uint_32 offset_x, png_uint_32 offset_y, int unit_type));
+extern void png_read_tIME PNGARG((png_struct *png_ptr, png_info *info,
+   png_time *mod_time));
+extern void png_read_tEXt PNGARG((png_struct *png_ptr, png_info *info,
+   char *key, char *text, png_uint_32 text_len));
+extern void png_read_zTXt PNGARG((png_struct *png_ptr, png_info *info,
+   char *key, char *text, png_uint_32 text_len, int compression));
+
+void
+png_build_gamma_table PNGARG((png_struct *png_ptr));
+
+/* combine a row of data, dealing with alpha, etc. if requested */
+extern void png_combine_row PNGARG((png_struct *png_ptr, png_byte *row,
+   int mask));
+/* expand an interlaced row */
+extern void png_do_read_interlace PNGARG((png_row_info *row_info,
+   png_byte *row, int pass));
+/* grab pixels out of a row for an interlaced pass */
+extern void png_do_write_interlace PNGARG((png_row_info *row_info,
+   png_byte *row, int pass));
+
+/* unfilter a row */
+extern void png_read_filter_row PNGARG((png_row_info *row_info,
+   png_byte *row, png_byte *prev_row, int filter));
+/* filter a row, and place the correct filter byte in the row */
+extern void png_write_filter_row PNGARG((png_row_info *row_info,
+   png_byte *row, png_byte *prev_row));
+/* finish a row while reading, dealing with interlacing passes, etc. */
+extern void png_read_finish_row PNGARG((png_struct *png_ptr));
+/* initialize the row buffers, etc. */
+extern void png_read_start_row PNGARG((png_struct *png_ptr));
+
+/* these are the functions that do the transformations */
+extern void png_do_read_rgbx PNGARG((png_row_info *row_info,
+   png_byte *row));
+extern void png_do_write_rgbx PNGARG((png_row_info *row_info,
+   png_byte *row));
+extern void png_do_read_xrgb PNGARG((png_row_info *row_info,
+   png_byte *row));
+extern void png_do_write_xrgb PNGARG((png_row_info *row_info,
+   png_byte *row));
+extern void png_do_swap PNGARG((png_row_info *row_info, png_byte *row));
+extern void png_do_unpack PNGARG((png_row_info *row_info, png_byte *row));
+extern void png_do_unshift PNGARG((png_row_info *row_info, png_byte *row,
+   png_color_8 *sig_bits));
+extern void png_do_invert PNGARG((png_row_info *row_info, png_byte *row));
+extern void png_do_gray_to_rgb PNGARG((png_row_info *row_info,
+   png_byte *row));
+extern void png_do_chop PNGARG((png_row_info *row_info, png_byte *row));
+extern void png_do_dither PNGARG((png_row_info *row_info,
+   png_byte *row, png_byte *palette_lookup, png_byte *dither_lookup));
+extern void png_do_bgr PNGARG((png_row_info *row_info, png_byte *row));
+extern void png_do_pack PNGARG((png_row_info *row_info,
+   png_byte *row, png_byte bit_depth));
+extern void png_do_shift PNGARG((png_row_info *row_info, png_byte *row,
+   png_color_8 *bit_depth));
+extern void png_do_background PNGARG((png_row_info *row_info, png_byte *row,
+   png_color_16 *trans_values, png_color_16 *background,
+   png_color_16 *background_1,
+   png_byte *gamma_table, png_byte *gamma_from_1, png_byte *gamma_to_1,
+   png_uint_16 **gamma_16, png_uint_16 **gamma_16_from_1,
+   png_uint_16 **gamma_16_to_1, int gamma_shift));
+extern void png_do_gamma PNGARG((png_row_info *row_info, png_byte *row,
+   png_byte *gamma_table, png_uint_16 **gamma_16_table,
+   int gamma_shift));
+extern void png_do_expand_palette PNGARG((png_row_info *row_info,
+   png_byte *row, png_color *palette, png_byte *trans, int num_trans));
+extern void png_do_expand PNGARG((png_row_info *row_info,
+   png_byte *row, png_color_16 *trans_value));
+
+/* unpack 16 and 32 bit values from a string */
+extern png_uint_32 png_get_uint_32 PNGARG((png_byte *buf));
+extern png_uint_16 png_get_uint_16 PNGARG((png_byte *buf));
+
+/* read bytes into buf, and update png_ptr->crc */
+extern void png_crc_read PNGARG((png_struct *png_ptr, png_byte *buf,
+   png_uint_32 length));
+/* skip length bytes, and update png_ptr->crc */
+extern void png_crc_skip PNGARG((png_struct *png_ptr, png_uint_32 length));
+
+/* the following decodes the appropriate chunks, and does error correction,
+   then calls the appropriate callback for the chunk if it is valid */
+
+/* decode the IHDR chunk */
+extern void png_handle_IHDR PNGARG((png_struct *png_ptr, png_info *info,
+   png_uint_32 length));
+extern void png_handle_PLTE PNGARG((png_struct *png_ptr, png_info *info,
+   png_uint_32 length));
+extern void png_handle_gAMA PNGARG((png_struct *png_ptr, png_info *info,
+   png_uint_32 length));
+extern void png_handle_sBIT PNGARG((png_struct *png_ptr, png_info *info, 
+   png_uint_32 length));
+extern void png_handle_cHRM PNGARG((png_struct *png_ptr, png_info *info, 
+   png_uint_32 length));
+extern void png_handle_tRNS PNGARG((png_struct *png_ptr, png_info *info, 
+   png_uint_32 length));
+extern void png_handle_bKGD PNGARG((png_struct *png_ptr, png_info *info, 
+   png_uint_32 length));
+extern void png_handle_hIST PNGARG((png_struct *png_ptr, png_info *info, 
+   png_uint_32 length));
+extern void png_handle_pHYs PNGARG((png_struct *png_ptr, png_info *info, 
+   png_uint_32 length));
+extern void png_handle_oFFs PNGARG((png_struct *png_ptr, png_info *info, 
+   png_uint_32 length));
+extern void png_handle_tIME PNGARG((png_struct *png_ptr, png_info *info, 
+   png_uint_32 length));
+extern void png_handle_tEXt PNGARG((png_struct *png_ptr, png_info *info, 
+   png_uint_32 length));
+extern void png_handle_zTXt PNGARG((png_struct *png_ptr, png_info *info, 
+   png_uint_32 length));
+
+/* handle the transformations for reading and writing */
+extern void png_do_read_transformations PNGARG((png_struct *png_ptr));
+extern void png_do_write_transformations PNGARG((png_struct *png_ptr));
+
+extern void png_init_read_transformations PNGARG((png_struct *png_ptr));
+
+
+#endif /* PNG_INTERNAL */
+
+/* do not put anything past this line */
+#endif /* _PNG_H */
diff --git a/pngchang.txt b/pngchang.txt
new file mode 100644
index 0000000..878a2f8
--- /dev/null
+++ b/pngchang.txt
@@ -0,0 +1,44 @@
+pngchange.txt - changes for libpng
+
+version 0.2
+	added reader into png.h
+	fixed small problems in stub file
+version 0.3
+	added pull reader
+	split up pngwrite.c to several files
+	added pnglib.txt
+	added example.c
+	cleaned up writer, adding a few new tranformations
+	fixed some bugs in writer
+	interfaced with zlib 0.5
+	added K&R support
+	added check for 64 KB blocks for 16 bit machines
+version 0.4
+	cleaned up code and commented code
+	simplified time handling into png_time
+	created png_color_16 and png_color_8 to handle color needs
+	cleaned up color type defines
+	fixed various bugs
+	made various names more consistant
+	interfaced with zlib 0.71
+	cleaned up zTXt reader and writer (using zlib's Reset functions)
+	split transformations into pngrtran.c and pngwtran.c
+version 0.5
+	interfaced with zlib 0.8
+	fixed many reading and writing bugs
+   saved using 3 spaces instead of tabs
+version 0.6
+   added png_large_malloc() and png_large_free()
+   added png_size_t
+   cleaned up some compiler warnings
+   added png_start_read_image()
+version 0.7
+   cleaned up lots of bugs
+   finished dithering and other stuff
+   added test program
+   changed name from pnglib to libpng
+version 0.71
+   changed pngtest.png for zlib 0.93
+   fixed error in libpng.txt and example.c
+
+
diff --git a/pngrcb.c b/pngrcb.c
new file mode 100644
index 0000000..50b4301
--- /dev/null
+++ b/pngrcb.c
@@ -0,0 +1,212 @@
+/* pngrcb.c - callbacks while reading a png file
+
+   libpng 1.0 beta 1 - version 0.71
+   For conditions of distribution and use, see copyright notice in png.h
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   June 26, 1995
+   */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+void
+png_read_IHDR(png_struct *png_ptr, png_info *info,
+   png_uint_32 width, png_uint_32 height, int bit_depth,
+   int color_type, int compression_type, int filter_type,
+   int interlace_type)
+{
+   if (!png_ptr || !info)
+      return;
+
+   info->width = width;
+   info->height = height;
+   info->bit_depth = bit_depth;
+   info->color_type = color_type;
+   info->compression_type = compression_type;
+   info->filter_type = filter_type;
+   info->interlace_type = interlace_type;
+   if (info->color_type == PNG_COLOR_TYPE_PALETTE)
+      info->channels = 1;
+   else if (info->color_type & PNG_COLOR_MASK_COLOR)
+      info->channels = 3;
+   else
+      info->channels = 1;
+   if (info->color_type & PNG_COLOR_MASK_ALPHA)
+      info->channels++;
+   info->pixel_depth = info->channels * info->bit_depth;
+   info->rowbytes = ((info->width * info->pixel_depth + 7) >> 3);
+}
+
+void
+png_read_PLTE(png_struct *png_ptr, png_info *info,
+   png_color *palette, int num)
+{
+   if (!png_ptr || !info)
+      return;
+
+   info->palette = palette;
+   info->num_palette = num;
+   info->valid |= PNG_INFO_PLTE;
+}
+
+void
+png_read_gAMA(png_struct *png_ptr, png_info *info, float gamma)
+{
+   if (!png_ptr || !info)
+      return;
+
+   info->gamma = gamma;
+   info->valid |= PNG_INFO_gAMA;
+}
+
+void
+png_read_sBIT(png_struct *png_ptr, png_info *info,
+   png_color_8 *sig_bit)
+{
+   if (!png_ptr || !info)
+      return;
+
+   memcpy(&(info->sig_bit), sig_bit, sizeof (png_color_8));
+   info->valid |= PNG_INFO_sBIT;
+}
+
+void
+png_read_cHRM(png_struct *png_ptr, png_info *info,
+   float white_x, float white_y, float red_x, float red_y,
+   float green_x, float green_y, float blue_x, float blue_y)
+{
+   if (!png_ptr || !info)
+      return;
+
+   info->x_white = white_x;
+   info->y_white = white_y;
+   info->x_red = red_x;
+   info->y_red = red_y;
+   info->x_green = green_x;
+   info->y_green = green_y;
+   info->x_blue = blue_x;
+   info->y_blue = blue_y;
+   info->valid |= PNG_INFO_cHRM;
+}
+
+void
+png_read_tRNS(png_struct *png_ptr, png_info *info,
+   png_byte *trans, int num_trans,   png_color_16 *trans_values)
+{
+   if (!png_ptr || !info)
+      return;
+
+   if (trans)
+   {
+      info->trans = trans;
+   }
+   else
+   {
+      memcpy(&(info->trans_values), trans_values,
+         sizeof(png_color_16));
+   }
+   info->num_trans = num_trans;
+   info->valid |= PNG_INFO_tRNS;
+}
+
+void
+png_read_bKGD(png_struct *png_ptr, png_info *info,
+   png_color_16 *background)
+{
+   if (!png_ptr || !info)
+      return;
+
+   memcpy(&(info->background), background, sizeof(png_color_16));
+   info->valid |= PNG_INFO_bKGD;
+}
+
+void
+png_read_hIST(png_struct *png_ptr, png_info *info, png_uint_16 *hist)
+{
+   if (!png_ptr || !info)
+      return;
+
+   info->hist = hist;
+   info->valid |= PNG_INFO_hIST;
+}
+
+void
+png_read_pHYs(png_struct *png_ptr, png_info *info,
+   png_uint_32 res_x, png_uint_32 res_y, int unit_type)
+{
+   if (!png_ptr || !info)
+      return;
+
+   info->x_pixels_per_unit = res_x;
+   info->y_pixels_per_unit = res_y;
+   info->phys_unit_type = unit_type;
+   info->valid |= PNG_INFO_pHYs;
+}
+
+void
+png_read_oFFs(png_struct *png_ptr, png_info *info,
+   png_uint_32 offset_x, png_uint_32 offset_y, int unit_type)
+{
+   if (!png_ptr || !info)
+      return;
+
+   info->x_offset = offset_x;
+   info->y_offset = offset_y;
+   info->offset_unit_type = unit_type;
+   info->valid |= PNG_INFO_oFFs;
+}
+
+void
+png_read_tIME(png_struct *png_ptr, png_info *info,
+   png_time *mod_time)
+{
+   if (!png_ptr || !info)
+      return;
+
+   memcpy(&(info->mod_time), mod_time, sizeof (png_time));
+   info->valid |= PNG_INFO_tIME;
+}
+
+void
+png_read_zTXt(png_struct *png_ptr, png_info *info,
+   char *key, char *text, png_uint_32 text_len, int compression)
+{
+   if (!png_ptr || !info)
+      return;
+
+   if (info->max_text <= info->num_text)
+   {
+      if (info->text)
+      {
+         info->max_text = info->num_text + 16;
+         info->text = (png_text *)png_realloc(png_ptr,
+            info->text,
+            info->max_text * sizeof (png_text));
+      }
+      else
+      {
+         info->max_text = info->num_text + 16;
+         info->text = (png_text *)png_malloc(png_ptr,
+            info->max_text * sizeof (png_text));
+         info->num_text = 0;
+      }
+   }
+
+   info->text[info->num_text].key = key;
+   info->text[info->num_text].text = text;
+   info->text[info->num_text].text_length = text_len;
+   info->text[info->num_text].compression = compression;
+   info->num_text++;
+}
+
+void
+png_read_tEXt(png_struct *png_ptr, png_info *info,
+   char *key, char *text, png_uint_32 text_len)
+{
+   if (!png_ptr || !info)
+      return;
+
+   png_read_zTXt(png_ptr, info, key, text, text_len, -1);
+}
+
+
diff --git a/pngread.c b/pngread.c
new file mode 100644
index 0000000..e1afee6
--- /dev/null
+++ b/pngread.c
@@ -0,0 +1,642 @@
+
+/* pngread.c - read a png file
+
+   libpng 1.0 beta 1 - version 0.71
+   For conditions of distribution and use, see copyright notice in png.h
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   June 26, 1995
+   */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* initialize png structure for reading, and allocate any memory needed */
+void
+png_read_init(png_struct *png_ptr)
+{
+   jmp_buf tmp_jmp;
+
+   memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
+   memset(png_ptr, 0, sizeof (png_struct));
+   memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
+
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = png_large_malloc(png_ptr, png_ptr->zbuf_size);
+   png_ptr->zstream = &(png_ptr->zstream_struct);
+   png_ptr->zstream->zalloc = png_zalloc;
+   png_ptr->zstream->zfree = png_zfree;
+   png_ptr->zstream->opaque = (voidp)png_ptr;
+   inflateInit(png_ptr->zstream);
+   png_ptr->zstream->next_out = png_ptr->zbuf;
+   png_ptr->zstream->avail_out = (uInt)png_ptr->zbuf_size;
+}
+
+/* read the information before the actual image data. */
+void
+png_read_info(png_struct *png_ptr, png_info *info)
+{
+   png_byte chunk_start[8];
+   png_uint_32 length;
+
+   png_read_data(png_ptr, chunk_start, 8);
+   if (memcmp(chunk_start, png_sig, 8))
+      png_error(png_ptr, "Not a Png File");
+
+   while (1)
+   {
+      png_uint_32 crc;
+
+      png_read_data(png_ptr, chunk_start, 8);
+      length = png_get_uint_32(chunk_start);
+      png_reset_crc(png_ptr);
+      png_calculate_crc(png_ptr, chunk_start + 4, 4);
+      if (!memcmp(chunk_start + 4, png_IHDR, 4))
+      {
+         if (png_ptr->mode != PNG_BEFORE_IHDR)
+            png_error(png_ptr, "Out of Place IHDR");
+
+         png_handle_IHDR(png_ptr, info, length);
+         png_ptr->mode = PNG_HAVE_IHDR;
+      }
+      else if (!memcmp(chunk_start + 4, png_PLTE, 4))
+      {
+         if (png_ptr->mode != PNG_HAVE_IHDR)
+            png_error(png_ptr, "Missing IHDR");
+
+         png_handle_PLTE(png_ptr, info, length);
+         png_ptr->mode = PNG_HAVE_PLTE;
+      }
+      else if (!memcmp(chunk_start + 4, png_gAMA, 4))
+      {
+         if (png_ptr->mode != PNG_HAVE_IHDR)
+            png_error(png_ptr, "Out of Place PLTE");
+
+         png_handle_gAMA(png_ptr, info, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_sBIT, 4))
+      {
+         if (png_ptr->mode != PNG_HAVE_IHDR)
+            png_error(png_ptr, "Out of Place sBIT");
+
+         png_handle_sBIT(png_ptr, info, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_cHRM, 4))
+      {
+         if (png_ptr->mode != PNG_HAVE_IHDR)
+            png_error(png_ptr, "Out of Place cHRM");
+
+         png_handle_cHRM(png_ptr, info, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_tRNS, 4))
+      {
+         if (png_ptr->mode != PNG_HAVE_IHDR &&
+            png_ptr->mode != PNG_HAVE_PLTE)
+            png_error(png_ptr, "Out of Place tRNS");
+
+         png_handle_tRNS(png_ptr, info, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_bKGD, 4))
+      {
+         if (png_ptr->mode != PNG_HAVE_IHDR &&
+            png_ptr->mode != PNG_HAVE_PLTE)
+            png_error(png_ptr, "Out of Place bKGD");
+
+         png_handle_bKGD(png_ptr, info, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_hIST, 4))
+      {
+         if (png_ptr->mode != PNG_HAVE_PLTE)
+            png_error(png_ptr, "Out of Place hIST");
+
+         png_handle_hIST(png_ptr, info, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_IDAT, 4))
+      {
+         png_ptr->idat_size = length;
+         png_ptr->mode = PNG_HAVE_IDAT;
+         break;
+      }
+      else if (!memcmp(chunk_start + 4, png_pHYs, 4))
+      {
+         if (png_ptr->mode != PNG_HAVE_IHDR &&
+            png_ptr->mode != PNG_HAVE_PLTE)
+            png_error(png_ptr, "Out of Place pHYs");
+
+         png_handle_pHYs(png_ptr, info, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_oFFs, 4))
+      {
+         if (png_ptr->mode != PNG_HAVE_IHDR &&
+            png_ptr->mode != PNG_HAVE_PLTE)
+            png_error(png_ptr, "Out of Place oFFs");
+
+         png_handle_oFFs(png_ptr, info, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_tIME, 4))
+      {
+         if (png_ptr->mode == PNG_BEFORE_IHDR ||
+            png_ptr->mode == PNG_AFTER_IEND)
+            png_error(png_ptr, "Out of Place tIME");
+
+         png_handle_tIME(png_ptr, info, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_tEXt, 4))
+      {
+         if (png_ptr->mode == PNG_BEFORE_IHDR ||
+            png_ptr->mode == PNG_AFTER_IEND)
+            png_error(png_ptr, "Out of Place tEXt");
+
+         png_handle_tEXt(png_ptr, info, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_zTXt, 4))
+      {
+         if (png_ptr->mode == PNG_BEFORE_IHDR ||
+            png_ptr->mode == PNG_AFTER_IEND)
+            png_error(png_ptr, "Out of Place zTXt");
+
+         png_handle_zTXt(png_ptr, info, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_IEND, 4))
+      {
+         png_error(png_ptr, "No Image in File");
+      }
+      else
+      {
+         if (isupper(chunk_start[4]))
+            png_error(png_ptr, "Unknown Critical Chunk");
+
+         png_crc_skip(png_ptr, length);
+      }
+      png_read_data(png_ptr, chunk_start, 4);
+      crc = png_get_uint_32(chunk_start);
+      if (((crc ^ 0xffffffffL) & 0xffffffffL) !=
+         (png_ptr->crc & 0xffffffffL))
+         png_error(png_ptr, "Bad CRC value");
+   }
+}
+
+/* initialize palette, background, etc, after transformations
+   are set, but before any reading takes place.  This allows
+   the user to obtail a gamma corrected palette, for example.
+   If the user doesn't call this, we will do it ourselves. */
+void
+png_start_read_image(png_struct *png_ptr)
+{
+   png_read_start_row(png_ptr);
+}
+
+void
+png_read_row(png_struct *png_ptr, png_byte *row, png_byte *dsp_row)
+{
+   int ret;
+
+   if (!(png_ptr->row_init))
+      png_read_start_row(png_ptr);
+
+   /* if interlaced and we do not need a new row, combine row and return */
+   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+   {
+      switch (png_ptr->pass)
+      {
+         case 0:
+            if (png_ptr->row_number & 7)
+            {
+               if (dsp_row)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 1:
+            if ((png_ptr->row_number & 7) || png_ptr->width < 5)
+            {
+               if (dsp_row)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 2:
+            if ((png_ptr->row_number & 7) != 4)
+            {
+               if (dsp_row && (png_ptr->row_number & 4))
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 3:
+            if ((png_ptr->row_number & 3) || png_ptr->width < 3)
+            {
+               if (dsp_row)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 4:
+            if ((png_ptr->row_number & 3) != 2)
+            {
+               if (dsp_row && (png_ptr->row_number & 2))
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 5:
+            if ((png_ptr->row_number & 1) || png_ptr->width < 2)
+            {
+               if (dsp_row)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 6:
+            if (!(png_ptr->row_number & 1))
+            {
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+      }
+   }
+
+   if (png_ptr->mode != PNG_HAVE_IDAT)
+      png_error(png_ptr, "invalid attempt to read row data");
+
+   png_ptr->zstream->next_out = png_ptr->row_buf;
+   png_ptr->zstream->avail_out = (uInt)png_ptr->irowbytes;
+   do
+   {
+      if (!(png_ptr->zstream->avail_in))
+      {
+         while (!png_ptr->idat_size)
+         {
+            png_byte buf[4];
+            png_uint_32 crc;
+
+            png_read_data(png_ptr, buf, 4);
+            crc = png_get_uint_32(buf);
+            if (((crc ^ 0xffffffffL) & 0xffffffffL) !=
+               (png_ptr->crc & 0xffffffffL))
+               png_error(png_ptr, "Bad CRC value");
+
+            png_read_data(png_ptr, buf, 4);
+            png_ptr->idat_size = png_get_uint_32(buf);
+            png_reset_crc(png_ptr);
+
+            png_crc_read(png_ptr, buf, 4);
+            if (memcmp(buf, png_IDAT, 4))
+               png_error(png_ptr, "Not enough image data");
+
+         }
+         png_ptr->zstream->avail_in = (uInt)png_ptr->zbuf_size;
+         png_ptr->zstream->next_in = png_ptr->zbuf;
+         if (png_ptr->zbuf_size > png_ptr->idat_size)
+            png_ptr->zstream->avail_in = (uInt)png_ptr->idat_size;
+         png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream->avail_in);
+         png_ptr->idat_size -= png_ptr->zstream->avail_in;
+      }
+      ret = inflate(png_ptr->zstream, Z_PARTIAL_FLUSH);
+      if (ret == Z_STREAM_END)
+      {
+         if (png_ptr->zstream->avail_out || png_ptr->zstream->avail_in ||
+            png_ptr->idat_size)
+            png_error(png_ptr, "Extra compressed data");
+         png_ptr->mode = PNG_AT_LAST_IDAT;
+         break;
+      }
+      if (ret != Z_OK)
+         png_error(png_ptr, "Compression Error");
+
+   } while (png_ptr->zstream->avail_out);
+
+   if (ret == Z_STREAM_END)
+      png_ptr->zlib_finished = 1;
+
+   png_ptr->row_info.color_type = png_ptr->color_type;
+   png_ptr->row_info.width = png_ptr->iwidth;
+   png_ptr->row_info.channels = png_ptr->channels;
+   png_ptr->row_info.bit_depth = png_ptr->bit_depth;
+   png_ptr->row_info.pixel_depth = png_ptr->pixel_depth;
+   png_ptr->row_info.rowbytes = ((png_ptr->row_info.width *
+      (png_uint_32)png_ptr->row_info.pixel_depth + 7) >> 3);
+
+   if (png_ptr->row_buf[0])
+      png_read_filter_row(&(png_ptr->row_info),
+         png_ptr->row_buf + 1, png_ptr->prev_row + 1,
+         (int)(png_ptr->row_buf[0]));
+
+   memcpy(png_ptr->prev_row, png_ptr->row_buf, (png_size_t)png_ptr->rowbytes + 1);
+
+   if (png_ptr->transformations)
+      png_do_read_transformations(png_ptr);
+
+   /* blow up interlaced rows to full size */
+   if (png_ptr->interlaced &&
+      (png_ptr->transformations & PNG_INTERLACE))
+   {
+      if (png_ptr->pass < 6)
+         png_do_read_interlace(&(png_ptr->row_info),
+            png_ptr->row_buf + 1, png_ptr->pass);
+
+      if (dsp_row)
+         png_combine_row(png_ptr, dsp_row,
+            png_pass_dsp_mask[png_ptr->pass]);
+      if (row)
+         png_combine_row(png_ptr, row,
+            png_pass_mask[png_ptr->pass]);
+   }
+   else
+   {
+      if (row)
+         png_combine_row(png_ptr, row, 0xff);
+      if (dsp_row)
+         png_combine_row(png_ptr, dsp_row, 0xff);
+   }
+   png_read_finish_row(png_ptr);
+}
+
+/* read a one or more rows of image data.   If the image is interlaced,
+   and png_set_interlace_handling() has been called, the rows need to
+   to contain the contents of the rows from the previous pass.  If
+   the image has alpha or transparency, and png_handle_alpha() has been
+   called, the rows contents must be initialized to the contents of the
+   screen.  row holds the actual image, and pixels are placed in it
+   as they arrive.  If the image is displayed after each pass, it will
+   appear to "sparkle" in.  display_row can be used to display a
+   "chunky" progressive image, with finer detail added as it becomes
+   available.  If you do not want this "chunky" display, you may pass
+   NULL for display_rows.  If you do not want the sparkle display, and
+   you have not called png_handle_alpha(), you may pass NULL for rows.
+   If you have called png_handle_alpha(), and the image has either an
+   alpha channel or a transparency chunk, you must provide a buffer for
+   rows.  In this case, you do not have to provide a display_rows buffer
+   also, but you may.  If the image is not interlaced, or if you have
+   not called png_set_interlace_handling(), the display_row buffer will
+   be ignored, so pass NULL to it. */
+void
+png_read_rows(png_struct *png_ptr, png_byte **row,
+   png_byte **display_row, png_uint_32 num_rows)
+{
+   png_uint_32 i;
+   png_byte **rp;
+   png_byte **dp;
+
+	rp = row;
+	dp = display_row;
+	for (i = 0; i < num_rows; i++)
+	{
+		png_byte *rptr;
+		png_byte *dptr;
+
+		if (rp)
+			rptr = *rp;
+		else
+			rptr = NULL;
+		if (dp)
+			dptr = *dp;
+		else
+			dptr = NULL;
+		png_read_row(png_ptr, rptr, dptr);
+		if (row)
+			rp++;
+		if (display_row)
+			dp++;
+	}
+}
+
+/* read the image.  If the image has an alpha channel or a transparency
+	chunk, and you have called png_handle_alpha(), you will need to
+   initialize the image to the current image that png will be overlaying.
+   Note that png_set_interlace_handling() has no effect on this call.
+   You only need to call this function once.  If you desire to have
+   an image for each pass of a interlaced image, use png_read_rows() */
+void
+png_read_image(png_struct *png_ptr, png_byte **image)
+{
+   png_uint_32 i;
+   int pass, j;
+   png_byte **rp;
+
+   pass = png_set_interlace_handling(png_ptr);
+   for (j = 0; j < pass; j++)
+   {
+      rp = image;
+      for (i = 0; i < png_ptr->height; i++)
+      {
+         png_read_row(png_ptr, *rp, NULL);
+         rp++;
+      }
+   }
+}
+
+/* read the end of the png file.  Will not read past the end of the
+   file, will verify the end is accurate, and will read any comments
+   or time information at the end of the file, if info is not NULL. */
+void
+png_read_end(png_struct *png_ptr, png_info *info)
+{
+   png_byte chunk_start[8];
+   png_uint_32 length;
+   png_uint_32 crc;
+
+   png_read_data(png_ptr, chunk_start, 4);
+   crc = png_get_uint_32(chunk_start);
+   if (((crc ^ 0xffffffffL) & 0xffffffffL) !=
+      (png_ptr->crc & 0xffffffffL))
+      png_error(png_ptr, "Bad CRC value");
+
+   do
+   {
+      png_read_data(png_ptr, chunk_start, 8);
+      length = png_get_uint_32(chunk_start);
+      png_reset_crc(png_ptr);
+      png_calculate_crc(png_ptr, chunk_start + 4, 4);
+
+      if (!memcmp(chunk_start + 4, png_IHDR, 4))
+      {
+         png_error(png_ptr, "invalid chunk after IDAT");
+      }
+      else if (!memcmp(chunk_start + 4, png_PLTE, 4))
+      {
+         png_error(png_ptr, "invalid chunk after IDAT");
+      }
+      else if (!memcmp(chunk_start + 4, png_gAMA, 4))
+      {
+         png_error(png_ptr, "invalid chunk after IDAT");
+      }
+      else if (!memcmp(chunk_start + 4, png_sBIT, 4))
+      {
+         png_error(png_ptr, "invalid chunk after IDAT");
+      }
+      else if (!memcmp(chunk_start + 4, png_cHRM, 4))
+      {
+         png_error(png_ptr, "invalid chunk after IDAT");
+      }
+      else if (!memcmp(chunk_start + 4, png_tRNS, 4))
+      {
+         png_error(png_ptr, "invalid chunk after IDAT");
+      }
+      else if (!memcmp(chunk_start + 4, png_bKGD, 4))
+      {
+         png_error(png_ptr, "invalid chunk after IDAT");
+      }
+      else if (!memcmp(chunk_start + 4, png_hIST, 4))
+      {
+         png_error(png_ptr, "invalid chunk after IDAT");
+      }
+      else if (!memcmp(chunk_start + 4, png_IDAT, 4))
+      {
+         if (length > 0 || png_ptr->mode != PNG_AT_LAST_IDAT)
+            png_error(png_ptr, "too many IDAT's found");
+      }
+      else if (!memcmp(chunk_start + 4, png_pHYs, 4))
+      {
+         png_error(png_ptr, "invalid chunk after IDAT");
+      }
+      else if (!memcmp(chunk_start + 4, png_oFFs, 4))
+      {
+         png_error(png_ptr, "invalid chunk after IDAT");
+      }
+      else if (!memcmp(chunk_start + 4, png_tIME, 4))
+      {
+         if (png_ptr->mode == PNG_BEFORE_IHDR ||
+            png_ptr->mode == PNG_AFTER_IEND)
+            png_error(png_ptr, "Out of Place tIME");
+
+         if (info)
+            png_handle_tIME(png_ptr, info, length);
+         else
+            png_crc_skip(png_ptr, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_tEXt, 4))
+      {
+         if (png_ptr->mode == PNG_BEFORE_IHDR ||
+            png_ptr->mode == PNG_AFTER_IEND)
+            png_error(png_ptr, "Out of Place tEXt");
+
+         if (info)
+            png_handle_tEXt(png_ptr, info, length);
+         else
+            png_crc_skip(png_ptr, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_zTXt, 4))
+      {
+         if (png_ptr->mode == PNG_BEFORE_IHDR ||
+            png_ptr->mode == PNG_AFTER_IEND)
+            png_error(png_ptr, "Out of Place zTXt");
+
+         if (info)
+            png_handle_zTXt(png_ptr, info, length);
+         else
+            png_crc_skip(png_ptr, length);
+      }
+      else if (!memcmp(chunk_start + 4, png_IEND, 4))
+      {
+         png_ptr->mode = PNG_AFTER_IEND;
+      }
+      else
+      {
+         if (isupper(chunk_start[4]))
+            png_error(png_ptr, "Unknown Critical Chunk");
+
+         png_crc_skip(png_ptr, length);
+      }
+      png_read_data(png_ptr, chunk_start, 4);
+      crc = png_get_uint_32(chunk_start);
+      if (((crc ^ 0xffffffffL) & 0xffffffffL) !=
+         (png_ptr->crc & 0xffffffffL))
+         png_error(png_ptr, "Bad CRC value");
+      if (png_ptr->mode == PNG_AT_LAST_IDAT)
+         png_ptr->mode = PNG_AFTER_IDAT;
+   } while (png_ptr->mode != PNG_AFTER_IEND);
+}
+
+/* free all memory used by the read */
+void
+png_read_destroy(png_struct *png_ptr, png_info *info, png_info *end_info)
+{
+   int i;
+   jmp_buf tmp_jmp;
+
+   if (info)
+   {
+      if (info->palette != png_ptr->palette)
+         png_free(png_ptr, info->palette);
+      if (info->trans != png_ptr->trans)
+         png_free(png_ptr, info->trans);
+      if (info->hist != png_ptr->hist)
+         png_free(png_ptr, info->hist);
+      for (i = 0; i < info->num_text; i++)
+      {
+         png_large_free(png_ptr, info->text[i].key);
+      }
+
+      png_free(png_ptr, info->text);
+      memset(info, 0, sizeof(png_info));
+   }
+
+   if (end_info)
+   {
+      for (i = 0; i < end_info->num_text; i++)
+      {
+         png_large_free(png_ptr, end_info->text[i].key);
+      }
+
+      png_free(png_ptr, end_info->text);
+      memset(end_info, 0, sizeof(png_info));
+   }
+
+   png_large_free(png_ptr, png_ptr->zbuf);
+   png_large_free(png_ptr, png_ptr->row_buf);
+   png_large_free(png_ptr, png_ptr->prev_row);
+   png_large_free(png_ptr, png_ptr->palette_lookup);
+   png_free(png_ptr, png_ptr->dither_index);
+   png_free(png_ptr, png_ptr->gamma_table);
+   png_free(png_ptr, png_ptr->gamma_from_1);
+   png_free(png_ptr, png_ptr->gamma_to_1);
+   if (png_ptr->gamma_16_table)
+   {
+      for (i = 0; i < (1 << (8 - png_ptr->gamma_shift)); i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_table[i]);
+      }
+   }
+   png_free(png_ptr, png_ptr->gamma_16_table);
+   if (png_ptr->gamma_16_from_1)
+   {
+      for (i = 0; i < (1 << (8 - png_ptr->gamma_shift)); i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_from_1[i]);
+      }
+   }
+   png_free(png_ptr, png_ptr->gamma_16_from_1);
+   if (png_ptr->gamma_16_to_1)
+   {
+      for (i = 0; i < (1 << (8 - png_ptr->gamma_shift)); i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_to_1[i]);
+      }
+   }
+   png_free(png_ptr, png_ptr->gamma_16_to_1);
+   png_free(png_ptr, png_ptr->trans);
+   png_free(png_ptr, png_ptr->hist);
+   if (!png_ptr->user_palette)
+      png_free(png_ptr, png_ptr->palette);
+
+   inflateEnd(png_ptr->zstream);
+
+   memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
+   memset(png_ptr, 0, sizeof (png_struct));
+   memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
+}
+
+
diff --git a/pngrtran.c b/pngrtran.c
new file mode 100644
index 0000000..0aa3b47
--- /dev/null
+++ b/pngrtran.c
@@ -0,0 +1,2774 @@
+
+/* pngrtran.c - transforms the data in a row for png readers
+
+   libpng 1.0 beta 1 - version 0.71
+   For conditions of distribution and use, see copyright notice in png.h
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   June 26, 1995
+   */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* handle alpha and tRNS via a background color */
+void
+png_set_background(png_struct *png_ptr,
+   png_color_16 *background_color, int background_gamma_code,
+   int need_expand, float background_gamma)
+{
+   png_ptr->transformations |= PNG_BACKGROUND;
+   memcpy(&(png_ptr->background), background_color,
+      sizeof(png_color_16));
+   png_ptr->background_gamma = background_gamma;
+   png_ptr->background_gamma_type = background_gamma_code;
+   png_ptr->background_expand = need_expand;
+}
+
+/* strip 16 bit depth files to 8 bit depth */
+void
+png_set_strip_16(png_struct *png_ptr)
+{
+   png_ptr->transformations |= PNG_16_TO_8;
+}
+
+/* dither file to 8 bit.  Supply a palette, the current number
+   of elements in the palette, the maximum number of elements
+   allowed, and a histogram, if possible.  If the current number
+   is greater then the maximum number, the palette will be
+   modified to fit in the maximum number */
+
+typedef struct dsort_struct
+{
+   struct dsort_struct *next;
+   png_byte left;
+   png_byte right;
+} dsort;
+
+void
+png_set_dither(png_struct *png_ptr, png_color *palette,
+   int num_palette, int maximum_colors, png_uint_16 *histogram,
+   int full_dither)
+{
+   png_ptr->transformations |= PNG_DITHER;
+
+   if (!full_dither)
+   {
+      int i;
+
+      png_ptr->dither_index = png_malloc(png_ptr,
+         num_palette * sizeof (png_byte));
+      for (i = 0; i < num_palette; i++)
+         png_ptr->dither_index[i] = i;
+   }
+
+   if (num_palette > maximum_colors)
+   {
+      if (histogram)
+      {
+         /* this is easy enough, just throw out the least used colors.
+            perhaps not the best solution, but good enough */
+
+         int i;
+         png_byte *sort;
+
+         /* initialize an array to sort colors */
+         sort = (png_byte *)png_malloc(png_ptr, num_palette * sizeof (png_byte));
+
+         /* initialize the sort array */
+         for (i = 0; i < num_palette; i++)
+            sort[i] = i;
+
+         /* find the least used palette entries by starting a
+            bubble sort, and running it until we have sorted
+            out enough colors.  Note that we don't care about
+            sorting all the colors, just finding which are
+            least used. */
+
+         for (i = num_palette - 1; i >= maximum_colors; i--)
+         {
+            int done; /* to stop early if the list is pre-sorted */
+            int j;
+
+            done = 1;
+            for (j = 0; j < i; j++)
+            {
+               if (histogram[sort[j]] < histogram[sort[j + 1]])
+               {
+                  png_byte t;
+
+                  t = sort[j];
+                  sort[j] = sort[j + 1];
+                  sort[j + 1] = t;
+                  done = 0;
+               }
+            }
+            if (done)
+               break;
+         }
+
+         /* swap the palette around, and set up a table, if necessary */
+         if (full_dither)
+         {
+            int j;
+
+            /* put all the useful colors within the max, but don't
+               move the others */
+            j = num_palette;
+            for (i = 0; i < maximum_colors; i++)
+            {
+               if (sort[i] >= maximum_colors)
+               {
+                  do
+                     j--;
+                  while (sort[j] >= maximum_colors);
+                  palette[i] = palette[j];
+               }
+            }
+         }
+         else
+         {
+            int j;
+
+            /* move all the used colors inside the max limit, and
+               develop a translation table */
+            j = num_palette;
+            for (i = 0; i < maximum_colors; i++)
+            {
+               /* only move the colors we need to */
+               if (sort[i] >= maximum_colors)
+               {
+                  png_color tmp_color;
+
+                  do
+                     j--;
+                  while (sort[j] >= maximum_colors);
+
+                  tmp_color = palette[j];
+                  palette[j] = palette[i];
+                  palette[i] = tmp_color;
+                  /* indicate where the color went */
+                  png_ptr->dither_index[j] = i;
+                  png_ptr->dither_index[i] = j;
+               }
+            }
+            /* find closest color for those colors we are not
+               using */
+            for (i = 0; i < num_palette; i++)
+            {
+               if (png_ptr->dither_index[i] >= maximum_colors)
+               {
+                  int min_d, j, min_j, index;
+
+                  /* find the closest color to one we threw out */
+                  index = png_ptr->dither_index[i];
+                  min_d = PNG_COLOR_DIST(palette[index],
+                        palette[0]);
+                  min_j = 0;
+                  for (j = 1; j < maximum_colors; j++)
+                  {
+                     int d;
+
+                     d = PNG_COLOR_DIST(palette[index],
+                        palette[j]);
+
+                     if (d < min_d)
+                     {
+                        min_d = d;
+                        min_j = j;
+                     }
+                  }
+                  /* point to closest color */
+                  png_ptr->dither_index[i] = min_j;
+               }
+            }
+         }
+         png_free(png_ptr, sort);
+      }
+      else
+      {
+         /* this is much harder to do simply (and quickly).  Perhaps
+            we need to go through a median cut routine, but those
+            don't always behave themselves with only a few colors
+            as input.  So we will just find the closest two colors,
+            and throw out one of them (chosen somewhat randomly).
+            */
+         int i;
+         int max_d;
+         int num_new_palette;
+         dsort **hash;
+         png_byte *index_to_palette;
+            /* where the original index currently is in the palette */
+         png_byte *palette_to_index;
+            /* which original index points to this palette color */
+
+         /* initialize palette index arrays */
+         index_to_palette = (png_byte *)png_malloc(png_ptr,
+            num_palette * sizeof (png_byte));
+         palette_to_index = (png_byte *)png_malloc(png_ptr,
+            num_palette * sizeof (png_byte));
+
+         /* initialize the sort array */
+         for (i = 0; i < num_palette; i++)
+         {
+            index_to_palette[i] = i;
+            palette_to_index[i] = i;
+         }
+
+         hash = (dsort **)png_malloc(png_ptr, 769 * sizeof (dsort *));
+         for (i = 0; i < 769; i++)
+            hash[i] = (dsort *)0;
+/*         memset(hash, 0, 769 * sizeof (dsort *)); */
+
+         num_new_palette = num_palette;
+
+         /* initial wild guess at how far apart the farthest pixel
+            pair we will be eliminating will be.  Larger
+            numbers mean more areas will be allocated, Smaller
+            numbers run the risk of not saving enough data, and
+            having to do this all over again.
+
+            I have not done extensive checking on this number.
+            */
+         max_d = 96;
+
+         while (num_new_palette > maximum_colors)
+         {
+            for (i = 0; i < num_new_palette - 1; i++)
+            {
+               int j;
+
+               for (j = i + 1; j < num_new_palette; j++)
+               {
+                  int d;
+
+                  d = PNG_COLOR_DIST(palette[i], palette[j]);
+
+                  if (d <= max_d)
+                  {
+                     dsort *t;
+
+                     t = png_malloc(png_ptr, sizeof (dsort));
+                     t->next = hash[d];
+                     t->left = i;
+                     t->right = j;
+                     hash[d] = t;
+                  }
+               }
+            }
+
+            for (i = 0; i <= max_d; i++)
+            {
+               if (hash[i])
+               {
+                  dsort *p;
+
+                  for (p = hash[i]; p; p = p->next)
+                  {
+                     if (index_to_palette[p->left] < num_new_palette &&
+                        index_to_palette[p->right] < num_new_palette)
+                     {
+                        int j, next_j;
+
+                        if (num_new_palette & 1)
+                        {
+                           j = p->left;
+                           next_j = p->right;
+                        }
+                        else
+                        {
+                           j = p->right;
+                           next_j = p->left;
+                        }
+
+                        num_new_palette--;
+                        palette[index_to_palette[j]] =
+                           palette[num_new_palette];
+                        if (!full_dither)
+                        {
+                           int k;
+
+                           for (k = 0; k < num_palette; k++)
+                           {
+                              if (png_ptr->dither_index[k] ==
+                                 index_to_palette[j])
+                                 png_ptr->dither_index[k] =
+                                    index_to_palette[next_j];
+                              if (png_ptr->dither_index[k] ==
+                                 num_new_palette)
+                                 png_ptr->dither_index[k] =
+                                    index_to_palette[j];
+                           }
+                        }
+
+                        index_to_palette[palette_to_index[num_new_palette]] =
+                           index_to_palette[j];
+                        palette_to_index[index_to_palette[j]] =
+                           palette_to_index[num_new_palette];
+
+                        index_to_palette[j] = num_new_palette;
+                        palette_to_index[num_new_palette] = j;
+                     }
+                     if (num_new_palette <= maximum_colors)
+                        break;
+                  }
+                  if (num_new_palette <= maximum_colors)
+                     break;
+               }
+            }
+
+            for (i = 0; i < 769; i++)
+            {
+               if (hash[i])
+               {
+                  dsort *p;
+
+                  p = hash[i];
+                  while (p)
+                  {
+                     dsort *t;
+
+                     t = p->next;
+                     png_free(png_ptr, p);
+                     p = t;
+                  }
+               }
+               hash[i] = 0;
+            }
+            max_d += 96;
+         }
+         png_free(png_ptr, hash);
+         png_free(png_ptr, palette_to_index);
+         png_free(png_ptr, index_to_palette);
+      }
+      num_palette = maximum_colors;
+   }
+   if (!(png_ptr->palette))
+   {
+      png_ptr->palette = palette;
+      png_ptr->user_palette = 1;
+   }
+   png_ptr->num_palette = num_palette;
+
+   if (full_dither)
+   {
+      int i;
+      int total_bits, num_red, num_green, num_blue;
+      png_uint_32 num_entries;
+      png_byte *distance;
+
+      total_bits = PNG_DITHER_RED_BITS + PNG_DITHER_GREEN_BITS +
+         PNG_DITHER_BLUE_BITS;
+
+      num_red = (1 << PNG_DITHER_RED_BITS);
+      num_green = (1 << PNG_DITHER_GREEN_BITS);
+      num_blue = (1 << PNG_DITHER_BLUE_BITS);
+      num_entries = ((png_uint_32)1 << total_bits);
+
+      png_ptr->palette_lookup = (png_byte *)png_large_malloc(png_ptr,
+         (png_size_t)num_entries * sizeof (png_byte));
+
+      memset(png_ptr->palette_lookup, 0, (png_size_t)num_entries * sizeof (png_byte));
+
+      distance = (png_byte *)png_large_malloc(png_ptr,
+         (png_size_t)num_entries * sizeof (png_byte));
+
+      memset(distance, 0xff, (png_size_t)num_entries * sizeof (png_byte));
+
+      for (i = 0; i < num_palette; i++)
+      {
+         int r, g, b, ir, ig, ib;
+
+         r = (palette[i].red >> (8 - PNG_DITHER_RED_BITS));
+         g = (palette[i].green >> (8 - PNG_DITHER_GREEN_BITS));
+         b = (palette[i].blue >> (8 - PNG_DITHER_BLUE_BITS));
+
+         for (ir = 0; ir < num_red; ir++)
+         {
+            int dr, index_r;
+
+            dr = abs(ir - r);
+            index_r = (ir << (PNG_DITHER_BLUE_BITS + PNG_DITHER_GREEN_BITS));
+            for (ig = 0; ig < num_green; ig++)
+            {
+               int dg, dt, dm, index_g;
+
+               dg = abs(ig - g);
+               dt = dr + dg;
+               dm = ((dr > dg) ? dr : dg);
+               index_g = index_r | (ig << PNG_DITHER_BLUE_BITS);
+               for (ib = 0; ib < num_blue; ib++)
+               {
+                  int index, db, dmax, d;
+
+                  index = index_g | ib;
+                  db = abs(ib - b);
+                  dmax = ((dm > db) ? dm : db);
+                  d = dmax + dt + db;
+
+                  if (d < distance[index])
+                  {
+                     distance[index] = d;
+                     png_ptr->palette_lookup[index] = i;
+                  }
+               }
+            }
+         }
+      }
+
+      png_large_free(png_ptr, distance);
+   }
+#ifdef oldway
+      d = TABLE_COLOR_DISTANCE(index, palette[i]);
+      dt = TABLE_DISTANCE(index);
+      if (d < dt)
+         png_ptr->palette_lookup[index] = i;
+
+      for (ir = r + 1, index2 = index; ir < num_red; ir++)
+      {
+         index2 += (1 << (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS));
+         d = RGB_COLOR_DISTANCE(r, g, b, palette[i]);
+         dt = TABLE_DISTANCE(index2);
+         if (d < dt)
+            png_ptr->palette_lookup[index2] = i;
+         else
+            break;
+      }
+
+
+
+   i = 0;
+   for (ir = 0; ir < num_red; ir++)
+   {
+      r = (ir * 255) / (num_red - 1);
+      for (ig = 0; ig < num_green; ig++)
+      {
+         g = (ig * 255) / (num_green - 1);
+         for (ib = 0; ib < num_blue; ib++)
+         {
+            int min_d, j, k, d;
+
+            b = (ib * 255) / (num_blue - 1);
+            min_d = 1024;
+            k = 0;
+            for (j = 0; j < png_ptr->num_palette; j++)
+            {
+               d = abs(r - (int)png_ptr->palette[j].red) +
+                  abs(g - (int)png_ptr->palette[j].green) +
+                  abs(b - (int)png_ptr->palette[j].blue);
+               if (d < min_d)
+               {
+                  min_d = d;
+                  k = j;
+               }
+            }
+            png_ptr->palette_lookup[i++] = k;
+         }
+      }
+   }
+#endif
+}
+
+/* transform the image from the file_gamma to the screen_gamma */
+void
+png_set_gamma(png_struct *png_ptr, float screen_gamma,
+   float file_gamma)
+{
+   png_ptr->transformations |= PNG_GAMMA;
+   png_ptr->gamma = file_gamma;
+   png_ptr->display_gamma = screen_gamma;
+}
+
+/* expand paletted images to rgb, expand grayscale images of
+   less then 8 bit depth to 8 bit depth, and expand tRNS chunks
+   to alpha channels */
+void
+png_set_expand(png_struct *png_ptr)
+{
+   png_ptr->transformations |= PNG_EXPAND;
+}
+
+void
+png_set_gray_to_rgb(png_struct *png_ptr)
+{
+   png_ptr->transformations |= PNG_GRAY_TO_RGB;
+}
+
+/* initialize everything needed for the read.  This includes modifying
+   the palette */
+void
+png_init_read_transformations(png_struct *png_ptr)
+{
+   int color_type;
+
+   color_type = png_ptr->color_type;
+
+   if (png_ptr->transformations & PNG_EXPAND)
+   {
+      if (color_type == PNG_COLOR_TYPE_GRAY &&
+         png_ptr->bit_depth < 8 &&
+         (!(png_ptr->transformations & PNG_BACKGROUND) ||
+         png_ptr->background_expand))
+      {
+         /* expand background chunk.  While this may not be
+            the fastest way to do this, it only happens once
+            per file. */
+         switch (png_ptr->bit_depth)
+         {
+            case 1:
+               png_ptr->background.gray *= 0xff;
+               break;
+            case 2:
+               png_ptr->background.gray *= 0x55;
+               break;
+            case 4:
+               png_ptr->background.gray *= 0x11;
+               break;
+         }
+      }
+      if (color_type == PNG_COLOR_TYPE_PALETTE &&
+         (png_ptr->transformations & PNG_BACKGROUND) &&
+         png_ptr->background_expand)
+      {
+         /* expand background chunk */
+         png_ptr->background.red =
+            png_ptr->palette[png_ptr->background.index].red;
+         png_ptr->background.green =
+            png_ptr->palette[png_ptr->background.index].green;
+         png_ptr->background.blue =
+            png_ptr->palette[png_ptr->background.index].blue;
+         color_type = PNG_COLOR_TYPE_RGB;
+      }
+   }
+
+   png_ptr->background_1 = png_ptr->background;
+   if (png_ptr->transformations & PNG_GAMMA)
+   {
+      png_build_gamma_table(png_ptr);
+      if ((png_ptr->transformations & PNG_BACKGROUND) &&
+         (color_type != PNG_COLOR_TYPE_PALETTE))
+      {
+         if (png_ptr->background_gamma_type != PNG_BACKGROUND_GAMMA_UNKNOWN)
+         {
+            double g, gs, m;
+
+            m = (double)((png_uint_32)1 << png_ptr->bit_depth);
+            g = 1.0;
+            gs = 1.0;
+
+            switch (png_ptr->background_gamma_type)
+            {
+               case PNG_BACKGROUND_GAMMA_SCREEN:
+                  g = (png_ptr->display_gamma);
+                  gs = 1.0;
+                  break;
+               case PNG_BACKGROUND_GAMMA_FILE:
+                  g = 1.0 / (png_ptr->gamma);
+                  gs = 1.0 / (png_ptr->gamma * png_ptr->display_gamma);
+                  break;
+               case PNG_BACKGROUND_GAMMA_UNIQUE:
+                  g = 1.0 / (png_ptr->background_gamma);
+                  gs = 1.0 / (png_ptr->background_gamma *
+                     png_ptr->display_gamma);
+                  break;
+            }
+
+            if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+            {
+               png_ptr->background_1.red = (png_uint_16)(pow(
+                  (double)png_ptr->background.red / m, g) * m + .5);
+               png_ptr->background_1.green = (png_uint_16)(pow(
+                  (double)png_ptr->background.green / m, g) * m + .5);
+               png_ptr->background_1.blue = (png_uint_16)(pow(
+                  (double)png_ptr->background.blue / m, g) * m + .5);
+               png_ptr->background.red = (png_uint_16)(pow(
+                  (double)png_ptr->background.red / m, gs) * m + .5);
+               png_ptr->background.green = (png_uint_16)(pow(
+                  (double)png_ptr->background.green / m, gs) * m + .5);
+               png_ptr->background.blue = (png_uint_16)(pow(
+                  (double)png_ptr->background.blue / m, gs) * m + .5);
+            }
+            else
+            {
+               png_ptr->background_1.gray = (png_uint_16)(pow(
+                  (double)png_ptr->background.gray / m, g) * m + .5);
+               png_ptr->background.gray = (png_uint_16)(pow(
+                  (double)png_ptr->background.gray / m, gs) * m + .5);
+            }
+         }
+      }
+   }
+}
+
+/* transform the row.  The order of transformations is significant,
+   and is very touchy.  If you add a transformation, take care to
+   decide how it fits in with the other transformations here */
+void
+png_do_read_transformations(png_struct *png_ptr)
+{
+   if ((png_ptr->transformations & PNG_EXPAND) &&
+      png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->palette, png_ptr->trans, png_ptr->num_trans);
+   }
+   else if (png_ptr->transformations & PNG_EXPAND)
+   {
+      if (png_ptr->num_trans)
+         png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1,
+            &(png_ptr->trans_values));
+      else
+         png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1,
+            NULL);
+   }
+   if (png_ptr->transformations & PNG_BACKGROUND)
+      png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         &(png_ptr->trans_values), &(png_ptr->background),
+         &(png_ptr->background_1),
+         png_ptr->gamma_table, png_ptr->gamma_from_1,
+         png_ptr->gamma_to_1, png_ptr->gamma_16_table,
+         png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1,
+         png_ptr->gamma_shift);
+   else if (png_ptr->transformations & PNG_GAMMA) 
+      png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->gamma_table, png_ptr->gamma_16_table,
+         png_ptr->gamma_shift);
+   if (png_ptr->transformations & PNG_16_TO_8)
+      png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   if (png_ptr->transformations & PNG_DITHER)
+      png_do_dither(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->palette_lookup, png_ptr->dither_index);
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         &(png_ptr->shift));
+   if (png_ptr->transformations & PNG_PACK)
+      png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   if (png_ptr->transformations & PNG_BGR)
+      png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   if (png_ptr->transformations & PNG_GRAY_TO_RGB)
+      png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   if (png_ptr->transformations & PNG_RGBA)
+      png_do_read_rgbx(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   if (png_ptr->transformations & PNG_XRGB)
+      png_do_read_xrgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
+}
+
+/* unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel,
+   without changing the actual values.  Thus, if you had a row with
+   a bit depth of 1, you would end up with bytes that only contained
+   the numbers 0 or 1.  If you would rather they contain 0 and 255, use
+   png_do_shift() after this. */
+void
+png_do_unpack(png_row_info *row_info, png_byte *row)
+{
+   if (row && row_info && row_info->bit_depth < 8)
+   {
+      switch (row_info->bit_depth)
+      {
+         case 1:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            int shift;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 3);
+            dp = row + (png_size_t)row_info->width - 1;
+            shift = 7 - (int)((row_info->width + 7) & 7);
+            for (i = 0; i < row_info->width; i++)
+            {
+               *dp = (*sp >> shift) & 0x1;
+               if (shift == 7)
+               {
+                  shift = 0;
+                  sp--;
+               }
+               else
+                  shift++;
+
+               dp--;
+            }
+            break;
+         }
+         case 2:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            int shift;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 2);
+            dp = row + (png_size_t)row_info->width - 1;
+            shift = (int)((3 - ((row_info->width + 3) & 3)) << 1);
+            for (i = 0; i < row_info->width; i++)
+            {
+               *dp = (*sp >> shift) & 0x3;
+               if (shift == 6)
+               {
+                  shift = 0;
+                  sp--;
+               }
+               else
+                  shift += 2;
+
+               dp--;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            int shift;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 1);
+            dp = row + (png_size_t)row_info->width - 1;
+            shift = (int)((1 - ((row_info->width + 1) & 1)) << 4);
+            for (i = 0; i < row_info->width; i++)
+            {
+               *dp = (*sp >> shift) & 0xf;
+               if (shift == 4)
+               {
+                  shift = 0;
+                  sp--;
+               }
+               else
+                  shift = 4;
+
+               dp--;
+            }
+            break;
+         }
+      }
+      row_info->bit_depth = 8;
+      row_info->pixel_depth = 8 * row_info->channels;
+      row_info->rowbytes = row_info->width * row_info->channels;
+   }
+}
+
+/* reverse the effects of png_do_shift.  This routine merely shifts the
+   pixels back to their significant bits values.  Thus, if you have
+   a row of bit depth 8, but only 5 are significant, this will shift
+   the values back to 0 through 31 */
+void
+png_do_unshift(png_row_info *row_info, png_byte *row,
+   png_color_8 *sig_bits)
+{
+   if (row && row_info && sig_bits)
+   {
+      int shift[4];
+      int channels;
+
+      channels = 0;
+      if (row_info->color_type & PNG_COLOR_MASK_COLOR)
+      {
+         shift[channels++] = row_info->bit_depth - sig_bits->red;
+         shift[channels++] = row_info->bit_depth - sig_bits->green;
+         shift[channels++] = row_info->bit_depth - sig_bits->blue;
+      }
+      else
+      {
+         shift[channels++] = row_info->bit_depth - sig_bits->gray;
+      }
+      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+      {
+         shift[channels++] = row_info->bit_depth - sig_bits->alpha;
+      }
+
+      switch (row_info->bit_depth)
+      {
+         case 2:
+         {
+            png_byte *bp;
+            png_uint_32 i;
+
+            for (bp = row, i = 0;
+               i < row_info->rowbytes;
+               i++, bp++)
+            {
+               *bp >>= 1;
+               *bp &= 0x55;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_byte *bp, mask;
+            png_uint_32 i;
+
+            mask = (png_byte)(((int)0xf0 >> shift[0]) & (int)0xf0) |
+               ((int)0xf >> shift[0]);
+            for (bp = row, i = 0;
+               i < row_info->rowbytes;
+               i++, bp++)
+            {
+               *bp >>= shift[0];
+               *bp &= mask;
+            }
+            break;
+         }
+         case 8:
+         {
+            png_byte *bp;
+            png_uint_32 i;
+
+            for (bp = row, i = 0;
+               i < row_info->width; i++)
+            {
+               int c;
+
+               for (c = 0; c < row_info->channels; c++, bp++)
+               {
+                  *bp >>= shift[c];
+               }
+            }
+            break;
+         }
+         case 16:
+         {
+            png_byte *bp;
+            png_uint_16 value;
+            png_uint_32 i;
+
+            for (bp = row, i = 0;
+               i < row_info->width; i++)
+            {
+               int c;
+
+               for (c = 0; c < row_info->channels; c++, bp += 2)
+               {
+                  value = (*bp << 8) + *(bp + 1);
+                  value >>= shift[c];
+                  *bp = value >> 8;
+                  *(bp + 1) = value & 0xff;
+               }
+            }
+            break;
+         }
+      }
+   }
+}
+
+/* chop rows of bit depth 16 down to 8 */
+void
+png_do_chop(png_row_info *row_info, png_byte *row)
+{
+   if (row && row_info && row_info->bit_depth == 16)
+   {
+      png_byte *sp, *dp;
+      png_uint_32 i;
+
+      sp = row + 2;
+      dp = row + 1;
+      for (i = 1; i < row_info->width * row_info->channels; i++)
+      {
+         *dp = *sp;
+         sp += 2;
+         dp++;
+      }
+      row_info->bit_depth = 8;
+      row_info->pixel_depth = 8 * row_info->channels;
+      row_info->rowbytes = row_info->width * row_info->channels;
+   }
+}
+
+/* add filler byte after rgb */
+void
+png_do_read_rgbx(png_row_info *row_info, png_byte *row)
+{
+   if (row && row_info && row_info->color_type == 2 &&
+      row_info->bit_depth == 8)
+   {
+      png_byte *sp, *dp;
+      png_uint_32 i;
+
+      for (i = 1, sp = row + (png_size_t)row_info->width * 3,
+         dp = row + (png_size_t)row_info->width * 4;
+         i < row_info->width;
+         i++)
+      {
+         *(--dp) = 0xff;
+         *(--dp) = *(--sp);
+         *(--dp) = *(--sp);
+         *(--dp) = *(--sp);
+      }
+      *(--dp) = 0xff;
+      row_info->channels = 4;
+      row_info->pixel_depth = 32;
+      row_info->rowbytes = row_info->width * 4;
+   }
+}
+
+/* add filler byte before rgb */
+void
+png_do_read_xrgb(png_row_info *row_info, png_byte *row)
+{
+   if (row && row_info && row_info->color_type == 2 &&
+      row_info->bit_depth == 8)
+   {
+      png_byte *sp, *dp;
+      png_uint_32 i;
+
+      for (i = 0, sp = row + (png_size_t)row_info->width * 3,
+         dp = row + (png_size_t)row_info->width * 4;
+         i < row_info->width;
+         i++)
+      {
+         *(--dp) = *(--sp);
+         *(--dp) = *(--sp);
+         *(--dp) = *(--sp);
+         *(--dp) = 0xff;
+      }
+      row_info->channels = 4;
+      row_info->pixel_depth = 32;
+      row_info->rowbytes = row_info->width * 4;
+   }
+}
+
+/* expand grayscale files to rgb, with or without alpha */
+void
+png_do_gray_to_rgb(png_row_info *row_info, png_byte *row)
+{
+   if (row && row_info && row_info->bit_depth >= 8 &&
+      !(row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            png_byte *sp, *dp;
+            png_uint_32 i;
+
+            for (i = 0, sp = row + (png_size_t)row_info->width - 1,
+               dp = row + (png_size_t)row_info->width * 3 - 1;
+               i < row_info->width;
+               i++)
+            {
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               sp--;
+            }
+         }
+         else
+         {
+            png_byte *sp, *dp;
+            png_uint_32 i;
+
+            for (i = 0, sp = row + (png_size_t)row_info->width * 2 - 1,
+               dp = row + (png_size_t)row_info->width * 6 - 1;
+               i < row_info->width;
+               i++)
+            {
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               sp--;
+               sp--;
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            png_byte *sp, *dp;
+            png_uint_32 i;
+
+            for (i = 0, sp = row + (png_size_t)row_info->width * 2 - 1,
+               dp = row + (png_size_t)row_info->width * 4 - 1;
+               i < row_info->width;
+               i++)
+            {
+               *(dp--) = *(sp--);
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               sp--;
+            }
+         }
+         else
+         {
+            png_byte *sp, *dp;
+            png_uint_32 i;
+
+            for (i = 0, sp = row + (png_size_t)row_info->width * 4 - 1,
+               dp = row + (png_size_t)row_info->width * 8 - 1;
+               i < row_info->width;
+               i++)
+            {
+               *(dp--) = *(sp--);
+               *(dp--) = *(sp--);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               sp--;
+               sp--;
+            }
+         }
+      }
+      row_info->channels += 2;
+      row_info->color_type |= PNG_COLOR_MASK_COLOR;
+      row_info->pixel_depth = row_info->channels * row_info->bit_depth;
+      row_info->rowbytes = ((row_info->width *
+         row_info->pixel_depth + 7) >> 3);
+   }
+}
+
+/* build a grayscale palette.  Palette is assumed to be 1 << bit_depth
+   large of png_color.  This lets grayscale images be treated as
+   paletted.  Most useful for gamma correction and simplification
+   of code. */
+void
+png_build_grayscale_palette(int bit_depth, png_color *palette)
+{
+   int num_palette;
+   int color_inc;
+   int i;
+   int v;
+
+   if (!palette)
+      return;
+
+   switch (bit_depth)
+   {
+      case 1:
+         num_palette = 2;
+         color_inc = 0xff;
+         break;
+      case 2:
+         num_palette = 4;
+         color_inc = 0x55;
+         break;
+      case 4:
+         num_palette = 16;
+         color_inc = 0x11;
+         break;
+      case 8:
+         num_palette = 256;
+         color_inc = 1;
+         break;
+      default:
+         num_palette = 0;
+         break;
+   }
+
+   for (i = 0, v = 0; i < num_palette; i++, v += color_inc)
+   {
+      palette[i].red = v;
+      palette[i].green = v;
+      palette[i].blue = v;
+   }
+}
+
+void
+png_correct_palette(png_struct *png_ptr, png_color *palette,
+   int num_palette)
+{
+   if ((png_ptr->transformations & (PNG_GAMMA)) &&
+      (png_ptr->transformations & (PNG_BACKGROUND)))
+   {
+      if (png_ptr->color_type == 3)
+      {
+         int i;
+         png_color back, back_1;
+
+         back.red = png_ptr->gamma_table[png_ptr->palette[
+            png_ptr->background.index].red];
+         back.green = png_ptr->gamma_table[png_ptr->palette[
+            png_ptr->background.index].green];
+         back.blue = png_ptr->gamma_table[png_ptr->palette[
+            png_ptr->background.index].blue];
+
+         back_1.red = png_ptr->gamma_to_1[png_ptr->palette[
+            png_ptr->background.index].red];
+         back_1.green = png_ptr->gamma_to_1[png_ptr->palette[
+            png_ptr->background.index].green];
+         back_1.blue = png_ptr->gamma_to_1[png_ptr->palette[
+            png_ptr->background.index].blue];
+
+         for (i = 0; i < num_palette; i++)
+         {
+            if (i < (int)png_ptr->num_trans &&
+               png_ptr->trans[i] == 0)
+            {
+               palette[i] = back;
+            }
+            else if (i < (int)png_ptr->num_trans &&
+               png_ptr->trans[i] != 0xff)
+            {
+               int v;
+
+               v = png_ptr->gamma_to_1[png_ptr->palette[i].red];
+               v = (int)(((png_uint_32)(v) *
+                  (png_uint_32)(png_ptr->trans[i]) +
+                  (png_uint_32)(back_1.red) *
+                  (png_uint_32)(255 - png_ptr->trans[i]) +
+                  127) / 255);
+               palette[i].red = png_ptr->gamma_from_1[v];
+
+               v = png_ptr->gamma_to_1[png_ptr->palette[i].green];
+               v = (int)(((png_uint_32)(v) *
+                  (png_uint_32)(png_ptr->trans[i]) +
+                  (png_uint_32)(back_1.green) *
+                  (png_uint_32)(255 - png_ptr->trans[i]) +
+                  127) / 255);
+               palette[i].green = png_ptr->gamma_from_1[v];
+
+               v = png_ptr->gamma_to_1[png_ptr->palette[i].blue];
+               v = (int)(((png_uint_32)(v) *
+                  (png_uint_32)(png_ptr->trans[i]) +
+                  (png_uint_32)(back_1.blue) *
+                  (png_uint_32)(255 - png_ptr->trans[i]) +
+                  127) / 255);
+               palette[i].blue = png_ptr->gamma_from_1[v];
+            }
+            else
+            {
+               palette[i].red = png_ptr->gamma_table[palette[i].red];
+               palette[i].green = png_ptr->gamma_table[palette[i].green];
+               palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+            }
+         }
+      }
+      else
+      {
+         int i, back;
+
+         back = png_ptr->gamma_table[png_ptr->background.gray];
+
+         for (i = 0; i < num_palette; i++)
+         {
+            if (palette[i].red == png_ptr->trans_values.gray)
+            {
+               palette[i].red = back;
+               palette[i].green = back;
+               palette[i].blue = back;
+            }
+            else
+            {
+               palette[i].red = png_ptr->gamma_table[palette[i].red];
+               palette[i].green = png_ptr->gamma_table[palette[i].green];
+               palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+            }
+         }
+      }
+   }
+   else if (png_ptr->transformations & (PNG_GAMMA))
+   {
+      int i;
+
+      for (i = 0; i < num_palette; i++)
+      {
+         palette[i].red = png_ptr->gamma_table[palette[i].red];
+         palette[i].green = png_ptr->gamma_table[palette[i].green];
+         palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+      }
+   }
+   else if (png_ptr->transformations & (PNG_BACKGROUND))
+   {
+      if (png_ptr->color_type == 3)
+      {
+         int i;
+         png_byte br, bg, bb;
+
+         br = palette[png_ptr->background.index].red;
+         bg = palette[png_ptr->background.index].green;
+         bb = palette[png_ptr->background.index].blue;
+
+         for (i = 0; i < num_palette; i++)
+         {
+            if (i >= (int)png_ptr->num_trans ||
+               png_ptr->trans[i] == 0)
+            {
+               palette[i].red = br;
+               palette[i].green = bg;
+               palette[i].blue = bb;
+            }
+            else if (i < (int)png_ptr->num_trans ||
+               png_ptr->trans[i] != 0xff)
+            {
+               palette[i].red = (png_byte)((
+                  (png_uint_32)(png_ptr->palette[i].red) *
+                  (png_uint_32)(png_ptr->trans[i]) +
+                  (png_uint_32)(br) *
+                  (png_uint_32)(255 - png_ptr->trans[i]) +
+                  127) / 255);
+               palette[i].green = (png_byte)((
+                  (png_uint_32)(png_ptr->palette[i].green) *
+                  (png_uint_32)(png_ptr->trans[i]) +
+                  (png_uint_32)(bg) *
+                  (png_uint_32)(255 - png_ptr->trans[i]) +
+                  127) / 255);
+               palette[i].blue = (png_byte)((
+                  (png_uint_32)(png_ptr->palette[i].blue) *
+                  (png_uint_32)(png_ptr->trans[i]) +
+                  (png_uint_32)(bb) *
+                  (png_uint_32)(255 - png_ptr->trans[i]) +
+                  127) / 255);
+            }
+         }
+      }
+      else /* assume grayscale palette (what else could it be?) */
+      {
+         int i;
+
+         for (i = 0; i < num_palette; i++)
+         {
+            if (i == (int)png_ptr->trans_values.gray)
+            {
+               palette[i].red = (png_byte)png_ptr->background.gray;
+               palette[i].green = (png_byte)png_ptr->background.gray;
+               palette[i].blue = (png_byte)png_ptr->background.gray;
+            }
+         }
+      }
+   }
+}
+
+/* replace any alpha or transparency with the supplied background color.
+   background is the color (in rgb or grey or palette index, as
+   appropriate).  note that paletted files are taken care of elsewhere */
+void
+png_do_background(png_row_info *row_info, png_byte *row,
+   png_color_16 *trans_values, png_color_16 *background,
+   png_color_16 *background_1,
+   png_byte *gamma_table, png_byte *gamma_from_1, png_byte *gamma_to_1,
+   png_uint_16 **gamma_16, png_uint_16 **gamma_16_from_1,
+   png_uint_16 **gamma_16_to_1, int gamma_shift)
+{
+   if (row && row_info && background &&
+      (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) ||
+      (row_info->color_type != PNG_COLOR_TYPE_PALETTE &&
+      trans_values)))
+   {
+      switch (row_info->color_type)
+      {
+         case PNG_COLOR_TYPE_GRAY:
+         {
+            switch (row_info->bit_depth)
+            {
+               case 1:
+               {
+                  png_byte *sp;
+                  int shift;
+                  png_uint_32 i;
+
+                  sp = row;
+                  shift = 7;
+                  for (i = 0; i < row_info->width; i++)
+                  {
+                     if (((*sp >> shift) & 0x1) ==
+                        trans_values->gray)
+                     {
+                        *sp &= ((0x7f7f >> (7 - shift)) & 0xff);
+                        *sp |= (background->gray << shift);
+                     }
+                     if (!shift)
+                     {
+                        shift = 7;
+                        sp++;
+                     }
+                     else
+                        shift--;
+                  }
+                  break;
+               }
+               case 2:
+               {
+                  png_byte *sp;
+                  int shift;
+                  png_uint_32 i;
+
+                  sp = row;
+                  shift = 6;
+                  for (i = 0; i < row_info->width; i++)
+                  {
+                     if (((*sp >> shift) & 0x3) ==
+                        trans_values->gray)
+                     {
+                        *sp &= ((0x3f3f >> (6 - shift)) & 0xff);
+                        *sp |= (background->gray << shift);
+                     }
+                     if (!shift)
+                     {
+                        shift = 6;
+                        sp++;
+                     }
+                     else
+                        shift -= 2;
+                  }
+                  break;
+               }
+               case 4:
+               {
+                  png_byte *sp;
+                  int shift;
+                  png_uint_32 i;
+
+                  sp = row + 1;
+                  shift = 4;
+                  for (i = 0; i < row_info->width; i++)
+                  {
+                     if (((*sp >> shift) & 0xf) ==
+                        trans_values->gray)
+                     {
+                        *sp &= ((0xf0f >> (4 - shift)) & 0xff);
+                        *sp |= (background->gray << shift);
+                     }
+                     if (!shift)
+                     {
+                        shift = 4;
+                        sp++;
+                     }
+                     else
+                        shift -= 4;
+                  }
+                  break;
+               }
+               case 8:
+               {
+                  if (gamma_table)
+                  {
+                     png_byte *sp;
+                     png_uint_32 i;
+
+                     for (i = 0, sp = row;
+                        i < row_info->width; i++, sp++)
+                     {
+                        if (*sp == trans_values->gray)
+                        {
+                           *sp = background->gray;
+                        }
+                        else
+                        {
+                           *sp = gamma_table[*sp];
+                        }
+                     }
+                  }
+                  else
+                  {
+                     png_byte *sp;
+                     png_uint_32 i;
+
+                     for (i = 0, sp = row;
+                        i < row_info->width; i++, sp++)
+                     {
+                        if (*sp == trans_values->gray)
+                        {
+                           *sp = background->gray;
+                        }
+                     }
+                  }
+                  break;
+               }
+               case 16:
+               {
+                  if (gamma_16)
+                  {
+                     png_byte *sp;
+                     png_uint_32 i;
+
+                     for (i = 0, sp = row;
+                        i < row_info->width; i++, sp += 2)
+                     {
+                        png_uint_16 v;
+
+                        v = ((png_uint_16)(*sp) << 8) +
+                           (png_uint_16)(*(sp + 1));
+                        if (v == trans_values->gray)
+                        {
+                           *sp = (background->gray >> 8) & 0xff;
+                           *(sp + 1) = background->gray & 0xff;
+                        }
+                        else
+                        {
+                           v = gamma_16[
+                              *(sp + 1) >> gamma_shift][*sp];
+                           *sp = (v >> 8) & 0xff;
+                           *(sp + 1) = v & 0xff;
+                        }
+                     }
+                  }
+                  else
+                  {
+                     png_byte *sp;
+                     png_uint_32 i;
+
+                     for (i = 0, sp = row;
+                        i < row_info->width; i++, sp += 2)
+                     {
+                        png_uint_16 v;
+
+                        v = ((png_uint_16)(*sp) << 8) +
+                           (png_uint_16)(*(sp + 1));
+                        if (v == trans_values->gray)
+                        {
+                           *sp = (background->gray >> 8) & 0xff;
+                           *(sp + 1) = background->gray & 0xff;
+                        }
+                     }
+                  }
+                  break;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_RGB:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               if (gamma_table)
+               {
+                  png_byte *sp;
+                  png_uint_32 i;
+
+                  for (i = 0, sp = row;
+                     i < row_info->width; i++, sp += 3)
+                  {
+                     if (*sp == trans_values->red &&
+                        *(sp + 1) == trans_values->green &&
+                        *(sp + 2) == trans_values->blue)
+                     {
+                        *sp = background->red;
+                        *(sp + 1) = background->green;
+                        *(sp + 2) = background->blue;
+                     }
+                     else
+                     {
+                        *sp = gamma_table[*sp];
+                        *(sp + 1) = gamma_table[*(sp + 1)];
+                        *(sp + 2) = gamma_table[*(sp + 2)];
+                     }
+                  }
+               }
+               else
+               {
+                  png_byte *sp;
+                  png_uint_32 i;
+
+                  for (i = 0, sp = row;
+                     i < row_info->width; i++, sp += 3)
+                  {
+                     if (*sp == trans_values->red &&
+                        *(sp + 1) == trans_values->green &&
+                        *(sp + 2) == trans_values->blue)
+                     {
+                        *sp = background->red;
+                        *(sp + 1) = background->green;
+                        *(sp + 2) = background->blue;
+                     }
+                  }
+               }
+            }
+            else if (row_info->bit_depth == 16)
+            {
+               if (gamma_16)
+               {
+                  png_byte *sp;
+                  png_uint_32 i;
+
+                  for (i = 0, sp = row;
+                     i < row_info->width; i++, sp += 6)
+                  {
+                     png_uint_16 r, g, b;
+
+                     r = ((png_uint_16)(*sp) << 8) +
+                        (png_uint_16)(*(sp + 1));
+                     g = ((png_uint_16)(*(sp + 2)) << 8) +
+                        (png_uint_16)(*(sp + 3));
+                     b = ((png_uint_16)(*(sp + 4)) << 8) +
+                        (png_uint_16)(*(sp + 5));
+                     if (r == trans_values->red &&
+                        g == trans_values->green &&
+                        b == trans_values->blue)
+                     {
+                        *sp = (background->red >> 8) & 0xff;
+                        *(sp + 1) = background->red & 0xff;
+                        *(sp + 2) = (background->green >> 8) & 0xff;
+                        *(sp + 3) = background->green & 0xff;
+                        *(sp + 4) = (background->blue >> 8) & 0xff;
+                        *(sp + 5) = background->blue & 0xff;
+                     }
+                     else
+                     {
+                        png_uint_16 v;
+                        v = gamma_16[
+                           *(sp + 1) >> gamma_shift][*sp];
+                        *sp = (v >> 8) & 0xff;
+                        *(sp + 1) = v & 0xff;
+                        v = gamma_16[
+                           *(sp + 3) >> gamma_shift][*(sp + 2)];
+                        *(sp + 2) = (v >> 8) & 0xff;
+                        *(sp + 3) = v & 0xff;
+                        v = gamma_16[
+                           *(sp + 5) >> gamma_shift][*(sp + 4)];
+                        *(sp + 4) = (v >> 8) & 0xff;
+                        *(sp + 5) = v & 0xff;
+                     }
+                  }
+               }
+               else
+               {
+                  png_byte *sp;
+                  png_uint_32 i;
+
+                  for (i = 0, sp = row;
+                     i < row_info->width; i++, sp += 6)
+                  {
+                     png_uint_16 r, g, b;
+
+                     r = ((png_uint_16)(*sp) << 8) +
+                        (png_uint_16)(*(sp + 1));
+                     g = ((png_uint_16)(*(sp + 2)) << 8) +
+                        (png_uint_16)(*(sp + 3));
+                     b = ((png_uint_16)(*(sp + 4)) << 8) +
+                        (png_uint_16)(*(sp + 5));
+                     if (r == trans_values->red &&
+                        g == trans_values->green &&
+                        b == trans_values->blue)
+                     {
+                        *sp = (background->red >> 8) & 0xff;
+                        *(sp + 1) = background->red & 0xff;
+                        *(sp + 2) = (background->green >> 8) & 0xff;
+                        *(sp + 3) = background->green & 0xff;
+                        *(sp + 4) = (background->blue >> 8) & 0xff;
+                        *(sp + 5) = background->blue & 0xff;
+                     }
+                  }
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_GRAY_ALPHA:
+         {
+            switch (row_info->bit_depth)
+            {
+               case 8:
+               {
+                  if (gamma_to_1 && gamma_from_1 && gamma_table)
+                  {
+                     png_byte *sp, *dp;
+                     png_uint_32 i;
+
+                     for (i = 0, sp = row,
+                        dp = row;
+                        i < row_info->width; i++, sp += 2, dp++)
+                     {
+                        png_uint_16 a;
+
+                        a = *(sp + 1);
+                        if (a == 0xff)
+                        {
+                           *dp = gamma_table[*sp];
+                        }
+                        else if (a == 0)
+                        {
+                           *dp = background->gray;
+                        }
+                        else
+                        {
+                           png_uint_16 v;
+
+                           v = gamma_to_1[*sp];
+                           v = ((png_uint_16)(v) * a +
+                              (png_uint_16)background_1->gray *
+                              (255 - a) + 127) / 255;
+                           *dp = gamma_from_1[v];
+                        }
+                     }
+                  }
+                  else
+                  {
+                     png_byte *sp, *dp;
+                     png_uint_32 i;
+
+                     for (i = 0, sp = row,
+                        dp = row;
+                        i < row_info->width; i++, sp += 2, dp++)
+                     {
+                        png_uint_16 a;
+
+                        a = *(sp + 1);
+                        if (a == 0xff)
+                        {
+                           *dp = *sp;
+                        }
+                        else if (a == 0)
+                        {
+                           *dp = background->gray;
+                        }
+                        else
+                        {
+                           *dp = ((png_uint_16)(*sp) * a +
+                              (png_uint_16)background_1->gray *
+                              (255 - a) + 127) / 255;
+                        }
+                     }
+                  }
+                  break;
+               }
+               case 16:
+               {
+                  if (gamma_16 && gamma_16_from_1 && gamma_16_to_1)
+                  {
+                     png_byte *sp, *dp;
+                     png_uint_32 i;
+
+                     for (i = 0, sp = row,
+                        dp = row;
+                        i < row_info->width; i++, sp += 4, dp += 2)
+                     {
+                        png_uint_16 a;
+
+                        a = ((png_uint_16)(*(sp + 2)) << 8) +
+                           (png_uint_16)(*(sp + 3));
+                        if (a == (png_uint_16)0xffff)
+                        {
+                           png_uint_32 v;
+
+                           v = gamma_16[
+                              *(sp + 1) >> gamma_shift][*sp];
+                           *dp = (png_byte)((v >> 8) & 0xff);
+                           *(dp + 1) = (png_byte)(v & 0xff);
+                        }
+                        else if (a == 0)
+                        {
+                           *dp = (background->gray >> 8) & 0xff;
+                           *(dp + 1) = background->gray & 0xff;
+                        }
+                        else
+                        {
+                           png_uint_32 g, v;
+
+                           g = gamma_16_to_1[
+                              *(sp + 1) >> gamma_shift][*sp];
+                           v = (g * (png_uint_32)a +
+                              (png_uint_32)background_1->gray *
+                              (png_uint_32)((png_uint_16)65535 - a) +
+                              (png_uint_16)32767) / (png_uint_16)65535;
+                           v = gamma_16_from_1[(size_t)(
+                              (v & 0xff) >> gamma_shift)][(size_t)(v >> 8)];
+                           *dp = (png_byte)((v >> 8) & 0xff);
+                           *(dp + 1) = (png_byte)(v & 0xff);
+                        }
+                     }
+                  }
+                  else
+                  {
+                     png_byte *sp, *dp;
+                     png_uint_32 i;
+
+                     for (i = 0, sp = row,
+                        dp = row;
+                        i < row_info->width; i++, sp += 4, dp += 2)
+                     {
+                        png_uint_16 a;
+
+                        a = ((png_uint_16)(*(sp + 2)) << 8) +
+                           (png_uint_16)(*(sp + 3));
+                        if (a == (png_uint_16)0xffff)
+                        {
+                           memcpy(dp, sp, 2);
+                        }
+                        else if (a == 0)
+                        {
+                           *dp = (background->gray >> 8) & 0xff;
+                           *(dp + 1) = background->gray & 0xff;
+                        }
+                        else
+                        {
+                           png_uint_32 g, v;
+
+                           g = ((png_uint_32)(*sp) << 8) +
+                              (png_uint_32)(*(sp + 1));
+                           v = (g * (png_uint_32)a +
+                              (png_uint_32)background_1->gray *
+                              (png_uint_32)((png_uint_16)65535 - a) +
+                              (png_uint_16)32767) / (png_uint_16)65535;
+                           *dp = (png_byte)((v >> 8) & 0xff);
+                           *(dp + 1) = (png_byte)(v & 0xff);
+                        }
+                     }
+                  }
+                  break;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_RGB_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               if (gamma_to_1 && gamma_from_1 && gamma_table)
+               {
+                  png_byte *sp, *dp;
+                  png_uint_32 i;
+
+                  for (i = 0, sp = row,
+                     dp = row;
+                     i < row_info->width; i++, sp += 4, dp += 3)
+                  {
+                     png_uint_16 a;
+
+                     a = *(sp + 3);
+                     if (a == 0xff)
+                     {
+                        *dp = gamma_table[*sp];
+                        *(dp + 1) = gamma_table[*(sp + 1)];
+                        *(dp + 2) = gamma_table[*(sp + 2)];
+                     }
+                     else if (a == 0)
+                     {
+                        *dp = background->red;
+                        *(dp + 1) = background->green;
+                        *(dp + 2) = background->blue;
+                     }
+                     else
+                     {
+                        png_uint_16 v;
+
+                        v = gamma_to_1[*sp];
+                        v = ((png_uint_16)(v) * a +
+                           (png_uint_16)background_1->red *
+                           (255 - a) + 127) / 255;
+                        *dp = gamma_from_1[v];
+                        v = gamma_to_1[*(sp + 1)];
+                        v = ((png_uint_16)(v) * a +
+                           (png_uint_16)background_1->green *
+                           (255 - a) + 127) / 255;
+                        *(dp + 1) = gamma_from_1[v];
+                        v = gamma_to_1[*(sp + 2)];
+                        v = ((png_uint_16)(v) * a +
+                           (png_uint_16)background_1->blue *
+                           (255 - a) + 127) / 255;
+                        *(dp + 2) = gamma_from_1[v];
+                     }
+                  }
+               }
+               else
+               {
+                  png_byte *sp, *dp;
+                  png_uint_32 i;
+
+                  for (i = 0, sp = row,
+                     dp = row;
+                     i < row_info->width; i++, sp += 4, dp += 3)
+                  {
+                     png_uint_16 a;
+
+                     a = *(sp + 3);
+                     if (a == 0xff)
+                     {
+                        *dp = *sp;
+                        *(dp + 1) = *(sp + 1);
+                        *(dp + 2) = *(sp + 2);
+                     }
+                     else if (a == 0)
+                     {
+                        *dp = background->red;
+                        *(dp + 1) = background->green;
+                        *(dp + 2) = background->blue;
+                     }
+                     else
+                     {
+                        *dp = ((png_uint_16)(*sp) * a +
+                           (png_uint_16)background->red *
+                           (255 - a) + 127) / 255;
+                        *(dp + 1) = ((png_uint_16)(*(sp + 1)) * a +
+                           (png_uint_16)background->green *
+                           (255 - a) + 127) / 255;
+                        *(dp + 2) = ((png_uint_16)(*(sp + 2)) * a +
+                           (png_uint_16)background->blue *
+                           (255 - a) + 127) / 255;
+                     }
+                  }
+               }
+            }
+            else if (row_info->bit_depth == 16)
+            {
+               if (gamma_16 && gamma_16_from_1 && gamma_16_to_1)
+               {
+                  png_byte *sp, *dp;
+                  png_uint_32 i;
+
+                  for (i = 0, sp = row,
+                     dp = row;
+                     i < row_info->width; i++, sp += 8, dp += 6)
+                  {
+                     png_uint_16 a;
+
+                     a = ((png_uint_16)(*(sp + 6)) << 8) +
+                        (png_uint_16)(*(sp + 7));
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_uint_16 v;
+
+                        v = gamma_16[
+                           *(sp + 1) >> gamma_shift][*sp];
+                        *dp = (v >> 8) & 0xff;
+                        *(dp + 1) = v & 0xff;
+                        v = gamma_16[
+                           *(sp + 3) >> gamma_shift][*(sp + 2)];
+                        *(dp + 2) = (v >> 8) & 0xff;
+                        *(dp + 3) = v & 0xff;
+                        v = gamma_16[
+                           *(sp + 5) >> gamma_shift][*(sp + 4)];
+                        *(dp + 4) = (v >> 8) & 0xff;
+                        *(dp + 5) = v & 0xff;
+                     }
+                     else if (a == 0)
+                     {
+                        *dp = (background->red >> 8) & 0xff;
+                        *(dp + 1) = background->red & 0xff;
+                        *(dp + 2) = (background->green >> 8) & 0xff;
+                        *(dp + 3) = background->green & 0xff;
+                        *(dp + 4) = (background->blue >> 8) & 0xff;
+                        *(dp + 5) = background->blue & 0xff;
+                     }
+                     else
+                     {
+                        png_uint_32 v;
+
+                        v = gamma_16_to_1[
+                           *(sp + 1) >> gamma_shift][*sp];
+                        v = (v * (png_uint_32)a +
+                           (png_uint_32)background->red *
+                           (png_uint_32)((png_uint_16)65535 - a) +
+                           (png_uint_16)32767) / (png_uint_16)65535;
+                        v = gamma_16_from_1[(size_t)(
+                           (v & 0xff) >> gamma_shift)][(size_t)(v >> 8)];
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                        v = gamma_16_to_1[
+                           *(sp + 3) >> gamma_shift][*(sp + 2)];
+                        v = (v * (png_uint_32)a +
+                           (png_uint_32)background->green *
+                           (png_uint_32)((png_uint_16)65535 - a) +
+                           (png_uint_16)32767) / (png_uint_16)65535;
+                        v = gamma_16_from_1[(size_t)(
+                           (v & 0xff) >> gamma_shift)][(size_t)(v >> 8)];
+                        *(dp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(v & 0xff);
+                        v = gamma_16_to_1[
+                           *(sp + 5) >> gamma_shift][*(sp + 4)];
+                        v = (v * (png_uint_32)a +
+                           (png_uint_32)background->blue *
+                           (png_uint_32)((png_uint_16)65535 - a) +
+                           (png_uint_16)32767) / (png_uint_16)65535;
+                        v = gamma_16_from_1[(size_t)(
+                           (v & 0xff) >> gamma_shift)][(size_t)(v >> 8)];
+                        *(dp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(v & 0xff);
+                     }
+                  }
+               }
+               else
+               {
+                  png_byte *sp, *dp;
+                  png_uint_32 i;
+
+                  for (i = 0, sp = row,
+                     dp = row;
+                     i < row_info->width; i++, sp += 8, dp += 6)
+                  {
+                     png_uint_16 a;
+
+                     a = ((png_uint_16)(*(sp + 6)) << 8) +
+                        (png_uint_16)(*(sp + 7));
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        memcpy(dp, sp, 6);
+                     }
+                     else if (a == 0)
+                     {
+                        *dp = (background->red >> 8) & 0xff;
+                        *(dp + 1) = background->red & 0xff;
+                        *(dp + 2) = (background->green >> 8) & 0xff;
+                        *(dp + 3) = background->green & 0xff;
+                        *(dp + 4) = (background->blue >> 8) & 0xff;
+                        *(dp + 5) = background->blue & 0xff;
+                     }
+                     else
+                     {
+                        png_uint_32 r, g, b, v;
+
+                        r = ((png_uint_32)(*sp) << 8) +
+                           (png_uint_32)(*(sp + 1));
+                        g = ((png_uint_32)(*(sp + 2)) << 8) +
+                           (png_uint_32)(*(sp + 3));
+                        b = ((png_uint_32)(*(sp + 4)) << 8) +
+                           (png_uint_32)(*(sp + 5));
+                        v = (r * (png_uint_32)a +
+                           (png_uint_32)background->red *
+                           (png_uint_32)((png_uint_32)65535 - a) +
+                           (png_uint_32)32767) / (png_uint_32)65535;
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                        v = (g * (png_uint_32)a +
+                           (png_uint_32)background->green *
+                           (png_uint_32)((png_uint_32)65535 - a) +
+                           (png_uint_32)32767) / (png_uint_32)65535;
+                        *(dp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(v & 0xff);
+                        v = (b * (png_uint_32)a +
+                           (png_uint_32)background->blue *
+                           (png_uint_32)((png_uint_32)65535 - a) +
+                           (png_uint_32)32767) / (png_uint_32)65535;
+                        *(dp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(v & 0xff);
+                     }
+                  }
+               }
+            }
+            break;
+         }
+      }
+      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+      {
+         row_info->color_type &= ~PNG_COLOR_MASK_ALPHA;
+         row_info->channels -= 1;
+         row_info->pixel_depth = row_info->channels *
+            row_info->bit_depth;
+         row_info->rowbytes = ((row_info->width *
+            row_info->pixel_depth + 7) >> 3);
+      }
+   }
+}
+
+/* gamma correct the image, avoiding the alpha channel.  Make sure
+   you do this after you deal with the trasparency issue on grayscale
+   or rgb images. If your bit depth is 8, use gamma_table, if it is 16,
+   use gamma_16_table and gamma_shift.  Build these with
+   build_gamma_table().  If your bit depth < 8, gamma correct a
+   palette, not the data.  */
+void
+png_do_gamma(png_row_info *row_info, png_byte *row,
+   png_byte *gamma_table, png_uint_16 **gamma_16_table,
+   int gamma_shift)
+{
+   if (row && row_info && ((row_info->bit_depth <= 8 && gamma_table) ||
+      (row_info->bit_depth == 16 && gamma_16_table)))
+   {
+      switch (row_info->color_type)
+      {
+         case PNG_COLOR_TYPE_RGB:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               png_byte *sp;
+               png_uint_32 i;
+
+               for (i = 0, sp = row;
+                  i < row_info->width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+               }
+            }
+            else if (row_info->bit_depth == 16)
+            {
+               png_byte *sp;
+               png_uint_32 i;
+
+               for (i = 0, sp = row;
+                  i < row_info->width; i++)
+               {
+                  png_uint_16 v;
+
+                  v = gamma_16_table[*(sp + 1) >>
+                     gamma_shift][*sp];
+                  *sp = (v >> 8) & 0xff;
+                  *(sp + 1) = v & 0xff;
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >>
+                     gamma_shift][*sp];
+                  *sp = (v >> 8) & 0xff;
+                  *(sp + 1) = v & 0xff;
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >>
+                     gamma_shift][*sp];
+                  *sp = (v >> 8) & 0xff;
+                  *(sp + 1) = v & 0xff;
+                  sp += 2;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_RGB_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               png_byte *sp;
+               png_uint_32 i;
+
+               for (i = 0, sp = row;
+                  i < row_info->width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  sp++;
+               }
+            }
+            else if (row_info->bit_depth == 16)
+            {
+               png_byte *sp;
+               png_uint_32 i;
+
+               for (i = 0, sp = row;
+                  i < row_info->width; i++)
+               {
+                  png_uint_16 v;
+
+                  v = gamma_16_table[*(sp + 1) >>
+                     gamma_shift][*sp];
+                  *sp = (v >> 8) & 0xff;
+                  *(sp + 1) = v & 0xff;
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >>
+                     gamma_shift][*sp];
+                  *sp = (v >> 8) & 0xff;
+                  *(sp + 1) = v & 0xff;
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >>
+                     gamma_shift][*sp];
+                  *sp = (v >> 8) & 0xff;
+                  *(sp + 1) = v & 0xff;
+                  sp += 4;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_GRAY_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               png_byte *sp;
+               png_uint_32 i;
+
+               for (i = 0, sp = row;
+                  i < row_info->width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  sp++;
+               }
+            }
+            else if (row_info->bit_depth == 16)
+            {
+               png_byte *sp;
+               png_uint_32 i;
+
+               for (i = 0, sp = row;
+                  i < row_info->width; i++)
+               {
+                  png_uint_16 v;
+
+                  v = gamma_16_table[*(sp + 1) >>
+                     gamma_shift][*sp];
+                  *sp = (v >> 8) & 0xff;
+                  *(sp + 1) = v & 0xff;
+                  sp += 4;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_GRAY:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               png_byte *sp;
+               png_uint_32 i;
+
+               for (i = 0, sp = row;
+                  i < row_info->width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+               }
+            }
+            else if (row_info->bit_depth == 16)
+            {
+               png_byte *sp;
+               png_uint_32 i;
+
+               for (i = 0, sp = row;
+                  i < row_info->width; i++)
+               {
+                  png_uint_16 v;
+
+                  v = gamma_16_table[*(sp + 1) >>
+                     gamma_shift][*sp];
+                  *sp = (v >> 8) & 0xff;
+                  *(sp + 1) = v & 0xff;
+                  sp += 2;
+               }
+            }
+            break;
+         }
+      }
+   }
+}
+
+/* expands a palette row to an rgb or rgba row depending
+   upon whether you supply trans and num_trans */
+void
+png_do_expand_palette(png_row_info *row_info, png_byte *row,
+   png_color *palette,
+   png_byte *trans, int num_trans)
+{
+   if (row && row_info && row_info->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (row_info->bit_depth < 8)
+      {
+         switch (row_info->bit_depth)
+         {
+            case 1:
+            {
+               png_byte *sp;
+               png_byte *dp;
+               int shift;
+               png_uint_32 i;
+
+               sp = row + (png_size_t)((row_info->width - 1) >> 3);
+               dp = row + (png_size_t)row_info->width - 1;
+               shift = 7 - (int)((row_info->width + 7) & 7);
+               for (i = 0; i < row_info->width; i++)
+               {
+                  if ((*sp >> shift) & 0x1)
+                     *dp = 1;
+                  else
+                     *dp = 0;
+                  if (shift == 7)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift++;
+
+                  dp--;
+               }
+               break;
+            }
+            case 2:
+            {
+               png_byte *sp;
+               png_byte *dp;
+               int shift, value;
+               png_uint_32 i;
+
+               sp = row + (png_size_t)((row_info->width - 1) >> 2);
+               dp = row + (png_size_t)row_info->width - 1;
+               shift = (int)((3 - ((row_info->width + 3) & 3)) << 1);
+               for (i = 0; i < row_info->width; i++)
+               {
+                  value = (*sp >> shift) & 0x3;
+                  *dp = value;
+                  if (shift == 6)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift += 2;
+
+                  dp--;
+               }
+               break;
+            }
+            case 4:
+            {
+               png_byte *sp;
+               png_byte *dp;
+               int shift, value;
+               png_uint_32 i;
+
+               sp = row + (png_size_t)((row_info->width - 1) >> 1);
+               dp = row + (png_size_t)row_info->width - 1;
+               shift = (int)((row_info->width & 1) << 2);
+               for (i = 0; i < row_info->width; i++)
+               {
+                  value = (*sp >> shift) & 0xf;
+                  *dp = value;
+                  if (shift == 4)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift += 4;
+
+                  dp--;
+               }
+               break;
+            }
+         }
+         row_info->bit_depth = 8;
+         row_info->pixel_depth = 8;
+         row_info->rowbytes = row_info->width;
+      }
+      switch (row_info->bit_depth)
+      {
+         case 8:
+         {
+            if (trans)
+            {
+               png_byte *sp, *dp;
+               png_uint_32 i;
+
+               sp = row + (png_size_t)row_info->width - 1;
+               dp = row + (png_size_t)(row_info->width << 2) - 1;
+
+               for (i = 0; i < row_info->width; i++)
+               {
+                  if (*sp >= (png_byte)num_trans)
+                     *dp-- = 0xff;
+                  else
+                     *dp-- = trans[*sp];
+                  *dp-- = palette[*sp].blue;
+                  *dp-- = palette[*sp].green;
+                  *dp-- = palette[*sp].red;
+                  sp--;
+               }
+               row_info->bit_depth = 8;
+               row_info->pixel_depth = 32;
+               row_info->rowbytes = row_info->width * 4;
+               row_info->color_type = 6;
+               row_info->channels = 4;
+            }
+            else
+            {
+               png_byte *sp, *dp;
+               png_uint_32 i;
+
+               sp = row + (png_size_t)row_info->width - 1;
+               dp = row + (png_size_t)(row_info->width * 3) - 1;
+
+               for (i = 0; i < row_info->width; i++)
+               {
+                  *dp-- = palette[*sp].blue;
+                  *dp-- = palette[*sp].green;
+                  *dp-- = palette[*sp].red;
+                  sp--;
+               }
+               row_info->bit_depth = 8;
+               row_info->pixel_depth = 24;
+               row_info->rowbytes = row_info->width * 3;
+               row_info->color_type = 2;
+               row_info->channels = 3;
+            }
+            break;
+         }
+      }
+   }
+}
+
+/* if the bit depth < 8, it is expanded to 8.  Also, if the
+   transparency value is supplied, an alpha channel is built. */
+void
+png_do_expand(png_row_info *row_info, png_byte *row,
+   png_color_16 *trans_value)
+{
+   if (row && row_info)
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_GRAY &&
+         row_info->bit_depth < 8)
+      {
+         switch (row_info->bit_depth)
+         {
+            case 1:
+            {
+               png_byte *sp;
+               png_byte *dp;
+               int shift;
+               png_uint_32 i;
+
+               sp = row + (png_size_t)((row_info->width - 1) >> 3);
+               dp = row + (png_size_t)row_info->width - 1;
+               shift = 7 - (int)((row_info->width + 7) & 7);
+               for (i = 0; i < row_info->width; i++)
+               {
+                  if ((*sp >> shift) & 0x1)
+                     *dp = 0xff;
+                  else
+                     *dp = 0;
+                  if (shift == 7)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift++;
+
+                  dp--;
+               }
+               break;
+            }
+            case 2:
+            {
+               png_byte *sp;
+               png_byte *dp;
+               int shift, value;
+               png_uint_32 i;
+
+               sp = row + (png_size_t)((row_info->width - 1) >> 2);
+               dp = row + (png_size_t)row_info->width - 1;
+               shift = (int)((3 - ((row_info->width + 3) & 3)) << 1);
+               for (i = 0; i < row_info->width; i++)
+               {
+                  value = (*sp >> shift) & 0x3;
+                  *dp = (value | (value << 2) | (value << 4) |
+                     (value << 6));
+                  if (shift == 6)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift += 2;
+
+                  dp--;
+               }
+               break;
+            }
+            case 4:
+            {
+               png_byte *sp;
+               png_byte *dp;
+               int shift, value;
+               png_uint_32 i;
+
+               sp = row + (png_size_t)((row_info->width - 1) >> 1);
+               dp = row + (png_size_t)row_info->width - 1;
+               shift = (int)((1 - ((row_info->width + 1) & 1)) << 2);
+               for (i = 0; i < row_info->width; i++)
+               {
+                  value = (*sp >> shift) & 0xf;
+                  *dp = (value | (value << 4));
+                  if (shift == 4)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift = 4;
+
+                  dp--;
+               }
+               break;
+            }
+         }
+         row_info->bit_depth = 8;
+         row_info->pixel_depth = 8;
+         row_info->rowbytes = row_info->width;
+      }
+      if (row_info->color_type == PNG_COLOR_TYPE_GRAY && trans_value)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            png_byte *sp, *dp;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)row_info->width - 1;
+            dp = row + (png_size_t)(row_info->width << 1) - 1;
+            for (i = 0; i < row_info->width; i++)
+            {
+               if (*sp == trans_value->gray)
+                  *dp-- = 0;
+               else
+                  *dp-- = 0xff;
+               *dp-- = *sp--;
+            }
+         }
+         else if (row_info->bit_depth == 16)
+         {
+            png_byte *sp, *dp;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)row_info->rowbytes - 1;
+            dp = row + (png_size_t)(row_info->rowbytes << 1) - 1;
+            for (i = 0; i < row_info->width; i++)
+            {
+               if (((png_uint_16)*(sp) |
+                  ((png_uint_16)*(sp - 1) << 8)) == trans_value->gray)
+               {
+                  *dp-- = 0;
+                  *dp-- = 0;
+               }
+               else
+               {
+                  *dp-- = 0xff;
+                  *dp-- = 0xff;
+               }
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+            }
+         }
+         row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+         row_info->channels = 2;
+         row_info->pixel_depth = (row_info->bit_depth << 1);
+         row_info->rowbytes =
+            ((row_info->width * row_info->pixel_depth) >> 3);
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            png_byte *sp, *dp;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)row_info->rowbytes - 1;
+            dp = row + (png_size_t)(row_info->width << 2) - 1;
+            for (i = 0; i < row_info->width; i++)
+            {
+               if (*(sp - 2) == trans_value->red &&
+                  *(sp - 1) == trans_value->green &&
+                  *(sp - 0) == trans_value->blue)
+                  *dp-- = 0;
+               else
+                  *dp-- = 0xff;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+            }
+         }
+         else if (row_info->bit_depth == 16)
+         {
+            png_byte *sp, *dp;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)row_info->rowbytes - 1;
+            dp = row + (png_size_t)(row_info->width << 3) - 1;
+            for (i = 0; i < row_info->width; i++)
+            {
+               if ((((png_uint_16)*(sp - 4) |
+                  ((png_uint_16)*(sp - 5) << 8)) == trans_value->red) &&
+                  (((png_uint_16)*(sp - 2) |
+                  ((png_uint_16)*(sp - 3) << 8)) == trans_value->green) &&
+                  (((png_uint_16)*(sp - 0) |
+                  ((png_uint_16)*(sp - 1) << 8)) == trans_value->blue))
+               {
+                  *dp-- = 0;
+                  *dp-- = 0;
+               }
+               else
+               {
+                  *dp-- = 0xff;
+                  *dp-- = 0xff;
+               }
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+            }
+         }
+         row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+         row_info->channels = 4;
+         row_info->pixel_depth = (row_info->bit_depth << 2);
+         row_info->rowbytes =
+            ((row_info->width * row_info->pixel_depth) >> 3);
+      }
+   }
+}
+
+void
+png_do_dither(png_row_info *row_info, png_byte *row,
+   png_byte *palette_lookup, png_byte *dither_lookup)
+{
+   if (row && row_info)
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB &&
+         palette_lookup && row_info->bit_depth == 8)
+      {
+         int r, g, b, p;
+         png_byte *sp, *dp;
+         png_uint_32 i;
+
+         sp = row;
+         dp = row;
+         for (i = 0; i < row_info->width; i++)
+         {
+            r = *sp++;
+            g = *sp++;
+            b = *sp++;
+
+            /* this looks real messy, but the compiler will reduce
+               it down to a reasonable formula.  For example, with
+               5 bits per color, we get:
+               p = (((r >> 3) & 0x1f) << 10) |
+                  (((g >> 3) & 0x1f) << 5) |
+                  ((b >> 3) & 0x1f);
+               */
+            p = (((r >> (8 - PNG_DITHER_RED_BITS)) &
+               ((1 << PNG_DITHER_RED_BITS) - 1)) <<
+               (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) |
+               (((g >> (8 - PNG_DITHER_GREEN_BITS)) &
+               ((1 << PNG_DITHER_GREEN_BITS) - 1)) <<
+               (PNG_DITHER_BLUE_BITS)) |
+               ((b >> (8 - PNG_DITHER_BLUE_BITS)) &
+               ((1 << PNG_DITHER_BLUE_BITS) - 1));
+
+            *dp++ = palette_lookup[p];
+         }
+         row_info->color_type = PNG_COLOR_TYPE_PALETTE;
+         row_info->channels = 1;
+         row_info->pixel_depth = row_info->bit_depth;
+         row_info->rowbytes =
+            ((row_info->width * row_info->pixel_depth + 7) >> 3);
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
+         palette_lookup && row_info->bit_depth == 8)
+      {
+         int r, g, b, p;
+         png_byte *sp, *dp;
+         png_uint_32 i;
+
+         sp = row;
+         dp = row;
+         for (i = 0; i < row_info->width; i++)
+         {
+            r = *sp++;
+            g = *sp++;
+            b = *sp++;
+            sp++;
+
+            p = (((r >> (8 - PNG_DITHER_RED_BITS)) &
+               ((1 << PNG_DITHER_RED_BITS) - 1)) <<
+               (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) |
+               (((g >> (8 - PNG_DITHER_GREEN_BITS)) &
+               ((1 << PNG_DITHER_GREEN_BITS) - 1)) <<
+               (PNG_DITHER_BLUE_BITS)) |
+               ((b >> (8 - PNG_DITHER_BLUE_BITS)) &
+               ((1 << PNG_DITHER_BLUE_BITS) - 1));
+
+            *dp++ = palette_lookup[p];
+         }
+         row_info->color_type = PNG_COLOR_TYPE_PALETTE;
+         row_info->channels = 1;
+         row_info->pixel_depth = row_info->bit_depth;
+         row_info->rowbytes =
+            ((row_info->width * row_info->pixel_depth + 7) >> 3);
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&
+         dither_lookup && row_info->bit_depth == 8)
+      {
+         png_byte *sp;
+         png_uint_32 i;
+
+         sp = row;
+         for (i = 0; i < row_info->width; i++, sp++)
+         {
+            *sp = dither_lookup[*sp];
+         }
+      }
+   }
+}
+
+static int png_gamma_shift[] =
+   {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0};
+
+void
+png_build_gamma_table(png_struct *png_ptr)
+{
+   if (png_ptr->bit_depth <= 8)
+   {
+      int i;
+      double g;
+
+      g = 1.0 / (png_ptr->gamma * png_ptr->display_gamma);
+
+      png_ptr->gamma_table = (png_byte *)png_malloc(png_ptr,
+         (png_uint_32)256);
+
+      for (i = 0; i < 256; i++)
+      {
+         png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0,
+            g) * 255.0 + .5);
+      }
+
+      if (png_ptr->transformations & PNG_BACKGROUND)
+      {
+         g = 1.0 / (png_ptr->gamma);
+
+         png_ptr->gamma_to_1 = (png_byte *)png_malloc(png_ptr,
+            (png_uint_32)256);
+
+         for (i = 0; i < 256; i++)
+         {
+            png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0,
+               g) * 255.0 + .5);
+         }
+
+         g = 1.0 / (png_ptr->display_gamma);
+
+         png_ptr->gamma_from_1 = (png_byte *)png_malloc(png_ptr,
+            (png_uint_32)256);
+
+         for (i = 0; i < 256; i++)
+         {
+            png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0,
+               g) * 255.0 + .5);
+         }
+      }
+   }
+   else
+   {
+      double g;
+      int i, j, shift, num;
+      int sig_bit;
+      png_uint_32 ig;
+
+      if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      {
+         sig_bit = (int)png_ptr->sig_bit.red;
+         if ((int)png_ptr->sig_bit.green > sig_bit)
+            sig_bit = png_ptr->sig_bit.green;
+         if ((int)png_ptr->sig_bit.blue > sig_bit)
+            sig_bit = png_ptr->sig_bit.blue;
+      }
+      else
+      {
+         sig_bit = (int)png_ptr->sig_bit.gray;
+      }
+
+      if (sig_bit > 0)
+         shift = 16 - sig_bit;
+      else
+         shift = 0;
+
+      if (png_ptr->transformations & PNG_16_TO_8)
+      {
+         if (shift < (16 - PNG_MAX_GAMMA_8))
+            shift = (16 - PNG_MAX_GAMMA_8);
+      }
+
+      if (shift > 8)
+         shift = 8;
+      if (shift < 0)
+         shift = 0;
+
+      png_ptr->gamma_shift = shift;
+
+      num = (1 << (8 - shift));
+
+      g = 1.0 / (png_ptr->gamma * png_ptr->display_gamma);
+
+      png_ptr->gamma_16_table = (png_uint_16 **)png_malloc(png_ptr,
+         num * sizeof (png_uint_16 *));
+
+      if ((png_ptr->transformations & PNG_16_TO_8) &&
+         !(png_ptr->transformations & PNG_BACKGROUND))
+      {
+         double fin, fout;
+         png_uint_32 last, max;
+
+         for (i = 0; i < num; i++)
+         {
+            png_ptr->gamma_16_table[i] = (png_uint_16 *)png_malloc(png_ptr,
+               256 * sizeof (png_uint_16));
+         }
+
+         g = 1.0 / g;
+      	last = 0;
+      	for (i = 0; i < 256; i++)
+         {
+		      fout = ((double)i + 0.5) / 256.0;
+      		fin = pow(fout, g);
+		      max = (png_uint_32)(fin * (double)(num << 8));
+      		while (last <= max)
+            {
+		      	png_ptr->gamma_16_table[(int)(last >> 8)]
+                  [(int)(last & 0xff)] =
+                  (png_uint_16)i | ((png_uint_16)i << 8);
+               last++;
+            }
+      	}
+      	while (last < (num << 8))
+         {
+		      png_ptr->gamma_16_table[(int)(last >> 8)][(int)(last & 0xff)] =
+               (png_uint_16)65535;
+            last++;
+         }
+      }
+      else
+      {
+         for (i = 0; i < num; i++)
+         {
+            png_ptr->gamma_16_table[i] = (png_uint_16 *)png_malloc(png_ptr,
+               256 * sizeof (png_uint_16));
+
+            ig = (((png_uint_32)i *
+               (png_uint_32)png_gamma_shift[shift]) >> 4);
+            for (j = 0; j < 256; j++)
+            {
+               png_ptr->gamma_16_table[i][j] =
+                  (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) /
+                     65535.0, g) * 65535.0 + .5);
+            }
+         }
+      }
+
+      if (png_ptr->transformations & PNG_BACKGROUND)
+      {
+         g = 1.0 / (png_ptr->gamma);
+
+         png_ptr->gamma_16_to_1 = (png_uint_16 **)png_malloc(png_ptr,
+            num * sizeof (png_uint_16 *));
+
+         for (i = 0; i < num; i++)
+         {
+            png_ptr->gamma_16_to_1[i] = (png_uint_16 *)png_malloc(png_ptr,
+               256 * sizeof (png_uint_16));
+
+            ig = (((png_uint_32)i *
+               (png_uint_32)png_gamma_shift[shift]) >> 4);
+            for (j = 0; j < 256; j++)
+            {
+               png_ptr->gamma_16_to_1[i][j] =
+                  (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) /
+                     65535.0, g) * 65535.0 + .5);
+            }
+         }
+         g = 1.0 / (png_ptr->display_gamma);
+
+         png_ptr->gamma_16_from_1 = (png_uint_16 **)png_malloc(png_ptr,
+            num * sizeof (png_uint_16 *));
+
+         for (i = 0; i < num; i++)
+         {
+            png_ptr->gamma_16_from_1[i] = (png_uint_16 *)png_malloc(png_ptr,
+               256 * sizeof (png_uint_16));
+
+            ig = (((png_uint_32)i *
+               (png_uint_32)png_gamma_shift[shift]) >> 4);
+            for (j = 0; j < 256; j++)
+            {
+               png_ptr->gamma_16_from_1[i][j] =
+                  (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) /
+                     65535.0, g) * 65535.0 + .5);
+            }
+         }
+      }
+   }
+}
diff --git a/pngrutil.c b/pngrutil.c
new file mode 100644
index 0000000..83a2973
--- /dev/null
+++ b/pngrutil.c
@@ -0,0 +1,1243 @@
+
+/* pngrutil.c - utilities to read a png file
+
+   libpng 1.0 beta 1 - version 0.71
+   For conditions of distribution and use, see copyright notice in png.h
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   June 26, 1995
+   */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* grab an uint 32 from a buffer */
+png_uint_32
+png_get_uint_32(png_byte *buf)
+{
+   png_uint_32 i;
+
+   i = ((png_uint_32)(*buf) << 24) +
+      ((png_uint_32)(*(buf + 1)) << 16) +
+      ((png_uint_32)(*(buf + 2)) << 8) +
+      (png_uint_32)(*(buf + 3));
+
+   return i;
+}
+
+/* grab an uint 16 from a buffer */
+png_uint_16
+png_get_uint_16(png_byte *buf)
+{
+   png_uint_16 i;
+
+   i = ((png_uint_16)(*buf) << 8) +
+      (png_uint_16)(*(buf + 1));
+
+   return i;
+}
+
+/* read data, and run it through the crc */
+void
+png_crc_read(png_struct *png_ptr, png_byte *buf, png_uint_32 length)
+{
+   png_read_data(png_ptr, buf, length);
+   png_calculate_crc(png_ptr, buf, length);
+}
+
+/* skip data, but calcuate the crc anyway */
+void
+png_crc_skip(png_struct *png_ptr, png_uint_32 length)
+{
+   png_uint_32 i;
+
+   for (i = length; i > png_ptr->zbuf_size; i -= png_ptr->zbuf_size)
+   {
+      png_read_data(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+      png_calculate_crc(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+   }
+   if (i)
+   {
+      png_read_data(png_ptr, png_ptr->zbuf, i);
+      png_calculate_crc(png_ptr, png_ptr->zbuf, i);
+   }
+}
+
+/* read and check the IDHR chunk */
+void
+png_handle_IHDR(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   png_byte buf[13];
+   png_uint_32 width, height;
+   int bit_depth, color_type, compression_type, filter_type;
+   int interlace_type;
+
+   /* check the length */
+   if (length != 13)
+      png_error(png_ptr, "Invalid IHDR chunk");
+
+   png_crc_read(png_ptr, buf, 13);
+
+   width = png_get_uint_32(buf);
+   height = png_get_uint_32(buf + 4);
+   bit_depth = buf[8];
+   color_type = buf[9];
+   compression_type = buf[10];
+   filter_type = buf[11];
+   interlace_type = buf[12];
+
+   /* check for width and height valid values */
+   if (width == 0 || height == 0)
+      png_error(png_ptr, "Invalid Width or Height Found");
+
+   /* check other values */
+   if (bit_depth != 1 && bit_depth != 2 &&
+      bit_depth != 4 && bit_depth != 8 &&
+      bit_depth != 16)
+      png_error(png_ptr, "Invalid Bit Depth Found");
+
+   if (color_type < 0 || color_type == 1 ||
+      color_type == 5 || color_type > 6)
+      png_error(png_ptr, "Invalid Color Type Found");
+
+   if (color_type == PNG_COLOR_TYPE_PALETTE &&
+      bit_depth == 16)
+      png_error(png_ptr, "Found Invalid Color Type and Bit Depth Combination");
+
+   if ((color_type == PNG_COLOR_TYPE_RGB ||
+      color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
+      color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
+      bit_depth < 8)
+      png_error(png_ptr, "Found Invalid Color Type and Bit Depth Combination");
+
+   if (interlace_type > 1)
+      png_error(png_ptr, "Found Invalid Interlace Value");
+
+   if (compression_type > 0)
+      png_error(png_ptr, "Found Invalid Compression Value");
+
+   if (filter_type > 0)
+      png_error(png_ptr, "Found Invalid Filter Value");
+
+   /* set internal variables */
+   png_ptr->width = width;
+   png_ptr->height = height;
+   png_ptr->bit_depth = bit_depth;
+   png_ptr->interlaced = interlace_type;
+   png_ptr->color_type = color_type;
+
+   /* find number of channels */
+   switch (png_ptr->color_type)
+   {
+      case 0:
+      case 3:
+         png_ptr->channels = 1;
+         break;
+      case 2:
+         png_ptr->channels = 3;
+         break;
+      case 4:
+         png_ptr->channels = 2;
+         break;
+      case 6:
+         png_ptr->channels = 4;
+         break;
+   }
+   /* set up other useful info */
+   png_ptr->pixel_depth = png_ptr->bit_depth *
+      png_ptr->channels;
+   png_ptr->rowbytes = ((png_ptr->width *
+      (png_uint_32)png_ptr->pixel_depth + 7) >> 3);
+   /* call the IHDR callback (which should just set up info) */
+   png_read_IHDR(png_ptr, info, width, height, bit_depth,
+      color_type, compression_type, filter_type, interlace_type);
+}
+
+/* read and check the palette */
+void
+png_handle_PLTE(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   int num, i;
+   png_color *palette;
+
+   if (length % 3)
+      png_error(png_ptr, "Invalid Palette Chunk");
+
+   num = (int)length / 3;
+   palette = (png_color *)png_malloc(png_ptr, num * sizeof (png_color));
+   for (i = 0; i < num; i++)
+   {
+      png_byte buf[3];
+
+      png_crc_read(png_ptr, buf, 3);
+      /* don't depend upon png_color being any order */
+      palette[i].red = buf[0];
+      palette[i].green = buf[1];
+      palette[i].blue = buf[2];
+   }
+   png_ptr->palette = palette;
+   png_ptr->num_palette = num;
+   png_read_PLTE(png_ptr, info, palette, num);
+}
+
+void
+png_handle_gAMA(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   png_uint_32 igamma;
+   float gamma;
+   png_byte buf[4];
+
+   if (length != 4)
+   {
+      png_crc_skip(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 4);
+   igamma = png_get_uint_32(buf);
+   /* check for zero gamma */
+   if (!igamma)
+      return;
+
+   gamma = (float)igamma / (float)100000.0;
+   png_read_gAMA(png_ptr, info, gamma);
+   png_ptr->gamma = gamma;
+}
+
+void
+png_handle_sBIT(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   int slen;
+   png_byte buf[4];
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      slen = 3;
+   else
+      slen = png_ptr->channels;
+
+   if (length != (png_uint_32)slen)
+   {
+      png_crc_skip(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, length);
+   if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+   {
+      png_ptr->sig_bit.red = buf[0];
+      png_ptr->sig_bit.green = buf[1];
+      png_ptr->sig_bit.blue = buf[2];
+      png_ptr->sig_bit.alpha = buf[3];
+   }
+   else
+   {
+      png_ptr->sig_bit.gray = buf[0];
+      png_ptr->sig_bit.alpha = buf[1];
+   }
+   png_read_sBIT(png_ptr, info, &(png_ptr->sig_bit));
+}
+
+void
+png_handle_cHRM(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   png_byte buf[4];
+   png_uint_32 v;
+   float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y;
+
+   if (length != 32)
+   {
+      png_crc_skip(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 4);
+   v = png_get_uint_32(buf);
+   white_x = (float)v / (float)100000.0;
+
+   png_crc_read(png_ptr, buf, 4);
+   v = png_get_uint_32(buf);
+   white_y = (float)v / (float)100000.0;
+
+   png_crc_read(png_ptr, buf, 4);
+   v = png_get_uint_32(buf);
+   red_x = (float)v / (float)100000.0;
+
+   png_crc_read(png_ptr, buf, 4);
+   v = png_get_uint_32(buf);
+   red_y = (float)v / (float)100000.0;
+
+   png_crc_read(png_ptr, buf, 4);
+   v = png_get_uint_32(buf);
+   green_x = (float)v / (float)100000.0;
+
+   png_crc_read(png_ptr, buf, 4);
+   v = png_get_uint_32(buf);
+   green_y = (float)v / (float)100000.0;
+
+   png_crc_read(png_ptr, buf, 4);
+   v = png_get_uint_32(buf);
+   blue_x = (float)v / (float)100000.0;
+
+   png_crc_read(png_ptr, buf, 4);
+   v = png_get_uint_32(buf);
+   blue_y = (float)v / (float)100000.0;
+
+   png_read_cHRM(png_ptr, info,
+      white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y);
+}
+
+void
+png_handle_tRNS(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (length > png_ptr->num_palette)
+      {
+         png_crc_skip(png_ptr, length);
+         return;
+      }
+
+      png_ptr->trans = png_malloc(png_ptr, length);
+      png_crc_read(png_ptr, png_ptr->trans, length);
+      png_ptr->num_trans = (int)length;
+   }
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+   {
+      png_byte buf[6];
+
+      if (length != 6)
+      {
+         png_crc_skip(png_ptr, length);
+         return;
+      }
+
+      png_crc_read(png_ptr, buf, length);
+      png_ptr->num_trans = 3;
+      png_ptr->trans_values.red = png_get_uint_16(buf);
+      png_ptr->trans_values.green = png_get_uint_16(buf + 2);
+      png_ptr->trans_values.blue = png_get_uint_16(buf + 4);
+   }
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      png_byte buf[6];
+
+      if (length != 2)
+      {
+         png_crc_skip(png_ptr, length);
+         return;
+      }
+
+      png_crc_read(png_ptr, buf, 2);
+      png_ptr->num_trans = 1;
+      png_ptr->trans_values.gray = png_get_uint_16(buf);
+   }
+   else
+      png_error(png_ptr, "Invalid tRNS chunk");
+
+   png_read_tRNS(png_ptr, info, png_ptr->trans, png_ptr->num_trans,
+      &(png_ptr->trans_values));
+}
+
+void
+png_handle_bKGD(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   int truelen;
+   png_byte buf[6];
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      truelen = 1;
+   else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      truelen = 6;
+   else
+      truelen = 2;
+
+   if (length != (png_uint_32)truelen)
+   {
+      png_crc_skip(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, length);
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      png_ptr->background.index = buf[0];
+   else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR))
+      png_ptr->background.gray = png_get_uint_16(buf);
+   else
+   {
+      png_ptr->background.red = png_get_uint_16(buf);
+      png_ptr->background.green = png_get_uint_16(buf + 2);
+      png_ptr->background.blue = png_get_uint_16(buf + 4);
+   }
+
+   png_read_bKGD(png_ptr, info, &(png_ptr->background));
+}
+
+void
+png_handle_hIST(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   int num, i;
+
+   if (length != 2 * png_ptr->num_palette)
+   {
+      png_crc_skip(png_ptr, length);
+      return;
+   }
+
+   num = (int)length / 2;
+   png_ptr->hist = png_malloc(png_ptr, num * sizeof (png_uint_16));
+   for (i = 0; i < num; i++)
+   {
+      png_byte buf[2];
+
+      png_crc_read(png_ptr, buf, 2);
+      png_ptr->hist[i] = png_get_uint_16(buf);
+   }
+   png_read_hIST(png_ptr, info, png_ptr->hist);
+}
+
+void
+png_handle_pHYs(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   png_byte buf[9];
+   png_uint_32 res_x, res_y;
+   int unit_type;
+
+   if (length != 9)
+   {
+      png_crc_skip(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 9);
+
+   res_x = png_get_uint_32(buf);
+   res_y = png_get_uint_32(buf + 4);
+   unit_type = buf[8];
+   png_read_pHYs(png_ptr, info, res_x, res_y, unit_type);
+}
+
+void
+png_handle_oFFs(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   png_byte buf[9];
+   png_uint_32 offset_x, offset_y;
+   int unit_type;
+
+   if (length != 9)
+   {
+      png_crc_skip(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 9);
+
+   offset_x = png_get_uint_32(buf);
+   offset_y = png_get_uint_32(buf + 4);
+   unit_type = buf[8];
+   png_read_oFFs(png_ptr, info, offset_x, offset_y, unit_type);
+}
+
+void
+png_handle_tIME(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   png_byte buf[7];
+   png_time mod_time;
+
+   if (length != 7)
+   {
+      png_crc_skip(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 7);
+
+   mod_time.second = buf[6];
+   mod_time.minute = buf[5];
+   mod_time.hour = buf[4];
+   mod_time.day = buf[3];
+   mod_time.month = buf[2];
+   mod_time.year = png_get_uint_16(buf);
+
+   png_read_tIME(png_ptr, info, &mod_time);
+}
+
+/* note: this does not correctly handle chunks that are > 64K */
+void
+png_handle_tEXt(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   char *key, *text;
+
+   text = NULL;
+
+   key = (char *)png_large_malloc(png_ptr, length + 1);
+   png_crc_read(png_ptr, (png_byte *)key, length);
+   key[(png_size_t)length] = '\0';
+
+   for (text = key; *text; text++)
+      /* empty loop */ ;
+
+   if (text != key + (png_size_t)length)
+      text++;
+
+   png_read_tEXt(png_ptr, info, key, text, length - (text - key));
+}
+
+/* note: this does not correctly handle chunks that are > 64K compressed */
+void
+png_handle_zTXt(png_struct *png_ptr, png_info *info, png_uint_32 length)
+{
+   char *key, *text;
+   int ret;
+   png_uint_32 text_size, key_size;
+
+   text = NULL;
+
+   key = png_large_malloc(png_ptr, length + 1);
+   png_crc_read(png_ptr, (png_byte *)key, length);
+   key[(png_size_t)length] = '\0';
+
+   for (text = key; *text; text++)
+      /* empty loop */ ;
+
+   /* zTXt can't have zero text */
+   if (text == key + (png_size_t)length)
+   {
+      png_large_free(png_ptr, key);
+      return;
+   }
+
+   text++;
+
+   if (*text) /* check compression byte */
+   {
+      png_large_free(png_ptr, key);
+      return;
+   }
+
+   text++;
+
+   png_ptr->zstream->next_in = (png_byte *)text;
+   png_ptr->zstream->avail_in = (uInt)(length - (text - key));
+   png_ptr->zstream->next_out = png_ptr->zbuf;
+   png_ptr->zstream->avail_out = (png_size_t)png_ptr->zbuf_size;
+
+   key_size = text - key;
+   text_size = 0;
+   text = NULL;
+
+   while (png_ptr->zstream->avail_in)
+   {
+      ret = inflate(png_ptr->zstream, Z_PARTIAL_FLUSH);
+      if (ret != Z_OK && ret != Z_STREAM_END)
+      {
+         inflateReset(png_ptr->zstream);
+         png_ptr->zstream->avail_in = 0;
+         png_large_free(png_ptr, key);
+         png_large_free(png_ptr, text);
+         return;
+      }
+      if (!png_ptr->zstream->avail_out || ret == Z_STREAM_END)
+      {
+         if (!text)
+         {
+            text = png_malloc(png_ptr,
+               png_ptr->zbuf_size - png_ptr->zstream->avail_out +
+                  key_size + 1);
+            memcpy(text + (png_size_t)key_size, png_ptr->zbuf,
+               (png_size_t)(png_ptr->zbuf_size - png_ptr->zstream->avail_out));
+            memcpy(text, key, (png_size_t)key_size);
+            text_size = key_size + (png_size_t)png_ptr->zbuf_size -
+               png_ptr->zstream->avail_out;
+            *(text + (png_size_t)text_size) = '\0';
+         }
+         else
+         {
+            char *tmp;
+
+            tmp = text;
+            text = png_large_malloc(png_ptr, text_size +
+               png_ptr->zbuf_size - png_ptr->zstream->avail_out + 1);
+            memcpy(text, tmp, (png_size_t)text_size);
+            png_large_free(png_ptr, tmp);
+            memcpy(text + (png_size_t)text_size, png_ptr->zbuf,
+               (png_size_t)(png_ptr->zbuf_size - png_ptr->zstream->avail_out));
+            text_size += png_ptr->zbuf_size - png_ptr->zstream->avail_out;
+            *(text + (png_size_t)text_size) = '\0';
+         }
+         if (ret != Z_STREAM_END)
+         {
+            png_ptr->zstream->next_out = png_ptr->zbuf;
+            png_ptr->zstream->avail_out = (uInt)png_ptr->zbuf_size;
+         }
+      }
+      else
+      {
+         break;
+      }
+
+      if (ret == Z_STREAM_END)
+         break;
+   }
+
+   inflateReset(png_ptr->zstream);
+   png_ptr->zstream->avail_in = 0;
+
+   if (ret != Z_STREAM_END)
+   {
+      png_large_free(png_ptr, key);
+      png_large_free(png_ptr, text);
+      return;
+   }
+
+   png_large_free(png_ptr, key);
+   key = text;
+   text += (png_size_t)key_size;
+   text_size -= key_size;
+
+   png_read_zTXt(png_ptr, info, key, text, text_size, 0);
+}
+
+/* Combines the row recently read in with the previous row.
+   This routine takes care of alpha and transparency if requested.
+   This routine also handles the two methods of progressive display
+   of interlaced images, depending on the mask value.
+   The mask value describes which pixels are to be combined with
+   the row.  The pattern always repeats every 8 pixels, so just 8
+   bits are needed.  A one indicates the pixels is to be combined,
+   a zero indicates the pixel is to be skipped.  This is in addition
+   to any alpha or transparency value associated with the pixel.  If
+   you want all pixels to be combined, pass 0xff (255) in mask.
+*/
+void
+png_combine_row(png_struct *png_ptr, png_byte *row,
+   int mask)
+{
+   if (mask == 0xff)
+   {
+      memcpy(row, png_ptr->row_buf + 1,
+         (png_size_t)((png_ptr->width *
+         png_ptr->row_info.pixel_depth + 7) >> 3));
+   }
+   else
+   {
+      switch (png_ptr->row_info.pixel_depth)
+      {
+         case 1:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            shift = 7;
+            m = 0x80;
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0x1;
+                  *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+                  *dp |= (value << shift);
+               }
+
+               if (shift == 0)
+               {
+                  shift = 7;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift--;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+         case 2:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            shift = 6;
+            m = 0x80;
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0x3;
+                  *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                  *dp |= (value << shift);
+               }
+
+               if (shift == 0)
+               {
+                  shift = 6;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift -= 2;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            shift = 4;
+            m = 0x80;
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0xf;
+                  *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                  *dp |= (value << shift);
+               }
+
+               if (shift == 0)
+               {
+                  shift = 4;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift -= 4;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+         default:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            png_uint_32 i;
+            int pixel_bytes, m;
+
+            pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  memcpy(dp, sp, pixel_bytes);
+               }
+
+               sp += pixel_bytes;
+               dp += pixel_bytes;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+      }
+   }
+}
+
+void
+png_do_read_interlace(png_row_info *row_info, png_byte *row, int pass)
+{
+   if (row && row_info)
+   {
+      png_uint_32 final_width;
+
+      final_width = row_info->width * png_pass_inc[pass];
+
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_byte *sp, *dp;
+            int sshift, dshift;
+            png_byte v;
+            png_uint_32 i;
+            int j;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 3);
+            sshift = 7 - (int)((row_info->width + 7) & 7);
+            dp = row + (png_size_t)((final_width - 1) >> 3);
+            dshift = 7 - (int)((final_width + 7) & 7);
+            for (i = row_info->width; i; i--)
+            {
+               v = (*sp >> sshift) & 0x1;
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == 7)
+                  {
+                     dshift = 0;
+                     dp--;
+                  }
+                  else
+                     dshift++;
+               }
+               if (sshift == 7)
+               {
+                  sshift = 0;
+                  sp--;
+               }
+               else
+                  sshift++;
+            }
+            break;
+         }
+         case 2:
+         {
+            png_byte *sp, *dp;
+            int sshift, dshift;
+            png_byte v;
+            png_uint_32 i, j;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 2);
+            sshift = (png_size_t)((3 - ((row_info->width + 3) & 3)) << 1);
+            dp = row + (png_size_t)((final_width - 1) >> 2);
+            dshift = (png_size_t)((3 - ((final_width + 3) & 3)) << 1);
+            for (i = row_info->width; i; i--)
+            {
+               v = (*sp >> sshift) & 0x3;
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff);
+                  *dp |= (v << dshift);
+                  if (dshift == 6)
+                  {
+                     dshift = 0;
+                     dp--;
+                  }
+                  else
+                     dshift += 2;
+               }
+               if (sshift == 6)
+               {
+                  sshift = 0;
+                  sp--;
+               }
+               else
+                  sshift += 2;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_byte *sp, *dp;
+            int sshift, dshift;
+            png_byte v;
+            png_uint_32 i;
+            int j;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 1);
+            sshift = (png_size_t)((1 - ((row_info->width + 1) & 1)) << 2);
+            dp = row + (png_size_t)((final_width - 1) >> 1);
+            dshift = (png_size_t)((1 - ((final_width + 1) & 1)) << 2);
+            for (i = row_info->width; i; i--)
+            {
+               v = (*sp >> sshift) & 0xf;
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff);
+                  *dp |= (v << dshift);
+                  if (dshift == 4)
+                  {
+                     dshift = 0;
+                     dp--;
+                  }
+                  else
+                     dshift = 4;
+               }
+               if (sshift == 4)
+               {
+                  sshift = 0;
+                  sp--;
+               }
+               else
+                  sshift = 4;
+            }
+            break;
+         }
+         default:
+         {
+            png_byte *sp, *dp;
+            png_byte v[8];
+            png_uint_32 i;
+            int j;
+            int pixel_bytes;
+
+            pixel_bytes = (row_info->pixel_depth >> 3);
+
+            sp = row + (png_size_t)((row_info->width - 1) * pixel_bytes);
+            dp = row + (png_size_t)((final_width - 1) * pixel_bytes);
+            for (i = row_info->width; i; i--)
+            {
+               memcpy(v, sp, pixel_bytes);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  memcpy(dp, v, pixel_bytes);
+                  dp -= pixel_bytes;
+               }
+               sp -= pixel_bytes;
+            }
+            break;
+         }
+      }
+      row_info->width = final_width;
+      row_info->rowbytes = ((final_width *
+         (png_uint_32)row_info->pixel_depth + 7) >> 3);
+   }
+}
+
+void
+png_read_filter_row(png_row_info *row_info, png_byte *row,
+   png_byte *prev_row, int filter)
+{
+   switch (filter)
+   {
+      case 0:
+         break;
+      case 1:
+      {
+         png_uint_32 i;
+         int bpp;
+         png_byte *rp;
+         png_byte *lp;
+
+         bpp = (row_info->pixel_depth + 7) / 8;
+         for (i = (png_uint_32)bpp, rp = row + bpp, lp = row;
+            i < row_info->rowbytes; i++, rp++, lp++)
+         {
+            *rp = (png_byte)(((int)(*rp) + (int)(*lp)) & 0xff);
+         }
+         break;
+      }
+      case 2:
+      {
+         png_uint_32 i;
+         png_byte *rp;
+         png_byte *pp;
+
+         for (i = 0, rp = row, pp = prev_row;
+            i < row_info->rowbytes; i++, rp++, pp++)
+         {
+            *rp = (png_byte)(((int)(*rp) + (int)(*pp)) & 0xff);
+         }
+         break;
+      }
+      case 3:
+      {
+         png_uint_32 i;
+         int bpp;
+         png_byte *rp;
+         png_byte *pp;
+         png_byte *lp;
+
+         bpp = (row_info->pixel_depth + 7) / 8;
+         for (i = 0, rp = row, pp = prev_row;
+            i < (png_uint_32)bpp; i++, rp++, pp++)
+         {
+            *rp = (png_byte)(((int)(*rp) +
+               ((int)(*pp) / 2)) & 0xff);
+         }
+         for (lp = row; i < row_info->rowbytes; i++, rp++, lp++, pp++)
+         {
+            *rp = (png_byte)(((int)(*rp) +
+               (int)(*pp + *lp) / 2) & 0xff);
+         }
+         break;
+      }
+      case 4:
+      {
+         int bpp;
+         png_uint_32 i;
+         png_byte *rp;
+         png_byte *pp;
+         png_byte *lp;
+         png_byte *cp;
+
+         bpp = (row_info->pixel_depth + 7) / 8;
+         for (i = 0, rp = row, pp = prev_row,
+            lp = row - bpp, cp = prev_row - bpp;
+            i < row_info->rowbytes; i++, rp++, pp++, lp++, cp++)
+         {
+            int a, b, c, pa, pb, pc, p;
+
+            b = *pp;
+            if (i >= (png_uint_32)bpp)
+            {
+               c = *cp;
+               a = *lp;
+            }
+            else
+            {
+               a = c = 0;
+            }
+            p = a + b - c;
+            pa = abs(p - a);
+            pb = abs(p - b);
+            pc = abs(p - c);
+
+            if (pa <= pb && pa <= pc)
+               p = a;
+            else if (pb <= pc)
+               p = b;
+            else
+               p = c;
+
+            *rp = (png_byte)(((int)(*rp) + p) & 0xff);
+         }
+         break;
+      }
+      default:
+         break;
+   }
+}
+
+void
+png_read_finish_row(png_struct *png_ptr)
+{
+   png_ptr->row_number++;
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+   if (png_ptr->interlaced)
+   {
+      png_ptr->row_number = 0;
+      memset(png_ptr->prev_row, 0, (png_size_t)png_ptr->rowbytes + 1);
+      do
+      {
+         png_ptr->pass++;
+         if (png_ptr->pass >= 7)
+            break;
+         png_ptr->iwidth = (png_ptr->width +
+            png_pass_inc[png_ptr->pass] - 1 -
+            png_pass_start[png_ptr->pass]) /
+            png_pass_inc[png_ptr->pass];
+         png_ptr->irowbytes = ((png_ptr->iwidth *
+            png_ptr->pixel_depth + 7) >> 3) + 1;
+         if (!(png_ptr->transformations & PNG_INTERLACE))
+         {
+            png_ptr->num_rows = (png_ptr->height +
+               png_pass_yinc[png_ptr->pass] - 1 -
+               png_pass_ystart[png_ptr->pass]) /
+               png_pass_yinc[png_ptr->pass];
+            if (!(png_ptr->num_rows))
+               continue;
+         }
+      } while (png_ptr->iwidth == 0);
+
+      if (png_ptr->pass < 7)
+         return;
+   }
+
+   if (!png_ptr->zlib_finished)
+   {
+      char extra;
+      int ret;
+
+      png_ptr->zstream->next_out = (Byte *)&extra;
+      png_ptr->zstream->avail_out = (uInt)1;
+      do
+      {
+         if (!(png_ptr->zstream->avail_in))
+         {
+            while (!png_ptr->idat_size)
+            {
+               png_byte buf[4];
+               png_uint_32 crc;
+
+               png_read_data(png_ptr, buf, 4);
+               crc = png_get_uint_32(buf);
+               if (((crc ^ 0xffffffffL) & 0xffffffffL) !=
+                  (png_ptr->crc & 0xffffffffL))
+                  png_error(png_ptr, "Bad CRC value");
+
+               png_read_data(png_ptr, buf, 4);
+               png_ptr->idat_size = png_get_uint_32(buf);
+               png_reset_crc(png_ptr);
+
+               png_crc_read(png_ptr, buf, 4);
+               if (memcmp(buf, png_IDAT, 4))
+                  png_error(png_ptr, "Not enough image data");
+
+            }
+            png_ptr->zstream->avail_in = (uInt)png_ptr->zbuf_size;
+            png_ptr->zstream->next_in = png_ptr->zbuf;
+            if (png_ptr->zbuf_size > png_ptr->idat_size)
+               png_ptr->zstream->avail_in = (uInt)png_ptr->idat_size;
+            png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream->avail_in);
+            png_ptr->idat_size -= png_ptr->zstream->avail_in;
+         }
+         ret = inflate(png_ptr->zstream, Z_PARTIAL_FLUSH);
+         if (ret == Z_STREAM_END)
+         {
+            if (!(png_ptr->zstream->avail_out) || png_ptr->zstream->avail_in ||
+               png_ptr->idat_size)
+               png_error(png_ptr, "Extra compressed data");
+            png_ptr->mode = PNG_AT_LAST_IDAT;
+            break;
+         }
+         if (ret != Z_OK)
+            png_error(png_ptr, "Compression Error");
+
+         if (!(png_ptr->zstream->avail_out))
+            png_error(png_ptr, "Extra compressed data");
+
+      } while (1);
+      png_ptr->zstream->avail_out = 0;
+   }
+
+   if (png_ptr->idat_size || png_ptr->zstream->avail_in)
+      png_error(png_ptr, "Extra compression data");
+
+   inflateReset(png_ptr->zstream);
+
+   png_ptr->mode = PNG_AT_LAST_IDAT;
+}
+
+void
+png_read_start_row(png_struct *png_ptr)
+{
+   int max_pixel_depth;
+   png_uint_32 rowbytes;
+
+   png_ptr->zstream->avail_in = 0;
+   png_init_read_transformations(png_ptr);
+   if (png_ptr->interlaced)
+   {
+      if (!(png_ptr->transformations & PNG_INTERLACE))
+         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
+            png_pass_ystart[0]) / png_pass_yinc[0];
+      else
+         png_ptr->num_rows = png_ptr->height;
+
+      png_ptr->iwidth = (png_ptr->width +
+         png_pass_inc[png_ptr->pass] - 1 -
+         png_pass_start[png_ptr->pass]) /
+         png_pass_inc[png_ptr->pass];
+      png_ptr->irowbytes = ((png_ptr->iwidth *
+         png_ptr->pixel_depth + 7) >> 3) + 1;
+   }
+   else
+   {
+      png_ptr->num_rows = png_ptr->height;
+      png_ptr->iwidth = png_ptr->width;
+      png_ptr->irowbytes = png_ptr->rowbytes + 1;
+   }
+
+   max_pixel_depth = png_ptr->pixel_depth;
+
+   if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8)
+   {
+      max_pixel_depth = 8;
+   }
+
+   if (png_ptr->transformations & (PNG_EXPAND | PNG_PACK))
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         if (png_ptr->num_trans)
+            max_pixel_depth = 32;
+         else
+            max_pixel_depth = 24;
+      }
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (max_pixel_depth < 8)
+            max_pixel_depth = 8;
+         if (png_ptr->num_trans)
+            max_pixel_depth *= 2;
+      }
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+      {
+         if (png_ptr->num_trans)
+         {
+            max_pixel_depth *= 4;
+            max_pixel_depth /= 3;
+         }
+      }
+   }
+
+   if (png_ptr->transformations & PNG_RGBA)
+   {
+      if (max_pixel_depth < 32)
+         max_pixel_depth = 32;
+   }
+
+   if (png_ptr->transformations & PNG_GRAY_TO_RGB)
+   {
+      if ((png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) ||
+         png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         if (max_pixel_depth <= 16)
+            max_pixel_depth = 32;
+         else if (max_pixel_depth <= 32)
+            max_pixel_depth = 64;
+      }
+      else
+      {
+         if (max_pixel_depth <= 8)
+            max_pixel_depth = 24;
+         else if (max_pixel_depth <= 16)
+            max_pixel_depth = 48;
+      }
+   }
+
+   /* align the width on the next larger 8 pixels.  Mainly used
+      for interlacing */
+   rowbytes = ((png_ptr->width + 7) & ~((png_uint_32)7));
+   /* calculate the maximum bytes needed, adding a byte and a pixel
+      for safety sake */
+   rowbytes = ((rowbytes * (png_uint_32)max_pixel_depth + 7) >> 3) +
+      1 + ((max_pixel_depth + 7) >> 3);
+#ifdef PNG_MAX_MALLOC_64K
+   if (rowbytes > 65536L)
+      png_error(png_ptr, "This image requires a row greater then 64KB");
+#endif
+   png_ptr->row_buf = (png_byte *)png_large_malloc(png_ptr, rowbytes);
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (png_ptr->rowbytes + 1 > 65536L)
+      png_error(png_ptr, "This image requires a row greater then 64KB");
+#endif
+   png_ptr->prev_row = png_large_malloc(png_ptr,
+      png_ptr->rowbytes + 1);
+
+   memset(png_ptr->prev_row, 0, (png_size_t)png_ptr->rowbytes + 1);
+
+   png_ptr->row_init = 1;
+
+   /* if we have to do any modifications of values for the transformations,
+      do them here */
+}
+
diff --git a/pngstub.c b/pngstub.c
new file mode 100644
index 0000000..6ae1469
--- /dev/null
+++ b/pngstub.c
@@ -0,0 +1,387 @@
+
+/* pngstub.c - stub functions for i/o and memory allocation
+
+   libpng 1.0 beta 1 - version 0.71
+   For conditions of distribution and use, see copyright notice in png.h
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   June 26, 1995
+
+   This file provides a location for all input/output, memory location,
+   and error handling.  Users which need special handling in these areas
+   are expected to modify the code in this file to meet their needs.  See
+   the instructions at each function. */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Write the data to whatever output you are using.  The default
+   routine writes to a file pointer.  If you need to write to something
+   else, this is the place to do it.  We suggest saving the old code
+   for future use, possibly in a #define.  Note that this routine sometimes
+   gets called with very small lengths, so you should implement some kind
+   of simple buffering if you are using unbuffered writes.  This should
+   never be asked to write more then 64K on a 16 bit machine.  The cast
+   to png_size_t is there for insurance, but if you are having problems
+   with it, you can take it out.  Just be sure to cast length to whatever
+   fwrite needs in that spot if you don't have a function prototype for
+   it. */
+void
+png_write_data(png_struct *png_ptr, png_byte *data, png_uint_32 length)
+{
+   png_uint_32 check;
+
+   check = fwrite(data, 1, (png_size_t)length, png_ptr->fp);
+   if (check != length)
+   {
+      png_error(png_ptr, "Write Error");
+   }
+}
+
+/* Read the data from whatever input you are using.  The default
+   routine reads from a file pointer.  If you need to read from something
+   else, this is the place to do it.  We suggest saving the old code
+   for future use.  Note that this routine sometimes gets called with
+   very small lengths, so you should implement some kind of simple
+   buffering if you are using unbuffered reads.  This should
+   never be asked to read more then 64K on a 16 bit machine.  The cast
+   to png_size_t is there for insurance, but if you are having problems
+   with it, you can take it out.  Just be sure to cast length to whatever
+   fread needs in that spot if you don't have a function prototype for
+   it. */
+void
+png_read_data(png_struct *png_ptr, png_byte *data, png_uint_32 length)
+{
+   png_uint_32 check;
+
+   check = fread(data, 1, (size_t)length, png_ptr->fp);
+   if (check != length)
+   {
+      png_error(png_ptr, "Read Error");
+   }
+}
+
+/* Initialize the input/output for the png file.  If you change
+   the read and write routines, you will probably need to change
+   this routine (or write your own).  If you change the parameters
+   of this routine, remember to change png.h also. */
+void
+png_init_io(png_struct *png_ptr, FILE *fp)
+{
+   png_ptr->fp = fp;
+}
+
+/* Allocate memory.  For reasonable files, size should never exceed
+   64K.  However, zlib may allocate more then 64K if you don't tell
+   it not to.  See zconf.h and png.h for more information. zlib does
+   need to allocate exactly 64K, so whatever you call here must
+   have the ability to do that. */
+
+/* Borland compilers have this habit of not giving you 64K chunks
+   that start on the segment in DOS mode.  This has not been observed
+   in Windows, and of course it doesn't matter in 32 bit mode, as there
+   are no segments.  Now libpng doesn't need that much memory normally,
+   but zlib does, so we have to normalize it, if necessary.  It would be
+   better if zlib worked in less then 64K, but it doesn't, so we
+   have to deal with it.  Truely, we are misusing farmalloc here,
+   as it is designed for use with huge pointers, which don't care
+   about segments.  So we allocate a large amount of memory, and
+   divvy off segments when needed.
+   */
+#ifdef __TURBOC__
+#ifndef __WIN32__
+
+/* NUM_SEG is the number of segments allocated at once */
+#define NUM_SEG 4
+typedef struct borland_seg_struct
+{
+   void *mem_ptr;
+   void *seg_ptr[NUM_SEG];
+   int seg_used[NUM_SEG];
+   int num_used;
+} borland_seg;
+
+borland_seg *save_array;
+int num_save_array;
+int max_save_array;
+
+#endif
+#endif
+
+void *
+png_large_malloc(png_struct *png_ptr, png_uint_32 size)
+{
+   void *ret;
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (size > (png_uint_32)65536L)
+      png_error(png_ptr, "Cannot Allocate > 64K");
+#endif
+
+#ifdef __TURBOC__
+#  ifdef __WIN32__
+   ret = farmalloc(size);
+#  else
+
+   if (size == 65536L)
+   {
+      unsigned long offset;
+      if (!save_array)
+      {
+         ret = farmalloc(size);
+         offset = (unsigned long)(ret);
+         offset &= 0xffffL;
+      }
+      else
+      {
+         ret = (void *)0;
+      }
+      if (save_array || offset)
+      {
+         int i, j;
+
+         if (ret)
+            farfree(ret);
+         ret = (void *)0;
+
+         if (!save_array)
+         {
+            unsigned long offset;
+            png_byte huge *ptr;
+            int i;
+
+            num_save_array = 1;
+            save_array = malloc(num_save_array * sizeof (borland_seg));
+            if (!save_array)
+               png_error(png_ptr, "Out of Memory");
+            save_array->mem_ptr = farmalloc(
+               (unsigned long)(NUM_SEG) * 65536L + 65528L);
+            if (!save_array->mem_ptr)
+               png_error(png_ptr, "Out of Memory");
+            offset = (unsigned long)(ret);
+            offset &= 0xffffL;
+            ptr = save_array->mem_ptr;
+            if (offset)
+               ptr += 65536L - offset;
+            for (i = 0; i < NUM_SEG; i++, ptr += 65536L)
+            {
+               save_array->seg_ptr[i] = ptr;
+               save_array->seg_used[i] = 0;
+            }
+            save_array->num_used = 0;
+         }
+
+         for (i = 0; i < num_save_array; i++)
+         {
+            for (j = 0; j < NUM_SEG; j++)
+            {
+               if (!save_array[i].seg_used[j])
+               {
+                  ret = save_array[i].seg_ptr[j];
+                  save_array[i].seg_used[j] = 1;
+                  save_array[i].num_used++;
+                  break;
+               }
+            }
+            if (ret)
+               break;
+         }
+
+         if (!ret)
+         {
+            unsigned long offset;
+            png_byte huge *ptr;
+
+            save_array = realloc(save_array,
+               (num_save_array + 1) * sizeof (borland_seg));
+            if (!save_array)
+               png_error(png_ptr, "Out of Memory");
+            save_array[num_save_array].mem_ptr = farmalloc(
+               (unsigned long)(NUM_SEG) * 65536L + 65528L);
+            if (!save_array[num_save_array].mem_ptr)
+               png_error(png_ptr, "Out of Memory");
+            offset = (unsigned long)(ret);
+            offset &= 0xffffL;
+            ptr = save_array[num_save_array].mem_ptr;
+            if (offset)
+               ptr += 65536L - offset;
+            for (i = 0; i < NUM_SEG; i++, ptr += 65536L)
+            {
+               save_array[num_save_array].seg_ptr[i] = ptr;
+               save_array[num_save_array].seg_used[i] = 0;
+            }
+            ret = save_array[num_save_array].seg_ptr[0];
+            save_array[num_save_array].seg_used[0] = 1;
+            save_array[num_save_array].num_used = 1;
+            num_save_array++;
+         }
+      }
+   }
+   else
+   {
+      ret = farmalloc(size);
+   }
+
+#  endif /* __WIN32__ */
+#else /* __TURBOC__ */
+#  ifdef _MSC_VER
+   ret = halloc(size, 1);
+#  else
+   /* everybody else, so normal malloc should do it. */
+   ret = malloc(size);
+#  endif
+#endif
+
+   if (!ret)
+   {
+      png_error(png_ptr, "Out of Memory");
+   }
+
+   return ret;
+}
+
+/* free a pointer allocated by png_large_malloc().  In the default
+  configuration, png_ptr is not used, but is passed in case it
+  is needed.  If ptr is NULL, return without taking any action. */
+void
+png_large_free(png_struct *png_ptr, void *ptr)
+{
+   if (!png_ptr)
+      return;
+
+   if (ptr != (void *)0)
+   {
+#ifdef __TURBOC__
+#  ifndef __WIN32__
+      int i, j;
+
+      for (i = 0; i < num_save_array; i++)
+      {
+         for (j = 0; j < NUM_SEG; j++)
+         {
+            if (ptr == save_array[i].seg_ptr[j])
+            {
+printf("freeing pointer: i, j: %d, %d\n", i, j);
+               save_array[i].seg_used[j] = 0;
+               ptr = 0;
+               save_array[i].num_used--;
+               if (!save_array[i].num_used)
+               {
+                  int k;
+printf("freeing array: %d\n", i);
+                  num_save_array--;
+                  farfree(save_array[i].mem_ptr);
+                  for (k = i; k < num_save_array; k++)
+                     save_array[k] = save_array[k + 1];
+                  if (!num_save_array)
+                  {
+                     free(save_array);
+                     save_array = 0;
+                  }
+               }
+               break;
+            }
+         }
+         if (!ptr)
+            break;
+      }
+
+#  endif
+      if (ptr)
+         farfree(ptr);
+#else
+#  ifdef _MSC_VER
+      hfree(ptr);
+#  else
+      free(ptr);
+#  endif
+#endif
+   }
+}
+
+/* Allocate memory.  This is called for smallish blocks only  It
+   should not get anywhere near 64K. */
+void *
+png_malloc(png_struct *png_ptr, png_uint_32 size)
+{
+   void *ret;
+
+   if (!png_ptr)
+      return ((void *)0);
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (size > (png_uint_32)65536L)
+      png_error(png_ptr, "Cannot Allocate > 64K");
+#endif
+
+   ret = malloc((png_size_t)size);
+
+   if (!ret)
+   {
+      png_error(png_ptr, "Out of Memory");
+   }
+
+   return ret;
+}
+
+/* Reallocate memory.  This will not get near 64K on a
+   even marginally reasonable file. */
+void *
+png_realloc(png_struct *png_ptr, void *ptr, png_uint_32 size)
+{
+   void *ret;
+
+   if (!png_ptr)
+      return ((void *)0);
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (size > (png_uint_32)65536L)
+      png_error(png_ptr, "Cannot Allocate > 64K");
+#endif
+
+   ret = realloc(ptr, (png_size_t)size);
+
+   if (!ret)
+   {
+      png_error(png_ptr, "Out of Memory");
+   }
+
+   return ret;
+}
+
+/* free a pointer allocated by png_malloc().  In the default
+  configuration, png_ptr is not used, but is passed incase it
+  is needed.  If ptr is NULL, return without taking any action. */
+void
+png_free(png_struct *png_ptr, void *ptr)
+{
+   if (!png_ptr)
+      return;
+
+   if (ptr != (void *)0)
+      free(ptr);
+}
+
+/* This function is called whenever there is an error.  Replace with
+   however you wish to handle the error.  Note that this function
+   MUST NOT return, or the program will crash */
+void
+png_error(png_struct *png_ptr, char *message)
+{
+   fprintf(stderr, "libpng error: %s\n", message);
+
+   longjmp(png_ptr->jmpbuf, 1);
+}
+
+/* This function is called when there is a warning, but the library
+   thinks it can continue anyway.  You don't have to do anything here
+   if you don't want to.  In the default configuration, png_ptr is
+   not used, but it is passed in case it may be useful. */
+void
+png_warning(png_struct *png_ptr, char *message)
+{
+   if (!png_ptr)
+      return;
+
+   fprintf(stderr, "libpng warning: %s\n", message);
+}
+
diff --git a/pngtest.c b/pngtest.c
new file mode 100644
index 0000000..ba60b13
--- /dev/null
+++ b/pngtest.c
@@ -0,0 +1,180 @@
+/* pngtest.c - a simple test program to test libpng 
+
+   libpng 1.0 beta 1 - version 0.71
+   For conditions of distribution and use, see copyright notice in png.h
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   June 26, 1995
+   */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "png.h"
+
+#ifdef __TURBOC__
+#include <mem.h>
+#endif
+
+/* defined so I can write to a file on gui/windowing platforms */
+#define STDERR stderr
+
+/* input and output filenames */
+char inname[] = "pngtest.png";
+char outname[] = "pngout.png";
+
+png_struct read_ptr;
+png_struct write_ptr;
+png_info info_ptr;
+png_info end_info;
+
+char inbuf[256], outbuf[256];
+
+int main()
+{
+	FILE *fpin, *fpout;
+	png_byte *row_buf;
+	png_uint_32 rowbytes;
+   png_uint_32 y;
+	int channels, num_pass, pass;
+
+	row_buf = (png_byte *)0;
+
+	fpin = fopen(inname, "rb");
+	if (!fpin)
+	{
+		fprintf(STDERR, "Could not find input file %s\n", inname);
+		return -1;
+	}
+
+	fpout = fopen(outname, "wb");
+	if (!fpin)
+	{
+		fprintf(STDERR, "could not open output file %s\n", outname);
+		fclose(fpin);
+		return -1;
+	}
+
+	if (setjmp(read_ptr.jmpbuf))
+	{
+		fprintf(STDERR, "libpng read error\n");
+		fclose(fpin);
+		fclose(fpout);
+		return -1;
+	}
+
+	if (setjmp(write_ptr.jmpbuf))
+	{
+		fprintf(STDERR, "libpng write error\n");
+		fclose(fpin);
+		fclose(fpout);
+		return -1;
+	}
+
+	png_read_init(&read_ptr);
+	png_write_init(&write_ptr);
+	png_info_init(&info_ptr);
+	png_info_init(&end_info);
+
+	png_init_io(&read_ptr, fpin);
+	png_init_io(&write_ptr, fpout);
+
+	png_read_info(&read_ptr, &info_ptr);
+	png_write_info(&write_ptr, &info_ptr);
+
+	if ((info_ptr.color_type & 3) == 2)
+		channels = 3;
+	else
+		channels = 1;
+	if (info_ptr.color_type & 4)
+		channels++;
+
+	rowbytes = ((info_ptr.width * info_ptr.bit_depth * channels + 7) >> 3);
+	row_buf = (png_byte *)malloc((size_t)rowbytes);
+	if (!row_buf)
+	{
+		fprintf(STDERR, "no memory to allocate row buffer\n");
+		png_read_destroy(&read_ptr, &info_ptr, (png_info *)0);
+		png_write_destroy(&write_ptr);
+		fclose(fpin);
+		fclose(fpout);
+		return -1;
+	}
+
+	if (info_ptr.interlace_type)
+	{
+		num_pass = png_set_interlace_handling(&read_ptr);
+		num_pass = png_set_interlace_handling(&write_ptr);
+	}
+	else
+	{
+		num_pass = 1;
+	}
+
+	for (pass = 0; pass < num_pass; pass++)
+	{
+		for (y = 0; y < info_ptr.height; y++)
+		{
+			png_read_rows(&read_ptr, &row_buf, (png_byte **)0, 1);
+			png_write_rows(&write_ptr, &row_buf, 1);
+		}
+	}
+
+	png_read_end(&read_ptr, &end_info);
+	png_write_end(&write_ptr, &end_info);
+
+	png_read_destroy(&read_ptr, &info_ptr, &end_info);
+	png_write_destroy(&write_ptr);
+
+	fclose(fpin);
+	fclose(fpout);
+
+	free(row_buf);
+
+	fpin = fopen(inname, "rb");
+
+	if (!fpin)
+	{
+		fprintf(STDERR, "could not find file %s\n", inname);
+		return -1;
+	}
+
+	fpout = fopen(outname, "rb");
+	if (!fpout)
+	{
+		fprintf(STDERR, "could not find file %s\n", outname);
+		fclose(fpin);
+		return -1;
+	}
+
+	while (1)
+	{
+		int num_in, num_out;
+
+		num_in = fread(inbuf, 1, 256, fpin);
+		num_out = fread(outbuf, 1, 256, fpout);
+
+		if (num_in != num_out)
+		{
+			fprintf(STDERR, "files are of a different size\n");
+			fclose(fpin);
+			fclose(fpout);
+			return -1;
+		}
+
+		if (!num_in)
+			break;
+
+		if (memcmp(inbuf, outbuf, num_in))
+		{
+			fprintf(STDERR, "files are different\n");
+			fclose(fpin);
+			fclose(fpout);
+			return -1;
+		}
+	}
+
+	fclose(fpin);
+	fclose(fpout);
+   fprintf(STDERR, "libpng passes test\n");
+
+   return 0;
+}
diff --git a/pngtest.png b/pngtest.png
new file mode 100644
index 0000000..6750663
--- /dev/null
+++ b/pngtest.png
Binary files differ
diff --git a/pngtodo.txt b/pngtodo.txt
new file mode 100644
index 0000000..b52a3aa
--- /dev/null
+++ b/pngtodo.txt
@@ -0,0 +1,22 @@
+pngtodo.txt - list of things to do for libpng
+
+   allow user to #define out unused transformations
+   medium memory model support
+   overlaying one image on top of another
+	optional palette creation
+	histogram creation
+	text conversion between different code types
+   cHRM transformation
+   support for other chunks being defined (sCAl, the gIF series,
+      and others that people come up with).
+	push reader
+   pull writer
+   better dithering
+   keep up with public chunks
+   other compression libraries
+   more exotic interlace handling
+   better filtering
+   C++ wrapper
+   other languages
+   comments of > 64K
+
diff --git a/pngtrans.c b/pngtrans.c
new file mode 100644
index 0000000..02b00bc
--- /dev/null
+++ b/pngtrans.c
@@ -0,0 +1,192 @@
+
+/* pngtrans.c - transforms the data in a row
+   routines used by both readers and writers
+
+   libpng 1.0 beta 1 - version 0.71
+   For conditions of distribution and use, see copyright notice in png.h
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   June 26, 1995
+   */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* turn on bgr to rgb mapping */
+void
+png_set_bgr(png_struct *png_ptr)
+{
+   png_ptr->transformations |= PNG_BGR;
+}
+
+/* turn on 16 bit byte swapping */
+void
+png_set_swap(png_struct *png_ptr)
+{
+   if (png_ptr->bit_depth == 16)
+      png_ptr->transformations |= PNG_SWAP_BYTES;
+}
+
+/* turn on pixel packing */
+void
+png_set_packing(png_struct *png_ptr)
+{
+   if (png_ptr->bit_depth < 8)
+   {
+      png_ptr->transformations |= PNG_PACK;
+      png_ptr->usr_bit_depth = 8;
+   }
+}
+
+void
+png_set_shift(png_struct *png_ptr, png_color_8 *true_bits)
+{
+   png_ptr->transformations |= PNG_SHIFT;
+   png_ptr->shift = *true_bits;
+}
+
+int
+png_set_interlace_handling(png_struct *png_ptr)
+{
+   if (png_ptr->interlaced)
+   {
+      png_ptr->transformations |= PNG_INTERLACE;
+      return 7;
+   }
+
+   return 1;
+}
+
+void
+png_set_rgbx(png_struct *png_ptr)
+{
+   png_ptr->transformations |= PNG_RGBA;
+   if (png_ptr->color_type == PNG_COLOR_TYPE_RGB &&
+      png_ptr->bit_depth == 8)
+      png_ptr->usr_channels = 4;
+}
+
+void
+png_set_xrgb(png_struct *png_ptr)
+{
+   png_ptr->transformations |= PNG_XRGB;
+   if (png_ptr->color_type == PNG_COLOR_TYPE_RGB &&
+      png_ptr->bit_depth == 8)
+      png_ptr->usr_channels = 4;
+}
+
+void
+png_set_invert_mono(png_struct *png_ptr)
+{
+   png_ptr->transformations |= PNG_INVERT_MONO;
+}
+
+/* invert monocrome grayscale data */
+void
+png_do_invert(png_row_info *row_info, png_byte *row)
+{
+   if (row && row_info && row_info->bit_depth == 1 &&
+      row_info->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      png_byte *rp;
+      png_uint_32 i;
+
+      for (i = 0, rp = row;
+         i < row_info->rowbytes;
+         i++, rp++)
+      {
+         *rp = ~(*rp);
+      }
+   }
+}
+
+/* swaps byte order on 16 bit depth images */
+void
+png_do_swap(png_row_info *row_info, png_byte *row)
+{
+   if (row && row_info && row_info->bit_depth == 16)
+   {
+      png_byte *rp, t;
+      png_uint_32 i;
+
+      for (i = 0, rp = row;
+         i < row_info->width * row_info->channels;
+         i++, rp += 2)
+      {
+         t = *rp;
+         *rp = *(rp + 1);
+         *(rp + 1) = t;
+      }
+   }
+}
+
+/* swaps red and blue */
+void
+png_do_bgr(png_row_info *row_info, png_byte *row)
+{
+   if (row && row_info && (row_info->color_type & 2))
+   {
+      if (row_info->color_type == 2 && row_info->bit_depth == 8)
+      {
+         png_byte *rp, t;
+         png_uint_32 i;
+
+         for (i = 0, rp = row;
+            i < row_info->width;
+            i++, rp += 3)
+         {
+            t = *rp;
+            *rp = *(rp + 2);
+            *(rp + 2) = t;
+         }
+      }
+      else if (row_info->color_type == 6 && row_info->bit_depth == 8)
+      {
+         png_byte *rp, t;
+         png_uint_32 i;
+
+         for (i = 0, rp = row;
+            i < row_info->width;
+            i++, rp += 4)
+         {
+            t = *rp;
+            *rp = *(rp + 2);
+            *(rp + 2) = t;
+         }
+      }
+      else if (row_info->color_type == 2 && row_info->bit_depth == 16)
+      {
+         png_byte *rp, t[2];
+         png_uint_32 i;
+
+         for (i = 0, rp = row;
+            i < row_info->width;
+            i++, rp += 6)
+         {
+            t[0] = *rp;
+            t[1] = *(rp + 1);
+            *rp = *(rp + 4);
+            *(rp + 1) = *(rp + 5);
+            *(rp + 4) = t[0];
+            *(rp + 5) = t[1];
+         }
+      }
+      else if (row_info->color_type == 6 && row_info->bit_depth == 16)
+      {
+         png_byte *rp, t[2];
+         png_uint_32 i;
+
+         for (i = 0, rp = row;
+            i < row_info->width;
+            i++, rp += 8)
+         {
+            t[0] = *rp;
+            t[1] = *(rp + 1);
+            *rp = *(rp + 4);
+            *(rp + 1) = *(rp + 5);
+            *(rp + 4) = t[0];
+            *(rp + 5) = t[1];
+         }
+      }
+   }
+}
+
diff --git a/pngwrite.c b/pngwrite.c
new file mode 100644
index 0000000..ac7e22f
--- /dev/null
+++ b/pngwrite.c
@@ -0,0 +1,408 @@
+
+/* pngwrite.c - general routines to write a png file
+
+   libpng 1.0 beta 1 - version 0.71
+   For conditions of distribution and use, see copyright notice in png.h
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   June 26, 1995
+   */
+
+/* get internal access to png.h */
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Writes all the png information.  This is the suggested way to use
+   the library.  If you have a new chunk to add, make a function to
+   write it, and put it in the correct location here.  If you want
+   the chunk written after the image data, put it in png_write_end().
+   I strongly encurage you to supply a PNG_INFO_ flag, and check
+   info->valid before writing the chunk, as that will keep the code
+   from breaking if you want to just write a plain png file.
+   If you have long comments, I suggest writing them in png_write_end(),
+   and compressing them. */
+void
+png_write_info(png_struct *png_ptr, png_info *info)
+{
+   png_write_sig(png_ptr); /* write PNG signature */
+   /* write IHDR information. */
+   png_write_IHDR(png_ptr, info->width, info->height, info->bit_depth,
+      info->color_type, info->compression_type, info->filter_type,
+      info->interlace_type);
+   /* the rest of these check to see if the valid field has the appropriate
+      flag set, and if it does, writes the chunk. */
+   if (info->valid & PNG_INFO_gAMA)
+      png_write_gAMA(png_ptr, info->gamma);
+   if (info->valid & PNG_INFO_sBIT)
+      png_write_sBIT(png_ptr, &(info->sig_bit), info->color_type);
+   if (info->valid & PNG_INFO_cHRM)
+      png_write_cHRM(png_ptr,
+         info->x_white, info->y_white,
+         info->x_red, info->y_red,
+         info->x_green, info->y_green,
+         info->x_blue, info->y_blue);
+   if (info->valid & PNG_INFO_PLTE)
+      png_write_PLTE(png_ptr, info->palette, info->num_palette);
+   if (info->valid & PNG_INFO_tRNS)
+      png_write_tRNS(png_ptr, info->trans, &(info->trans_values),
+         info->num_trans, info->color_type);
+   if (info->valid & PNG_INFO_bKGD)
+      png_write_bKGD(png_ptr, &(info->background), info->color_type);
+   if (info->valid & PNG_INFO_hIST)
+      png_write_hIST(png_ptr, info->hist, info->num_palette);
+   if (info->valid & PNG_INFO_pHYs)
+      png_write_pHYs(png_ptr, info->x_pixels_per_unit,
+         info->y_pixels_per_unit, info->phys_unit_type);
+   if (info->valid & PNG_INFO_oFFs)
+      png_write_oFFs(png_ptr, info->x_offset, info->y_offset,
+         info->offset_unit_type);
+   if (info->valid & PNG_INFO_tIME)
+      png_write_tIME(png_ptr, &(info->mod_time));
+   /* Check to see if we need to write text chunks */
+   if (info->num_text)
+   {
+      int i; /* local counter */
+
+      /* loop through the text chunks */
+      for (i = 0; i < info->num_text; i++)
+      {
+         /* if chunk is compressed */
+         if (info->text[i].compression >= 0)
+         {
+            /* write compressed chunk */
+            png_write_zTXt(png_ptr, info->text[i].key,
+               info->text[i].text, info->text[i].text_length,
+               info->text[i].compression);
+         }
+         else
+         {
+            /* write uncompressed chunk */
+            png_write_tEXt(png_ptr, info->text[i].key,
+               info->text[i].text, info->text[i].text_length);
+         }
+      }
+   }
+}
+
+/* writes the end of the png file.  If you don't want to write comments or
+   time information, you can pass NULL for info.  If you already wrote these
+   in png_write_info(), do not write them again here.  If you have long
+   comments, I suggest writing them here, and compressing them. */
+void
+png_write_end(png_struct *png_ptr, png_info *info)
+{
+   /* see if user wants us to write information chunks */
+   if (info)
+   {
+      /* check to see if user has supplied a time chunk */
+      if (info->valid & PNG_INFO_tIME)
+         png_write_tIME(png_ptr, &(info->mod_time));
+      /* check to see if we need to write comment chunks */
+      if (info->num_text)
+      {
+         int i; /* local index variable */
+
+         /* loop through comment chunks */
+         for (i = 0; i < info->num_text; i++)
+         {
+            /* check to see if comment is to be compressed */
+            if (info->text[i].compression >= 0)
+            {
+               /* write compressed chunk */
+               png_write_zTXt(png_ptr, info->text[i].key,
+                  info->text[i].text, info->text[i].text_length,
+                  info->text[i].compression);
+            }
+            else
+            {
+               /* write uncompressed chunk */
+               png_write_tEXt(png_ptr, info->text[i].key,
+                  info->text[i].text, info->text[i].text_length);
+            }
+         }
+      }
+   }
+   /* write end of png file */
+   png_write_IEND(png_ptr);
+}
+
+/* initialize the info structure */
+void
+png_info_init(png_info *info)
+{
+   /* set everything to 0 */
+   memset(info, 0, sizeof (png_info));
+}
+
+void
+png_convert_from_struct_tm(png_time *ptime, struct tm *ttime)
+{
+   ptime->year = 1900 + ttime->tm_year;
+   ptime->month = ttime->tm_mon + 1;
+   ptime->day = ttime->tm_mday;
+   ptime->hour = ttime->tm_hour;
+   ptime->minute = ttime->tm_min;
+   ptime->second = ttime->tm_sec;
+}
+
+void
+png_convert_from_time_t(png_time *ptime, time_t ttime)
+{
+   struct tm *tbuf;
+
+   tbuf = gmtime(&ttime);
+   png_convert_from_struct_tm(ptime, tbuf);
+}
+
+/* initialize png structure, and allocate any memory needed */
+void
+png_write_init(png_struct *png_ptr)
+{
+   jmp_buf tmp_jmp; /* to save current jump buffer */
+
+   /* save jump buffer */
+   memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
+   /* reset all variables to 0 */
+   memset(png_ptr, 0, sizeof (png_struct));
+   /* restore jump buffer */
+   memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = png_large_malloc(png_ptr, png_ptr->zbuf_size);
+   /* initialize zlib */
+   png_ptr->zstream = &(png_ptr->zstream_struct);
+   png_ptr->zstream->zalloc = png_zalloc;
+   png_ptr->zstream->zfree = png_zfree;
+   png_ptr->zstream->opaque = (voidp)png_ptr;
+   deflateInit(png_ptr->zstream, Z_BEST_COMPRESSION);
+   png_ptr->zstream->next_out = png_ptr->zbuf;
+   png_ptr->zstream->avail_out = (uInt)png_ptr->zbuf_size;
+}
+
+/* write a few rows of image data.  If the image is interlaced,
+   either you will have to write the 7 sub images, or, if you
+   have called png_set_interlace_handling(), you will have to
+   "write" the image seven times */
+void
+png_write_rows(png_struct *png_ptr, png_byte **row,
+   png_uint_32 num_rows)
+{
+   png_uint_32 i; /* row counter */
+   png_byte **rp; /* row pointer */
+
+   /* loop through the rows */
+   for (i = 0, rp = row; i < num_rows; i++, rp++)
+   {
+      png_write_row(png_ptr, *rp);
+   }
+}
+
+/* write the image.  You only need to call this function once, even
+   if you are writing an interlaced image. */
+void
+png_write_image(png_struct *png_ptr, png_byte **image)
+{
+   png_uint_32 i; /* row index */
+   int pass, num_pass; /* pass variables */
+   png_byte **rp; /* points to current row */
+
+   /* intialize interlace handling.  If image is not interlaced,
+      this will set pass to 1 */
+   num_pass = png_set_interlace_handling(png_ptr);
+   /* loop through passes */
+   for (pass = 0; pass < num_pass; pass++)
+   {
+      /* loop through image */
+      for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
+      {
+         png_write_row(png_ptr, *rp);
+      }
+   }
+}
+
+/* write a row of image data */
+void
+png_write_row(png_struct *png_ptr, png_byte *row)
+{
+   /* initialize transformations and other stuff if first time */
+   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
+   {
+      png_write_start_row(png_ptr);
+   }
+
+   /* if interlaced and not interested in row, return */
+   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+   {
+      switch (png_ptr->pass)
+      {
+         case 0:
+            if (png_ptr->row_number & 7)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 1:
+            if ((png_ptr->row_number & 7) || png_ptr->width < 5)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 2:
+            if ((png_ptr->row_number & 7) != 4)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 3:
+            if ((png_ptr->row_number & 3) || png_ptr->width < 3)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 4:
+            if ((png_ptr->row_number & 3) != 2)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 5:
+            if ((png_ptr->row_number & 1) || png_ptr->width < 2)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 6:
+            if (!(png_ptr->row_number & 1))
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+      }
+   }
+
+   /* set up row info for transformations */
+   png_ptr->row_info.color_type = png_ptr->color_type;
+   png_ptr->row_info.width = png_ptr->usr_width;
+   png_ptr->row_info.channels = png_ptr->usr_channels;
+   png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth;
+   png_ptr->row_info.pixel_depth = png_ptr->row_info.bit_depth *
+      png_ptr->row_info.channels;
+   png_ptr->row_info.rowbytes = ((png_ptr->row_info.width *
+      (png_uint_32)png_ptr->row_info.pixel_depth + 7) >> 3);
+
+   /* copy users row into buffer, leaving room for filter byte */
+   memcpy(png_ptr->row_buf + 1, row, (png_size_t)png_ptr->row_info.rowbytes);
+
+   /* handle interlacing */
+   if (png_ptr->interlaced && png_ptr->pass < 6 &&
+      (png_ptr->transformations & PNG_INTERLACE))
+   {
+      png_do_write_interlace(&(png_ptr->row_info),
+         png_ptr->row_buf + 1, png_ptr->pass);
+      /* this should always get caught above, but still ... */
+      if (!(png_ptr->row_info.width))
+      {
+         png_write_finish_row(png_ptr);
+         return;
+      }
+   }
+
+   /* handle other transformations */
+   if (png_ptr->transformations)
+      png_do_write_transformations(png_ptr);
+
+   /* filter rows that have been proved to help */
+   if (png_ptr->bit_depth >= 8 && png_ptr->color_type != 3)
+   {
+      /* save row to previous row */
+      memcpy(png_ptr->save_row, png_ptr->row_buf,
+         (png_size_t)png_ptr->row_info.rowbytes + 1);
+
+      /* filter row */
+      png_write_filter_row(&(png_ptr->row_info), png_ptr->row_buf,
+         png_ptr->prev_row);
+
+      /* trade saved pointer and prev pointer so next row references are correctly */
+      { /* scope limiter */
+         png_byte *tptr;
+
+         tptr = png_ptr->prev_row;
+         png_ptr->prev_row = png_ptr->save_row;
+         png_ptr->save_row = tptr;
+      }
+   }
+   else
+      /* set filter row to "none" */
+      png_ptr->row_buf[0] = 0;
+
+   /* set up the zlib input buffer */
+   png_ptr->zstream->next_in = png_ptr->row_buf;
+   png_ptr->zstream->avail_in = (uInt)png_ptr->row_info.rowbytes + 1;
+
+#ifdef zlibinout
+/* temp zlib problem */
+{
+   extern FILE *fpzlibin;
+
+   fwrite(png_ptr->row_buf, 1, png_ptr->zstream->avail_in, fpzlibin);
+}
+/* end temp zlib problem */
+#endif
+
+   /* repeat until we have compressed all the data */
+   do
+   {
+      int ret; /* return of zlib */
+
+      /* compress the data */
+      ret = deflate(png_ptr->zstream, Z_NO_FLUSH);
+      /* check for compression errors */
+      if (ret != Z_OK)
+      {
+         if (png_ptr->zstream->msg)
+            png_error(png_ptr, png_ptr->zstream->msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+
+      /* see if it is time to write another IDAT */
+      if (!png_ptr->zstream->avail_out)
+      {
+         /* write the IDAT and reset the zlib output buffer */
+         png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+         png_ptr->zstream->next_out = png_ptr->zbuf;
+         png_ptr->zstream->avail_out = (uInt)png_ptr->zbuf_size;
+      }
+   /* repeat until all data has been compressed */
+   } while (png_ptr->zstream->avail_in);
+
+   /* finish row - updates counters and flushes zlib if last row */
+   png_write_finish_row(png_ptr);
+}
+
+/* free any memory used in png struct */
+void
+png_write_destroy(png_struct *png_ptr)
+{
+   jmp_buf tmp_jmp; /* save jump buffer */
+
+   /* free any memory zlib uses */
+   deflateEnd(png_ptr->zstream);
+   /* free our memory.  png_free checks NULL for us. */
+   png_large_free(png_ptr, png_ptr->zbuf);
+   png_large_free(png_ptr, png_ptr->row_buf);
+   png_large_free(png_ptr, png_ptr->prev_row);
+   png_large_free(png_ptr, png_ptr->save_row);
+   /* reset structure */
+   memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
+   memset(png_ptr, 0, sizeof (png_struct));
+   memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
+}
+
diff --git a/pngwtran.c b/pngwtran.c
new file mode 100644
index 0000000..7423ade
--- /dev/null
+++ b/pngwtran.c
@@ -0,0 +1,331 @@
+
+/* pngwtran.c - transforms the data in a row for png writers
+
+   libpng 1.0 beta 1 - version 0.71
+   For conditions of distribution and use, see copyright notice in png.h
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   June 26, 1995
+   */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* transform the data according to the users wishes.  The order of
+   transformations is significant. */
+void
+png_do_write_transformations(png_struct *png_ptr)
+{
+   if (png_ptr->transformations & PNG_RGBA)
+      png_do_write_rgbx(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   if (png_ptr->transformations & PNG_XRGB)
+      png_do_write_xrgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   if (png_ptr->transformations & PNG_PACK)
+      png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->bit_depth);
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         &(png_ptr->shift));
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   if (png_ptr->transformations & PNG_BGR)
+      png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1);
+}
+
+/* pack pixels into bytes.  Pass the true bit depth in bit_depth.  The
+   row_info bit depth should be 8 (one pixel per byte).  The channels
+   should be 1 (this only happens on grayscale and paletted images) */
+void
+png_do_pack(png_row_info *row_info, png_byte *row, png_byte bit_depth)
+{
+   if (row_info && row && row_info->bit_depth == 8 &&
+      row_info->channels == 1)
+   {
+      switch (bit_depth)
+      {
+         case 1:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            int mask;
+            png_int_32 i;
+            int v;
+
+            sp = row;
+            dp = row;
+            mask = 0x80;
+            v = 0;
+            for (i = 0; i < row_info->width; i++)
+            {
+               if (*sp)
+                  v |= mask;
+               sp++;
+               if (mask > 1)
+                  mask >>= 1;
+               else
+               {
+                  mask = 0x80;
+                  *dp = v;
+                  dp++;
+                  v = 0;
+               }
+            }
+            if (mask != 0x80)
+               *dp = v;
+            break;
+         }
+         case 2:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            int shift;
+            png_int_32 i;
+            int v;
+            png_byte value;
+
+            sp = row;
+            dp = row;
+            shift = 6;
+            v = 0;
+            for (i = 0; i < row_info->width; i++)
+            {
+               value = *sp & 0x3;
+               v |= (value << shift);
+               if (shift == 0)
+               {
+                  shift = 6;
+                  *dp = v;
+                  dp++;
+                  v = 0;
+               }
+               else
+                  shift -= 2;
+               sp++;
+            }
+            if (shift != 6)
+                   *dp = v;
+            break;
+         }
+         case 4:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            int shift;
+            png_int_32 i;
+            int v;
+            png_byte value;
+
+            sp = row;
+            dp = row;
+            shift = 4;
+            v = 0;
+            for (i = 0; i < row_info->width; i++)
+            {
+               value = *sp & 0xf;
+               v |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 4;
+                  *dp = v;
+                  dp++;
+                  v = 0;
+               }
+               else
+                  shift -= 4;
+
+               sp++;
+            }
+            if (shift != 4)
+               *dp = v;
+            break;
+         }
+      }
+      row_info->bit_depth = bit_depth;
+      row_info->pixel_depth = bit_depth * row_info->channels;
+      row_info->rowbytes =
+         ((row_info->width * row_info->pixel_depth + 7) >> 3);
+   }
+}
+
+/* shift pixel values to take advantage of whole range.  Pass the
+   true number of bits in bit_depth.  The row should be packed
+   according to row_info->bit_depth.  Thus, if you had a row of
+   bit depth 4, but the pixels only had values from 0 to 7, you
+   would pass 3 as bit_depth, and this routine would translate the
+   data to 0 to 15. */
+void
+png_do_shift(png_row_info *row_info, png_byte *row, png_color_8 *bit_depth)
+{
+   if (row && row_info &&
+      row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      int shift_start[4], shift_dec[4];
+      int channels;
+
+      channels = 0;
+      if (row_info->color_type & PNG_COLOR_MASK_COLOR)
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->red;
+         shift_dec[channels] = bit_depth->red;
+         channels++;
+         shift_start[channels] = row_info->bit_depth - bit_depth->green;
+         shift_dec[channels] = bit_depth->green;
+         channels++;
+         shift_start[channels] = row_info->bit_depth - bit_depth->blue;
+         shift_dec[channels] = bit_depth->blue;
+         channels++;
+      }
+      else
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->gray;
+         shift_dec[channels] = bit_depth->gray;
+         channels++;
+      }
+      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->alpha;
+         shift_dec[channels] = bit_depth->alpha;
+         channels++;
+      }
+
+      /* with low row dephts, could only be grayscale, so one channel */
+      if (row_info->bit_depth < 8)
+      {
+         png_byte *bp;
+         png_uint_32 i;
+         int j;
+         png_byte mask;
+
+         if (bit_depth->gray == 1 && row_info->bit_depth == 2)
+            mask = 0x55;
+         else if (row_info->bit_depth == 4 && bit_depth->gray == 3)
+            mask = 0x11;
+         else
+            mask = 0xff;
+
+         for (bp = row, i = 0; i < row_info->rowbytes; i++, bp++)
+         {
+            int v;
+
+            v = *bp;
+            *bp = 0;
+            for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
+            {
+               if (j > 0)
+                  *bp |= (png_byte)((v << j) & 0xff);
+               else
+                  *bp |= (png_byte)((v >> (-j)) & mask);
+            }
+         }
+      }
+      else if (row_info->bit_depth == 8)
+      {
+         png_byte *bp;
+         png_uint_32 i;
+         int j;
+
+         for (bp = row, i = 0; i < row_info->width; i++)
+         {
+            int c;
+
+            for (c = 0; c < channels; c++, bp++)
+            {
+               int v;
+
+               v = *bp;
+               *bp = 0;
+               for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+               {
+                  if (j > 0)
+                     *bp |= (png_byte)((v << j) & 0xff);
+                  else
+                     *bp |= (png_byte)((v >> (-j)) & 0xff);
+               }
+            }
+         }
+      }
+      else
+      {
+         png_byte *bp;
+         png_uint_32 i;
+         int j;
+
+         for (bp = row, i = 0;
+            i < row_info->width * row_info->channels;
+            i++)
+         {
+            int c;
+
+            for (c = 0; c < channels; c++, bp += 2)
+            {
+               png_uint_16 value, v;
+
+               v = ((png_uint_16)(*bp) << 8) + (png_uint_16)(*(bp + 1));
+               value = 0;
+               for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+               {
+                  if (j > 0)
+                     value |= (png_uint_16)((v << j) & (png_uint_16)0xffff);
+                  else
+                     value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff);
+               }
+               *bp = value >> 8;
+               *(bp + 1) = value & 0xff;
+            }
+         }
+      }
+   }
+}
+
+/* remove filler byte after rgb */
+void
+png_do_write_rgbx(png_row_info *row_info, png_byte *row)
+{
+   if (row && row_info && row_info->color_type == PNG_COLOR_TYPE_RGB &&
+      row_info->bit_depth == 8)
+   {
+      png_byte *sp, *dp;
+      png_uint_32 i;
+
+      for (i = 1, sp = row + 4, dp = row + 3;
+         i < row_info->width;
+         i++)
+      {
+         *dp++ = *sp++;
+         *dp++ = *sp++;
+         *dp++ = *sp++;
+         sp++;
+      }
+      row_info->channels = 3;
+      row_info->pixel_depth = 24;
+      row_info->rowbytes = row_info->width * 3;
+   }
+}
+
+/* remove filler byte before rgb */
+void
+png_do_write_xrgb(png_row_info *row_info, png_byte *row)
+{
+   if (row && row_info && row_info->color_type == PNG_COLOR_TYPE_RGB &&
+      row_info->bit_depth == 8)
+   {
+      png_byte *sp, *dp;
+      png_uint_32 i;
+
+      for (i = 0, sp = row, dp = row;
+         i < row_info->width;
+         i++)
+      {
+         sp++;
+         *dp++ = *sp++;
+         *dp++ = *sp++;
+         *dp++ = *sp++;
+      }
+      row_info->channels = 3;
+      row_info->pixel_depth = 24;
+      row_info->rowbytes = row_info->width * 3;
+   }
+}
+
diff --git a/pngwutil.c b/pngwutil.c
new file mode 100644
index 0000000..89743d7
--- /dev/null
+++ b/pngwutil.c
@@ -0,0 +1,1080 @@
+
+/* pngwutil.c - utilities to write a png file
+
+   libpng 1.0 beta 1 - version 0.71
+   For conditions of distribution and use, see copyright notice in png.h
+   Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
+   June 26, 1995
+   */
+#define PNG_INTERNAL
+#include "png.h"
+
+/* place a 32 bit number into a buffer in png byte order.  We work
+   with unsigned numbers for convenience, you may have to cast
+   signed numbers (if you use any, most png data is unsigned). */
+void
+png_save_uint_32(png_byte *buf, png_uint_32 i)
+{
+   buf[0] = (png_byte)((i >> 24) & 0xff);
+   buf[1] = (png_byte)((i >> 16) & 0xff);
+   buf[2] = (png_byte)((i >> 8) & 0xff);
+   buf[3] = (png_byte)(i & 0xff);
+}
+
+/* place a 16 bit number into a buffer in png byte order */
+void
+png_save_uint_16(png_byte *buf, png_uint_16 i)
+{
+   buf[0] = (png_byte)((i >> 8) & 0xff);
+   buf[1] = (png_byte)(i & 0xff);
+}
+
+/* write a 32 bit number */
+void
+png_write_uint_32(png_struct *png_ptr, png_uint_32 i)
+{
+   png_byte buf[4];
+
+   buf[0] = (png_byte)((i >> 24) & 0xff);
+   buf[1] = (png_byte)((i >> 16) & 0xff);
+   buf[2] = (png_byte)((i >> 8) & 0xff);
+   buf[3] = (png_byte)(i & 0xff);
+   png_write_data(png_ptr, buf, 4);
+}
+
+/* write a 16 bit number */
+void
+png_write_uint_16(png_struct *png_ptr, png_uint_16 i)
+{
+   png_byte buf[2];
+
+   buf[0] = (png_byte)((i >> 8) & 0xff);
+   buf[1] = (png_byte)(i & 0xff);
+   png_write_data(png_ptr, buf, 2);
+}
+
+/* Write a png chunk all at once.  The type is an array of ASCII characters
+   representing the chunk name.  The array must be at least 4 bytes in
+   length, and does not need to be null terminated.  To be safe, pass the
+   pre-defined chunk names here, and if you need a new one, define it
+   where the others are defined.  The length is the length of the data.
+   All the data must be present.  If that is not possible, use the
+   png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end()
+   functions instead.  */
+void
+png_write_chunk(png_struct *png_ptr, png_byte *type,
+   png_byte *data, png_uint_32 length)
+{
+   /* write length */
+   png_write_uint_32(png_ptr, length);
+   /* write chunk name */
+   png_write_data(png_ptr, type, (png_uint_32)4);
+   /* reset the crc and run the chunk name over it */
+   png_reset_crc(png_ptr);
+   png_calculate_crc(png_ptr, type, (png_uint_32)4);
+   /* write the data and update the crc */
+   if (length)
+   {
+      png_calculate_crc(png_ptr, data, length);
+      png_write_data(png_ptr, data, length);
+   }
+   /* write the crc */
+   png_write_uint_32(png_ptr, ~png_ptr->crc);
+}
+
+/* Write the start of a png chunk.  The type is the chunk type.
+   The total_length is the sum of the lengths of all the data you will be
+   passing in png_write_chunk_data() */
+void
+png_write_chunk_start(png_struct *png_ptr, png_byte *type,
+   png_uint_32 total_length)
+{
+   /* write the length */
+   png_write_uint_32(png_ptr, total_length);
+   /* write the chunk name */
+   png_write_data(png_ptr, type, (png_uint_32)4);
+   /* reset the crc and run it over the chunk name */
+   png_reset_crc(png_ptr);
+   png_calculate_crc(png_ptr, type, (png_uint_32)4);
+}
+
+/* write the data of a png chunk started with png_write_chunk_start().
+   Note that multiple calls to this function are allowed, and that the
+   sum of the lengths from these calls *must* add up to the total_length
+   given to png_write_chunk_start() */
+void
+png_write_chunk_data(png_struct *png_ptr, png_byte *data, png_uint_32 length)
+{
+   /* write the data, and run the crc over it */
+   if (length)
+   {
+      png_calculate_crc(png_ptr, data, length);
+      png_write_data(png_ptr, data, length);
+   }
+}
+
+/* finish a chunk started with png_write_chunk_start() */
+void
+png_write_chunk_end(png_struct *png_ptr)
+{
+   /* write the crc */
+   png_write_uint_32(png_ptr, ~png_ptr->crc);
+}
+
+/* simple function to write the signature */
+void
+png_write_sig(png_struct *png_ptr)
+{
+   /* write the 8 byte signature */
+   png_write_data(png_ptr, png_sig, (png_uint_32)8);
+}
+
+/* Write the IHDR chunk, and update the png_struct with the necessary
+   information.  Note that the rest of this code depends upon this
+   information being correct.  */
+void
+png_write_IHDR(png_struct *png_ptr, png_uint_32 width, png_uint_32 height,
+   int bit_depth, int color_type, int compression_type, int filter_type,
+   int interlace_type)
+{
+   png_byte buf[13]; /* buffer to store the IHDR info */
+
+   /* pack the header information into the buffer */
+   png_save_uint_32(buf, width);
+   png_save_uint_32(buf + 4, height);
+   buf[8] = bit_depth;
+   buf[9] = color_type;
+   buf[10] = compression_type;
+   buf[11] = filter_type;
+   buf[12] = interlace_type;
+   /* save off the relevent information */
+   png_ptr->bit_depth = bit_depth;
+   png_ptr->color_type = color_type;
+   png_ptr->interlaced = interlace_type;
+   png_ptr->width = width;
+   png_ptr->height = height;
+
+   switch (color_type)
+   {
+      case 0:
+      case 3:
+         png_ptr->channels = 1;
+         break;
+      case 2:
+         png_ptr->channels = 3;
+         break;
+      case 4:
+         png_ptr->channels = 2;
+         break;
+      case 6:
+         png_ptr->channels = 4;
+         break;
+   }
+   png_ptr->pixel_depth = bit_depth * png_ptr->channels;
+   png_ptr->rowbytes = ((width * (png_uint_32)png_ptr->pixel_depth + 7) >> 3);
+   /* set the usr info, so any transformations can modify it */
+   png_ptr->usr_width = png_ptr->width;
+   png_ptr->usr_bit_depth = png_ptr->bit_depth;
+    png_ptr->usr_channels = png_ptr->channels;
+
+   /* write the chunk */
+   png_write_chunk(png_ptr, png_IHDR, buf, (png_uint_32)13);
+}
+
+/* write the palette.  We are careful not to trust png_color to be in the
+   correct order for PNG, so people can redefine it to any convient
+   structure. */
+void
+png_write_PLTE(png_struct *png_ptr, png_color *palette, int number)
+{
+   int i;
+   png_color *pal_ptr;
+   png_byte buf[3];
+
+   png_write_chunk_start(png_ptr, png_PLTE, number * 3);
+   for (i = 0, pal_ptr = palette;
+      i < number;
+      i++, pal_ptr++)
+   {
+      buf[0] = pal_ptr->red;
+      buf[1] = pal_ptr->green;
+      buf[2] = pal_ptr->blue;
+      png_write_chunk_data(png_ptr, buf, (png_uint_32)3);
+   }
+   png_write_chunk_end(png_ptr);
+}
+
+/* write an IDAT chunk */
+void
+png_write_IDAT(png_struct *png_ptr, png_byte *data, png_uint_32 length)
+{
+#ifdef zlibinout
+/* temp zlib problem */
+{
+   extern FILE *fpzlibout;
+
+   fwrite(data, 1, length, fpzlibout);
+}
+/* end temp zlib problem */
+#endif
+
+   png_write_chunk(png_ptr, png_IDAT, data, length);
+}
+
+/* write an IEND chunk */
+void
+png_write_IEND(png_struct *png_ptr)
+{
+   png_write_chunk(png_ptr, png_IEND, NULL, (png_uint_32)0);
+}
+
+/* write a gAMA chunk */
+void
+png_write_gAMA(png_struct *png_ptr, float gamma)
+{
+   png_uint_32 igamma;
+   png_byte buf[4];
+
+   /* gamma is saved in 1/100,000ths */
+   igamma = (png_uint_32)(gamma * 100000.0 + 0.5);
+   png_save_uint_32(buf, igamma);
+   png_write_chunk(png_ptr, png_gAMA, buf, (png_uint_32)4);
+}
+
+/* write the sBIT chunk */
+void
+png_write_sBIT(png_struct *png_ptr, png_color_8 *sbit, int color_type)
+{
+   png_byte buf[4];
+   int size;
+
+   /* make sure we don't depend upon the order of png_color_8 */
+   if (color_type & PNG_COLOR_MASK_COLOR)
+   {
+      buf[0] = sbit->red;
+      buf[1] = sbit->green;
+      buf[2] = sbit->blue;
+      size = 3;
+   }
+   else
+   {
+      buf[0] = sbit->gray;
+      size = 1;
+   }
+
+   if (color_type & PNG_COLOR_MASK_ALPHA)
+   {
+      buf[size++] = sbit->alpha;
+   }
+
+   png_write_chunk(png_ptr, png_sBIT, buf, (png_uint_32)size);
+}
+
+/* write the cHRM chunk */
+void
+png_write_cHRM(png_struct *png_ptr, float white_x, float white_y,
+   float red_x, float red_y, float green_x, float green_y,
+   float blue_x, float blue_y)
+{
+   png_uint_32 itemp;
+   png_byte buf[32];
+
+   /* each value is saved int 1/100,000ths */
+   itemp = (png_uint_32)(white_x * 100000.0 + 0.5);
+   png_save_uint_32(buf, itemp);
+   itemp = (png_uint_32)(white_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 4, itemp);
+   itemp = (png_uint_32)(red_x * 100000.0 + 0.5);
+   png_save_uint_32(buf + 8, itemp);
+   itemp = (png_uint_32)(red_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 12, itemp);
+   itemp = (png_uint_32)(green_x * 100000.0 + 0.5);
+   png_save_uint_32(buf + 16, itemp);
+   itemp = (png_uint_32)(green_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 20, itemp);
+   itemp = (png_uint_32)(blue_x * 100000.0 + 0.5);
+   png_save_uint_32(buf + 24, itemp);
+   itemp = (png_uint_32)(blue_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 28, itemp);
+   png_write_chunk(png_ptr, png_cHRM, buf, (png_uint_32)32);
+}
+
+/* write the tRNS chunk */
+void
+png_write_tRNS(png_struct *png_ptr, png_byte *trans, png_color_16 *tran,
+   int num_trans, int color_type)
+{
+   png_byte buf[6];
+
+   if (color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      /* write the chunk out as it is */
+      png_write_chunk(png_ptr, png_tRNS, trans, (png_uint_32)num_trans);
+   }
+   else if (color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      /* one 16 bit value */
+      png_save_uint_16(buf, tran->gray);
+      png_write_chunk(png_ptr, png_tRNS, buf, (png_uint_32)2);
+   }
+   else if (color_type == PNG_COLOR_TYPE_RGB)
+   {
+      /* three 16 bit values */
+      png_save_uint_16(buf, tran->red);
+      png_save_uint_16(buf + 2, tran->green);
+      png_save_uint_16(buf + 4, tran->blue);
+      png_write_chunk(png_ptr, png_tRNS, buf, (png_uint_32)6);
+   }
+}
+
+/* write the background chunk */
+void
+png_write_bKGD(png_struct *png_ptr, png_color_16 *back, int color_type)
+{
+   png_byte buf[6];
+
+   if (color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      buf[0] = back->index;
+      png_write_chunk(png_ptr, png_bKGD, buf, (png_uint_32)1);
+   }
+   else if (color_type & PNG_COLOR_MASK_COLOR)
+   {
+      png_save_uint_16(buf, back->red);
+      png_save_uint_16(buf + 2, back->green);
+      png_save_uint_16(buf + 4, back->blue);
+      png_write_chunk(png_ptr, png_bKGD, buf, (png_uint_32)6);
+   }
+   else
+   {
+      png_save_uint_16(buf, back->gray);
+      png_write_chunk(png_ptr, png_bKGD, buf, (png_uint_32)2);
+   }
+}
+
+/* write the histogram */
+void
+png_write_hIST(png_struct *png_ptr, png_uint_16 *hist, int number)
+{
+   int i;
+   png_byte buf[3];
+
+   png_write_chunk_start(png_ptr, png_hIST, (png_uint_32)(number * 2));
+   for (i = 0; i < number; i++)
+   {
+      png_save_uint_16(buf, hist[i]);
+      png_write_chunk_data(png_ptr, buf, (png_uint_32)2);
+   }
+   png_write_chunk_end(png_ptr);
+}
+
+/* write a tEXt chunk */
+void
+png_write_tEXt(png_struct *png_ptr, char *key, char *text,
+   png_uint_32 text_len)
+{
+   int key_len;
+
+   key_len = strlen(key);
+   /* make sure we count the 0 after the key */
+   png_write_chunk_start(png_ptr, png_tEXt,
+      (png_uint_32)(key_len + text_len + 1));
+   /* key has an 0 at the end.  How nice */
+   png_write_chunk_data(png_ptr, (png_byte *)key, (png_uint_32)(key_len + 1));
+   if (text && text_len)
+      png_write_chunk_data(png_ptr, (png_byte *)text, (png_uint_32)text_len);
+   png_write_chunk_end(png_ptr);
+}
+
+/* write a compressed chunk */
+void
+png_write_zTXt(png_struct *png_ptr, char *key, char *text,
+   png_uint_32 text_len, int compression)
+{
+   int key_len;
+   char buf[1];
+   int i, ret;
+   char **output_ptr = NULL; /* array of pointers to output */
+   int num_output_ptr = 0; /* number of output pointers used */
+   int max_output_ptr = 0; /* size of output_ptr */
+
+   key_len = strlen(key);
+
+   /* we can't write the chunk until we find out how much data we have,
+      which means we need to run the compresser first, and save the
+      output.  This shouldn't be a problem, as the vast majority of
+      comments should be reasonable, but we will set up an array of
+      malloced pointers to be sure. */
+
+   /* set up the compression buffers */
+   png_ptr->zstream->avail_in = (uInt)text_len;
+   png_ptr->zstream->next_in = (Byte *)text;
+   png_ptr->zstream->avail_out = (uInt)png_ptr->zbuf_size;
+   png_ptr->zstream->next_out = (Byte *)png_ptr->zbuf;
+
+   /* this is the same compression loop as in png_write_row() */
+   do
+   {
+      /* compress the data */
+      ret = deflate(png_ptr->zstream, Z_NO_FLUSH);
+      if (ret != Z_OK)
+      {
+         /* error */
+         if (png_ptr->zstream->msg)
+            png_error(png_ptr, png_ptr->zstream->msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+      /* check to see if we need more room */
+      if (!png_ptr->zstream->avail_out && png_ptr->zstream->avail_in)
+      {
+         /* make sure the output array has room */
+         if (num_output_ptr >= max_output_ptr)
+         {
+            max_output_ptr = num_output_ptr + 4;
+            if (output_ptr)
+               output_ptr = png_realloc(png_ptr, output_ptr,
+                  max_output_ptr * sizeof (char *));
+            else
+               output_ptr = png_malloc(png_ptr,
+                  max_output_ptr * sizeof (char *));
+         }
+
+         /* save the data */
+         output_ptr[num_output_ptr] = png_large_malloc(png_ptr,
+            png_ptr->zbuf_size);
+         memcpy(output_ptr[num_output_ptr], png_ptr->zbuf,
+            (png_size_t)png_ptr->zbuf_size);
+         num_output_ptr++;
+
+         /* and reset the buffer */
+         png_ptr->zstream->avail_out = (uInt)png_ptr->zbuf_size;
+         png_ptr->zstream->next_out = png_ptr->zbuf;
+      }
+   /* continue until we don't have anymore to compress */
+   } while (png_ptr->zstream->avail_in);
+
+   /* finish the compression */
+   do
+   {
+      /* tell zlib we are finished */
+      ret = deflate(png_ptr->zstream, Z_FINISH);
+      if (ret != Z_OK && ret != Z_STREAM_END)
+      {
+         /* we got an error */
+         if (png_ptr->zstream->msg)
+            png_error(png_ptr, png_ptr->zstream->msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+
+      /* check to see if we need more room */
+      if (!png_ptr->zstream->avail_out && ret == Z_OK)
+      {
+         /* check to make sure our output array has room */
+         if (num_output_ptr >= max_output_ptr)
+         {
+            max_output_ptr = num_output_ptr + 4;
+            if (output_ptr)
+               output_ptr = png_realloc(png_ptr, output_ptr,
+                  max_output_ptr * sizeof (char *));
+            else
+               output_ptr = png_malloc(png_ptr,
+                  max_output_ptr * sizeof (char *));
+         }
+
+         /* save off the data */
+         output_ptr[num_output_ptr] = png_large_malloc(png_ptr,
+            png_ptr->zbuf_size);
+         memcpy(output_ptr[num_output_ptr], png_ptr->zbuf,
+            (png_size_t)png_ptr->zbuf_size);
+         num_output_ptr++;
+
+         /* and reset the buffer pointers */
+         png_ptr->zstream->avail_out = (uInt)png_ptr->zbuf_size;
+         png_ptr->zstream->next_out = png_ptr->zbuf;
+      }
+   } while (ret != Z_STREAM_END);
+
+   /* text length is number of buffers plus last buffer */
+   text_len = png_ptr->zbuf_size * num_output_ptr;
+   if (png_ptr->zstream->avail_out < png_ptr->zbuf_size)
+      text_len += (png_uint_32)(png_ptr->zbuf_size -
+         png_ptr->zstream->avail_out);
+
+   /* write start of chunk */
+   png_write_chunk_start(png_ptr, png_zTXt,
+      (png_uint_32)(key_len + text_len + 2));
+   /* write key */
+   png_write_chunk_data(png_ptr, (png_byte *)key, (png_uint_32)(key_len + 1));
+   buf[0] = compression;
+   /* write compression */
+   png_write_chunk_data(png_ptr, (png_byte *)buf, (png_uint_32)1);
+
+   /* write saved output buffers, if any */
+   for (i = 0; i < num_output_ptr; i++)
+   {
+      png_write_chunk_data(png_ptr, (png_byte *)output_ptr[i], png_ptr->zbuf_size);
+      png_large_free(png_ptr, output_ptr[i]);
+   }
+   if (max_output_ptr)
+      png_free(png_ptr, output_ptr);
+   /* write anything left in zbuf */
+   if (png_ptr->zstream->avail_out < png_ptr->zbuf_size)
+      png_write_chunk_data(png_ptr, png_ptr->zbuf,
+         png_ptr->zbuf_size - png_ptr->zstream->avail_out);
+   /* close the chunk */
+   png_write_chunk_end(png_ptr);
+
+   /* reset zlib for another zTXt or the image data */
+/*   deflateReset(png_ptr->zstream); */
+   deflateEnd(png_ptr->zstream);
+   deflateInit(png_ptr->zstream, -1);
+}
+
+/* write the pHYs chunk */
+void
+png_write_pHYs(png_struct *png_ptr, png_uint_32 x_pixels_per_unit,
+   png_uint_32 y_pixels_per_unit,
+   int unit_type)
+{
+   png_byte buf[9];
+
+   png_save_uint_32(buf, x_pixels_per_unit);
+   png_save_uint_32(buf + 4, y_pixels_per_unit);
+   buf[8] = unit_type;
+
+   png_write_chunk(png_ptr, png_pHYs, buf, (png_uint_32)9);
+}
+
+/* write the oFFs chunk */
+void
+png_write_oFFs(png_struct *png_ptr, png_uint_32 x_offset,
+   png_uint_32 y_offset,
+   int unit_type)
+{
+   png_byte buf[9];
+
+   png_save_uint_32(buf, x_offset);
+   png_save_uint_32(buf + 4, y_offset);
+   buf[8] = unit_type;
+
+   png_write_chunk(png_ptr, png_oFFs, buf, (png_uint_32)9);
+}
+
+/* two time chunks are given.  This chunk assumes you have a gmtime()
+   function.  If you don't have that, use the other tIME function */
+void
+png_write_tIME(png_struct *png_ptr, png_time *mod_time)
+{
+   png_byte buf[7];
+
+   png_save_uint_16(buf, mod_time->year);
+   buf[2] = mod_time->month;
+   buf[3] = mod_time->day;
+   buf[4] = mod_time->hour;
+   buf[5] = mod_time->minute;
+   buf[6] = mod_time->second;
+
+   png_write_chunk(png_ptr, png_tIME, buf, (png_uint_32)7);
+}
+
+/* initializes the row writing capability of libpng */
+void
+png_write_start_row(png_struct *png_ptr)
+{
+   /* set up row buffer */
+   png_ptr->row_buf = (png_byte *)png_large_malloc(png_ptr,
+      (((png_uint_32)png_ptr->usr_channels *
+      (png_uint_32)png_ptr->usr_bit_depth *
+      png_ptr->width) >> 3) + 1);
+   /* set up filtering buffers, if filtering */
+   if (png_ptr->bit_depth >= 8 && png_ptr->color_type != 3)
+   {
+      png_ptr->prev_row = (png_byte *)png_large_malloc(png_ptr,
+         png_ptr->rowbytes + 1);
+      memset(png_ptr->prev_row, 0, (png_size_t)png_ptr->rowbytes + 1);
+      png_ptr->save_row = (png_byte *)png_large_malloc(png_ptr,
+         png_ptr->rowbytes + 1);
+      memset(png_ptr->save_row, 0, (png_size_t)png_ptr->rowbytes + 1);
+   }
+
+   /* if interlaced, we need to set up width and height of pass */
+   if (png_ptr->interlaced)
+   {
+      if (!(png_ptr->transformations & PNG_INTERLACE))
+      {
+         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
+            png_pass_ystart[0]) / png_pass_yinc[0];
+         png_ptr->usr_width = (png_ptr->width +
+            png_pass_inc[0] - 1 -
+            png_pass_start[0]) /
+            png_pass_inc[0];
+      }
+      else
+      {
+         png_ptr->num_rows = png_ptr->height;
+         png_ptr->usr_width = png_ptr->width;
+      }
+   }
+   else
+   {
+      png_ptr->num_rows = png_ptr->height;
+      png_ptr->usr_width = png_ptr->width;
+   }
+   png_ptr->zstream->avail_out = (uInt)png_ptr->zbuf_size;
+   png_ptr->zstream->next_out = png_ptr->zbuf;
+}
+
+/* Internal use only.   Called when finished processing a row of data */
+void
+png_write_finish_row(png_struct *png_ptr)
+{
+   int ret;
+
+   /* next row */
+   png_ptr->row_number++;
+   /* see if we are done */
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+   /* if interlaced, go to next pass */
+   if (png_ptr->interlaced)
+   {
+      png_ptr->row_number = 0;
+      if (png_ptr->transformations & PNG_INTERLACE)
+      {
+         png_ptr->pass++;
+      }
+      else
+      {
+         /* loop until we find a non-zero width or height pass */
+         do
+         {
+            png_ptr->pass++;
+            if (png_ptr->pass >= 7)
+               break;
+            png_ptr->usr_width = (png_ptr->width +
+               png_pass_inc[png_ptr->pass] - 1 -
+               png_pass_start[png_ptr->pass]) /
+               png_pass_inc[png_ptr->pass];
+            png_ptr->num_rows = (png_ptr->height +
+               png_pass_yinc[png_ptr->pass] - 1 -
+               png_pass_ystart[png_ptr->pass]) /
+               png_pass_yinc[png_ptr->pass];
+         } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0);
+
+      }
+
+      /* reset filter row */
+      if (png_ptr->prev_row)
+         memset(png_ptr->prev_row, 0, (png_size_t)png_ptr->rowbytes + 1);
+      /* if we have more data to get, go get it */
+      if (png_ptr->pass < 7)
+         return;
+   }
+
+   /* if we get here, we've just written the last row, so we need
+      to flush the compressor */
+   do
+   {
+      /* tell the compressor we are done */
+      ret = deflate(png_ptr->zstream, Z_FINISH);
+      /* check for an error */
+      if (ret != Z_OK && ret != Z_STREAM_END)
+      {
+         if (png_ptr->zstream->msg)
+            png_error(png_ptr, png_ptr->zstream->msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+      /* check to see if we need more room */
+      if (!png_ptr->zstream->avail_out && ret == Z_OK)
+      {
+         png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+         png_ptr->zstream->next_out = png_ptr->zbuf;
+         png_ptr->zstream->avail_out = (uInt)png_ptr->zbuf_size;
+      }
+   } while (ret != Z_STREAM_END);
+
+   /* write any extra space */
+   if (png_ptr->zstream->avail_out < png_ptr->zbuf_size)
+   {
+      png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size -
+         png_ptr->zstream->avail_out);
+   }
+
+/*   deflateReset(png_ptr->zstream); */
+   deflateEnd(png_ptr->zstream);
+   deflateInit(png_ptr->zstream, -1);
+
+}
+
+/* pick out the correct pixels for the interlace pass.
+
+   The basic idea here is to go through the row with a source
+   pointer and a destination pointer (sp and dp), and copy the
+   correct pixels for the pass.  As the row gets compacted,
+   sp will always be >= dp, so we should never overwrite anything.
+   See the default: case for the easiest code to understand.
+   */
+void
+png_do_write_interlace(png_row_info *row_info, png_byte *row, int pass)
+{
+   /* we don't have to do anything on the last pass (6) */
+   if (row && row_info && pass < 6)
+   {
+      /* each pixel depth is handled seperately */
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+
+            dp = row;
+            d = 0;
+            shift = 7;
+            for (i = png_pass_start[pass];
+               i < row_info->width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 3);
+               value = (int)(*sp >> (7 - (int)(i & 7))) & 0x1;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 7;
+                  *dp++ = d;
+                  d = 0;
+               }
+               else
+                  shift--;
+
+            }
+            if (shift != 7)
+               *dp = d;
+            break;
+         }
+         case 2:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+
+            dp = row;
+            shift = 6;
+            d = 0;
+            for (i = png_pass_start[pass];
+               i < row_info->width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 2);
+               value = (*sp >> ((3 - (int)(i & 3)) << 1)) & 0x3;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 6;
+                  *dp++ = d;
+                  d = 0;
+               }
+               else
+                  shift -= 2;
+            }
+            if (shift != 6)
+                   *dp = d;
+            break;
+         }
+         case 4:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+
+            dp = row;
+            shift = 4;
+            d = 0;
+            for (i = png_pass_start[pass];
+               i < row_info->width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 1);
+               value = (*sp >> ((1 - (int)(i & 1)) << 2)) & 0xf;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 4;
+                  *dp++ = d;
+                  d = 0;
+               }
+               else
+                  shift -= 4;
+            }
+            if (shift != 4)
+               *dp = d;
+            break;
+         }
+         default:
+         {
+            png_byte *sp;
+            png_byte *dp;
+            png_uint_32 i;
+            int pixel_bytes;
+
+            /* start at the beginning */
+            dp = row;
+            /* find out how many bytes each pixel takes up */
+            pixel_bytes = (row_info->pixel_depth >> 3);
+            /* loop through the row, only looking at the pixels that
+               matter */
+            for (i = png_pass_start[pass];
+               i < row_info->width;
+               i += png_pass_inc[pass])
+            {
+               /* find out where the original pixel is */
+               sp = row + (png_size_t)(i * pixel_bytes);
+               /* move the pixel */
+               if (dp != sp)
+                  memcpy(dp, sp, pixel_bytes);
+               /* next pixel */
+               dp += pixel_bytes;
+            }
+            break;
+         }
+      }
+      /* set new row width */
+      row_info->width = (row_info->width +
+         png_pass_inc[pass] - 1 -
+         png_pass_start[pass]) /
+         png_pass_inc[pass];
+      row_info->rowbytes = ((row_info->width *
+         row_info->pixel_depth + 7) >> 3);
+
+   }
+}
+
+/* this filters the row.  Both row and prev_row have space at the
+   first byte for the filter byte. */
+void
+png_write_filter_row(png_row_info *row_info, png_byte *row,
+   png_byte *prev_row)
+{
+   int minf, bpp;
+   png_uint_32 i, v;
+   png_uint_32 s, mins;
+   png_byte *rp, *pp, *cp, *lp;
+
+   /* find out how many bytes offset each pixel is */
+   bpp = (row_info->pixel_depth + 7) / 8;
+   if (bpp < 1)
+      bpp = 1;
+
+   /* the prediction method we use is to find which method provides
+      the smallest value when summing the abs of the distances from
+      zero using anything >= 128 as negitive numbers. */
+   for (i = 0, s = 0, rp = row + 1; i < row_info->rowbytes; i++, rp++)
+   {
+      v = *rp;
+      if (v < 128)
+         s += v;
+      else
+         s += 256 - (png_int_32)v;
+   }
+
+   mins = s;
+   minf = 0;
+
+   /* check sub filter */
+   for (i = 0, s = 0, rp = row + 1, lp = row + 1 - bpp;
+      i < row_info->rowbytes; i++, rp++, lp++)
+   {
+      if (i >= bpp)
+         v = (png_byte)(((int)*rp - (int)*lp) & 0xff);
+      else
+         v = *rp;
+
+      if (v < 128)
+         s += v;
+      else
+         s += 256 - v;
+   }
+
+   if (s < mins)
+   {
+      mins = s;
+      minf = 1;
+   }
+
+   /* check up filter */
+   for (i = 0, s = 0, rp = row + 1, pp = prev_row + 1;
+      i < row_info->rowbytes; i++, rp++, pp++)
+   {
+      v = (png_byte)(((int)*rp - (int)*pp) & 0xff);
+
+      if (v < 128)
+         s += v;
+      else
+         s += 256 - v;
+   }
+
+   if (s < mins)
+   {
+      mins = s;
+      minf = 2;
+   }
+
+   /* check avg filter */
+   for (i = 0, s = 0, rp = row + 1, pp = prev_row + 1, lp = row + 1 - bpp;
+      i < row_info->rowbytes; i++, rp++, pp++, lp++)
+   {
+      if (i >= bpp)
+         v = (png_byte)(((int)*rp - (((int)*pp + (int)*lp) / 2)) & 0xff);
+      else
+         v = (png_byte)(((int)*rp - ((int)*pp / 2)) & 0xff);
+
+      if (v < 128)
+         s += v;
+      else
+         s += 256 - v;
+   }
+
+   if (s < mins)
+   {
+      mins = s;
+      minf = 3;
+   }
+
+   /* check paeth filter */
+   for (i = 0, s = 0, rp = row + 1, pp = prev_row + 1, lp = row + 1 - bpp,
+         cp = prev_row + 1 - bpp;
+      i < row_info->rowbytes; i++, rp++, pp++, lp++, cp++)
+   {
+      int a, b, c, pa, pb, pc, p;
+
+      b = *pp;
+      if (i >= bpp)
+      {
+         c = *cp;
+         a = *lp;
+      }
+      else
+      {
+         a = c = 0;
+      }
+      p = a + b - c;
+      pa = abs(p - a);
+      pb = abs(p - b);
+      pc = abs(p - c);
+
+      if (pa <= pb && pa <= pc)
+         p = a;
+      else if (pb <= pc)
+         p = b;
+      else
+         p = c;
+
+      v = (png_byte)(((int)*rp - p) & 0xff);
+
+      if (v < 128)
+         s += v;
+      else
+         s += 256 - v;
+   }
+
+   if (s < mins)
+   {
+      mins = s;
+      minf = 4;
+   }
+
+   /* set filter byte */
+   row[0] = minf;
+
+   /* do filter */
+   switch (minf)
+   {
+      /* sub filter */
+      case 1:
+         for (i = bpp, rp = row + (png_size_t)row_info->rowbytes,
+            lp = row + (png_size_t)row_info->rowbytes - bpp;
+            i < row_info->rowbytes; i++, rp--, lp--)
+         {
+            *rp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
+         }
+         break;
+      /* up filter */
+      case 2:
+         for (i = 0, rp = row + (png_size_t)row_info->rowbytes,
+            pp = prev_row + (png_size_t)row_info->rowbytes;
+            i < row_info->rowbytes; i++, rp--, pp--)
+         {
+            *rp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
+         }
+         break;
+      /* avg filter */
+      case 3:
+         for (i = row_info->rowbytes,
+            rp = row + (png_size_t)row_info->rowbytes,
+            pp = prev_row + (png_size_t)row_info->rowbytes,
+            lp = row + (png_size_t)row_info->rowbytes - bpp;
+            i > bpp; i--, rp--, lp--, pp--)
+         {
+            *rp = (png_byte)(((int)*rp - (((int)*lp + (int)*pp) /
+               2)) & 0xff);
+         }
+         for (; i > 0; i--, rp--, pp--)
+         {
+            *rp = (png_byte)(((int)*rp - ((int)*pp / 2)) & 0xff);
+         }
+         break;
+      /* paeth filter */
+      case 4:
+         for (i = row_info->rowbytes,
+            rp = row + (png_size_t)row_info->rowbytes,
+            pp = prev_row + (png_size_t)row_info->rowbytes,
+            lp = row + (png_size_t)row_info->rowbytes - bpp,
+            cp = prev_row + (png_size_t)row_info->rowbytes - bpp;
+            i > 0; i--, rp--, lp--, pp--, cp--)
+         {
+            int a, b, c, pa, pb, pc, p;
+
+            b = *pp;
+            if (i > bpp)
+            {
+               c = *cp;
+               a = *lp;
+            }
+            else
+            {
+               a = c = 0;
+            }
+            p = a + b - c;
+            pa = abs(p - a);
+            pb = abs(p - b);
+            pc = abs(p - c);
+
+            if (pa <= pb && pa <= pc)
+               p = a;
+            else if (pb <= pc)
+               p = b;
+            else
+               p = c;
+
+            *rp = (png_byte)(((int)*rp - p) & 0xff);
+         }
+         break;
+   }
+}
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..3ca55c5
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1,79 @@
+readme.txt - for libpng 0.71
+
+This is the first beta version of libpng 1.0.  By beta, I mean that
+all the code for 1.0 is there, and it works on all the machines
+I have running all the tests I have devised.  However, there is
+always one more bug (at least), and I don't have many #define's in
+the code (yet) for various platforms that I do not have.  Also, I'd
+like to see if I can get the code to compile with as few warnings
+as possible.  Finally, as people use my code, they may have
+suggestions for additions that will make pnglib easier to port.
+
+For a detailed description on using libpng, read libpng.txt.  For
+usage information and restrictions (what little they are) on libpng,
+see png.h.  For a description on using zlib (the compression library
+used by libpng) and zlib's restrictions, see zlib.h
+
+I have included a make file, but you will probably have to modify it
+for your own needs.  I'm using Borland C++, running large memory
+model on Windows 3.11, but it should work on almost anything.  Support
+for medium memory model is planned, but is not in 1.0 (probably in 1.1).
+
+You will need zlib 0.93 to run this.  zlib is a compression
+library that is useful for more things then just png files.  If
+you need a compression library, check out zlib.h
+
+zlib should be available at the same place that libpng is.
+If not, it should be at ftp.uu.net in /graphics/png
+Eventually, it will be at ftp.uu.net in /pub/archiving/zip/zlib
+
+You will also want a copy of the PNG specification.  It should
+be available at the same place you picked up libpng.  If it is
+not there, try ftp.uu.net in the /graphics/png directory.
+
+This code is currently being archived at ftp.uu.net in the
+/graphics/png directory, and at ftp.group42.com in the /pub/png
+directory, and on CompuServe, Lib 20 (PNG) at GO GRAPHSUP.
+If you can't find it in any of those places, e-mail me, and I'll
+tell you where it is.
+
+If you have any code changes, requests, problems, etc., please e-mail
+them to me.  Also, I'd appreciate any make files or project files,
+and any modifications you needed to make to get libpng to compile,
+along with a #define variable to tell what compiler/system you are on.
+If you needed to add transformations to libpng, or wish libpng would
+provide the image in a different way, drop me a note (and code, if
+possible), so I can consider supporting the transformation.
+Finally, if you get any warning messages when compiling libpng
+(note: not zlib), and they are easy to fix, I'd appreciate the
+fix.  Please mention "libpng" somewhere in the subject line.  Thanks.
+
+You can reach me at:
+
+internet: schalnat&group42.com
+CompuServe: 75501,1625
+
+Please do not send me general questions about PNG.  Send them to
+the address in the specification.  At the same time, please do
+not send libpng questions to that address, send them to me.  I'll
+get them in the end anyway.  If you have a question about something
+in the PNG specification that is related to using libpng, send it
+to me.  Send me any questions that start with "I was using libpng,
+and ...".  If in doubt, send questions to me.  I'll bounce them
+to others, if necessary.
+
+Please do not send suggestions on how to change PNG.  We have
+been discussing PNG for 6 months now, and it is official and
+finished.  If you have suggestions for libpng, however, I'll
+gladly listen.  Even if your suggestion is not used for version
+1.0, it may be used later.
+
+Good luck, and happy coding.
+
+-Guy Eric Schalnat
+ Group 42, Inc.
+ Internet: schalnat@group42.com
+ CompuServe: 75501,1625
+ Web: www.group42.com
+ FTP: ftp.group42.com
+