commit 0d5805822f8817a17937462a2fd0606ffdad378e Author: Guy Schalnat Date: Thu Jul 20 02:43:20 1995 -0500 Imported from libpng-0.71.tar diff --git a/ansi2knr.c b/ansi2knr.c new file mode 100644 index 00000000..39242154 --- /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 +#include + +#ifdef BSD +#include +#else +#ifdef VMS + extern int strlen(), strncmp(); +#else +#include +#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 +#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 00000000..830f26da --- /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 + +/* 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 00000000..4ddd1c1a --- /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 + +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_ 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_ 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 00000000..5d3da0f0 --- /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 00000000..5eb0b79a --- /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 00000000..ea41c611 --- /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 00000000..1613e909 --- /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 00000000..d65d7c64 --- /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 00000000..b0fc8e58 --- /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 +/* need the time information for reading tIME chunks */ +#include + +/* for FILE. If you are not using standard io, you don't need this */ +#include + +/* include setjmp.h for error handling */ +#include + +/* 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 +#include +#ifdef BSD +#include +#else +#include +#endif +#include + +/* 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 +#include "alloc.h" +#endif + +#ifdef _MSC_VER +#include +#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 00000000..878a2f81 --- /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 00000000..50b43014 --- /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 00000000..e1afee66 --- /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 00000000..0aa3b47f --- /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 00000000..83a2973d --- /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 00000000..6ae1469e --- /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 00000000..ba60b134 --- /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 +#include +#include "png.h" + +#ifdef __TURBOC__ +#include +#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 00000000..67506634 Binary files /dev/null and b/pngtest.png differ diff --git a/pngtodo.txt b/pngtodo.txt new file mode 100644 index 00000000..b52a3aaa --- /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 00000000..02b00bcb --- /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 00000000..ac7e22f6 --- /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 00000000..7423ade3 --- /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 00000000..89743d75 --- /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 00000000..3ca55c55 --- /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 +