Blame contrib/gregbook/rpng-win.c

Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
   rpng - simple PNG display program                             rpng-win.c
Packit 0ba690
Packit 0ba690
   This program decodes and displays PNG images, with gamma correction and
Packit 0ba690
   optionally with a user-specified background color (in case the image has
Packit 0ba690
   transparency).  It is very nearly the most basic PNG viewer possible.
Packit 0ba690
   This version is for 32-bit Windows; it may compile under 16-bit Windows
Packit 0ba690
   with a little tweaking (or maybe not).
Packit 0ba690
Packit 0ba690
   to do:
Packit 0ba690
    - handle quoted command-line args (especially filenames with spaces)
Packit 0ba690
    - have minimum window width:  oh well
Packit 0ba690
    - use %.1023s to simplify truncation of title-bar string?
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
   Changelog:
Packit 0ba690
    - 1.00:  initial public release
Packit 0ba690
    - 1.01:  modified to allow abbreviated options; fixed long/ulong mis-
Packit 0ba690
              match; switched to png_jmpbuf() macro
Packit 0ba690
    - 1.02:  added extra set of parentheses to png_jmpbuf() macro; fixed
Packit 0ba690
              command-line parsing bug
Packit 0ba690
    - 1.10:  enabled "message window"/console (thanks to David Geldreich)
Packit 0ba690
    - 2.00:  dual-licensed (added GNU GPL)
Packit 0ba690
    - 2.01:  fixed improper display of usage screen on PNG error(s)
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
      Copyright (c) 1998-2008 Greg Roelofs.  All rights reserved.
Packit 0ba690
Packit 0ba690
      This software is provided "as is," without warranty of any kind,
Packit 0ba690
      express or implied.  In no event shall the author or contributors
Packit 0ba690
      be held liable for any damages arising in any way from the use of
Packit 0ba690
      this software.
Packit 0ba690
Packit 0ba690
      The contents of this file are DUAL-LICENSED.  You may modify and/or
Packit 0ba690
      redistribute this software according to the terms of one of the
Packit 0ba690
      following two licenses (at your option):
Packit 0ba690
Packit 0ba690
Packit 0ba690
      LICENSE 1 ("BSD-like with advertising clause"):
Packit 0ba690
Packit 0ba690
      Permission is granted to anyone to use this software for any purpose,
Packit 0ba690
      including commercial applications, and to alter it and redistribute
Packit 0ba690
      it freely, subject to the following restrictions:
Packit 0ba690
Packit 0ba690
      1. Redistributions of source code must retain the above copyright
Packit 0ba690
         notice, disclaimer, and this list of conditions.
Packit 0ba690
      2. Redistributions in binary form must reproduce the above copyright
Packit 0ba690
         notice, disclaimer, and this list of conditions in the documenta-
Packit 0ba690
         tion and/or other materials provided with the distribution.
Packit 0ba690
      3. All advertising materials mentioning features or use of this
Packit 0ba690
         software must display the following acknowledgment:
Packit 0ba690
Packit 0ba690
            This product includes software developed by Greg Roelofs
Packit 0ba690
            and contributors for the book, "PNG: The Definitive Guide,"
Packit 0ba690
            published by O'Reilly and Associates.
Packit 0ba690
Packit 0ba690
Packit 0ba690
      LICENSE 2 (GNU GPL v2 or later):
Packit 0ba690
Packit 0ba690
      This program is free software; you can redistribute it and/or modify
Packit 0ba690
      it under the terms of the GNU General Public License as published by
Packit 0ba690
      the Free Software Foundation; either version 2 of the License, or
Packit 0ba690
      (at your option) any later version.
Packit 0ba690
Packit 0ba690
      This program is distributed in the hope that it will be useful,
Packit 0ba690
      but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 0ba690
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 0ba690
      GNU General Public License for more details.
Packit 0ba690
Packit 0ba690
      You should have received a copy of the GNU General Public License
Packit 0ba690
      along with this program; if not, write to the Free Software Foundation,
Packit 0ba690
      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
