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