dslinux/lib/libpng/contrib/gregbook LICENSE Makefile.sgi Makefile.unx Makefile.w32 README README.w32 makevms.com readpng.c readpng.h readpng2.c readpng2.h rpng-win.c rpng-x.c rpng2-win.c rpng2-x.c toucan.png wpng.c writepng.c writepng.h

pepsiman dslinux_pepsiman at user.in-berlin.de
Sat Nov 25 11:05:15 CET 2006


Update of /cvsroot/dslinux/dslinux/lib/libpng/contrib/gregbook
In directory antilope:/tmp/cvs-serv15394/contrib/gregbook

Added Files:
	LICENSE Makefile.sgi Makefile.unx Makefile.w32 README 
	README.w32 makevms.com readpng.c readpng.h readpng2.c 
	readpng2.h rpng-win.c rpng-x.c rpng2-win.c rpng2-x.c 
	toucan.png wpng.c writepng.c writepng.h 
Log Message:
Merge libpng from uclinux_branch

--- NEW FILE: README.w32 ---
See the main README file for basic instructions on compiling and running
the programs.  See http://www.cdrom.com/pub/png/pngbook.html for further
information and links to the source code, and Chapters 13-15 of the book
for detailed discussion of the three programs.

Since the two viewers, rpng and rpng2, are both designed to write infor-
mation to the console (i.e., a DOS-window command line) while displaying
the image in a graphical window--and since I haven't yet figured out how
to do that under Windows--here are the usage screens for the two programs:


rpng-win 1.02 of 19 March 2000:  Simple PNG Viewer for Windows
   Compiled with libpng 1.0.5; using libpng 1.0.5.
   Compiled with zlib 1.1.3; using zlib 1.1.3.

Usage:  rpng-win [-gamma exp] [-bgcolor bg] file.png
    exp 	transfer-function exponent (``gamma'') of the display
		  system in floating-point format (e.g., ``2.2''); equal
		  to the product of the lookup-table exponent (varies)
		  and the CRT exponent (usually 2.2); must be positive
    bg  	desired background color in 7-character hex RGB format
		  (e.g., ``#ff7f00'' for orange:  same as HTML colors);
		  used with transparent images

Press Q, Esc or mouse button 1 after image is displayed to quit.


rpng2-win 1.04 of 19 March 2000:  Progressive PNG Viewer for Windows
   Compiled with libpng 1.0.5; using libpng 1.0.5.
   Compiled with zlib 1.1.3; using zlib 1.1.3.

Usage:  rpng2-win [-gamma exp] [-bgcolor bg | -bgpat pat] [-timing] file.png

    exp 	transfer-function exponent (``gamma'') of the display
		  system in floating-point format (e.g., ``2.2''); equal
		  to the product of the lookup-table exponent (varies)
		  and the CRT exponent (usually 2.2); must be positive
    bg  	desired background color in 7-character hex RGB format
		  (e.g., ``#ff7f00'' for orange:  same as HTML colors);
		  used with transparent images; overrides -bgpat
    pat 	desired background pattern number (1-16); used with
		  transparent images; overrides -bgcolor
    -timing	enables delay for every block read, to simulate modem
		  download of image (~36 Kbps)

Press Q, Esc or mouse button 1 after image is displayed to quit.


The usage screen for the third (non-windowed) program, wpng, can be seen
simply by invoking it without any parameters (``wpng'').

Greg Roelofs
19 March 2000

--- NEW FILE: rpng2-x.c ---
/*---------------------------------------------------------------------------

   rpng2 - progressive-model PNG display program                  rpng2-x.c

   This program decodes and displays PNG files progressively, as if it were
   a web browser (though the front end is only set up to read from files).
   It supports gamma correction, user-specified background colors, and user-
   specified background patterns (for transparent images).  This version is
   for the X Window System (tested by the author under Unix and by Martin
   Zinser under OpenVMS; may work under OS/2 with a little tweaking).

   Thanks to Adam Costello and Pieter S. van der Meulen for the "diamond"
   and "radial waves" patterns, respectively.

   to do:
    - 8-bit support
    - finish resizable checkerboard-gradient (sizes 4-128?)
    - use %.1023s to simplify truncation of title-bar string?

[...1364 lines suppressed...]

    if (have_nondefault_visual)
        XFree(visual_list);
}





static int rpng2_x_msb(ulg u32val)
{
    int i;

    for (i = 31;  i >= 0;  --i) {
        if (u32val & 0x80000000L)
            break;
        u32val <<= 1;
    }
    return i;
}

--- NEW FILE: writepng.c ---
/*---------------------------------------------------------------------------

   wpng - simple PNG-writing program                             writepng.c

  ---------------------------------------------------------------------------

      Copyright (c) 1998-2000 Greg Roelofs.  All rights reserved.

      This software is provided "as is," without warranty of any kind,
      express or implied.  In no event shall the author or contributors
      be held liable for any damages arising in any way from the use of
      this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute
      it freely, subject to the following restrictions:

      1. Redistributions of source code must retain the above copyright
         notice, disclaimer, and this list of conditions.
      2. Redistributions in binary form must reproduce the above copyright
         notice, disclaimer, and this list of conditions in the documenta-
         tion and/or other materials provided with the distribution.
      3. All advertising materials mentioning features or use of this
         software must display the following acknowledgment:

            This product includes software developed by Greg Roelofs
            and contributors for the book, "PNG: The Definitive Guide,"
            published by O'Reilly and Associates.

  ---------------------------------------------------------------------------*/


#include <stdlib.h>     /* for exit() prototype */

#include "png.h"        /* libpng header; includes zlib.h and setjmp.h */
#include "writepng.h"   /* typedefs, common macros, public prototypes */


/* local prototype */

static void writepng_error_handler(png_structp png_ptr, png_const_charp msg);



void writepng_version_info(void)
{
  fprintf(stderr, "   Compiled with libpng %s; using libpng %s.\n",
    PNG_LIBPNG_VER_STRING, png_libpng_ver);
  fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
    ZLIB_VERSION, zlib_version);
}




/* returns 0 for success, 2 for libpng problem, 4 for out of memory, 11 for
 *  unexpected pnmtype; note that outfile might be stdout */

int writepng_init(mainprog_info *mainprog_ptr)
{
    png_structp  png_ptr;       /* note:  temporary variables! */
    png_infop  info_ptr;
    int color_type, interlace_type;


    /* could also replace libpng warning-handler (final NULL), but no need: */

    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
      writepng_error_handler, NULL);
    if (!png_ptr)
        return 4;   /* out of memory */

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
        png_destroy_write_struct(&png_ptr, NULL);
        return 4;   /* out of memory */
    }


    /* setjmp() must be called in every function that calls a PNG-writing
     * libpng function, unless an alternate error handler was installed--
     * but compatible error handlers must either use longjmp() themselves
     * (as in this program) or exit immediately, so here we go: */

    if (setjmp(mainprog_ptr->jmpbuf)) {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        return 2;
    }


    /* make sure outfile is (re)opened in BINARY mode */

    png_init_io(png_ptr, mainprog_ptr->outfile);


    /* set the compression levels--in general, always want to leave filtering
     * turned on (except for palette images) and allow all of the filters,
     * which is the default; want 32K zlib window, unless entire image buffer
     * is 16K or smaller (unknown here)--also the default; usually want max
     * compression (NOT the default); and remaining compression flags should
     * be left alone */

    png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
/*
    >> this is default for no filtering; Z_FILTERED is default otherwise:
    png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
    >> these are all defaults:
    png_set_compression_mem_level(png_ptr, 8);
    png_set_compression_window_bits(png_ptr, 15);
    png_set_compression_method(png_ptr, 8);
 */


    /* set the image parameters appropriately */

    if (mainprog_ptr->pnmtype == 5)
        color_type = PNG_COLOR_TYPE_GRAY;
    else if (mainprog_ptr->pnmtype == 6)
        color_type = PNG_COLOR_TYPE_RGB;
    else if (mainprog_ptr->pnmtype == 8)
        color_type = PNG_COLOR_TYPE_RGB_ALPHA;
    else {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        return 11;
    }

    interlace_type = mainprog_ptr->interlaced? PNG_INTERLACE_ADAM7 :
                                               PNG_INTERLACE_NONE;

    png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height,
      mainprog_ptr->sample_depth, color_type, interlace_type,
      PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

    if (mainprog_ptr->gamma > 0.0)
        png_set_gAMA(png_ptr, info_ptr, mainprog_ptr->gamma);

    if (mainprog_ptr->have_bg) {   /* we know it's RGBA, not gray+alpha */
        png_color_16  background;

        background.red = mainprog_ptr->bg_red;
        background.green = mainprog_ptr->bg_green;
        background.blue = mainprog_ptr->bg_blue;
        png_set_bKGD(png_ptr, info_ptr, &background);
    }

    if (mainprog_ptr->have_time) {
        png_time  modtime;

        png_convert_from_time_t(&modtime, mainprog_ptr->modtime);
        png_set_tIME(png_ptr, info_ptr, &modtime);
    }

    if (mainprog_ptr->have_text) {
        png_text  text[6];
        int  num_text = 0;

        if (mainprog_ptr->have_text & TEXT_TITLE) {
            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
            text[num_text].key = "Title";
            text[num_text].text = mainprog_ptr->title;
            ++num_text;
        }
        if (mainprog_ptr->have_text & TEXT_AUTHOR) {
            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
            text[num_text].key = "Author";
            text[num_text].text = mainprog_ptr->author;
            ++num_text;
        }
        if (mainprog_ptr->have_text & TEXT_DESC) {
            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
            text[num_text].key = "Description";
            text[num_text].text = mainprog_ptr->desc;
            ++num_text;
        }
        if (mainprog_ptr->have_text & TEXT_COPY) {
            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
            text[num_text].key = "Copyright";
            text[num_text].text = mainprog_ptr->copyright;
            ++num_text;
        }
        if (mainprog_ptr->have_text & TEXT_EMAIL) {
            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
            text[num_text].key = "E-mail";
            text[num_text].text = mainprog_ptr->email;
            ++num_text;
        }
        if (mainprog_ptr->have_text & TEXT_URL) {
            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
            text[num_text].key = "URL";
            text[num_text].text = mainprog_ptr->url;
            ++num_text;
        }
        png_set_text(png_ptr, info_ptr, text, num_text);
    }


    /* write all chunks up to (but not including) first IDAT */

    png_write_info(png_ptr, info_ptr);


    /* if we wanted to write any more text info *after* the image data, we
     * would set up text struct(s) here and call png_set_text() again, with
     * just the new data; png_set_tIME() could also go here, but it would
     * have no effect since we already called it above (only one tIME chunk
     * allowed) */


    /* set up the transformations:  for now, just pack low-bit-depth pixels
     * into bytes (one, two or four pixels per byte) */

    png_set_packing(png_ptr);
/*  png_set_shift(png_ptr, &sig_bit);  to scale low-bit-depth values */


    /* make sure we save our pointers for use in writepng_encode_image() */

    mainprog_ptr->png_ptr = png_ptr;
    mainprog_ptr->info_ptr = info_ptr;


    /* OK, that's all we need to do for now; return happy */

    return 0;
}





/* returns 0 for success, 2 for libpng (longjmp) problem */

int writepng_encode_image(mainprog_info *mainprog_ptr)
{
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;


    /* as always, setjmp() must be called in every function that calls a
     * PNG-writing libpng function */

    if (setjmp(mainprog_ptr->jmpbuf)) {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        mainprog_ptr->png_ptr = NULL;
        mainprog_ptr->info_ptr = NULL;
        return 2;
    }


    /* and now we just write the whole image; libpng takes care of interlacing
     * for us */

    png_write_image(png_ptr, mainprog_ptr->row_pointers);


    /* since that's it, we also close out the end of the PNG file now--if we
     * had any text or time info to write after the IDATs, second argument
     * would be info_ptr, but we optimize slightly by sending NULL pointer: */

    png_write_end(png_ptr, NULL);

    return 0;
}





/* returns 0 if succeeds, 2 if libpng problem */

int writepng_encode_row(mainprog_info *mainprog_ptr)  /* NON-interlaced only! */
{
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;


    /* as always, setjmp() must be called in every function that calls a
     * PNG-writing libpng function */

    if (setjmp(mainprog_ptr->jmpbuf)) {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        mainprog_ptr->png_ptr = NULL;
        mainprog_ptr->info_ptr = NULL;
        return 2;
    }


    /* image_data points at our one row of image data */

    png_write_row(png_ptr, mainprog_ptr->image_data);

    return 0;
}





/* returns 0 if succeeds, 2 if libpng problem */

int writepng_encode_finish(mainprog_info *mainprog_ptr)   /* NON-interlaced! */
{
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;


    /* as always, setjmp() must be called in every function that calls a
     * PNG-writing libpng function */

    if (setjmp(mainprog_ptr->jmpbuf)) {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        mainprog_ptr->png_ptr = NULL;
        mainprog_ptr->info_ptr = NULL;
        return 2;
    }


    /* close out PNG file; if we had any text or time info to write after
     * the IDATs, second argument would be info_ptr: */

    png_write_end(png_ptr, NULL);

    return 0;
}





void writepng_cleanup(mainprog_info *mainprog_ptr)
{
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;

    if (png_ptr && info_ptr)
        png_destroy_write_struct(&png_ptr, &info_ptr);
}





static void writepng_error_handler(png_structp png_ptr, png_const_charp msg)
{
    mainprog_info  *mainprog_ptr;

    /* This function, aside from the extra step of retrieving the "error
     * pointer" (below) and the fact that it exists within the application
     * rather than within libpng, is essentially identical to libpng's
     * default error handler.  The second point is critical:  since both
     * setjmp() and longjmp() are called from the same code, they are
     * guaranteed to have compatible notions of how big a jmp_buf is,
     * regardless of whether _BSD_SOURCE or anything else has (or has not)
     * been defined. */

    fprintf(stderr, "writepng libpng error: %s\n", msg);
    fflush(stderr);

    mainprog_ptr = png_get_error_ptr(png_ptr);
    if (mainprog_ptr == NULL) {         /* we are completely hosed now */
        fprintf(stderr,
          "writepng severe error:  jmpbuf not recoverable; terminating.\n");
        fflush(stderr);
        exit(99);
    }

    longjmp(mainprog_ptr->jmpbuf, 1);
}

--- NEW FILE: LICENSE ---
  ---------------------------------------------------------------------------

      Copyright (c) 1998-2000 Greg Roelofs.  All rights reserved.

      This software is provided "as is," without warranty of any kind,
      express or implied.  In no event shall the author or contributors
      be held liable for any damages arising in any way from the use of
      this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute
      it freely, subject to the following restrictions:

      1. Redistributions of source code must retain the above copyright
         notice, disclaimer, and this list of conditions.
      2. Redistributions in binary form must reproduce the above copyright
         notice, disclaimer, and this list of conditions in the documenta-
         tion and/or other materials provided with the distribution.
      3. All advertising materials mentioning features or use of this
         software must display the following acknowledgment:

            This product includes software developed by Greg Roelofs
            and contributors for the book, "PNG: The Definitive Guide,"
            published by O'Reilly and Associates.

  ---------------------------------------------------------------------------

--- NEW FILE: rpng-win.c ---
/*---------------------------------------------------------------------------

   rpng - simple PNG display program                             rpng-win.c

   This program decodes and displays PNG images, with gamma correction and
   optionally with a user-specified background color (in case the image has
   transparency).  It is very nearly the most basic PNG viewer possible.
   This version is for 32-bit Windows; it may compile under 16-bit Windows
   with a little tweaking (or maybe not).

   to do:
    - handle quoted command-line args (especially filenames with spaces)
    - have minimum window width:  oh well
    - use %.1023s to simplify truncation of title-bar string?

  ---------------------------------------------------------------------------

   Changelog:
    - 1.00:  initial public release
    - 1.01:  modified to allow abbreviated options; fixed long/ulong mis-
              match; switched to png_jmpbuf() macro
    - 1.02:  added extra set of parentheses to png_jmpbuf() macro; fixed
              command-line parsing bug
    - 1.10:  enabled "message window"/console (thanks to David Geldreich)

  ---------------------------------------------------------------------------

      Copyright (c) 1998-2001 Greg Roelofs.  All rights reserved.

      This software is provided "as is," without warranty of any kind,
      express or implied.  In no event shall the author or contributors
      be held liable for any damages arising in any way from the use of
      this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute
      it freely, subject to the following restrictions:

      1. Redistributions of source code must retain the above copyright
         notice, disclaimer, and this list of conditions.
      2. Redistributions in binary form must reproduce the above copyright
         notice, disclaimer, and this list of conditions in the documenta-
         tion and/or other materials provided with the distribution.
      3. All advertising materials mentioning features or use of this
         software must display the following acknowledgment:

            This product includes software developed by Greg Roelofs
            and contributors for the book, "PNG: The Definitive Guide,"
            published by O'Reilly and Associates.

  ---------------------------------------------------------------------------*/