#define PROGNAME  "rpng-win"
Packit 0ba690
#define LONGNAME  "Simple PNG Viewer for Windows"
Packit 0ba690
#define VERSION   "2.01 of 16 March 2008"
Packit 0ba690
Packit 0ba690
#include <stdio.h>
Packit 0ba690
#include <stdlib.h>
Packit 0ba690
#include <string.h>
Packit 0ba690
#include <time.h>
Packit 0ba690
#include <windows.h>
Packit 0ba690
#include <conio.h>      /* only for _getch() */
Packit 0ba690
Packit 0ba690
/* #define DEBUG  :  this enables the Trace() macros */
Packit 0ba690
Packit 0ba690
#include "readpng.h"    /* typedefs, common macros, readpng prototypes */
Packit 0ba690
Packit 0ba690
Packit 0ba690
/* could just include png.h, but this macro is the only thing we need
Packit 0ba690
 * (name and typedefs changed to local versions); note that side effects
Packit 0ba690
 * only happen with alpha (which could easily be avoided with
Packit 0ba690
 * "ush acopy = (alpha);") */
Packit 0ba690
Packit 0ba690
#define alpha_composite(composite, fg, alpha, bg) {               \
Packit 0ba690
    ush temp = ((ush)(fg)*(ush)(alpha) +                          \
Packit 0ba690
                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
Packit 0ba690
    (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
/* local prototypes */
Packit 0ba690
static int        rpng_win_create_window(HINSTANCE hInst, int showmode);
Packit 0ba690
static int        rpng_win_display_image(void);
Packit 0ba690
static void       rpng_win_cleanup(void);
Packit 0ba690
LRESULT CALLBACK  rpng_win_wndproc(HWND, UINT, WPARAM, LPARAM);
Packit 0ba690
Packit 0ba690
Packit 0ba690
static char titlebar[1024];
Packit 0ba690
static char *progname = PROGNAME;
Packit 0ba690
static char *appname = LONGNAME;
Packit 0ba690
static char *filename;
Packit 0ba690
static FILE *infile;
Packit 0ba690
Packit 0ba690
static char *bgstr;
Packit 0ba690
static uch bg_red=0, bg_green=0, bg_blue=0;
Packit 0ba690
Packit 0ba690
static double display_exponent;
Packit 0ba690
Packit 0ba690
static ulg image_width, image_height, image_rowbytes;
Packit 0ba690
static int image_channels;
Packit 0ba690
static uch *image_data;
Packit 0ba690
Packit 0ba690
/* Windows-specific variables */
Packit 0ba690
static ulg wimage_rowbytes;
Packit 0ba690
static uch *dib;
Packit 0ba690
static uch *wimage_data;
Packit 0ba690
static BITMAPINFOHEADER *bmih;
Packit 0ba690
Packit 0ba690
static HWND global_hwnd;
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
Packit 0ba690
{
Packit 0ba690
    char *args[1024];                 /* arbitrary limit, but should suffice */
Packit 0ba690
    char *p, *q, **argv = args;
Packit 0ba690
    int argc = 0;
Packit 0ba690
    int rc, alen, flen;
Packit 0ba690
    int error = 0;
Packit 0ba690
    int have_bg = FALSE;
Packit 0ba690
    double LUT_exponent;              /* just the lookup table */
Packit 0ba690
    double CRT_exponent = 2.2;        /* just the monitor */
Packit 0ba690
    double default_display_exponent;  /* whole display system */
Packit 0ba690
    MSG msg;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    filename = (char *)NULL;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* First reenable console output, which normally goes to the bit bucket
Packit 0ba690
     * for windowed apps.  Closing the console window will terminate the
Packit 0ba690
     * app.  Thanks to David.Geldreich@realviz.com for supplying the magical
Packit 0ba690
     * incantation. */
Packit 0ba690
Packit 0ba690
    AllocConsole();
Packit 0ba690
    freopen("CONOUT$", "a", stderr);
Packit 0ba690
    freopen("CONOUT$", "a", stdout);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* Next set the default value for our display-system exponent, i.e.,
Packit 0ba690
     * the product of the CRT exponent and the exponent corresponding to
Packit 0ba690
     * the frame-buffer's lookup table (LUT), if any.  This is not an
Packit 0ba690
     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
Packit 0ba690
     * ones), but it should cover 99% of the current possibilities.  And
Packit 0ba690
     * yes, these ifdefs are completely wasted in a Windows program... */
Packit 0ba690
Packit 0ba690
#if defined(NeXT)
Packit 0ba690
    LUT_exponent = 1.0 / 2.2;
Packit 0ba690
    /*
Packit 0ba690
    if (some_next_function_that_returns_gamma(&next_gamma))
Packit 0ba690
        LUT_exponent = 1.0 / next_gamma;
Packit 0ba690
     */
Packit 0ba690
#elif defined(sgi)
Packit 0ba690
    LUT_exponent = 1.0 / 1.7;
Packit 0ba690
    /* there doesn't seem to be any documented function to get the
Packit 0ba690
     * "gamma" value, so we do it the hard way */
Packit 0ba690
    infile = fopen("/etc/config/system.glGammaVal", "r");
Packit 0ba690
    if (infile) {
Packit 0ba690
        double sgi_gamma;
Packit 0ba690
Packit 0ba690
        fgets(tmpline, 80, infile);
Packit 0ba690
        fclose(infile);
Packit 0ba690
        sgi_gamma = atof(tmpline);
Packit 0ba690
        if (sgi_gamma > 0.0)
Packit 0ba690
            LUT_exponent = 1.0 / sgi_gamma;
Packit 0ba690
    }
Packit 0ba690
#elif defined(Macintosh)
Packit 0ba690
    LUT_exponent = 1.8 / 2.61;
Packit 0ba690
    /*
Packit 0ba690
    if (some_mac_function_that_returns_gamma(&mac_gamma))
Packit 0ba690
        LUT_exponent = mac_gamma / 2.61;
Packit 0ba690
     */
Packit 0ba690
#else
Packit 0ba690
    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
Packit 0ba690
#endif
Packit 0ba690
Packit 0ba690
    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
Packit 0ba690
    default_display_exponent = LUT_exponent * CRT_exponent;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* If the user has set the SCREEN_GAMMA environment variable as suggested
Packit 0ba690
     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
Packit 0ba690
     * use the default value we just calculated.  Either way, the user may
Packit 0ba690
     * override this via a command-line option. */
Packit 0ba690
Packit 0ba690
    if ((p = getenv("SCREEN_GAMMA")) != NULL)
Packit 0ba690
        display_exponent = atof(p);
Packit 0ba690
    else
Packit 0ba690
        display_exponent = default_display_exponent;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* Windows really hates command lines, so we have to set up our own argv.
Packit 0ba690
     * Note that we do NOT bother with quoted arguments here, so don't use
Packit 0ba690
     * filenames with spaces in 'em! */
Packit 0ba690
Packit 0ba690
    argv[argc++] = PROGNAME;
Packit 0ba690
    p = cmd;
Packit 0ba690
    for (;;) {
Packit 0ba690
        if (*p == ' ')
Packit 0ba690
            while (*++p == ' ')
Packit 0ba690
                ;
Packit 0ba690
        /* now p points at the first non-space after some spaces */
Packit 0ba690
        if (*p == '\0')
Packit 0ba690
            break;    /* nothing after the spaces:  done */
Packit 0ba690
        argv[argc++] = q = p;
Packit 0ba690
        while (*q && *q != ' ')
Packit 0ba690
            ++q;
Packit 0ba690
        /* now q points at a space or the end of the string */
Packit 0ba690
        if (*q == '\0')
Packit 0ba690
            break;    /* last argv already terminated; quit */
Packit 0ba690
        *q = '\0';    /* change space to terminator */
Packit 0ba690
        p = q + 1;
Packit 0ba690
    }
Packit 0ba690
    argv[argc] = NULL;   /* terminate the argv array itself */
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* Now parse the command line for options and the PNG filename. */
Packit 0ba690
Packit 0ba690
    while (*++argv && !error) {
Packit 0ba690
        if (!strncmp(*argv, "-gamma", 2)) {
Packit 0ba690
            if (!*++argv)
Packit 0ba690
                ++error;
Packit 0ba690
            else {
Packit 0ba690
                display_exponent = atof(*argv);
Packit 0ba690
                if (display_exponent <= 0.0)
Packit 0ba690
                    ++error;
Packit 0ba690
            }
Packit 0ba690
        } else if (!strncmp(*argv, "-bgcolor", 2)) {
Packit 0ba690
            if (!*++argv)
Packit 0ba690
                ++error;
Packit 0ba690
            else {
Packit 0ba690
                bgstr = *argv;
Packit 0ba690
                if (strlen(bgstr) != 7 || bgstr[0] != '#')
Packit 0ba690
                    ++error;
Packit 0ba690
                else
Packit 0ba690
                    have_bg = TRUE;
Packit 0ba690
            }
Packit 0ba690
        } else {
Packit 0ba690
            if (**argv != '-') {
Packit 0ba690
                filename = *argv;
Packit 0ba690
                if (argv[1])   /* shouldn't be any more args after filename */
Packit 0ba690
                    ++error;
Packit 0ba690
            } else
Packit 0ba690
                ++error;   /* not expecting any other options */
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (!filename)
Packit 0ba690
        ++error;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* print usage screen if any errors up to this point */
Packit 0ba690
Packit 0ba690
    if (error) {
Packit 0ba690
        int ch;
Packit 0ba690
Packit 0ba690
        fprintf(stderr, "\n%s %s:  %s\n\n", PROGNAME, VERSION, appname);
Packit 0ba690
        readpng_version_info();
Packit 0ba690
        fprintf(stderr, "\n"
Packit 0ba690
          "Usage:  %s [-gamma exp] [-bgcolor bg] file.png\n"
Packit 0ba690
          "    exp \ttransfer-function exponent (``gamma'') of the display\n"
Packit 0ba690
          "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
Packit 0ba690
          "\t\t  to the product of the lookup-table exponent (varies)\n"
Packit 0ba690
          "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
Packit 0ba690
          "    bg  \tdesired background color in 7-character hex RGB format\n"
Packit 0ba690
          "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
Packit 0ba690
          "\t\t  used with transparent images\n"
Packit 0ba690
          "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
Packit 0ba690
          "Press Q or Esc to quit this usage screen.\n"
Packit 0ba690
          "\n", PROGNAME, default_display_exponent);
Packit 0ba690
        do
Packit 0ba690
            ch = _getch();
Packit 0ba690
        while (ch != 'q' && ch != 'Q' && ch != 0x1B);
Packit 0ba690
        exit(1);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    if (!(infile = fopen(filename, "rb"))) {
Packit 0ba690
        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
Packit 0ba690
        ++error;
Packit 0ba690
    } else {
Packit 0ba690
        if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) {
Packit 0ba690
            switch (rc) {
Packit 0ba690
                case 1:
Packit 0ba690
                    fprintf(stderr, PROGNAME
Packit 0ba690
                      ":  [%s] is not a PNG file: incorrect signature\n",
Packit 0ba690
                      filename);
Packit 0ba690
                    break;
Packit 0ba690
                case 2:
Packit 0ba690
                    fprintf(stderr, PROGNAME
Packit 0ba690
                      ":  [%s] has bad IHDR (libpng longjmp)\n", filename);
Packit 0ba690
                    break;
Packit 0ba690
                case 4:
Packit 0ba690
                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
Packit 0ba690
                    break;
Packit 0ba690
                default:
Packit 0ba690
                    fprintf(stderr, PROGNAME
Packit 0ba690
                      ":  unknown readpng_init() error\n");
Packit 0ba690
                    break;
Packit 0ba690
            }
Packit 0ba690
            ++error;
Packit 0ba690
        }
Packit 0ba690
        if (error)
Packit 0ba690
            fclose(infile);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    if (error) {
Packit 0ba690
        int ch;
Packit 0ba690
Packit 0ba690
        fprintf(stderr, PROGNAME ":  aborting.\n");
Packit 0ba690
        do
Packit 0ba690
            ch = _getch();
Packit 0ba690
        while (ch != 'q' && ch != 'Q' && ch != 0x1B);
Packit 0ba690
        exit(2);
Packit 0ba690
    } else {
Packit 0ba690
        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
Packit 0ba690
        fprintf(stderr,
Packit 0ba690
          "\n   [console window:  closing this window will terminate %s]\n\n",
Packit 0ba690
          PROGNAME);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* set the title-bar string, but make sure buffer doesn't overflow */
Packit 0ba690
Packit 0ba690
    alen = strlen(appname);
Packit 0ba690
    flen = strlen(filename);
Packit 0ba690
    if (alen + flen + 3 > 1023)
Packit 0ba690
        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
Packit 0ba690
    else
Packit 0ba690
        sprintf(titlebar, "%s:  %s", appname, filename);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* if the user didn't specify a background color on the command line,
Packit 0ba690
     * check for one in the PNG file--if not, the initialized values of 0
Packit 0ba690
     * (black) will be used */
Packit 0ba690
Packit 0ba690
    if (have_bg) {
Packit 0ba690
        unsigned r, g, b;   /* this approach quiets compiler warnings */
Packit 0ba690
Packit 0ba690
        sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
Packit 0ba690
        bg_red   = (uch)r;
Packit 0ba690
        bg_green = (uch)g;
Packit 0ba690
        bg_blue  = (uch)b;
Packit 0ba690
    } else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) {
Packit 0ba690
        readpng_cleanup(TRUE);
Packit 0ba690
        fprintf(stderr, PROGNAME
Packit 0ba690
          ":  libpng error while checking for background color\n");
Packit 0ba690
        exit(2);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* do the basic Windows initialization stuff, make the window and fill it
Packit 0ba690
     * with the background color */
Packit 0ba690
Packit 0ba690
    if (rpng_win_create_window(hInst, showmode))
Packit 0ba690
        exit(2);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* decode the image, all at once */
Packit 0ba690
Packit 0ba690
    Trace((stderr, "calling readpng_get_image()\n"))
Packit 0ba690
    image_data = readpng_get_image(display_exponent, &image_channels,
Packit 0ba690
      &image_rowbytes);
Packit 0ba690
    Trace((stderr, "done with readpng_get_image()\n"))
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* done with PNG file, so clean up to minimize memory usage (but do NOT
Packit 0ba690
     * nuke image_data!) */
Packit 0ba690
Packit 0ba690
    readpng_cleanup(FALSE);
Packit 0ba690
    fclose(infile);
Packit 0ba690
Packit 0ba690
    if (!image_data) {
Packit 0ba690
        fprintf(stderr, PROGNAME ":  unable to decode PNG image\n");
Packit 0ba690
        exit(3);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* display image (composite with background if requested) */
Packit 0ba690
Packit 0ba690
    Trace((stderr, "calling rpng_win_display_image()\n"))
Packit 0ba690
    if (rpng_win_display_image()) {
Packit 0ba690
        free(image_data);
Packit 0ba690
        exit(4);
Packit 0ba690
    }
Packit 0ba690
    Trace((stderr, "done with rpng_win_display_image()\n"))
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* wait for the user to tell us when to quit */
Packit 0ba690
Packit 0ba690
    printf(
Packit 0ba690
      "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
Packit 0ba690
    fflush(stdout);
Packit 0ba690
Packit 0ba690
    while (GetMessage(&msg, NULL, 0, 0)) {
Packit 0ba690
        TranslateMessage(&msg;;
Packit 0ba690
        DispatchMessage(&msg;;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* OK, we're done:  clean up all image and Windows resources and go away */
Packit 0ba690
Packit 0ba690
    rpng_win_cleanup();
Packit 0ba690
Packit 0ba690
    return msg.wParam;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static int rpng_win_create_window(HINSTANCE hInst, int showmode)
Packit 0ba690
{
Packit 0ba690
    uch *dest;
Packit 0ba690
    int extra_width, extra_height;
Packit 0ba690
    ulg i, j;
Packit 0ba690
    WNDCLASSEX wndclass;
Packit 0ba690
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Allocate memory for the display-specific version of the image (round up
Packit 0ba690
    to multiple of 4 for Windows DIB).
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    wimage_rowbytes = ((3*image_width + 3L) >> 2) << 2;
Packit 0ba690
Packit 0ba690
    if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
Packit 0ba690
                              wimage_rowbytes*image_height)))
Packit 0ba690
    {
Packit 0ba690
        return 4;   /* fail */
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Initialize the DIB.  Negative height means to use top-down BMP ordering
Packit 0ba690
    (must be uncompressed, but that's what we want).  Bit count of 1, 4 or 8
Packit 0ba690
    implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values
Packit 0ba690
    directly => wimage_data begins immediately after BMP header.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    memset(dib, 0, sizeof(BITMAPINFOHEADER));
Packit 0ba690
    bmih = (BITMAPINFOHEADER *)dib;
Packit 0ba690
    bmih->biSize = sizeof(BITMAPINFOHEADER);
Packit 0ba690
    bmih->biWidth = image_width;
Packit 0ba690
    bmih->biHeight = -((long)image_height);
Packit 0ba690
    bmih->biPlanes = 1;
Packit 0ba690
    bmih->biBitCount = 24;
Packit 0ba690
    bmih->biCompression = 0;
Packit 0ba690
    wimage_data = dib + sizeof(BITMAPINFOHEADER);
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Fill in background color (black by default); data are in BGR order.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    for (j = 0;  j < image_height;  ++j) {
Packit 0ba690
        dest = wimage_data + j*wimage_rowbytes;
Packit 0ba690
        for (i = image_width;  i > 0;  --i) {
Packit 0ba690
            *dest++ = bg_blue;
Packit 0ba690
            *dest++ = bg_green;
Packit 0ba690
            *dest++ = bg_red;
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Set the window parameters.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    memset(&wndclass, 0, sizeof(wndclass));
Packit 0ba690
Packit 0ba690
    wndclass.cbSize = sizeof(wndclass);
Packit 0ba690
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
Packit 0ba690
    wndclass.lpfnWndProc = rpng_win_wndproc;
Packit 0ba690
    wndclass.hInstance = hInst;
Packit 0ba690
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
Packit 0ba690
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
Packit 0ba690
    wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
Packit 0ba690
    wndclass.lpszMenuName = NULL;
Packit 0ba690
    wndclass.lpszClassName = progname;
Packit 0ba690
    wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
Packit 0ba690
Packit 0ba690
    RegisterClassEx(&wndclass);
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Finally, create the window.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    extra_width  = 2*(GetSystemMetrics(SM_CXBORDER) +
Packit 0ba690
                      GetSystemMetrics(SM_CXDLGFRAME));
Packit 0ba690
    extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
Packit 0ba690
                      GetSystemMetrics(SM_CYDLGFRAME)) +
Packit 0ba690
                      GetSystemMetrics(SM_CYCAPTION);
Packit 0ba690
Packit 0ba690
    global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW,
Packit 0ba690
      CW_USEDEFAULT, CW_USEDEFAULT, image_width+extra_width,
Packit 0ba690
      image_height+extra_height, NULL, NULL, hInst, NULL);
Packit 0ba690
Packit 0ba690
    ShowWindow(global_hwnd, showmode);
Packit 0ba690
    UpdateWindow(global_hwnd);
Packit 0ba690
Packit 0ba690
    return 0;
Packit 0ba690
Packit 0ba690
} /* end function rpng_win_create_window() */
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static int rpng_win_display_image()
Packit 0ba690
{
Packit 0ba690
    uch *src, *dest;
Packit 0ba690
    uch r, g, b, a;
Packit 0ba690
    ulg i, row, lastrow;
Packit 0ba690
    RECT rect;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    Trace((stderr, "beginning display loop (image_channels == %d)\n",
Packit 0ba690
      image_channels))
Packit 0ba690
    Trace((stderr, "(width = %ld, rowbytes = %ld, wimage_rowbytes = %d)\n",
Packit 0ba690
      image_width, image_rowbytes, wimage_rowbytes))
Packit 0ba690
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Blast image data to buffer.  This whole routine takes place before the
Packit 0ba690
    message loop begins, so there's no real point in any pseudo-progressive
Packit 0ba690
    display...
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    for (lastrow = row = 0;  row < image_height;  ++row) {
Packit 0ba690
        src = image_data + row*image_rowbytes;
Packit 0ba690
        dest = wimage_data + row*wimage_rowbytes;
Packit 0ba690
        if (image_channels == 3) {
Packit 0ba690
            for (i = image_width;  i > 0;  --i) {
Packit 0ba690
                r = *src++;
Packit 0ba690
                g = *src++;
Packit 0ba690
                b = *src++;
Packit 0ba690
                *dest++ = b;
Packit 0ba690
                *dest++ = g;   /* note reverse order */
Packit 0ba690
                *dest++ = r;
Packit 0ba690
            }
Packit 0ba690
        } else /* if (image_channels == 4) */ {
Packit 0ba690
            for (i = image_width;  i > 0;  --i) {
Packit 0ba690
                r = *src++;
Packit 0ba690
                g = *src++;
Packit 0ba690
                b = *src++;
Packit 0ba690
                a = *src++;
Packit 0ba690
                if (a == 255) {
Packit 0ba690
                    *dest++ = b;
Packit 0ba690
                    *dest++ = g;
Packit 0ba690
                    *dest++ = r;
Packit 0ba690
                } else if (a == 0) {
Packit 0ba690
                    *dest++ = bg_blue;
Packit 0ba690
                    *dest++ = bg_green;
Packit 0ba690
                    *dest++ = bg_red;
Packit 0ba690
                } else {
Packit 0ba690
                    /* this macro (copied from png.h) composites the
Packit 0ba690
                     * foreground and background values and puts the
Packit 0ba690
                     * result into the first argument; there are no
Packit 0ba690
                     * side effects with the first argument */
Packit 0ba690
                    alpha_composite(*dest++, b, a, bg_blue);
Packit 0ba690
                    alpha_composite(*dest++, g, a, bg_green);
Packit 0ba690
                    alpha_composite(*dest++, r, a, bg_red);
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
        /* display after every 16 lines */
Packit 0ba690
        if (((row+1) & 0xf) == 0) {
Packit 0ba690
            rect.left = 0L;
Packit 0ba690
            rect.top = (LONG)lastrow;
Packit 0ba690
            rect.right = (LONG)image_width;      /* possibly off by one? */
Packit 0ba690
            rect.bottom = (LONG)lastrow + 16L;   /* possibly off by one? */
Packit 0ba690
            InvalidateRect(global_hwnd, &rect, FALSE);
Packit 0ba690
            UpdateWindow(global_hwnd);     /* similar to XFlush() */
Packit 0ba690
            lastrow = row + 1;
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    Trace((stderr, "calling final image-flush routine\n"))
Packit 0ba690
    if (lastrow < image_height) {
Packit 0ba690
        rect.left = 0L;
Packit 0ba690
        rect.top = (LONG)lastrow;
Packit 0ba690
        rect.right = (LONG)image_width;      /* possibly off by one? */
Packit 0ba690
        rect.bottom = (LONG)image_height;    /* possibly off by one? */
Packit 0ba690
        InvalidateRect(global_hwnd, &rect, FALSE);
Packit 0ba690
        UpdateWindow(global_hwnd);     /* similar to XFlush() */
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
/*
Packit 0ba690
    last param determines whether or not background is wiped before paint
Packit 0ba690
    InvalidateRect(global_hwnd, NULL, TRUE);
Packit 0ba690
    UpdateWindow(global_hwnd);
Packit 0ba690
 */
Packit 0ba690
Packit 0ba690
    return 0;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static void rpng_win_cleanup()
Packit 0ba690
{
Packit 0ba690
    if (image_data) {
Packit 0ba690
        free(image_data);
Packit 0ba690
        image_data = NULL;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (dib) {
Packit 0ba690
        free(dib);
Packit 0ba690
        dib = NULL;
Packit 0ba690
    }
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
LRESULT CALLBACK rpng_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
Packit 0ba690
{
Packit 0ba690
    HDC         hdc;
Packit 0ba690
    PAINTSTRUCT ps;
Packit 0ba690
    int rc;
Packit 0ba690
Packit 0ba690
    switch (iMsg) {
Packit 0ba690
        case WM_CREATE:
Packit 0ba690
            /* one-time processing here, if any */
Packit 0ba690
            return 0;
Packit 0ba690
Packit 0ba690
        case WM_PAINT:
Packit 0ba690
            hdc = BeginPaint(hwnd, &ps);
Packit 0ba690
                    /*                    dest                          */
Packit 0ba690
            rc = StretchDIBits(hdc, 0, 0, image_width, image_height,
Packit 0ba690
                    /*                    source                        */
Packit 0ba690
                                    0, 0, image_width, image_height,
Packit 0ba690
                                    wimage_data, (BITMAPINFO *)bmih,
Packit 0ba690
                    /*              iUsage: no clue                     */
Packit 0ba690
                                    0, SRCCOPY);
Packit 0ba690
            EndPaint(hwnd, &ps);
Packit 0ba690
            return 0;
Packit 0ba690
Packit 0ba690
        /* wait for the user to tell us when to quit */
Packit 0ba690
        case WM_CHAR:
Packit 0ba690
            switch (wP) {      /* only need one, so ignore repeat count */
Packit 0ba690
                case 'q':
Packit 0ba690
                case 'Q':
Packit 0ba690
                case 0x1B:     /* Esc key */
Packit 0ba690
                    PostQuitMessage(0);
Packit 0ba690
            }
Packit 0ba690
            return 0;
Packit 0ba690
Packit 0ba690
        case WM_LBUTTONDOWN:   /* another way of quitting */
Packit 0ba690
        case WM_DESTROY:
Packit 0ba690
            PostQuitMessage(0);
Packit 0ba690
            return 0;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    return DefWindowProc(hwnd, iMsg, wP, lP);
Packit 0ba690
}