#define PROGNAME  "rpng-win"
#define LONGNAME  "Simple PNG Viewer for Windows"
#define VERSION   "1.20 of 28 May 2001"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <windows.h>
#include <conio.h>      /* only for _getch() */

/* #define DEBUG  :  this enables the Trace() macros */

#include "readpng.h"    /* typedefs, common macros, readpng prototypes */


/* could just include png.h, but this macro is the only thing we need
 * (name and typedefs changed to local versions); note that side effects
 * only happen with alpha (which could easily be avoided with
 * "ush acopy = (alpha);") */

#define alpha_composite(composite, fg, alpha, bg) {               \
    ush temp = ((ush)(fg)*(ush)(alpha) +                          \
                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
    (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
}


/* local prototypes */
static int        rpng_win_create_window(HINSTANCE hInst, int showmode);
static int        rpng_win_display_image(void);
static void       rpng_win_cleanup(void);
LRESULT CALLBACK  rpng_win_wndproc(HWND, UINT, WPARAM, LPARAM);


static char titlebar[1024], *window_name = titlebar;
static char *progname = PROGNAME;
static char *appname = LONGNAME;
static char *icon_name = PROGNAME;     /* GRR:  not (yet) used */
static char *filename;
static FILE *infile;

static char *bgstr;
static uch bg_red=0, bg_green=0, bg_blue=0;

static double display_exponent;

static ulg image_width, image_height, image_rowbytes;
static int image_channels;
static uch *image_data;

/* Windows-specific variables */
static ulg wimage_rowbytes;
static uch *dib;
static uch *wimage_data;
static BITMAPINFOHEADER *bmih;

static HWND global_hwnd;




int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
{
    char *args[1024];                 /* arbitrary limit, but should suffice */
    char *p, *q, **argv = args;
    int argc = 0;
    int rc, alen, flen;
    int error = 0;
    int have_bg = FALSE;
    double LUT_exponent;              /* just the lookup table */
    double CRT_exponent = 2.2;        /* just the monitor */
    double default_display_exponent;  /* whole display system */
    MSG msg;


    filename = (char *)NULL;


    /* First reenable console output, which normally goes to the bit bucket
     * for windowed apps.  Closing the console window will terminate the
     * app.  Thanks to David.Geldreich at realviz.com for supplying the magical
     * incantation. */

    AllocConsole();
    freopen("CONOUT$", "a", stderr);
    freopen("CONOUT$", "a", stdout);


    /* Next set the default value for our display-system exponent, i.e.,
     * the product of the CRT exponent and the exponent corresponding to
     * the frame-buffer's lookup table (LUT), if any.  This is not an
     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
     * ones), but it should cover 99% of the current possibilities.  And
     * yes, these ifdefs are completely wasted in a Windows program... */

#if defined(NeXT)
    LUT_exponent = 1.0 / 2.2;
    /*
    if (some_next_function_that_returns_gamma(&next_gamma))
        LUT_exponent = 1.0 / next_gamma;
     */
#elif defined(sgi)
    LUT_exponent = 1.0 / 1.7;
    /* there doesn't seem to be any documented function to get the
     * "gamma" value, so we do it the hard way */
    infile = fopen("/etc/config/system.glGammaVal", "r");
    if (infile) {
        double sgi_gamma;

        fgets(tmpline, 80, infile);
        fclose(infile);
        sgi_gamma = atof(tmpline);
        if (sgi_gamma > 0.0)
            LUT_exponent = 1.0 / sgi_gamma;
    }
#elif defined(Macintosh)
    LUT_exponent = 1.8 / 2.61;
    /*
    if (some_mac_function_that_returns_gamma(&mac_gamma))
        LUT_exponent = mac_gamma / 2.61;
     */
#else
    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
#endif

    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
    default_display_exponent = LUT_exponent * CRT_exponent;


    /* If the user has set the SCREEN_GAMMA environment variable as suggested
     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
     * use the default value we just calculated.  Either way, the user may
     * override this via a command-line option. */

    if ((p = getenv("SCREEN_GAMMA")) != NULL)
        display_exponent = atof(p);
    else
        display_exponent = default_display_exponent;


    /* Windows really hates command lines, so we have to set up our own argv.
     * Note that we do NOT bother with quoted arguments here, so don't use
     * filenames with spaces in 'em! */

    argv[argc++] = PROGNAME;
    p = cmd;
    for (;;) {
        if (*p == ' ')
            while (*++p == ' ')
                ;
        /* now p points at the first non-space after some spaces */
        if (*p == '\0')
            break;    /* nothing after the spaces:  done */
        argv[argc++] = q = p;
        while (*q && *q != ' ')
            ++q;
        /* now q points at a space or the end of the string */
        if (*q == '\0')
            break;    /* last argv already terminated; quit */
        *q = '\0';    /* change space to terminator */
        p = q + 1;
    }
    argv[argc] = NULL;   /* terminate the argv array itself */


    /* Now parse the command line for options and the PNG filename. */

    while (*++argv && !error) {
        if (!strncmp(*argv, "-gamma", 2)) {
            if (!*++argv)
                ++error;
            else {
                display_exponent = atof(*argv);
                if (display_exponent <= 0.0)
                    ++error;
            }
        } else if (!strncmp(*argv, "-bgcolor", 2)) {
            if (!*++argv)
                ++error;
            else {
                bgstr = *argv;
                if (strlen(bgstr) != 7 || bgstr[0] != '#')
                    ++error;
                else
                    have_bg = TRUE;
            }
        } else {
            if (**argv != '-') {
                filename = *argv;
                if (argv[1])   /* shouldn't be any more args after filename */
                    ++error;
            } else
                ++error;   /* not expecting any other options */
        }
    }

    if (!filename) {
        ++error;
    } else if (!(infile = fopen(filename, "rb"))) {
        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
        ++error;
    } else {
        if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) {
            switch (rc) {
                case 1:
                    fprintf(stderr, PROGNAME
                      ":  [%s] is not a PNG file: incorrect signature\n",
                      filename);
                    break;
                case 2:
                    fprintf(stderr, PROGNAME
                      ":  [%s] has bad IHDR (libpng longjmp)\n",
                      filename);
                    break;
                case 4:
                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
                    break;
                default:
                    fprintf(stderr, PROGNAME
                      ":  unknown readpng_init() error\n");
                    break;
            }
            ++error;
        }
        if (error)
            fclose(infile);
    }


    /* usage screen */

    if (error) {
        int ch;

        fprintf(stderr, "\n%s %s:  %s\n\n", PROGNAME, VERSION, appname);
        readpng_version_info();
        fprintf(stderr, "\n"
          "Usage:  %s [-gamma exp] [-bgcolor bg] file.png\n"
          "    exp \ttransfer-function exponent (``gamma'') of the display\n"
          "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
          "\t\t  to the product of the lookup-table exponent (varies)\n"
          "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
          "    bg  \tdesired background color in 7-character hex RGB format\n"
          "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
          "\t\t  used with transparent images\n"
          "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
          "Press Q or Esc to quit this usage screen.\n"
          "\n", PROGNAME, default_display_exponent);
        do
            ch = _getch();
        while (ch != 'q' && ch != 'Q' && ch != 0x1B);
        exit(1);
    } else {
        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
        fprintf(stderr,
          "\n   [console window:  closing this window will terminate %s]\n\n",
          PROGNAME);
    }


    /* set the title-bar string, but make sure buffer doesn't overflow */

    alen = strlen(appname);
    flen = strlen(filename);
    if (alen + flen + 3 > 1023)
        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
    else
        sprintf(titlebar, "%s:  %s", appname, filename);


    /* if the user didn't specify a background color on the command line,
     * check for one in the PNG file--if not, the initialized values of 0
     * (black) will be used */

    if (have_bg)
        sscanf(bgstr+1, "%2x%2x%2x", &bg_red, &bg_green, &bg_blue);
    else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) {
        readpng_cleanup(TRUE);
        fprintf(stderr, PROGNAME
          ":  libpng error while checking for background color\n");
        exit(2);
    }


    /* do the basic Windows initialization stuff, make the window and fill it
     * with the background color */

    if (rpng_win_create_window(hInst, showmode))
        exit(2);


    /* decode the image, all at once */

    Trace((stderr, "calling readpng_get_image()\n"))
    image_data = readpng_get_image(display_exponent, &image_channels,
      &image_rowbytes);
    Trace((stderr, "done with readpng_get_image()\n"))


    /* done with PNG file, so clean up to minimize memory usage (but do NOT
     * nuke image_data!) */

    readpng_cleanup(FALSE);
    fclose(infile);

    if (!image_data) {
        fprintf(stderr, PROGNAME ":  unable to decode PNG image\n");
        exit(3);
    }


    /* display image (composite with background if requested) */

    Trace((stderr, "calling rpng_win_display_image()\n"))
    if (rpng_win_display_image()) {
        free(image_data);
        exit(4);
    }
    Trace((stderr, "done with rpng_win_display_image()\n"))


    /* wait for the user to tell us when to quit */

    printf(
      "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
    fflush(stdout);

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }


    /* OK, we're done:  clean up all image and Windows resources and go away */

    rpng_win_cleanup();

    return msg.wParam;
}





static int rpng_win_create_window(HINSTANCE hInst, int showmode)
{
    uch *dest;
    int extra_width, extra_height;
    ulg i, j;
    WNDCLASSEX wndclass;


/*---------------------------------------------------------------------------
    Allocate memory for the display-specific version of the image (round up
    to multiple of 4 for Windows DIB).
  ---------------------------------------------------------------------------*/

    wimage_rowbytes = ((3*image_width + 3L) >> 2) << 2;

    if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
                              wimage_rowbytes*image_height)))
    {
        return 4;   /* fail */
    }

/*---------------------------------------------------------------------------
    Initialize the DIB.  Negative height means to use top-down BMP ordering
    (must be uncompressed, but that's what we want).  Bit count of 1, 4 or 8
    implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values
    directly => wimage_data begins immediately after BMP header.
  ---------------------------------------------------------------------------*/

    memset(dib, 0, sizeof(BITMAPINFOHEADER));
    bmih = (BITMAPINFOHEADER *)dib;
    bmih->biSize = sizeof(BITMAPINFOHEADER);
    bmih->biWidth = image_width;
    bmih->biHeight = -((long)image_height);
    bmih->biPlanes = 1;
    bmih->biBitCount = 24;
    bmih->biCompression = 0;
    wimage_data = dib + sizeof(BITMAPINFOHEADER);

/*---------------------------------------------------------------------------
    Fill in background color (black by default); data are in BGR order.
  ---------------------------------------------------------------------------*/

    for (j = 0;  j < image_height;  ++j) {
        dest = wimage_data + j*wimage_rowbytes;
        for (i = image_width;  i > 0;  --i) {
            *dest++ = bg_blue;
            *dest++ = bg_green;
            *dest++ = bg_red;
        }
    }

/*---------------------------------------------------------------------------
    Set the window parameters.
  ---------------------------------------------------------------------------*/

    memset(&wndclass, 0, sizeof(wndclass));

    wndclass.cbSize = sizeof(wndclass);
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = rpng_win_wndproc;
    wndclass.hInstance = hInst;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = progname;
    wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    RegisterClassEx(&wndclass);

/*---------------------------------------------------------------------------
    Finally, create the window.
  ---------------------------------------------------------------------------*/

    extra_width  = 2*(GetSystemMetrics(SM_CXBORDER) +
                      GetSystemMetrics(SM_CXDLGFRAME));
    extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
                      GetSystemMetrics(SM_CYDLGFRAME)) +
                      GetSystemMetrics(SM_CYCAPTION);

    global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, CW_USEDEFAULT, image_width+extra_width,
      image_height+extra_height, NULL, NULL, hInst, NULL);

    ShowWindow(global_hwnd, showmode);
    UpdateWindow(global_hwnd);

    return 0;

} /* end function rpng_win_create_window() */





static int rpng_win_display_image()
{
    uch *src, *dest;
    uch r, g, b, a;
    ulg i, row, lastrow;
    RECT rect;


    Trace((stderr, "beginning display loop (image_channels == %d)\n",
      image_channels))
    Trace((stderr, "(width = %ld, rowbytes = %ld, wimage_rowbytes = %d)\n",
      image_width, image_rowbytes, wimage_rowbytes))


/*---------------------------------------------------------------------------
    Blast image data to buffer.  This whole routine takes place before the
    message loop begins, so there's no real point in any pseudo-progressive
    display...
  ---------------------------------------------------------------------------*/

    for (lastrow = row = 0;  row < image_height;  ++row) {
        src = image_data + row*image_rowbytes;
        dest = wimage_data + row*wimage_rowbytes;
        if (image_channels == 3) {
            for (i = image_width;  i > 0;  --i) {
                r = *src++;
                g = *src++;
                b = *src++;
                *dest++ = b;
                *dest++ = g;   /* note reverse order */
                *dest++ = r;
            }
        } else /* if (image_channels == 4) */ {
            for (i = image_width;  i > 0;  --i) {
                r = *src++;
                g = *src++;
                b = *src++;
                a = *src++;
                if (a == 255) {
                    *dest++ = b;
                    *dest++ = g;
                    *dest++ = r;
                } else if (a == 0) {
                    *dest++ = bg_blue;
                    *dest++ = bg_green;
                    *dest++ = bg_red;
                } else {
                    /* this macro (copied from png.h) composites the
                     * foreground and background values and puts the
                     * result into the first argument; there are no
                     * side effects with the first argument */
                    alpha_composite(*dest++, b, a, bg_blue);
                    alpha_composite(*dest++, g, a, bg_green);
                    alpha_composite(*dest++, r, a, bg_red);
                }
            }
        }
        /* display after every 16 lines */
        if (((row+1) & 0xf) == 0) {
            rect.left = 0L;
            rect.top = (LONG)lastrow;
            rect.right = (LONG)image_width;      /* possibly off by one? */
            rect.bottom = (LONG)lastrow + 16L;   /* possibly off by one? */
            InvalidateRect(global_hwnd, &rect, FALSE);
            UpdateWindow(global_hwnd);     /* similar to XFlush() */
            lastrow = row + 1;
        }
    }

    Trace((stderr, "calling final image-flush routine\n"))
    if (lastrow < image_height) {
        rect.left = 0L;
        rect.top = (LONG)lastrow;
        rect.right = (LONG)image_width;      /* possibly off by one? */
        rect.bottom = (LONG)image_height;    /* possibly off by one? */
        InvalidateRect(global_hwnd, &rect, FALSE);
        UpdateWindow(global_hwnd);     /* similar to XFlush() */
    }

/*
    last param determines whether or not background is wiped before paint
    InvalidateRect(global_hwnd, NULL, TRUE);
    UpdateWindow(global_hwnd);
 */

    return 0;
}





static void rpng_win_cleanup()
{
    if (image_data) {
        free(image_data);
        image_data = NULL;
    }

    if (dib) {
        free(dib);
        dib = NULL;
    }
}





LRESULT CALLBACK rpng_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
{
    HDC         hdc;
    PAINTSTRUCT ps;
    int rc;

    switch (iMsg) {
        case WM_CREATE:
            /* one-time processing here, if any */
            return 0;

        case WM_PAINT:
            hdc = BeginPaint(hwnd, &ps);
                    /*                    dest                          */
            rc = StretchDIBits(hdc, 0, 0, image_width, image_height,
                    /*                    source                        */
                                    0, 0, image_width, image_height,
                                    wimage_data, (BITMAPINFO *)bmih,
                    /*              iUsage: no clue                     */
                                    0, SRCCOPY);
            EndPaint(hwnd, &ps);
            return 0;

        /* wait for the user to tell us when to quit */
        case WM_CHAR:
            switch (wP) {      /* only need one, so ignore repeat count */
                case 'q':
                case 'Q':
                case 0x1B:     /* Esc key */
                    PostQuitMessage(0);
            }
            return 0;

        case WM_LBUTTONDOWN:   /* another way of quitting */
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }

    return DefWindowProc(hwnd, iMsg, wP, lP);
}

--- NEW FILE: readpng2.c ---
/*---------------------------------------------------------------------------

   rpng2 - progressive-model PNG display program                 readpng2.c

  ---------------------------------------------------------------------------

      Copyright (c) 1998-2001 Greg Roelofs.  All rights reserved.

      This software is provided "as is," without warranty of any kind,
      express or implied.  In no event shall the author or contributors
      be held liable for any damages arising in any way from the use of
      this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute
      it freely, subject to the following restrictions:

      1. Redistributions of source code must retain the above copyright
         notice, disclaimer, and this list of conditions.
      2. Redistributions in binary form must reproduce the above copyright
         notice, disclaimer, and this list of conditions in the documenta-
         tion and/or other materials provided with the distribution.
      3. All advertising materials mentioning features or use of this
         software must display the following acknowledgment:

            This product includes software developed by Greg Roelofs
            and contributors for the book, "PNG: The Definitive Guide,"
            published by O'Reilly and Associates.

  ---------------------------------------------------------------------------*/


#include <stdlib.h>     /* for exit() prototype */

#include "png.h"        /* libpng header; includes zlib.h and setjmp.h */
#include "readpng2.h"   /* typedefs, common macros, public prototypes */


/* local prototypes */

static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr);
static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row,
                                 png_uint_32 row_num, int pass);
static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr);
static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg);




void readpng2_version_info(void)
{
    fprintf(stderr, "   Compiled with libpng %s; using libpng %s.\n",
      PNG_LIBPNG_VER_STRING, png_libpng_ver);

    fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
      ZLIB_VERSION, zlib_version);
}




int readpng2_check_sig(uch *sig, int num)
{
    return png_check_sig(sig, num);
}




/* returns 0 for success, 2 for libpng problem, 4 for out of memory */

int readpng2_init(mainprog_info *mainprog_ptr)
{
    png_structp  png_ptr;       /* note:  temporary variables! */
    png_infop  info_ptr;


    /* could also replace libpng warning-handler (final NULL), but no need: */

    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
      readpng2_error_handler, NULL);
    if (!png_ptr)
        return 4;   /* out of memory */

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
        png_destroy_read_struct(&png_ptr, NULL, NULL);
        return 4;   /* out of memory */
    }


    /* we could create a second info struct here (end_info), but it's only
     * useful if we want to keep pre- and post-IDAT chunk info separated
     * (mainly for PNG-aware image editors and converters) */


    /* setjmp() must be called in every function that calls a PNG-reading
     * libpng function, unless an alternate error handler was installed--
     * but compatible error handlers must either use longjmp() themselves
     * (as in this program) or exit immediately, so here we are: */

    if (setjmp(mainprog_ptr->jmpbuf)) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        return 2;
    }


    /* instead of doing png_init_io() here, now we set up our callback
     * functions for progressive decoding */

    png_set_progressive_read_fn(png_ptr, mainprog_ptr,
      readpng2_info_callback, readpng2_row_callback, readpng2_end_callback);

    /* make sure we save our pointers for use in readpng2_decode_data() */

    mainprog_ptr->png_ptr = png_ptr;
    mainprog_ptr->info_ptr = info_ptr;


    /* and that's all there is to initialization */

    return 0;
}




/* returns 0 for success, 2 for libpng (longjmp) problem */

int readpng2_decode_data(mainprog_info *mainprog_ptr, uch *rawbuf, ulg length)
{
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;


    /* setjmp() must be called in every function that calls a PNG-reading
     * libpng function */

    if (setjmp(mainprog_ptr->jmpbuf)) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        mainprog_ptr->png_ptr = NULL;
        mainprog_ptr->info_ptr = NULL;
        return 2;
    }


    /* hand off the next chunk of input data to libpng for decoding */

    png_process_data(png_ptr, info_ptr, rawbuf, length);

    return 0;
}




static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr)
{
    mainprog_info  *mainprog_ptr;
    int  color_type, bit_depth;
    double  gamma;


    /* setjmp() doesn't make sense here, because we'd either have to exit(),
     * longjmp() ourselves, or return control to libpng, which doesn't want
     * to see us again.  By not doing anything here, libpng will instead jump
     * to readpng2_decode_data(), which can return an error value to the main
     * program. */


    /* retrieve the pointer to our special-purpose struct, using the png_ptr
     * that libpng passed back to us (i.e., not a global this time--there's
     * no real difference for a single image, but for a multithreaded browser
     * decoding several PNG images at the same time, one needs to avoid mixing
     * up different images' structs) */

    mainprog_ptr = png_get_progressive_ptr(png_ptr);

    if (mainprog_ptr == NULL) {         /* we be hosed */
        fprintf(stderr,
          "readpng2 error:  main struct not recoverable in info_callback.\n");
        fflush(stderr);
        return;
        /*
         * Alternatively, we could call our error-handler just like libpng
         * does, which would effectively terminate the program.  Since this
         * can only happen if png_ptr gets redirected somewhere odd or the
         * main PNG struct gets wiped, we're probably toast anyway.  (If
         * png_ptr itself is NULL, we would not have been called.)
         */
    }


    /* this is just like in the non-progressive case */

    png_get_IHDR(png_ptr, info_ptr, &mainprog_ptr->width,
      &mainprog_ptr->height, &bit_depth, &color_type, NULL, NULL, NULL);


    /* since we know we've read all of the PNG file's "header" (i.e., up
     * to IDAT), we can check for a background color here */

    if (mainprog_ptr->need_bgcolor &&
        png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
    {
        png_color_16p pBackground;

        /* it is not obvious from the libpng documentation, but this function
         * takes a pointer to a pointer, and it always returns valid red,
         * green and blue values, regardless of color_type: */
        png_get_bKGD(png_ptr, info_ptr, &pBackground);

        /* however, it always returns the raw bKGD data, regardless of any
         * bit-depth transformations, so check depth and adjust if necessary */
        if (bit_depth == 16) {
            mainprog_ptr->bg_red   = pBackground->red   >> 8;
            mainprog_ptr->bg_green = pBackground->green >> 8;
            mainprog_ptr->bg_blue  = pBackground->blue  >> 8;
        } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
            if (bit_depth == 1)
                mainprog_ptr->bg_red = mainprog_ptr->bg_green =
                  mainprog_ptr->bg_blue = pBackground->gray? 255 : 0;
            else if (bit_depth == 2)
                mainprog_ptr->bg_red = mainprog_ptr->bg_green =
                  mainprog_ptr->bg_blue = (255/3) * pBackground->gray;
            else /* bit_depth == 4 */
                mainprog_ptr->bg_red = mainprog_ptr->bg_green =
                  mainprog_ptr->bg_blue = (255/15) * pBackground->gray;
        } else {
            mainprog_ptr->bg_red   = (uch)pBackground->red;
            mainprog_ptr->bg_green = (uch)pBackground->green;
            mainprog_ptr->bg_blue  = (uch)pBackground->blue;
        }
    }


    /* as before, let libpng expand palette images to RGB, low-bit-depth
     * grayscale images to 8 bits, transparency chunks to full alpha channel;
     * strip 16-bit-per-sample images to 8 bits per sample; and convert
     * grayscale to RGB[A] */

    if (color_type == PNG_COLOR_TYPE_PALETTE)
        png_set_expand(png_ptr);
    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
        png_set_expand(png_ptr);
    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
        png_set_expand(png_ptr);
    if (bit_depth == 16)
        png_set_strip_16(png_ptr);
    if (color_type == PNG_COLOR_TYPE_GRAY ||
        color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
        png_set_gray_to_rgb(png_ptr);


    /* Unlike the basic viewer, which was designed to operate on local files,
     * this program is intended to simulate a web browser--even though we
     * actually read from a local file, too.  But because we are pretending
     * that most of the images originate on the Internet, we follow the recom-
     * mendation of the sRGB proposal and treat unlabelled images (no gAMA
     * chunk) as existing in the sRGB color space.  That is, we assume that
     * such images have a file gamma of 0.45455, which corresponds to a PC-like
     * display system.  This change in assumptions will have no effect on a
     * PC-like system, but on a Mac, SGI, NeXT or other system with a non-
     * identity lookup table, it will darken unlabelled images, which effec-
     * tively favors images from PC-like systems over those originating on
     * the local platform.  Note that mainprog_ptr->display_exponent is the
     * "gamma" value for the entire display system, i.e., the product of
     * LUT_exponent and CRT_exponent. */

    if (png_get_gAMA(png_ptr, info_ptr, &gamma))
        png_set_gamma(png_ptr, mainprog_ptr->display_exponent, gamma);
    else
        png_set_gamma(png_ptr, mainprog_ptr->display_exponent, 0.45455);


    /* we'll let libpng expand interlaced images, too */

    mainprog_ptr->passes = png_set_interlace_handling(png_ptr);


    /* all transformations have been registered; now update info_ptr data and
     * then get rowbytes and channels */

    png_read_update_info(png_ptr, info_ptr);

    mainprog_ptr->rowbytes = (int)png_get_rowbytes(png_ptr, info_ptr);
    mainprog_ptr->channels = png_get_channels(png_ptr, info_ptr);


    /* Call the main program to allocate memory for the image buffer and
     * initialize windows and whatnot.  (The old-style function-pointer
     * invocation is used for compatibility with a few supposedly ANSI
     * compilers that nevertheless barf on "fn_ptr()"-style syntax.) */

    (*mainprog_ptr->mainprog_init)();


    /* and that takes care of initialization */

    return;
}





static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row,
                                  png_uint_32 row_num, int pass)
{
    mainprog_info  *mainprog_ptr;


    /* first check whether the row differs from the previous pass; if not,
     * nothing to combine or display */

    if (!new_row)
        return;


    /* retrieve the pointer to our special-purpose struct so we can access
     * the old rows and image-display callback function */

    mainprog_ptr = png_get_progressive_ptr(png_ptr);


    /* save the pass number for optional use by the front end */

    mainprog_ptr->pass = pass;


    /* have libpng either combine the new row data with the existing row data
     * from previous passes (if interlaced) or else just copy the new row
     * into the main program's image buffer */

    png_progressive_combine_row(png_ptr, mainprog_ptr->row_pointers[row_num],
      new_row);


    /* finally, call the display routine in the main program with the number
     * of the row we just updated */

    (*mainprog_ptr->mainprog_display_row)(row_num);


    /* and we're ready for more */

    return;
}





static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr)
{
    mainprog_info  *mainprog_ptr;


    /* retrieve the pointer to our special-purpose struct */

    mainprog_ptr = png_get_progressive_ptr(png_ptr);


    /* let the main program know that it should flush any buffered image
     * data to the display now and set a "done" flag or whatever, but note
     * that it SHOULD NOT DESTROY THE PNG STRUCTS YET--in other words, do
     * NOT call readpng2_cleanup() either here or in the finish_display()
     * routine; wait until control returns to the main program via
     * readpng2_decode_data() */

    (*mainprog_ptr->mainprog_finish_display)();


    /* all done */

    return;
}





void readpng2_cleanup(mainprog_info *mainprog_ptr)
{
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;

    if (png_ptr && info_ptr)
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

    mainprog_ptr->png_ptr = NULL;
    mainprog_ptr->info_ptr = NULL;
}





static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg)
{
    mainprog_info  *mainprog_ptr;

    /* This function, aside from the extra step of retrieving the "error
     * pointer" (below) and the fact that it exists within the application
     * rather than within libpng, is essentially identical to libpng's
     * default error handler.  The second point is critical:  since both
     * setjmp() and longjmp() are called from the same code, they are
     * guaranteed to have compatible notions of how big a jmp_buf is,
     * regardless of whether _BSD_SOURCE or anything else has (or has not)
     * been defined. */

    fprintf(stderr, "readpng2 libpng error: %s\n", msg);
    fflush(stderr);

    mainprog_ptr = png_get_error_ptr(png_ptr);
    if (mainprog_ptr == NULL) {         /* we are completely hosed now */
        fprintf(stderr,
          "readpng2 severe error:  jmpbuf not recoverable; terminating.\n");
        fflush(stderr);
        exit(99);
    }

    longjmp(mainprog_ptr->jmpbuf, 1);
}

--- NEW FILE: rpng-x.c ---
/*---------------------------------------------------------------------------

   rpng - simple PNG display program                               rpng-x.c

   This program decodes and displays PNG images, with gamma correction and
   optionally with a user-specified background color (in case the image has
   transparency).  It is very nearly the most basic PNG viewer possible.
   This version is for the X Window System (tested by author under Unix and
   by Martin Zinser under OpenVMS; may work under OS/2 with some tweaking).

   to do:
    - 8-bit support
    - use %.1023s to simplify truncation of title-bar string?

  ---------------------------------------------------------------------------

   Changelog:
    - 1.01:  initial public release
    - 1.02:  modified to allow abbreviated options; fixed long/ulong mis-
              match; switched to png_jmpbuf() macro
    - 1.10:  added support for non-default visuals; fixed X pixel-conversion
    - 1.11:  added extra set of parentheses to png_jmpbuf() macro; fixed
              command-line parsing bug
    - 1.12:  fixed small X memory leak (thanks to Francois Petitjean)

  ---------------------------------------------------------------------------

      Copyright (c) 1998-2001 Greg Roelofs.  All rights reserved.

      This software is provided "as is," without warranty of any kind,
      express or implied.  In no event shall the author or contributors
      be held liable for any damages arising in any way from the use of
      this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute
      it freely, subject to the following restrictions:

      1. Redistributions of source code must retain the above copyright
         notice, disclaimer, and this list of conditions.
      2. Redistributions in binary form must reproduce the above copyright
         notice, disclaimer, and this list of conditions in the documenta-
         tion and/or other materials provided with the distribution.
      3. All advertising materials mentioning features or use of this
         software must display the following acknowledgment:

            This product includes software developed by Greg Roelofs
            and contributors for the book, "PNG: The Definitive Guide,"
            published by O'Reilly and Associates.

  ---------------------------------------------------------------------------*/

#define PROGNAME  "rpng-x"
#define LONGNAME  "Simple PNG Viewer for X"
#define VERSION   "1.12 of 28 May 2001"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/keysym.h>

/* #define DEBUG  :  this enables the Trace() macros */

#include "readpng.h"   /* typedefs, common macros, readpng prototypes */


/* could just include png.h, but this macro is the only thing we need
 * (name and typedefs changed to local versions); note that side effects
 * only happen with alpha (which could easily be avoided with
 * "ush acopy = (alpha);") */

#define alpha_composite(composite, fg, alpha, bg) {               \
    ush temp = ((ush)(fg)*(ush)(alpha) +                          \
                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
    (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
}


/* local prototypes */
static int  rpng_x_create_window(void);
static int  rpng_x_display_image(void);
static void rpng_x_cleanup(void);
static int  rpng_x_msb(ulg u32val);


static char titlebar[1024], *window_name = titlebar;
static char *appname = LONGNAME;
static char *icon_name = PROGNAME;
static char *filename;
static FILE *infile;

static char *bgstr;
static uch bg_red=0, bg_green=0, bg_blue=0;

static double display_exponent;

static ulg image_width, image_height, image_rowbytes;
static int image_channels;
static uch *image_data;

/* X-specific variables */
static char *displayname;
static XImage *ximage;
static Display *display;
static int depth;
static Visual *visual;
static XVisualInfo *visual_list;
static int RShift, GShift, BShift;
static ulg RMask, GMask, BMask;
static Window window;
static GC gc;
static Colormap colormap;

static int have_nondefault_visual = FALSE;
static int have_colormap = FALSE;
static int have_window = FALSE;
/*
ulg numcolors=0, pixels[256];
ush reds[256], greens[256], blues[256];
 */




int main(int argc, char **argv)
{
#ifdef sgi
    char tmpline[80];
#endif
    char *p;
    int rc, alen, flen;
    int error = 0;
    int have_bg = FALSE;
    double LUT_exponent;               /* just the lookup table */
    double CRT_exponent = 2.2;         /* just the monitor */
    double default_display_exponent;   /* whole display system */
    XEvent e;
    KeySym k;


    displayname = (char *)NULL;
    filename = (char *)NULL;


    /* First set the default value for our display-system exponent, i.e.,
     * the product of the CRT exponent and the exponent corresponding to
     * the frame-buffer's lookup table (LUT), if any.  This is not an
     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
     * ones), but it should cover 99% of the current possibilities. */

#if defined(NeXT)
    LUT_exponent = 1.0 / 2.2;
    /*
    if (some_next_function_that_returns_gamma(&next_gamma))
        LUT_exponent = 1.0 / next_gamma;
     */
#elif defined(sgi)
    LUT_exponent = 1.0 / 1.7;
    /* there doesn't seem to be any documented function to get the
     * "gamma" value, so we do it the hard way */
    infile = fopen("/etc/config/system.glGammaVal", "r");
    if (infile) {
        double sgi_gamma;

        fgets(tmpline, 80, infile);
        fclose(infile);
        sgi_gamma = atof(tmpline);
        if (sgi_gamma > 0.0)
            LUT_exponent = 1.0 / sgi_gamma;
    }
#elif defined(Macintosh)
    LUT_exponent = 1.8 / 2.61;
    /*
    if (some_mac_function_that_returns_gamma(&mac_gamma))
        LUT_exponent = mac_gamma / 2.61;
     */
#else
    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
#endif

    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
    default_display_exponent = LUT_exponent * CRT_exponent;


    /* If the user has set the SCREEN_GAMMA environment variable as suggested
     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
     * use the default value we just calculated.  Either way, the user may
     * override this via a command-line option. */

    if ((p = getenv("SCREEN_GAMMA")) != NULL)
        display_exponent = atof(p);
    else
        display_exponent = default_display_exponent;


    /* Now parse the command line for options and the PNG filename. */

    while (*++argv && !error) {
        if (!strncmp(*argv, "-display", 2)) {
            if (!*++argv)
                ++error;
            else
                displayname = *argv;
        } else if (!strncmp(*argv, "-gamma", 2)) {
            if (!*++argv)
                ++error;
            else {
                display_exponent = atof(*argv);
                if (display_exponent <= 0.0)
                    ++error;
            }
        } else if (!strncmp(*argv, "-bgcolor", 2)) {
            if (!*++argv)
                ++error;
            else {
                bgstr = *argv;
                if (strlen(bgstr) != 7 || bgstr[0] != '#')
                    ++error; 
                else 
                    have_bg = TRUE;
            }
        } else {
            if (**argv != '-') {
                filename = *argv;
                if (argv[1])   /* shouldn't be any more args after filename */
                    ++error;
            } else
                ++error;   /* not expecting any other options */
        }
    }

    if (!filename) {
        ++error;
    } else if (!(infile = fopen(filename, "rb"))) {
        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
        ++error;
    } else {
        if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) {
            switch (rc) {
                case 1:
                    fprintf(stderr, PROGNAME
                      ":  [%s] is not a PNG file: incorrect signature\n",
                      filename);
                    break;
                case 2:
                    fprintf(stderr, PROGNAME
                      ":  [%s] has bad IHDR (libpng longjmp)\n",
                      filename);
                    break;
                case 4:
                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
                    break;
                default:
                    fprintf(stderr, PROGNAME
                      ":  unknown readpng_init() error\n");
                    break;
            }
            ++error;
        } else {
            display = XOpenDisplay(displayname);
            if (!display) {
                readpng_cleanup(TRUE);
                fprintf(stderr, PROGNAME ":  can't open X display [%s]\n",
                  displayname? displayname : "default");
                ++error;
            }
        }
        if (error)
            fclose(infile);
    }


    /* usage screen */

    if (error) {
        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
        readpng_version_info();
        fprintf(stderr, "\n"
          "Usage:  %s [-display xdpy] [-gamma exp] [-bgcolor bg] file.png\n"
          "    xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
          "    exp \ttransfer-function exponent (``gamma'') of the display\n"
          "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
          "\t\t  to the product of the lookup-table exponent (varies)\n"
          "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
          "    bg  \tdesired background color in 7-character hex RGB format\n"
          "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
          "\t\t  used with transparent images\n"
          "\nPress Q, Esc or mouse button 1 (within image window, after image\n"
          "is displayed) to quit.\n"
          "\n", PROGNAME, default_display_exponent);
        exit(1);
    }


    /* set the title-bar string, but make sure buffer doesn't overflow */

    alen = strlen(appname);
    flen = strlen(filename);
    if (alen + flen + 3 > 1023)
        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
    else
        sprintf(titlebar, "%s:  %s", appname, filename);


    /* if the user didn't specify a background color on the command line,
     * check for one in the PNG file--if not, the initialized values of 0
     * (black) will be used */

    if (have_bg) {
        unsigned r, g, b;   /* this approach quiets compiler warnings */

        sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
        bg_red   = (uch)r;
        bg_green = (uch)g;
        bg_blue  = (uch)b;
    } else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) {
        readpng_cleanup(TRUE);
        fprintf(stderr, PROGNAME
          ":  libpng error while checking for background color\n");
        exit(2);
    }


    /* do the basic X initialization stuff, make the window and fill it
     * with the background color */

    if (rpng_x_create_window())
        exit(2);


    /* decode the image, all at once */

    Trace((stderr, "calling readpng_get_image()\n"))
    image_data = readpng_get_image(display_exponent, &image_channels,
      &image_rowbytes);
    Trace((stderr, "done with readpng_get_image()\n"))


    /* done with PNG file, so clean up to minimize memory usage (but do NOT
     * nuke image_data!) */

    readpng_cleanup(FALSE);
    fclose(infile);

    if (!image_data) {
        fprintf(stderr, PROGNAME ":  unable to decode PNG image\n");
        exit(3);
    }


    /* display image (composite with background if requested) */

    Trace((stderr, "calling rpng_x_display_image()\n"))
    if (rpng_x_display_image()) {
        free(image_data);
        exit(4);
    }
    Trace((stderr, "done with rpng_x_display_image()\n"))


    /* wait for the user to tell us when to quit */

    printf(
      "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
    fflush(stdout);

    do
        XNextEvent(display, &e);
    while (!(e.type == ButtonPress && e.xbutton.button == Button1) &&
           !(e.type == KeyPress &&    /*  v--- or 1 for shifted keys */
             ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape) ));


    /* OK, we're done:  clean up all image and X resources and go away */

    rpng_x_cleanup();

    return 0;
}





static int rpng_x_create_window(void)
{
    uch *xdata;
    int need_colormap = FALSE;
    int screen, pad;
    ulg bg_pixel = 0L;
    ulg attrmask;
    Window root;
    XEvent e;
    XGCValues gcvalues;
    XSetWindowAttributes attr;
    XSizeHints *size_hints;
    XTextProperty windowName, *pWindowName = &windowName;
    XTextProperty iconName, *pIconName = &iconName;
    XVisualInfo visual_info;
    XWMHints *wm_hints;


    screen = DefaultScreen(display);
    depth = DisplayPlanes(display, screen);
    root = RootWindow(display, screen);

#ifdef DEBUG
    XSynchronize(display, True);
#endif

#if 0
/* GRR:  add 8-bit support */
    if (/* depth != 8 && */ depth != 16 && depth != 24 && depth != 32) {
        fprintf(stderr,
          "screen depth %d not supported (only 16-, 24- or 32-bit TrueColor)\n",
          depth);
        return 2;
    }

    XMatchVisualInfo(display, screen, depth,
      (depth == 8)? PseudoColor : TrueColor, &visual_info);
    visual = visual_info.visual;
#else
    if (depth != 16 && depth != 24 && depth != 32) {
        int visuals_matched = 0;

        Trace((stderr, "default depth is %d:  checking other visuals\n",
          depth))

        /* 24-bit first */
        visual_info.screen = screen;
        visual_info.depth = 24;
        visual_list = XGetVisualInfo(display,
          VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
        if (visuals_matched == 0) {
/* GRR:  add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
            fprintf(stderr, "default screen depth %d not supported, and no"
              " 24-bit visuals found\n", depth);
            return 2;
        }
        Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
          visuals_matched))
        visual = visual_list[0].visual;
        depth = visual_list[0].depth;
/*
        colormap_size = visual_list[0].colormap_size;
        visual_class = visual->class;
        visualID = XVisualIDFromVisual(visual);
 */
        have_nondefault_visual = TRUE;
        need_colormap = TRUE;
    } else {
        XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
        visual = visual_info.visual;
    }
#endif

    RMask = visual->red_mask;
    GMask = visual->green_mask;
    BMask = visual->blue_mask;

/* GRR:  add/check 8-bit support */
    if (depth == 8 || need_colormap) {
        colormap = XCreateColormap(display, root, visual, AllocNone);
        if (!colormap) {
            fprintf(stderr, "XCreateColormap() failed\n");
            return 2;
        }
        have_colormap = TRUE;
    }
    if (depth == 15 || depth == 16) {
        RShift = 15 - rpng_x_msb(RMask);    /* these are right-shifts */
        GShift = 15 - rpng_x_msb(GMask);
        BShift = 15 - rpng_x_msb(BMask);
    } else if (depth > 16) {
#define NO_24BIT_MASKS
#ifdef NO_24BIT_MASKS
        RShift = rpng_x_msb(RMask) - 7;     /* these are left-shifts */
        GShift = rpng_x_msb(GMask) - 7;
        BShift = rpng_x_msb(BMask) - 7;
#else
        RShift = 7 - rpng_x_msb(RMask);     /* these are right-shifts, too */
        GShift = 7 - rpng_x_msb(GMask);
        BShift = 7 - rpng_x_msb(BMask);
#endif
    }
    if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
        fprintf(stderr, "rpng internal logic error:  negative X shift(s)!\n");
        return 2;
    }

/*---------------------------------------------------------------------------
    Finally, create the window.
  ---------------------------------------------------------------------------*/

    attr.backing_store = Always;
    attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
    attrmask = CWBackingStore | CWEventMask;
    if (have_nondefault_visual) {
        attr.colormap = colormap;
        attr.background_pixel = 0;
        attr.border_pixel = 1;
        attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
    }

    window = XCreateWindow(display, root, 0, 0, image_width, image_height, 0,
      depth, InputOutput, visual, attrmask, &attr);

    if (window == None) {
        fprintf(stderr, "XCreateWindow() failed\n");
        return 2;
    } else
        have_window = TRUE;

    if (depth == 8)
        XSetWindowColormap(display, window, colormap);

    if (!XStringListToTextProperty(&window_name, 1, pWindowName))
        pWindowName = NULL;
    if (!XStringListToTextProperty(&icon_name, 1, pIconName))
        pIconName = NULL;

    /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */

    if ((size_hints = XAllocSizeHints()) != NULL) {
        /* window will not be resizable */
        size_hints->flags = PMinSize | PMaxSize;
        size_hints->min_width = size_hints->max_width = (int)image_width;
        size_hints->min_height = size_hints->max_height = (int)image_height;
    }

    if ((wm_hints = XAllocWMHints()) != NULL) {
        wm_hints->initial_state = NormalState;
        wm_hints->input = True;
     /* wm_hints->icon_pixmap = icon_pixmap; */
        wm_hints->flags = StateHint | InputHint  /* | IconPixmapHint */ ;
    }

    XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
      size_hints, wm_hints, NULL);

    /* various properties and hints no longer needed; free memory */
    if (pWindowName)
       XFree(pWindowName->value);
    if (pIconName)
       XFree(pIconName->value);
    if (size_hints)
        XFree(size_hints);
    if (wm_hints)
       XFree(wm_hints);

    XMapWindow(display, window);

    gc = XCreateGC(display, window, 0, &gcvalues);

/*---------------------------------------------------------------------------
    Fill window with the specified background color.
  ---------------------------------------------------------------------------*/

    if (depth == 24 || depth == 32) {
        bg_pixel = ((ulg)bg_red   << RShift) |
                   ((ulg)bg_green << GShift) |
                   ((ulg)bg_blue  << BShift);
    } else if (depth == 16) {
        bg_pixel = ((((ulg)bg_red   << 8) >> RShift) & RMask) |
                   ((((ulg)bg_green << 8) >> GShift) & GMask) |
                   ((((ulg)bg_blue  << 8) >> BShift) & BMask);
    } else /* depth == 8 */ {

        /* GRR:  add 8-bit support */

    }

    XSetForeground(display, gc, bg_pixel);
    XFillRectangle(display, window, gc, 0, 0, image_width, image_height);

/*---------------------------------------------------------------------------
    Wait for first Expose event to do any drawing, then flush.
  ---------------------------------------------------------------------------*/

    do
        XNextEvent(display, &e);
    while (e.type != Expose || e.xexpose.count);

    XFlush(display);

/*---------------------------------------------------------------------------
    Allocate memory for the X- and display-specific version of the image.
  ---------------------------------------------------------------------------*/

    if (depth == 24 || depth == 32) {
        xdata = (uch *)malloc(4*image_width*image_height);
        pad = 32;
    } else if (depth == 16) {
        xdata = (uch *)malloc(2*image_width*image_height);
        pad = 16;
    } else /* depth == 8 */ {
        xdata = (uch *)malloc(image_width*image_height);
        pad = 8;
    }

    if (!xdata) {
        fprintf(stderr, PROGNAME ":  unable to allocate image memory\n");
        return 4;
    }

    ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
      (char *)xdata, image_width, image_height, pad, 0);

    if (!ximage) {
        fprintf(stderr, PROGNAME ":  XCreateImage() failed\n");
        free(xdata);
        return 3;
    }

    /* to avoid testing the byte order every pixel (or doubling the size of
     * the drawing routine with a giant if-test), we arbitrarily set the byte
     * order to MSBFirst and let Xlib worry about inverting things on little-
     * endian machines (like Linux/x86, old VAXen, etc.)--this is not the most
     * efficient approach (the giant if-test would be better), but in the
     * interest of clarity, we take the easy way out... */

    ximage->byte_order = MSBFirst;

    return 0;

} /* end function rpng_x_create_window() */





static int rpng_x_display_image(void)
{
    uch *src;
    char *dest;
    uch r, g, b, a;
    ulg i, row, lastrow = 0;
    ulg pixel;
    int ximage_rowbytes = ximage->bytes_per_line;
/*  int bpp = ximage->bits_per_pixel;  */


    Trace((stderr, "beginning display loop (image_channels == %d)\n",
      image_channels))
    Trace((stderr, "   (width = %ld, rowbytes = %ld, ximage_rowbytes = %d)\n",
      image_width, image_rowbytes, ximage_rowbytes))
    Trace((stderr, "   (bpp = %d)\n", ximage->bits_per_pixel))
    Trace((stderr, "   (byte_order = %s)\n", ximage->byte_order == MSBFirst?
      "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown")))

    if (depth == 24 || depth == 32) {
        ulg red, green, blue;

        for (lastrow = row = 0;  row < image_height;  ++row) {
            src = image_data + row*image_rowbytes;
            dest = ximage->data + row*ximage_rowbytes;
            if (image_channels == 3) {
                for (i = image_width;  i > 0;  --i) {
                    red   = *src++;
                    green = *src++;
                    blue  = *src++;
#ifdef NO_24BIT_MASKS
                    pixel = (red   << RShift) |
                            (green << GShift) |
                            (blue  << BShift);
                    /* recall that we set ximage->byte_order = MSBFirst above */
                    /* GRR BUG:  this assumes bpp == 32, but may be 24: */
                    *dest++ = (char)((pixel >> 24) & 0xff);
                    *dest++ = (char)((pixel >> 16) & 0xff);
                    *dest++ = (char)((pixel >>  8) & 0xff);
                    *dest++ = (char)( pixel        & 0xff);
#else
                    red   = (RShift < 0)? red   << (-RShift) : red   >> RShift;
                    green = (GShift < 0)? green << (-GShift) : green >> GShift;
                    blue  = (BShift < 0)? blue  << (-BShift) : blue  >> BShift;
                    pixel = (red & RMask) | (green & GMask) | (blue & BMask);
                    /* recall that we set ximage->byte_order = MSBFirst above */
                    *dest++ = (char)((pixel >> 24) & 0xff);
                    *dest++ = (char)((pixel >> 16) & 0xff);
                    *dest++ = (char)((pixel >>  8) & 0xff);
                    *dest++ = (char)( pixel        & 0xff);
#endif
                }
            } else /* if (image_channels == 4) */ {
                for (i = image_width;  i > 0;  --i) {
                    r = *src++;
                    g = *src++;
                    b = *src++;
                    a = *src++;
                    if (a == 255) {
                        red   = r;
                        green = g;
                        blue  = b;
                    } else if (a == 0) {
                        red   = bg_red;
                        green = bg_green;
                        blue  = bg_blue;
                    } else {
                        /* this macro (from png.h) composites the foreground
                         * and background values and puts the result into the
                         * first argument */
                        alpha_composite(red,   r, a, bg_red);
                        alpha_composite(green, g, a, bg_green);
                        alpha_composite(blue,  b, a, bg_blue);
                    }
                    pixel = (red   << RShift) |
                            (green << GShift) |
                            (blue  << BShift);
                    /* recall that we set ximage->byte_order = MSBFirst above */
                    *dest++ = (char)((pixel >> 24) & 0xff);
                    *dest++ = (char)((pixel >> 16) & 0xff);
                    *dest++ = (char)((pixel >>  8) & 0xff);
                    *dest++ = (char)( pixel        & 0xff);
                }
            }
            /* display after every 16 lines */
            if (((row+1) & 0xf) == 0) {
                XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
                  (int)lastrow, image_width, 16);
                XFlush(display);
                lastrow = row + 1;
            }
        }

    } else if (depth == 16) {
        ush red, green, blue;

        for (lastrow = row = 0;  row < image_height;  ++row) {
            src = image_data + row*image_rowbytes;
            dest = ximage->data + row*ximage_rowbytes;
            if (image_channels == 3) {
                for (i = image_width;  i > 0;  --i) {
                    red   = ((ush)(*src) << 8);
                    ++src;
                    green = ((ush)(*src) << 8);
                    ++src;
                    blue  = ((ush)(*src) << 8);
                    ++src;
                    pixel = ((red   >> RShift) & RMask) |
                            ((green >> GShift) & GMask) |
                            ((blue  >> BShift) & BMask);
                    /* recall that we set ximage->byte_order = MSBFirst above */
                    *dest++ = (char)((pixel >>  8) & 0xff);
                    *dest++ = (char)( pixel        & 0xff);
                }
            } else /* if (image_channels == 4) */ {
                for (i = image_width;  i > 0;  --i) {
                    r = *src++;
                    g = *src++;
                    b = *src++;
                    a = *src++;
                    if (a == 255) {
                        red   = ((ush)r << 8);
                        green = ((ush)g << 8);
                        blue  = ((ush)b << 8);
                    } else if (a == 0) {
                        red   = ((ush)bg_red   << 8);
                        green = ((ush)bg_green << 8);
                        blue  = ((ush)bg_blue  << 8);
                    } else {
                        /* this macro (from png.h) composites the foreground
                         * and background values and puts the result back into
                         * the first argument (== fg byte here:  safe) */
                        alpha_composite(r, r, a, bg_red);
                        alpha_composite(g, g, a, bg_green);
                        alpha_composite(b, b, a, bg_blue);
                        red   = ((ush)r << 8);
                        green = ((ush)g << 8);
                        blue  = ((ush)b << 8);
                    }
                    pixel = ((red   >> RShift) & RMask) |
                            ((green >> GShift) & GMask) |
                            ((blue  >> BShift) & BMask);
                    /* recall that we set ximage->byte_order = MSBFirst above */
                    *dest++ = (char)((pixel >>  8) & 0xff);
                    *dest++ = (char)( pixel        & 0xff);
                }
            }
            /* display after every 16 lines */
            if (((row+1) & 0xf) == 0) {
                XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
                  (int)lastrow, image_width, 16);
                XFlush(display);
                lastrow = row + 1;
            }
        }

    } else /* depth == 8 */ {

        /* GRR:  add 8-bit support */

    }

    Trace((stderr, "calling final XPutImage()\n"))
    if (lastrow < image_height) {
        XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
          (int)lastrow, image_width, image_height-lastrow);
        XFlush(display);
    }

    return 0;
}




static void rpng_x_cleanup(void)
{
    if (image_data) {
        free(image_data);
        image_data = NULL;
    }

    if (ximage) {
        if (ximage->data) {
            free(ximage->data);           /* we allocated it, so we free it */
            ximage->data = (char *)NULL;  /*  instead of XDestroyImage() */
        }
        XDestroyImage(ximage);
        ximage = NULL;
    }

    XFreeGC(display, gc);

    if (have_window)
        XDestroyWindow(display, window);

    if (have_colormap)
        XFreeColormap(display, colormap);

    if (have_nondefault_visual)
        XFree(visual_list);
}





static int rpng_x_msb(ulg u32val)
{
    int i;

    for (i = 31;  i >= 0;  --i) {
        if (u32val & 0x80000000L)
            break;
        u32val <<= 1;
    }
    return i;
}

--- NEW FILE: Makefile.sgi ---
# Sample makefile for rpng-x / rpng2-x / wpng for SGI using cc and make.
# Greg Roelofs
# Last modified:  16 January 1000
#
#	The programs built by this makefile are described in the book,
#	"PNG:  The Definitive Guide," by Greg Roelofs (O'Reilly and
#	Associates, 1999).  Go buy a copy, eh?  Buy some for friends
#	and family, too.  (Not that this is a blatant plug or anything.)
#
# Invoke this makefile from a shell prompt in the usual way; for example:
#
#	make -f Makefile.sgi
#
# This makefile assumes libpng and zlib have already been built or downloaded
# and are both installed in /usr/local/{include,lib} (as indicated by the
# PNG* and Z* macros below).  Edit as appropriate--choose only ONE each of
# the PNGINC, PNGLIB, ZINC and ZLIB lines.
#
# This makefile builds dynamically linked executables (against libpng and zlib,
# that is), but that can be changed by uncommenting the appropriate PNGLIB and
# ZLIB lines.


# macros --------------------------------------------------------------------

PNGINC = -I/usr/local/include
PNGLIB = -L/usr/local/lib -lpng	# dynamically linked against libpng
#PNGLIB = /usr/local/lib/libpng.a	# statically linked against libpng
# or:
#PNGINC = -I../..
#PNGLIB = -L../.. -lpng
#PNGLIB = ../../libpng.a

ZINC = -I/usr/local/include
ZLIB = -L/usr/local/lib -lz		# dynamically linked against zlib
#ZLIB = /usr/local/lib/libz.a		# statically linked against zlib
#ZINC = -I../zlib
#ZLIB = -L../zlib -lz
#ZLIB = ../../../zlib/libz.a

XINC = -I/usr/include/X11		# old-style, stock X distributions
XLIB = -L/usr/lib/X11 -lX11
#XINC = -I/usr/openwin/include    	# Sun workstations (OpenWindows)
#XLIB = -L/usr/openwin/lib -lX11
#XINC = -I/usr/X11R6/include		# new X distributions (XFree86, etc.)
#XLIB = -L/usr/X11R6/lib -lX11

INCS = $(PNGINC) $(ZINC) $(XINC)
RLIBS = $(PNGLIB) $(ZLIB) $(XLIB) -lm
WLIBS = $(PNGLIB) $(ZLIB)

CC = cc
LD = cc
RM = rm -f
CFLAGS = -O -fullwarn $(INCS)
LDFLAGS =
O = .o
E =

RPNG  = rpng-x
RPNG2 = rpng2-x
WPNG  = wpng

ROBJS  = $(RPNG)$(O) readpng$(O)
ROBJS2 = $(RPNG2)$(O) readpng2$(O)
WOBJS  = $(WPNG)$(O) writepng$(O)

EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E)


# implicit make rules -------------------------------------------------------

.c$(O):
	$(CC) -c $(CFLAGS) $<


# dependencies --------------------------------------------------------------

all:  $(EXES)

$(RPNG)$(E): $(ROBJS)
	$(LD) $(LDFLAGS) -o $@ $(ROBJS) $(RLIBS)

$(RPNG2)$(E): $(ROBJS2)
	$(LD) $(LDFLAGS) -o $@ $(ROBJS2) $(RLIBS)

$(WPNG)$(E): $(WOBJS)
	$(LD) $(LDFLAGS) -o $@ $(WOBJS) $(WLIBS)

$(RPNG)$(O):	$(RPNG).c readpng.h
$(RPNG2)$(O):	$(RPNG2).c readpng2.h
$(WPNG)$(O):	$(WPNG).c writepng.h

readpng$(O):	readpng.c readpng.h
readpng2$(O):	readpng2.c readpng2.h
writepng$(O):	writepng.c writepng.h


# maintenance ---------------------------------------------------------------

clean:
	$(RM) $(EXES) $(ROBJS) $(ROBJS2) $(WOBJS)

--- NEW FILE: Makefile.unx ---
# Sample makefile for rpng-x / rpng2-x / wpng using gcc and make.
# Greg Roelofs
# Last modified:  7 February 2001
#
#	The programs built by this makefile are described in the book,
#	"PNG:  The Definitive Guide," by Greg Roelofs (O'Reilly and
#	Associates, 1999).  Go buy a copy, eh?  Buy some for friends
#	and family, too.  (Not that this is a blatant plug or anything.)
#
# Invoke this makefile from a shell prompt in the usual way; for example:
#
#	make -f Makefile.unx
#
# This makefile assumes libpng and zlib have already been built or downloaded
# and are both installed in /usr/local/{include,lib} (as indicated by the
# PNG* and Z* macros below).  Edit as appropriate--choose only ONE each of
# the PNGINC, PNGLIB, ZINC and ZLIB lines.
#
# This makefile builds statically linked executables (against libpng and zlib,
# that is), but that can be changed by uncommenting the appropriate PNGLIB and
# ZLIB lines.


# macros --------------------------------------------------------------------

PNGINC = -I/usr/local/include
#PNGLIB = -L/usr/local/lib -lpng	# dynamically linked against libpng
PNGLIB = /usr/local/lib/libpng.a	# statically linked against libpng
# or:
#PNGINC = -I../libpng
#PNGLIB = -L../libpng -lpng
#PNGLIB = ../libpng/libpng.a

ZINC = -I/usr/local/include
#ZLIB = -L/usr/local/lib -lz		# dynamically linked against zlib
ZLIB = /usr/local/lib/libz.a		# statically linked against zlib
#ZINC = -I../zlib
#ZLIB = -L../zlib -lz
#ZLIB = ../zlib/libz.a

#XINC = -I/usr/include			# old-style, stock X distributions
#XLIB = -L/usr/lib/X11 -lX11
#XINC = -I/usr/openwin/include		# Sun workstations (OpenWindows)
#XLIB = -L/usr/openwin/lib -lX11
XINC = -I/usr/X11R6/include		# new X distributions (XFree86, etc.)
XLIB = -L/usr/X11R6/lib -lX11

INCS = $(PNGINC) $(ZINC) $(XINC)
RLIBS = $(PNGLIB) $(ZLIB) $(XLIB) -lm
WLIBS = $(PNGLIB) $(ZLIB)

CC = gcc
LD = gcc
RM = rm -f
CFLAGS = -O -Wall $(INCS)
# [note that -Wall is a gcc-specific compilation flag ("most warnings on")]
# [-ansi, -pedantic and -W can also be used]
LDFLAGS =
O = .o
E =

RPNG  = rpng-x
RPNG2 = rpng2-x
WPNG  = wpng

ROBJS  = $(RPNG)$(O) readpng$(O)
ROBJS2 = $(RPNG2)$(O) readpng2$(O)
WOBJS  = $(WPNG)$(O) writepng$(O)

EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E)


# implicit make rules -------------------------------------------------------

.c$(O):
	$(CC) -c $(CFLAGS) $<


# dependencies --------------------------------------------------------------

all:  $(EXES)

$(RPNG)$(E): $(ROBJS)
	$(LD) $(LDFLAGS) -o $@ $(ROBJS) $(RLIBS)

$(RPNG2)$(E): $(ROBJS2)
	$(LD) $(LDFLAGS) -o $@ $(ROBJS2) $(RLIBS)

$(WPNG)$(E): $(WOBJS)
	$(LD) $(LDFLAGS) -o $@ $(WOBJS) $(WLIBS)

$(RPNG)$(O):	$(RPNG).c readpng.h
$(RPNG2)$(O):	$(RPNG2).c readpng2.h
$(WPNG)$(O):	$(WPNG).c writepng.h

readpng$(O):	readpng.c readpng.h
readpng2$(O):	readpng2.c readpng2.h
writepng$(O):	writepng.c writepng.h


# maintenance ---------------------------------------------------------------

clean:
	$(RM) $(EXES) $(ROBJS) $(ROBJS2) $(WOBJS)

--- NEW FILE: makevms.com ---
$!------------------------------------------------------------------------------
$! make "PNG: The Definitive Guide" demo programs (for X) under OpenVMS
$!
$! Script created by Martin Zinser for libpng; modified by Greg Roelofs
$! for standalone pngbook source distribution.
$!
$!
$!    Set locations where zlib and libpng sources live.
$!
$ zpath   = ""
$ pngpath = ""
$!
$ if f$search("[---.zlib]zlib.h").nes."" then zpath = "[---.zlib]"
$ if f$search("[--]png.h").nes."" then pngpath = "[--]"
$!
$ if f$search("[-.zlib]zlib.h").nes."" then zpath = "[-.zlib]"
$ if f$search("[-.libpng]png.h").nes."" then pngpath = "[-.libpng]"
$!
$ if zpath .eqs. ""
$ then
$   write sys$output "zlib include not found. Exiting..."
$   exit 2
$ endif 
$!
$ if pngpath .eqs. ""
$ then
$   write sys$output "libpng include not found. Exiting..."
$   exit 2
$ endif 
$!
$!    Look for the compiler used.
$!
$ ccopt="/include=(''zpath',''pngpath')"
$ if f$getsyi("HW_MODEL").ge.1024
$ then
$  ccopt = "/prefix=all"+ccopt
$  comp  = "__decc__=1"
$  if f$trnlnm("SYS").eqs."" then define sys sys$library:
$ else
$  if f$search("SYS$SYSTEM:DECC$COMPILER.EXE").eqs.""
$   then
$    if f$trnlnm("SYS").eqs."" then define sys sys$library:
$    if f$search("SYS$SYSTEM:VAXC.EXE").eqs.""
$     then
$      comp  = "__gcc__=1"
$      CC :== GCC
$     else
$      comp = "__vaxc__=1"
$     endif
$   else
$    if f$trnlnm("SYS").eqs."" then define sys decc$library_include:
$    ccopt = "/decc/prefix=all"+ccopt
$    comp  = "__decc__=1"
$  endif
$ endif
$ open/write lopt lib.opt
$ write lopt "''pngpath'libpng.olb/lib"
$ write lopt "''zpath'libz.olb/lib"
$ close lopt
$ open/write xopt x11.opt
$ write xopt "sys$library:decw$xlibshr.exe/share"
$ close xopt
$!
$!    Build 'em.
$!
$ write sys$output "Compiling PNG book programs ..."
$   CALL MAKE readpng.OBJ "cc ''CCOPT' readpng" -
	readpng.c readpng.h
$   CALL MAKE readpng2.OBJ "cc ''CCOPT' readpng2" -
	readpng2.c readpng2.h
$   CALL MAKE writepng.OBJ "cc ''CCOPT' writepng" -
	writepng.c writepng.h
$   write sys$output "Building rpng-x..."
$   CALL MAKE rpng-x.OBJ "cc ''CCOPT' rpng-x" -
	rpng-x.c readpng.h
$   call make rpng-x.exe -
	"LINK rpng-x,readpng,lib.opt/opt,x11.opt/opt" -
	rpng-x.obj readpng.obj
$   write sys$output "Building rpng2-x..."
$   CALL MAKE rpng2-x.OBJ "cc ''CCOPT' rpng2-x" -
	rpng2-x.c readpng2.h
$   call make rpng2-x.exe -
	"LINK rpng2-x,readpng2,lib.opt/opt,x11.opt/opt" -
	rpng2-x.obj readpng2.obj
$   write sys$output "Building wpng..."
$   CALL MAKE wpng.OBJ "cc ''CCOPT' wpng" -
	wpng.c writepng.h
$   call make wpng.exe -
	"LINK wpng,writepng,lib.opt/opt" -
	wpng.obj writepng.obj
$ exit
$!
$!
$MAKE: SUBROUTINE   !SUBROUTINE TO CHECK DEPENDENCIES
$ V = 'F$Verify(0)
$! P1 = What we are trying to make
$! P2 = Command to make it
$! P3 - P8  What it depends on
$
$ If F$Search(P1) .Eqs. "" Then Goto Makeit
$ Time = F$CvTime(F$File(P1,"RDT"))
$arg=3
$Loop:
$       Argument = P'arg
$       If Argument .Eqs. "" Then Goto Exit
$       El=0
$Loop2:
$       File = F$Element(El," ",Argument)
$       If File .Eqs. " " Then Goto Endl
$       AFile = ""
$Loop3:
$       OFile = AFile
$       AFile = F$Search(File)
$       If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl
$       If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit
$       Goto Loop3
$NextEL:
$       El = El + 1
$       Goto Loop2
$EndL:
$ arg=arg+1
$ If arg .Le. 8 Then Goto Loop
$ Goto Exit
$
$Makeit:
$ VV=F$VERIFY(0)
$ write sys$output P2
$ 'P2
$ VV='F$Verify(VV)
$Exit:
$ If V Then Set Verify
$ENDSUBROUTINE

--- NEW FILE: wpng.c ---
/*---------------------------------------------------------------------------

   wpng - simple PNG-writing program                                 wpng.c

   This program converts certain NetPBM binary files (grayscale and RGB,
   maxval = 255) to PNG.  Non-interlaced PNGs are written progressively;
   interlaced PNGs are read and written in one memory-intensive blast.
   Thanks to Jean-loup Gailly for providing the necessary trick to read
   interactive text from the keyboard while stdin is redirected.

   NOTE:  includes provisional support for PNM type "8" (portable alphamap)
          images, presumed to be a 32-bit interleaved RGBA format; no pro-
          vision for possible interleaved grayscale+alpha (16-bit) format.
          THIS IS UNLIKELY TO BECOME AN OFFICIAL NETPBM ALPHA FORMAT!

   to do:
    - delete output file if quit before calling any writepng routines
    - process backspace with -text option under DOS/Win? (currently get ^H)

  ---------------------------------------------------------------------------

   Changelog:
    - 1.01:  initial public release
    - 1.02:  modified to allow abbreviated options
    - 1.03:  removed extraneous character from usage screen; fixed bug in
              command-line parsing

  ---------------------------------------------------------------------------

      Copyright (c) 1998-2000 Greg Roelofs.  All rights reserved.

      This software is provided "as is," without warranty of any kind,
      express or implied.  In no event shall the author or contributors
      be held liable for any damages arising in any way from the use of
      this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute
      it freely, subject to the following restrictions:

      1. Redistributions of source code must retain the above copyright
         notice, disclaimer, and this list of conditions.
      2. Redistributions in binary form must reproduce the above copyright
         notice, disclaimer, and this list of conditions in the documenta-
         tion and/or other materials provided with the distribution.
      3. All advertising materials mentioning features or use of this
         software must display the following acknowledgment:

            This product includes software developed by Greg Roelofs
            and contributors for the book, "PNG: The Definitive Guide,"
            published by O'Reilly and Associates.

  ---------------------------------------------------------------------------*/

#define PROGNAME  "wpng"
#define VERSION   "1.03 of 19 March 2000"
#define APPNAME   "Simple PGM/PPM/PAM to PNG Converter"

#if defined(__MSDOS__) || defined(__OS2__)
#  define DOS_OS2_W32
#elif defined(_WIN32) || defined(__WIN32__)
#  define DOS_OS2_W32
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>     /* for jmpbuf declaration in writepng.h */
#include <time.h>

#ifdef DOS_OS2_W32
#  include <io.h>       /* for isatty(), setmode() prototypes */
#  include <fcntl.h>    /* O_BINARY for fdopen() without text translation */
#  ifdef __EMX__
#    ifndef getch
#      define getch() _read_kbd(0, 1, 0)    /* need getche() */
#    endif
#  else /* !__EMX__ */
#    ifdef __GO32__
#      include <pc.h>
#      define getch() getkey()  /* GRR:  need getche() */
#    else
#      include <conio.h>        /* for getche() console input */
#    endif
#  endif /* ?__EMX__ */
#  define FGETS(buf,len,stream)  dos_kbd_gets(buf,len)
#else
#  include <unistd.h>           /* for isatty() prototype */
#  define FGETS fgets
#endif

/* #define DEBUG  :  this enables the Trace() macros */

/* #define FORBID_LATIN1_CTRL  :  this requires the user to re-enter any
   text that includes control characters discouraged by the PNG spec; text
   that includes an escape character (27) must be re-entered regardless */

#include "writepng.h"   /* typedefs, common macros, writepng prototypes */



/* local prototypes */

static int  wpng_isvalid_latin1(uch *p, int len);
static void wpng_cleanup(void);

#ifdef DOS_OS2_W32
   static char *dos_kbd_gets(char *buf, int len);
#endif



static mainprog_info wpng_info;   /* lone global */



int main(int argc, char **argv)
{
#ifndef DOS_OS2_W32
    FILE *keybd;
#endif
#ifdef sgi
    FILE *tmpfile;      /* or we could just use keybd, since no overlap */
    char tmpline[80];
#endif
    char *inname = NULL, outname[256];
    char *p, pnmchar, pnmline[256];
    char *bgstr, *textbuf = NULL;
    ulg rowbytes;
    int rc, len = 0;
    int error = 0;
    int text = FALSE;
    int maxval;
    double LUT_exponent;                /* just the lookup table */
    double CRT_exponent = 2.2;          /* just the monitor */
    double default_display_exponent;    /* whole display system */
    double default_gamma = 0.0;


    wpng_info.infile = NULL;
    wpng_info.outfile = NULL;
    wpng_info.image_data = NULL;
    wpng_info.row_pointers = NULL;
    wpng_info.filter = FALSE;
    wpng_info.interlaced = FALSE;
    wpng_info.have_bg = FALSE;
    wpng_info.have_time = FALSE;
    wpng_info.have_text = 0;
    wpng_info.gamma = 0.0;


    /* First get the default value for our display-system exponent, i.e.,
     * the product of the CRT exponent and the exponent corresponding to
     * the frame-buffer's lookup table (LUT), if any.  If the PNM image
     * looks correct on the user's display system, its file gamma is the
     * inverse of this value.  (Note that this is not an exhaustive list
     * of LUT values--e.g., OpenStep has a lot of weird ones--but it should
     * cover 99% of the current possibilities.  This section must ensure
     * that default_display_exponent is positive.) */

#if defined(NeXT)
    /* third-party utilities can modify the default LUT exponent */
    LUT_exponent = 1.0 / 2.2;
    /*
    if (some_next_function_that_returns_gamma(&next_gamma))
        LUT_exponent = 1.0 / next_gamma;
     */
#elif defined(sgi)
    LUT_exponent = 1.0 / 1.7;
    /* there doesn't seem to be any documented function to
     * get the "gamma" value, so we do it the hard way */
    tmpfile = fopen("/etc/config/system.glGammaVal", "r");
    if (tmpfile) {
        double sgi_gamma;

        fgets(tmpline, 80, tmpfile);
        fclose(tmpfile);
        sgi_gamma = atof(tmpline);
        if (sgi_gamma > 0.0)
            LUT_exponent = 1.0 / sgi_gamma;
    }
#elif defined(Macintosh)
    LUT_exponent = 1.8 / 2.61;
    /*
    if (some_mac_function_that_returns_gamma(&mac_gamma))
        LUT_exponent = mac_gamma / 2.61;
     */
#else
    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
#endif

    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
    default_display_exponent = LUT_exponent * CRT_exponent;


    /* If the user has set the SCREEN_GAMMA environment variable as suggested
     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
     * use the default value we just calculated.  Either way, the user may
     * override this via a command-line option. */

    if ((p = getenv("SCREEN_GAMMA")) != NULL) {
        double exponent = atof(p);

        if (exponent > 0.0)
            default_gamma = 1.0 / exponent;
    }

    if (default_gamma == 0.0)
        default_gamma = 1.0 / default_display_exponent;


    /* Now parse the command line for options and the PNM filename. */

    while (*++argv && !error) {
        if (!strncmp(*argv, "-i", 2)) {
            wpng_info.interlaced = TRUE;
        } else if (!strncmp(*argv, "-time", 3)) {
            wpng_info.modtime = time(NULL);
            wpng_info.have_time = TRUE;
        } else if (!strncmp(*argv, "-text", 3)) {
            text = TRUE;
        } else if (!strncmp(*argv, "-gamma", 2)) {
            if (!*++argv)
                ++error;
            else {
                wpng_info.gamma = atof(*argv);
                if (wpng_info.gamma <= 0.0)
                    ++error;
                else if (wpng_info.gamma > 1.01)
                    fprintf(stderr, PROGNAME
                      " warning:  file gammas are usually less than 1.0\n");
            }
        } else if (!strncmp(*argv, "-bgcolor", 4)) {
            if (!*++argv)
                ++error;
            else {
                bgstr = *argv;
                if (strlen(bgstr) != 7 || bgstr[0] != '#')
                    ++error;
                else {
                    unsigned r, g, b;  /* this way quiets compiler warnings */

                    sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
                    wpng_info.bg_red   = (uch)r;
                    wpng_info.bg_green = (uch)g;
                    wpng_info.bg_blue  = (uch)b;
                    wpng_info.have_bg = TRUE;
                }
            }
        } else {
            if (**argv != '-') {
                inname = *argv;
                if (argv[1])   /* shouldn't be any more args after filename */
                    ++error;
            } else
                ++error;   /* not expecting any other options */
        }
    }


    /* open the input and output files, or register an error and abort */

    if (!inname) {
        if (isatty(0)) {
            fprintf(stderr, PROGNAME
              ":  must give input filename or provide image data via stdin\n");
            ++error;
        } else {
#ifdef DOS_OS2_W32
            /* some buggy C libraries require BOTH setmode() and fdopen(bin) */
            setmode(fileno(stdin), O_BINARY);
            setmode(fileno(stdout), O_BINARY);
#endif
            if ((wpng_info.infile = fdopen(fileno(stdin), "rb")) == NULL) {
                fprintf(stderr, PROGNAME
                  ":  unable to reopen stdin in binary mode\n");
                ++error;
            } else
            if ((wpng_info.outfile = fdopen(fileno(stdout), "wb")) == NULL) {
                fprintf(stderr, PROGNAME
                  ":  unable to reopen stdout in binary mode\n");
                fclose(wpng_info.infile);
                ++error;
            } else
                wpng_info.filter = TRUE;
        }
    } else if ((len = strlen(inname)) > 250) {
        fprintf(stderr, PROGNAME ":  input filename is too long [%d chars]\n",
          len);
        ++error;
    } else if (!(wpng_info.infile = fopen(inname, "rb"))) {
        fprintf(stderr, PROGNAME ":  can't open input file [%s]\n", inname);
        ++error;
    }

    if (!error) {
        fgets(pnmline, 256, wpng_info.infile);
        if (pnmline[0] != 'P' || ((pnmchar = pnmline[1]) != '5' &&
            pnmchar != '6' && pnmchar != '8'))
        {
            fprintf(stderr, PROGNAME
              ":  input file [%s] is not a binary PGM, PPM or PAM file\n",
              inname);
            ++error;
        } else {
            wpng_info.pnmtype = (int)(pnmchar - '0');
            if (wpng_info.pnmtype != 8)
                wpng_info.have_bg = FALSE;  /* no need for bg if opaque */
            do {
                fgets(pnmline, 256, wpng_info.infile);  /* lose any comments */
            } while (pnmline[0] == '#');
            sscanf(pnmline, "%ld %ld", &wpng_info.width, &wpng_info.height);
            do {
                fgets(pnmline, 256, wpng_info.infile);  /* more comment lines */
            } while (pnmline[0] == '#');
            sscanf(pnmline, "%d", &maxval);
            if (wpng_info.width <= 0L || wpng_info.height <= 0L ||
                maxval != 255)
            {
                fprintf(stderr, PROGNAME
                  ":  only positive width/height, maxval == 255 allowed \n");
                ++error;
            }
            wpng_info.sample_depth = 8;  /* <==> maxval 255 */

            if (!wpng_info.filter) {
                /* make outname from inname */
                if ((p = strrchr(inname, '.')) == NULL ||
                    (p - inname) != (len - 4))
                {
                    strcpy(outname, inname);
                    strcpy(outname+len, ".png");
                } else {
                    len -= 4;
                    strncpy(outname, inname, len);
                    strcpy(outname+len, ".png");
                }
                /* check if outname already exists; if not, open */
                if ((wpng_info.outfile = fopen(outname, "rb")) != NULL) {
                    fprintf(stderr, PROGNAME ":  output file exists [%s]\n",
                      outname);
                    fclose(wpng_info.outfile);
                    ++error;
                } else if (!(wpng_info.outfile = fopen(outname, "wb"))) {
                    fprintf(stderr, PROGNAME ":  can't open output file [%s]\n",
                      outname);
                    ++error;
                }
            }
        }
        if (error) {
            fclose(wpng_info.infile);
            wpng_info.infile = NULL;
            if (wpng_info.filter) {
                fclose(wpng_info.outfile);
                wpng_info.outfile = NULL;
            }
        }
    }


    /* if we had any errors, print usage and die horrible death...arrr! */

    if (error) {
        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, APPNAME);
        writepng_version_info();
        fprintf(stderr, "\n"
"Usage:  %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] pnmfile\n"
"or: ... | %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] | ...\n"
         "    exp \ttransfer-function exponent (``gamma'') of the image in\n"
         "\t\t  floating-point format (e.g., ``%.5f''); if image looks\n"
         "\t\t  correct on given display system, image gamma is equal to\n"
         "\t\t  inverse of display-system exponent, i.e., 1 / (LUT * CRT)\n"
         "\t\t  (where LUT = lookup-table exponent and CRT = CRT exponent;\n"
         "\t\t  first varies, second is usually 2.2, all are positive)\n"
         "    bg  \tdesired background color for alpha-channel images, in\n"
         "\t\t  7-character hex RGB format (e.g., ``#ff7700'' for orange:\n"
         "\t\t  same as HTML colors)\n"
         "    -text\tprompt interactively for text info (tEXt chunks)\n"
         "    -time\tinclude a tIME chunk (last modification time)\n"
         "    -interlace\twrite interlaced PNG image\n"
         "\n"
"pnmfile or stdin must be a binary PGM (`P5'), PPM (`P6') or (extremely\n"
"unofficial and unsupported!) PAM (`P8') file.  Currently it is required\n"
"to have maxval == 255 (i.e., no scaling).  If pnmfile is specified, it\n"
"is converted to the corresponding PNG file with the same base name but a\n"
"``.png'' extension; files read from stdin are converted and sent to stdout.\n"
"The conversion is progressive (low memory usage) unless interlacing is\n"
"requested; in that case the whole image will be buffered in memory and\n"
"written in one call.\n"
         "\n", PROGNAME, PROGNAME, default_gamma);
        exit(1);
    }


    /* prepare the text buffers for libpng's use; note that even though
     * PNG's png_text struct includes a length field, we don't have to fill
     * it out */

    if (text &&
#ifndef DOS_OS2_W32
        (keybd = fdopen(fileno(stderr), "r")) != NULL &&
#endif
        (textbuf = (char *)malloc((5 + 9)*75)) != NULL)
    {
        int i, valid, result;

        fprintf(stderr,
          "Enter text info (no more than 72 characters per line);\n");
        fprintf(stderr, "to skip a field, hit the <Enter> key.\n");
        /* note:  just <Enter> leaves len == 1 */

        do {
            valid = TRUE;
            p = textbuf + TEXT_TITLE_OFFSET;
            fprintf(stderr, "  Title: ");
            fflush(stderr);
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
                if (p[len-1] == '\n')
                    p[--len] = '\0';
                wpng_info.title = p;
                wpng_info.have_text |= TEXT_TITLE;
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
                      " %u is %sdiscouraged by the PNG\n    specification "
                      "[first occurrence was at character position #%d]\n",
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
                      result+1);
                    fflush(stderr);
#ifdef FORBID_LATIN1_CTRL
                    wpng_info.have_text &= ~TEXT_TITLE;
                    valid = FALSE;
#else
                    if (p[result] == 27) {    /* escape character */
                        wpng_info.have_text &= ~TEXT_TITLE;
                        valid = FALSE;
                    }
#endif
                }
            }
        } while (!valid);

        do {
            valid = TRUE;
            p = textbuf + TEXT_AUTHOR_OFFSET;
            fprintf(stderr, "  Author: ");
            fflush(stderr);
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
                if (p[len-1] == '\n')
                    p[--len] = '\0';
                wpng_info.author = p;
                wpng_info.have_text |= TEXT_AUTHOR;
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
                      " %u is %sdiscouraged by the PNG\n    specification "
                      "[first occurrence was at character position #%d]\n",
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
                      result+1);
                    fflush(stderr);
#ifdef FORBID_LATIN1_CTRL
                    wpng_info.have_text &= ~TEXT_AUTHOR;
                    valid = FALSE;
#else
                    if (p[result] == 27) {    /* escape character */
                        wpng_info.have_text &= ~TEXT_AUTHOR;
                        valid = FALSE;
                    }
#endif
                }
            }
        } while (!valid);

        do {
            valid = TRUE;
            p = textbuf + TEXT_DESC_OFFSET;
            fprintf(stderr, "  Description (up to 9 lines):\n");
            for (i = 1;  i < 10;  ++i) {
                fprintf(stderr, "    [%d] ", i);
                fflush(stderr);
                if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1)
                    p += len;   /* now points at NULL; char before is newline */
                else
                    break;
            }
            if ((len = p - (textbuf + TEXT_DESC_OFFSET)) > 1) {
                if (p[-1] == '\n') {
                    p[-1] = '\0';
                    --len;
                }
                wpng_info.desc = textbuf + TEXT_DESC_OFFSET;
                wpng_info.have_text |= TEXT_DESC;
                p = textbuf + TEXT_DESC_OFFSET;
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
                      " %u is %sdiscouraged by the PNG\n    specification "
                      "[first occurrence was at character position #%d]\n",
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
                      result+1);
                    fflush(stderr);
#ifdef FORBID_LATIN1_CTRL
                    wpng_info.have_text &= ~TEXT_DESC;
                    valid = FALSE;
#else
                    if (p[result] == 27) {    /* escape character */
                        wpng_info.have_text &= ~TEXT_DESC;
                        valid = FALSE;
                    }
#endif
                }
            }
        } while (!valid);

        do {
            valid = TRUE;
            p = textbuf + TEXT_COPY_OFFSET;
            fprintf(stderr, "  Copyright: ");
            fflush(stderr);
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
                if (p[len-1] == '\n')
                    p[--len] = '\0';
                wpng_info.copyright = p;
                wpng_info.have_text |= TEXT_COPY;
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
                      " %u is %sdiscouraged by the PNG\n    specification "
                      "[first occurrence was at character position #%d]\n",
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
                      result+1);
                    fflush(stderr);
#ifdef FORBID_LATIN1_CTRL
                    wpng_info.have_text &= ~TEXT_COPY;
                    valid = FALSE;
#else
                    if (p[result] == 27) {    /* escape character */
                        wpng_info.have_text &= ~TEXT_COPY;
                        valid = FALSE;
                    }
#endif
                }
            }
        } while (!valid);

        do {
            valid = TRUE;
            p = textbuf + TEXT_EMAIL_OFFSET;
            fprintf(stderr, "  E-mail: ");
            fflush(stderr);
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
                if (p[len-1] == '\n')
                    p[--len] = '\0';
                wpng_info.email = p;
                wpng_info.have_text |= TEXT_EMAIL;
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
                      " %u is %sdiscouraged by the PNG\n    specification "
                      "[first occurrence was at character position #%d]\n",
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
                      result+1);
                    fflush(stderr);
#ifdef FORBID_LATIN1_CTRL
                    wpng_info.have_text &= ~TEXT_EMAIL;
                    valid = FALSE;
#else
                    if (p[result] == 27) {    /* escape character */
                        wpng_info.have_text &= ~TEXT_EMAIL;
                        valid = FALSE;
                    }
#endif
                }
            }
        } while (!valid);

        do {
            valid = TRUE;
            p = textbuf + TEXT_URL_OFFSET;
            fprintf(stderr, "  URL: ");
            fflush(stderr);
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
                if (p[len-1] == '\n')
                    p[--len] = '\0';
                wpng_info.url = p;
                wpng_info.have_text |= TEXT_URL;
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
                      " %u is %sdiscouraged by the PNG\n    specification "
                      "[first occurrence was at character position #%d]\n",
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
                      result+1);
                    fflush(stderr);
#ifdef FORBID_LATIN1_CTRL
                    wpng_info.have_text &= ~TEXT_URL;
                    valid = FALSE;
#else
                    if (p[result] == 27) {    /* escape character */
                        wpng_info.have_text &= ~TEXT_URL;
                        valid = FALSE;
                    }
#endif
                }
            }
        } while (!valid);

#ifndef DOS_OS2_W32
        fclose(keybd);
#endif

    } else if (text) {
        fprintf(stderr, PROGNAME ":  unable to allocate memory for text\n");
        text = FALSE;
        wpng_info.have_text = 0;
    }


    /* allocate libpng stuff, initialize transformations, write pre-IDAT data */

    if ((rc = writepng_init(&wpng_info)) != 0) {
        switch (rc) {
            case 2:
                fprintf(stderr, PROGNAME
                  ":  libpng initialization problem (longjmp)\n");
                break;
            case 4:
                fprintf(stderr, PROGNAME ":  insufficient memory\n");
                break;
            case 11:
                fprintf(stderr, PROGNAME
                  ":  internal logic error (unexpected PNM type)\n");
                break;
            default:
                fprintf(stderr, PROGNAME
                  ":  unknown writepng_init() error\n");
                break;
        }
        exit(rc);
    }


    /* free textbuf, since it's a completely local variable and all text info
     * has just been written to the PNG file */

    if (text && textbuf) {
        free(textbuf);
        textbuf = NULL;
    }


    /* calculate rowbytes on basis of image type; note that this becomes much
     * more complicated if we choose to support PBM type, ASCII PNM types, or
     * 16-bit-per-sample binary data [currently not an official NetPBM type] */

    if (wpng_info.pnmtype == 5)
        rowbytes = wpng_info.width;
    else if (wpng_info.pnmtype == 6)
        rowbytes = wpng_info.width * 3;
    else /* if (wpng_info.pnmtype == 8) */
        rowbytes = wpng_info.width * 4;


    /* read and write the image, either in its entirety (if writing interlaced
     * PNG) or row by row (if non-interlaced) */

    fprintf(stderr, "Encoding image data...\n");
    fflush(stderr);

    if (wpng_info.interlaced) {
        long i;
        ulg bytes;
        ulg image_bytes = rowbytes * wpng_info.height;   /* overflow? */

        wpng_info.image_data = (uch *)malloc(image_bytes);
        wpng_info.row_pointers = (uch **)malloc(wpng_info.height*sizeof(uch *));
        if (wpng_info.image_data == NULL || wpng_info.row_pointers == NULL) {
            fprintf(stderr, PROGNAME ":  insufficient memory for image data\n");
            writepng_cleanup(&wpng_info);
            wpng_cleanup();
            exit(5);
        }
        for (i = 0;  i < wpng_info.height;  ++i)
            wpng_info.row_pointers[i] = wpng_info.image_data + i*rowbytes;
        bytes = fread(wpng_info.image_data, 1, image_bytes, wpng_info.infile);
        if (bytes != image_bytes) {
            fprintf(stderr, PROGNAME ":  expected %lu bytes, got %lu bytes\n",
              image_bytes, bytes);
            fprintf(stderr, "  (continuing anyway)\n");
        }
        if (writepng_encode_image(&wpng_info) != 0) {
            fprintf(stderr, PROGNAME
              ":  libpng problem (longjmp) while writing image data\n");
            writepng_cleanup(&wpng_info);
            wpng_cleanup();
            exit(2);
        }

    } else /* not interlaced:  write progressively (row by row) */ {
        long j;
        ulg bytes;

        wpng_info.image_data = (uch *)malloc(rowbytes);
        if (wpng_info.image_data == NULL) {
            fprintf(stderr, PROGNAME ":  insufficient memory for row data\n");
            writepng_cleanup(&wpng_info);
            wpng_cleanup();
            exit(5);
        }
        error = 0;
        for (j = wpng_info.height;  j > 0L;  --j) {
            bytes = fread(wpng_info.image_data, 1, rowbytes, wpng_info.infile);
            if (bytes != rowbytes) {
                fprintf(stderr, PROGNAME
                  ":  expected %lu bytes, got %lu bytes (row %ld)\n", rowbytes,
                  bytes, wpng_info.height-j);
                ++error;
                break;
            }
            if (writepng_encode_row(&wpng_info) != 0) {
                fprintf(stderr, PROGNAME
                  ":  libpng problem (longjmp) while writing row %ld\n",
                  wpng_info.height-j);
                ++error;
                break;
            }
        }
        if (error) {
            writepng_cleanup(&wpng_info);
            wpng_cleanup();
            exit(2);
        }
        if (writepng_encode_finish(&wpng_info) != 0) {
            fprintf(stderr, PROGNAME ":  error on final libpng call\n");
            writepng_cleanup(&wpng_info);
            wpng_cleanup();
            exit(2);
        }
    }


    /* OK, we're done (successfully):  clean up all resources and quit */

    fprintf(stderr, "Done.\n");
    fflush(stderr);

    writepng_cleanup(&wpng_info);
    wpng_cleanup();

    return 0;
}





static int wpng_isvalid_latin1(uch *p, int len)
{
    int i, result = -1;

    for (i = 0;  i < len;  ++i) {
        if (p[i] == 10 || (p[i] > 31 && p[i] < 127) || p[i] > 160)
            continue;           /* character is completely OK */
        if (result < 0 || (p[result] != 27 && p[i] == 27))
            result = i;         /* mark location of first questionable one */
    }                           /*  or of first escape character (bad) */

    return result;
}





static void wpng_cleanup(void)
{
    if (wpng_info.outfile) {
        fclose(wpng_info.outfile);
        wpng_info.outfile = NULL;
    }

    if (wpng_info.infile) {
        fclose(wpng_info.infile);
        wpng_info.infile = NULL;
    }

    if (wpng_info.image_data) {
        free(wpng_info.image_data);
        wpng_info.image_data = NULL;
    }

    if (wpng_info.row_pointers) {
        free(wpng_info.row_pointers);
        wpng_info.row_pointers = NULL;
    }
}




#ifdef DOS_OS2_W32

static char *dos_kbd_gets(char *buf, int len)
{
    int ch, count=0;

    do {
        buf[count++] = ch = getche();
    } while (ch != '\r' && count < len-1);

    buf[count--] = '\0';        /* terminate string */
    if (buf[count] == '\r')     /* Enter key makes CR, so change to newline */
        buf[count] = '\n';

    fprintf(stderr, "\n");      /* Enter key does *not* cause a newline */
    fflush(stderr);

    return buf;
}

#endif /* DOS_OS2_W32 */

--- NEW FILE: rpng2-win.c ---
/*---------------------------------------------------------------------------

   rpng2 - progressive-model PNG display program                rpng2-win.c

   This program decodes and displays PNG files progressively, as if it were
   a web browser (though the front end is only set up to read from files).
   It supports gamma correction, user-specified background colors, and user-
   specified background patterns (for transparent images).  This version is
   for 32-bit Windows; it may compile under 16-bit Windows with a little
   tweaking (or maybe not).  Thanks to Adam Costello and Pieter S. van der
   Meulen for the "diamond" and "radial waves" patterns, respectively.

   to do:
    - handle quoted command-line args (especially filenames with spaces)
    - finish resizable checkerboard-gradient (sizes 4-128?)
    - use %.1023s to simplify truncation of title-bar string?
    - have minimum window width:  oh well

  ---------------------------------------------------------------------------
[...1124 lines suppressed...]
            return 0;

        /* wait for the user to tell us when to quit */
        case WM_CHAR:
            switch (wP) {       /* only need one, so ignore repeat count */
                case 'q':
                case 'Q':
                case 0x1B:      /* Esc key */
                    PostQuitMessage(0);
            }
            return 0;

        case WM_LBUTTONDOWN:    /* another way of quitting */
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }

    return DefWindowProc(hwnd, iMsg, wP, lP);
}

--- NEW FILE: writepng.h ---
/*---------------------------------------------------------------------------

   wpng - simple PNG-writing program                             writepng.h

  ---------------------------------------------------------------------------

      Copyright (c) 1998-2000 Greg Roelofs.  All rights reserved.

      This software is provided "as is," without warranty of any kind,
      express or implied.  In no event shall the author or contributors
      be held liable for any damages arising in any way from the use of
      this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute
      it freely, subject to the following restrictions:

      1. Redistributions of source code must retain the above copyright
         notice, disclaimer, and this list of conditions.
      2. Redistributions in binary form must reproduce the above copyright
         notice, disclaimer, and this list of conditions in the documenta-
         tion and/or other materials provided with the distribution.
      3. All advertising materials mentioning features or use of this
         software must display the following acknowledgment:

            This product includes software developed by Greg Roelofs
            and contributors for the book, "PNG: The Definitive Guide,"
            published by O'Reilly and Associates.

  ---------------------------------------------------------------------------*/

#ifndef TRUE
#  define TRUE 1
#  define FALSE 0
#endif

#ifndef MAX
#  define MAX(a,b)  ((a) > (b)? (a) : (b))
#  define MIN(a,b)  ((a) < (b)? (a) : (b))
#endif

#ifdef DEBUG
#  define Trace(x)  {fprintf x ; fflush(stderr); fflush(stdout);}
#else
#  define Trace(x)  ;
#endif

#define TEXT_TITLE    0x01
#define TEXT_AUTHOR   0x02
#define TEXT_DESC     0x04
#define TEXT_COPY     0x08
#define TEXT_EMAIL    0x10
#define TEXT_URL      0x20

#define TEXT_TITLE_OFFSET        0
#define TEXT_AUTHOR_OFFSET      72
#define TEXT_COPY_OFFSET     (2*72)
#define TEXT_EMAIL_OFFSET    (3*72)
#define TEXT_URL_OFFSET      (4*72)
#define TEXT_DESC_OFFSET     (5*72)

typedef unsigned char   uch;
typedef unsigned short  ush;
typedef unsigned long   ulg;

typedef struct _mainprog_info {
    double gamma;
    long width;
    long height;
    time_t modtime;
    FILE *infile;
    FILE *outfile;
    void *png_ptr;
    void *info_ptr;
    uch *image_data;
    uch **row_pointers;
    char *title;
    char *author;
    char *desc;
    char *copyright;
    char *email;
    char *url;
    int filter;    /* command-line-filter flag, not PNG row filter! */
    int pnmtype;
    int sample_depth;
    int interlaced;
    int have_bg;
    int have_time;
    int have_text;
    jmp_buf jmpbuf;
    uch bg_red;
    uch bg_green;
    uch bg_blue;
} mainprog_info;


/* prototypes for public functions in writepng.c */

void writepng_version_info(void);

int writepng_init(mainprog_info *mainprog_ptr);

int writepng_encode_image(mainprog_info *mainprog_ptr);

int writepng_encode_row(mainprog_info *mainprog_ptr);

int writepng_encode_finish(mainprog_info *mainprog_ptr);

void writepng_cleanup(mainprog_info *mainprog_ptr);

--- NEW FILE: readpng.h ---
/*---------------------------------------------------------------------------

   rpng - simple PNG display program                              readpng.h

  ---------------------------------------------------------------------------

      Copyright (c) 1998-2000 Greg Roelofs.  All rights reserved.

      This software is provided "as is," without warranty of any kind,
      express or implied.  In no event shall the author or contributors
      be held liable for any damages arising in any way from the use of
      this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute
      it freely, subject to the following restrictions:

      1. Redistributions of source code must retain the above copyright
         notice, disclaimer, and this list of conditions.
      2. Redistributions in binary form must reproduce the above copyright
         notice, disclaimer, and this list of conditions in the documenta-
         tion and/or other materials provided with the distribution.
      3. All advertising materials mentioning features or use of this
         software must display the following acknowledgment:

            This product includes software developed by Greg Roelofs
            and contributors for the book, "PNG: The Definitive Guide,"
            published by O'Reilly and Associates.

  ---------------------------------------------------------------------------*/

#ifndef TRUE
#  define TRUE 1
#  define FALSE 0
#endif

#ifndef MAX
#  define MAX(a,b)  ((a) > (b)? (a) : (b))
#  define MIN(a,b)  ((a) < (b)? (a) : (b))
#endif

#ifdef DEBUG
#  define Trace(x)  {fprintf x ; fflush(stderr); fflush(stdout);}
#else
#  define Trace(x)  ;
#endif

typedef unsigned char   uch;
typedef unsigned short  ush;
typedef unsigned long   ulg;


/* prototypes for public functions in readpng.c */

void readpng_version_info(void);

int readpng_init(FILE *infile, ulg *pWidth, ulg *pHeight);

int readpng_get_bgcolor(uch *bg_red, uch *bg_green, uch *bg_blue);

uch *readpng_get_image(double display_exponent, int *pChannels,
                       ulg *pRowbytes);

void readpng_cleanup(int free_image_data);

--- NEW FILE: README ---
                     ===========================
                      PNG: The Definitive Guide
                     ===========================

                             Source Code

Chapters 13, 14 and 15 of "PNG: The Definitive Guide" discuss three free,
cross-platform demo programs that show how to use the libpng reference
library:  rpng, rpng2 and wpng.  rpng and rpng2 are viewers; the first is
a very simple example that that shows how a standard file-viewer might use
libpng, while the second is designed to process streaming data and shows
how a web browser might be written.  wpng is a simple command-line program
that reads binary PGM and PPM files (the ``raw'' grayscale and RGB subsets
of PBMPLUS/NetPBM) and converts them to PNG.

The source code for all three demo programs currently compiles under
Unix, OpenVMS, and 32-bit Windows.  (Special thanks to Martin Zinser,
zinser at decus.de, for making the necessary changes for OpenVMS and for
providing an appropriate build script.)  Build instructions can be found
below.

Files:

   README             this file
   LICENSE            terms of distribution and reuse (BSD-like)

   Makefile.unx       Unix makefile
   Makefile.w32       Windows (MSVC) makefile
   makevms.com        OpenVMS build script

   rpng-win.c         Windows front end for the basic viewer
   rpng-x.c           X Window System (Unix, OpenVMS) front end
   readpng.c          generic back end for the basic viewer
   readpng.h          header file for the basic viewer

   rpng2-win.c        Windows front end for the progressive viewer
   rpng2-x.c          X front end for the progressive viewer
   readpng2.c         generic back end for the progressive viewer
   readpng2.h         header file for the progressive viewer

   wpng.c             generic (text) front end for the converter
   writepng.c         generic back end for the converter
   writepng.h         header file for the converter

   toucan.png         transparent PNG for testing (by Stefan Schneider)

Note that the programs are designed to be functional, but their primary
purpose is to illustrate how to use libpng to add PNG support to other
programs.  As such, their user interfaces are crude and definitely not
intended for everyday use.

Please see http://www.libpng.org/pub/png/pngbook.html for further infor-
mation and links to the latest version of the source code, and Chapters
13-15 of the book for detailed discussion of the three programs.

Greg Roelofs
newt at pobox.com
30 May 2001


BUILD INSTRUCTIONS

 - Prerequisites (in order of compilation):

      - zlib		http://www.gzip.org/zlib/
      - libpng		http://www.libpng.org/pub/png/libpng.html
      - pngbook		http://www.libpng.org/pub/png/book/sources.html

     The pngbook demo programs are explicitly designed to demonstrate proper
     coding techniques for using the libpng reference library.  As a result,
     you need to download and build both zlib (on which libpng depends) and
     libpng.  A common build setup is to place the zlib, libpng and pngbook
     subdirectory trees ("folders") in the same parent directory.  Then the
     libpng build can refer to files in ../zlib (or ..\zlib or [-.zlib]),
     and similarly for the pngbook build.

     Note that all three packages are designed to be built from a command
     line by default; those who wish to use a graphical or other integrated
     development environments are on their own.


 - Unix:

     Unpack the latest pngbook sources (which should correspond to this
     README file) into a directory and change into that directory.

     Copy Makefile.unx to Makefile and edit the PNG* and Z* variables
     appropriately (possibly also the X* variables if necessary).

     make

     There is no "install" target, so copy the three executables somewhere
     in your path or run them from the current directory.  All three will
     print a basic usage screen when run without any command-line arguments;
     see the book for more details.


 - Windows:

     Unpack the latest pngbook sources (which should correspond to this
     README file) into a folder, open a "DOS shell" or "command prompt"
     or equivalent command-line window, and cd into the folder where you
     unpacked the source code.

     For MSVC, set up the necessary environment variables by invoking
 
        %devstudio%\vc\bin\vcvars32.bat

     where where %devstudio% is the installation directory for MSVC /
     DevStudio.  If you get "environment out of space" errors under 95/98,
     create a desktop shortcut with "c:\windows\command.com /e:4096" as
     the program command line and set the working directory to the pngbook
     directory.  Then double-click to open the new DOS-prompt window with
     a bigger environment and retry the commands above.

     Copy Makefile.w32 to Makefile and edit the PNGPATH and ZPATH variables
     appropriately (possibly also the "INC" and "LIB" variables if needed).
     Note that the names of the dynamic and static libpng and zlib libraries
     used in the makefile may change in later releases of the libraries.
     Also note that, as of libpng version 1.0.5, MSVC DLL builds do not work.
     This makefile therefore builds statically linked executables, but if
     the DLL problems ever get fixed, uncommenting the appropriate PNGLIB
     and ZLIB lines will build dynamically linked executables instead.

     Do the build by typing

        nmake

     The result should be three executables:  rpng-win.exe, rpng2-win.exe,
     and wpng.exe.  Copy them somewhere in your PATH or run them from the
     current folder.  Like the Unix versions, the two windowed programs
     (rpng and rpng2) now display a usage screen in a console window when
     invoked without command-line arguments; this is new behavior as of
     the May 2001 release.  Note that the programs use the Unix-style "-"
     character to specify options, instead of the more common DOS/Windows
     "/" character.  (For example:  "rpng2-win -bgpat 4 foo.png", not
     "rpng2-win /bgpat 4 foo.png")


 - OpenVMS:

     Unpack the pngbook sources into a subdirectory and change into that
     subdirectory.

     Edit makevms.com appropriately, specifically the zpath and pngpath
     variables.

     @makevms

     To run the programs, they probably first need to be set up as "foreign
     symbols," with "disk" and "dir" set appropriately:

     $ rpng == "$disk:[dir]rpng-x.exe"
     $ rpng2 == "$disk:[dir]rpng2-x.exe"
     $ wpng == "$disk:[dir]wpng.exe"

     All three will print a basic usage screen when run without any command-
     line arguments; see the book for more details.  Note that the options
     style is Unix-like, i.e., preceded by "-" rather than "/".


RUNNING THE PROGRAMS:  (VERY) BRIEF INTRO

     rpng is a simple PNG viewer that can display transparent PNGs with a
     specified background color; for example,

	rpng -bgcolor #ff0000 toucan.png

     would display the image with a red background.  rpng2 is a progressive
     viewer that simulates a web browser in some respects; it can display
     images against either a background color or a dynamically generated
     background image.  For example:

	rpng2 -bgpat 16 toucan.png

     wpng is a purely command-line image converter from binary PBMPLUS/NetPBM
     format (.pgm or .ppm) to PNG; for example,

	wpng -time < toucan.ppm > toucan.png

     would convert the specified PPM file (using redirection) to PNG, auto-
     matically setting the PNG modification-time chunk.

     All options can be abbreviated to the shortest unique value; for example,
     "-bgc" for -bgcolor (versus "-bgp" for -bgpat), or "-g" for -gamma.

--- NEW FILE: toucan.png ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: readpng.c ---
/*---------------------------------------------------------------------------

   rpng - simple PNG display program                              readpng.c

  ---------------------------------------------------------------------------

      Copyright (c) 1998-2000 Greg Roelofs.  All rights reserved.

      This software is provided "as is," without warranty of any kind,
      express or implied.  In no event shall the author or contributors
      be held liable for any damages arising in any way from the use of
      this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute
      it freely, subject to the following restrictions:

      1. Redistributions of source code must retain the above copyright
         notice, disclaimer, and this list of conditions.
      2. Redistributions in binary form must reproduce the above copyright
         notice, disclaimer, and this list of conditions in the documenta-
         tion and/or other materials provided with the distribution.
      3. All advertising materials mentioning features or use of this
         software must display the following acknowledgment:

            This product includes software developed by Greg Roelofs
            and contributors for the book, "PNG: The Definitive Guide,"
            published by O'Reilly and Associates.

  ---------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>

#include "png.h"        /* libpng header; includes zlib.h */
#include "readpng.h"    /* typedefs, common macros, public prototypes */

/* future versions of libpng will provide this macro: */
#ifndef png_jmpbuf
#  define png_jmpbuf(png_ptr)   ((png_ptr)->jmpbuf)
#endif


static png_structp png_ptr = NULL;
static png_infop info_ptr = NULL;

png_uint_32  width, height;
int  bit_depth, color_type;
uch  *image_data = NULL;


void readpng_version_info(void)
{
    fprintf(stderr, "   Compiled with libpng %s; using libpng %s.\n",
      PNG_LIBPNG_VER_STRING, png_libpng_ver);
    fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
      ZLIB_VERSION, zlib_version);
}


/* return value = 0 for success, 1 for bad sig, 2 for bad IHDR, 4 for no mem */

int readpng_init(FILE *infile, ulg *pWidth, ulg *pHeight)
{
    uch sig[8];


    /* first do a quick check that the file really is a PNG image; could
     * have used slightly more general png_sig_cmp() function instead */

    fread(sig, 1, 8, infile);
    if (!png_check_sig(sig, 8))
        return 1;   /* bad signature */


    /* could pass pointers to user-defined error handlers instead of NULLs: */

    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png_ptr)
        return 4;   /* out of memory */

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
        png_destroy_read_struct(&png_ptr, NULL, NULL);
        return 4;   /* out of memory */
    }


    /* we could create a second info struct here (end_info), but it's only
     * useful if we want to keep pre- and post-IDAT chunk info separated
     * (mainly for PNG-aware image editors and converters) */


    /* setjmp() must be called in every function that calls a PNG-reading
     * libpng function */

    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        return 2;
    }


    png_init_io(png_ptr, infile);
    png_set_sig_bytes(png_ptr, 8);  /* we already read the 8 signature bytes */

    png_read_info(png_ptr, info_ptr);  /* read all PNG info up to image data */


    /* alternatively, could make separate calls to png_get_image_width(),
     * etc., but want bit_depth and color_type for later [don't care about
     * compression_type and filter_type => NULLs] */

    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
      NULL, NULL, NULL);
    *pWidth = width;
    *pHeight = height;


    /* OK, that's all we need for now; return happy */

    return 0;
}




/* returns 0 if succeeds, 1 if fails due to no bKGD chunk, 2 if libpng error;
 * scales values to 8-bit if necessary */

int readpng_get_bgcolor(uch *red, uch *green, uch *blue)
{
    png_color_16p pBackground;


    /* setjmp() must be called in every function that calls a PNG-reading
     * libpng function */

    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        return 2;
    }


    if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
        return 1;

    /* it is not obvious from the libpng documentation, but this function
     * takes a pointer to a pointer, and it always returns valid red, green
     * and blue values, regardless of color_type: */

    png_get_bKGD(png_ptr, info_ptr, &pBackground);


    /* however, it always returns the raw bKGD data, regardless of any
     * bit-depth transformations, so check depth and adjust if necessary */

    if (bit_depth == 16) {
        *red   = pBackground->red   >> 8;
        *green = pBackground->green >> 8;
        *blue  = pBackground->blue  >> 8;
    } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
        if (bit_depth == 1)
            *red = *green = *blue = pBackground->gray? 255 : 0;
        else if (bit_depth == 2)
            *red = *green = *blue = (255/3) * pBackground->gray;
        else /* bit_depth == 4 */
            *red = *green = *blue = (255/15) * pBackground->gray;
    } else {
        *red   = (uch)pBackground->red;
        *green = (uch)pBackground->green;
        *blue  = (uch)pBackground->blue;
    }

    return 0;
}




/* display_exponent == LUT_exponent * CRT_exponent */

uch *readpng_get_image(double display_exponent, int *pChannels, ulg *pRowbytes)
{
    double  gamma;
    png_uint_32  i, rowbytes;
    png_bytepp  row_pointers = NULL;


    /* setjmp() must be called in every function that calls a PNG-reading
     * libpng function */

    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        return NULL;
    }


    /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
     * transparency chunks to full alpha channel; strip 16-bit-per-sample
     * images to 8 bits per sample; and convert grayscale to RGB[A] */

    if (color_type == PNG_COLOR_TYPE_PALETTE)
        png_set_expand(png_ptr);
    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
        png_set_expand(png_ptr);
    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
        png_set_expand(png_ptr);
    if (bit_depth == 16)
        png_set_strip_16(png_ptr);
    if (color_type == PNG_COLOR_TYPE_GRAY ||
        color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
        png_set_gray_to_rgb(png_ptr);


    /* unlike the example in the libpng documentation, we have *no* idea where
     * this file may have come from--so if it doesn't have a file gamma, don't
     * do any correction ("do no harm") */

    if (png_get_gAMA(png_ptr, info_ptr, &gamma))
        png_set_gamma(png_ptr, display_exponent, gamma);


    /* all transformations have been registered; now update info_ptr data,
     * get rowbytes and channels, and allocate image memory */

    png_read_update_info(png_ptr, info_ptr);

    *pRowbytes = rowbytes = png_get_rowbytes(png_ptr, info_ptr);
    *pChannels = (int)png_get_channels(png_ptr, info_ptr);

    if ((image_data = (uch *)malloc(rowbytes*height)) == NULL) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        return NULL;
    }
    if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        free(image_data);
        image_data = NULL;
        return NULL;
    }

    Trace((stderr, "readpng_get_image:  rowbytes = %ld, height = %ld\n", rowbytes, height));


    /* set the individual row_pointers to point at the correct offsets */

    for (i = 0;  i < height;  ++i)
        row_pointers[i] = image_data + i*rowbytes;


    /* now we can go ahead and just read the whole image */

    png_read_image(png_ptr, row_pointers);


    /* and we're done!  (png_read_end() can be omitted if no processing of
     * post-IDAT text/time/etc. is desired) */

    free(row_pointers);
    row_pointers = NULL;

    png_read_end(png_ptr, NULL);

    return image_data;
}


void readpng_cleanup(int free_image_data)
{
    if (free_image_data && image_data) {
        free(image_data);
        image_data = NULL;
    }

    if (png_ptr && info_ptr) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        png_ptr = NULL;
        info_ptr = NULL;
    }
}

--- NEW FILE: Makefile.w32 ---
# Sample makefile for rpng-win / rpng2-win / wpng using MSVC and NMAKE.
# Greg Roelofs
# Last modified:  16 February 1999
#
#	The programs built by this makefile are described in the book,
#	"PNG:  The Definitive Guide," by Greg Roelofs (O'Reilly and
#	Associates, 1999).  Go buy a copy, eh?  Buy some for friends
#	and family, too.  (Not that this is a blatant plug or anything.)
#
# Invoke this makefile from a DOS prompt window via:
#
#	%devstudio%\vc\bin\vcvars32.bat
#	nmake -nologo -f Makefile.w32
#
# where %devstudio% is the installation directory for MSVC / DevStudio.  If
# you get "environment out of space" errors, create a desktop shortcut with
# "c:\windows\command.com /e:4096" as the program command line and set the
# working directory to this directory.  Then double-click to open the new
# DOS-prompt window with a bigger environment and retry the commands above.
# 
# This makefile assumes libpng and zlib have already been built or downloaded
# and are in subdirectories at the same level as the current subdirectory
# (as indicated by the PNGPATH and ZPATH macros below).  Edit as appropriate.
#
# Note that the names of the dynamic and static libpng and zlib libraries
# used below may change in later releases of the libraries.  This makefile
# builds statically linked executables, but that can be changed by uncom-
# menting the appropriate PNGLIB and ZLIB lines.

!include <ntwin32.mak>


# macros --------------------------------------------------------------------

PNGPATH = ../..
PNGINC = -I$(PNGPATH)
#PNGLIB = $(PNGPATH)/pngdll.lib
PNGLIB = $(PNGPATH)/libpng.lib

ZPATH = ../../../zlib
ZINC = -I$(ZPATH)
#ZLIB = $(ZPATH)/zlibdll.lib
ZLIB = $(ZPATH)/zlibstat.lib

WINLIBS = -defaultlib:user32.lib gdi32.lib
# ["real" apps may also need comctl32.lib, comdlg32.lib, winmm.lib, etc.]

INCS = $(PNGINC) $(ZINC)
RLIBS = $(PNGLIB) $(ZLIB) $(WINLIBS)
WLIBS = $(PNGLIB) $(ZLIB)

CC = cl
LD = link
RM = del
CFLAGS = -nologo -O -W3 $(INCS) $(cvars)
# [note that -Wall is an MSVC-specific compilation flag ("all warnings on")]
# [see %devstudio%\vc\include\win32.mak for cvars macro definition]
O = .obj
E = .exe

RLDFLAGS = -nologo -subsystem:windows
WLDFLAGS = -nologo

RPNG  = rpng-win
RPNG2 = rpng2-win
WPNG  = wpng

ROBJS  = $(RPNG)$(O) readpng$(O)
ROBJS2 = $(RPNG2)$(O) readpng2$(O)
WOBJS  = $(WPNG)$(O) writepng$(O)

EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E)


# implicit make rules -------------------------------------------------------

.c$(O):
	$(CC) -c $(CFLAGS) $<


# dependencies --------------------------------------------------------------

all:  $(EXES)

$(RPNG)$(E): $(ROBJS)
	$(LD) $(RLDFLAGS) -out:$@ $(ROBJS) $(RLIBS)

$(RPNG2)$(E): $(ROBJS2)
	$(LD) $(RLDFLAGS) -out:$@ $(ROBJS2) $(RLIBS)

$(WPNG)$(E): $(WOBJS)
	$(LD) $(WLDFLAGS) -out:$@ $(WOBJS) $(WLIBS)

$(RPNG)$(O):	$(RPNG).c readpng.h
$(RPNG2)$(O):	$(RPNG2).c readpng2.h
$(WPNG)$(O):	$(WPNG).c writepng.h

readpng$(O):	readpng.c readpng.h
readpng2$(O):	readpng2.c readpng2.h
writepng$(O):	writepng.c writepng.h


# maintenance ---------------------------------------------------------------

clean:
#	ideally we could just do this:
#	$(RM) $(EXES) $(ROBJS) $(ROBJS2) $(WOBJS)
#	...but the Windows "DEL" command is none too bright, so:
	$(RM) r*$(E)
	$(RM) w*$(E)
	$(RM) r*$(O)
	$(RM) w*$(O)

--- NEW FILE: readpng2.h ---
/*---------------------------------------------------------------------------

   rpng2 - progressive-model PNG display program                 readpng2.h

  ---------------------------------------------------------------------------

      Copyright (c) 1998-2001 Greg Roelofs.  All rights reserved.

      This software is provided "as is," without warranty of any kind,
      express or implied.  In no event shall the author or contributors
      be held liable for any damages arising in any way from the use of
      this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute
      it freely, subject to the following restrictions:

      1. Redistributions of source code must retain the above copyright
         notice, disclaimer, and this list of conditions.
      2. Redistributions in binary form must reproduce the above copyright
         notice, disclaimer, and this list of conditions in the documenta-
         tion and/or other materials provided with the distribution.
      3. All advertising materials mentioning features or use of this
         software must display the following acknowledgment:

            This product includes software developed by Greg Roelofs
            and contributors for the book, "PNG: The Definitive Guide,"
            published by O'Reilly and Associates.

  ---------------------------------------------------------------------------*/

#ifndef TRUE
#  define TRUE 1
#  define FALSE 0
#endif

#ifndef MAX
#  define MAX(a,b)  ((a) > (b)? (a) : (b))
#  define MIN(a,b)  ((a) < (b)? (a) : (b))
#endif

#ifdef DEBUG
#  define Trace(x)  {fprintf x ; fflush(stderr); fflush(stdout);}
#else
#  define Trace(x)  ;
#endif

typedef unsigned char   uch;
typedef unsigned short  ush;
typedef unsigned long   ulg;

typedef struct _mainprog_info {
    double display_exponent;
    ulg width;
    ulg height;
    void *png_ptr;
    void *info_ptr;
    void (*mainprog_init)(void);
    void (*mainprog_display_row)(ulg row_num);
    void (*mainprog_finish_display)(void);
    uch *image_data;
    uch **row_pointers;
    jmp_buf jmpbuf;
    int passes;              /* not used */
    int pass;
    int rowbytes;
    int channels;
    int need_bgcolor;
#if (defined(__i386__) || defined(_M_IX86))
    int nommxfilters;
    int nommxcombine;
    int nommxinterlace;
#endif
    int done;
    uch bg_red;
    uch bg_green;
    uch bg_blue;
} mainprog_info;


/* prototypes for public functions in readpng2.c */

void readpng2_version_info(void);

int readpng2_check_sig(uch *sig, int num);

int readpng2_init(mainprog_info *mainprog_ptr);

int readpng2_decode_data(mainprog_info *mainprog_ptr, uch *rawbuf, ulg length);

void readpng2_cleanup(mainprog_info *mainprog_ptr);




More information about the dslinux-commit mailing list