all repos — mgba @ 6882339f625b97f344c6464915c2038bc453ea3c

mGBA Game Boy Advance Emulator

src/third-party/libpng/contrib/gregbook/rpng2-win.c (view raw)

   1/*---------------------------------------------------------------------------
   2
   3   rpng2 - progressive-model PNG display program                rpng2-win.c
   4
   5   This program decodes and displays PNG files progressively, as if it were
   6   a web browser (though the front end is only set up to read from files).
   7   It supports gamma correction, user-specified background colors, and user-
   8   specified background patterns (for transparent images).  This version is
   9   for 32-bit Windows; it may compile under 16-bit Windows with a little
  10   tweaking (or maybe not).  Thanks to Adam Costello and Pieter S. van der
  11   Meulen for the "diamond" and "radial waves" patterns, respectively.
  12
  13   to do (someday, maybe):
  14    - handle quoted command-line args (especially filenames with spaces)
  15    - finish resizable checkerboard-gradient (sizes 4-128?)
  16    - use %.1023s to simplify truncation of title-bar string?
  17    - have minimum window width:  oh well
  18
  19  ---------------------------------------------------------------------------
  20
  21   Changelog:
  22    - 1.01:  initial public release
  23    - 1.02:  fixed cut-and-paste error in usage screen (oops...)
  24    - 1.03:  modified to allow abbreviated options
  25    - 1.04:  removed bogus extra argument from usage fprintf() [Glenn R-P?];
  26              fixed command-line parsing bug
  27    - 1.10:  enabled "message window"/console (thanks to David Geldreich)
  28    - 1.20:  added runtime MMX-enabling/disabling and new -mmx* options
  29    - 1.21:  made minor tweak to usage screen to fit within 25-line console
  30    - 1.22:  added AMD64/EM64T support (__x86_64__)
  31    - 2.00:  dual-licensed (added GNU GPL)
  32    - 2.01:  fixed 64-bit typo in readpng2.c
  33    - 2.02:  fixed improper display of usage screen on PNG error(s); fixed
  34              unexpected-EOF and file-read-error cases
  35    - 2.03:  removed runtime MMX-enabling/disabling and obsolete -mmx* options
  36    - 2.04:  
  37             (GR-P)
  38
  39  ---------------------------------------------------------------------------
  40
  41      Copyright (c) 1998-2008 Greg Roelofs.  All rights reserved.
  42
  43      This software is provided "as is," without warranty of any kind,
  44      express or implied.  In no event shall the author or contributors
  45      be held liable for any damages arising in any way from the use of
  46      this software.
  47
  48      The contents of this file are DUAL-LICENSED.  You may modify and/or
  49      redistribute this software according to the terms of one of the
  50      following two licenses (at your option):
  51
  52
  53      LICENSE 1 ("BSD-like with advertising clause"):
  54
  55      Permission is granted to anyone to use this software for any purpose,
  56      including commercial applications, and to alter it and redistribute
  57      it freely, subject to the following restrictions:
  58
  59      1. Redistributions of source code must retain the above copyright
  60         notice, disclaimer, and this list of conditions.
  61      2. Redistributions in binary form must reproduce the above copyright
  62         notice, disclaimer, and this list of conditions in the documenta-
  63         tion and/or other materials provided with the distribution.
  64      3. All advertising materials mentioning features or use of this
  65         software must display the following acknowledgment:
  66
  67            This product includes software developed by Greg Roelofs
  68            and contributors for the book, "PNG: The Definitive Guide,"
  69            published by O'Reilly and Associates.
  70
  71
  72      LICENSE 2 (GNU GPL v2 or later):
  73
  74      This program is free software; you can redistribute it and/or modify
  75      it under the terms of the GNU General Public License as published by
  76      the Free Software Foundation; either version 2 of the License, or
  77      (at your option) any later version.
  78
  79      This program is distributed in the hope that it will be useful,
  80      but WITHOUT ANY WARRANTY; without even the implied warranty of
  81      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  82      GNU General Public License for more details.
  83
  84      You should have received a copy of the GNU General Public License
  85      along with this program; if not, write to the Free Software Foundation,
  86      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  87
  88  ---------------------------------------------------------------------------*/
  89
  90#define PROGNAME  "rpng2-win"
  91#define LONGNAME  "Progressive PNG Viewer for Windows"
  92#define VERSION   "2.02 of 16 March 2008"
  93
  94#include <stdio.h>
  95#include <stdlib.h>
  96#include <string.h>
  97#include <setjmp.h>    /* for jmpbuf declaration in readpng2.h */
  98#include <time.h>
  99#include <math.h>      /* only for PvdM background code */
 100#include <windows.h>
 101#ifdef __CYGWIN__
 102/* getch replacement. Turns out, we don't really need this,
 103 * but leave it here if we ever enable any of the uses of
 104 * _getch in the main code
 105 */
 106#include <unistd.h>
 107#include <termio.h>
 108#include <sys/ioctl.h>
 109int repl_getch( void )
 110{
 111  char ch;
 112  int fd = fileno(stdin);
 113  struct termio old_tty, new_tty;
 114
 115  ioctl(fd, TCGETA, &old_tty);
 116  new_tty = old_tty;
 117  new_tty.c_lflag &= ~(ICANON | ECHO | ISIG);
 118  ioctl(fd, TCSETA, &new_tty);
 119  fread(&ch, 1, sizeof(ch), stdin);
 120  ioctl(fd, TCSETA, &old_tty);
 121
 122  return ch;
 123}
 124#define _getch repl_getch
 125#else
 126#include <conio.h>     /* only for _getch() */
 127#endif
 128
 129/* all for PvdM background code: */
 130#ifndef PI
 131#  define PI             3.141592653589793238
 132#endif
 133#define PI_2             (PI*0.5)
 134#define INV_PI_360       (360.0 / PI)
 135#define MAX(a,b)         (a>b?a:b)
 136#define MIN(a,b)         (a<b?a:b)
 137#define CLIP(a,min,max)  MAX(min,MIN((a),max))
 138#define ABS(a)           ((a)<0?-(a):(a))
 139#define CLIP8P(c)        MAX(0,(MIN((c),255)))   /* 8-bit pos. integer (uch) */
 140#define ROUNDF(f)        ((int)(f + 0.5))
 141
 142#define rgb1_max   bg_freq
 143#define rgb1_min   bg_gray
 144#define rgb2_max   bg_bsat
 145#define rgb2_min   bg_brot
 146
 147/* #define DEBUG */     /* this enables the Trace() macros */
 148
 149#include "readpng2.h"   /* typedefs, common macros, readpng2 prototypes */
 150
 151
 152/* could just include png.h, but this macro is the only thing we need
 153 * (name and typedefs changed to local versions); note that side effects
 154 * only happen with alpha (which could easily be avoided with
 155 * "ush acopy = (alpha);") */
 156
 157#define alpha_composite(composite, fg, alpha, bg) {               \
 158    ush temp = ((ush)(fg)*(ush)(alpha) +                          \
 159                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
 160    (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
 161}
 162
 163
 164#define INBUFSIZE 4096   /* with pseudo-timing on (1 sec delay/block), this
 165                          *  block size corresponds roughly to a download
 166                          *  speed 10% faster than theoretical 33.6K maximum
 167                          *  (assuming 8 data bits, 1 stop bit and no other
 168                          *  overhead) */
 169
 170/* local prototypes */
 171static void       rpng2_win_init(void);
 172static int        rpng2_win_create_window(void);
 173static int        rpng2_win_load_bg_image(void);
 174static void       rpng2_win_display_row(ulg row);
 175static void       rpng2_win_finish_display(void);
 176static void       rpng2_win_cleanup(void);
 177LRESULT CALLBACK  rpng2_win_wndproc(HWND, UINT, WPARAM, LPARAM);
 178
 179
 180static char titlebar[1024];
 181static char *progname = PROGNAME;
 182static char *appname = LONGNAME;
 183static char *filename;
 184static FILE *infile;
 185
 186static mainprog_info rpng2_info;
 187
 188static uch inbuf[INBUFSIZE];
 189static int incount;
 190
 191static int pat = 6;         /* must be less than num_bgpat */
 192static int bg_image = 0;
 193static int bgscale = 16;
 194static ulg bg_rowbytes;
 195static uch *bg_data;
 196
 197static struct rgb_color {
 198    uch r, g, b;
 199} rgb[] = {
 200    {  0,   0,   0},    /*  0:  black */
 201    {255, 255, 255},    /*  1:  white */
 202    {173, 132,  57},    /*  2:  tan */
 203    { 64, 132,   0},    /*  3:  medium green */
 204    {189, 117,   1},    /*  4:  gold */
 205    {253, 249,   1},    /*  5:  yellow */
 206    {  0,   0, 255},    /*  6:  blue */
 207    {  0,   0, 120},    /*  7:  medium blue */
 208    {255,   0, 255},    /*  8:  magenta */
 209    { 64,   0,  64},    /*  9:  dark magenta */
 210    {255,   0,   0},    /* 10:  red */
 211    { 64,   0,   0},    /* 11:  dark red */
 212    {255, 127,   0},    /* 12:  orange */
 213    {192,  96,   0},    /* 13:  darker orange */
 214    { 24,  60,   0},    /* 14:  dark green-yellow */
 215    { 85, 125, 200}     /* 15:  ice blue */
 216};
 217/* not used for now, but should be for error-checking:
 218static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
 219 */
 220
 221/*
 222    This whole struct is a fairly cheesy way to keep the number of
 223    command-line options to a minimum.  The radial-waves background
 224    type is a particularly poor fit to the integer elements of the
 225    struct...but a few macros and a little fixed-point math will do
 226    wonders for ya.
 227
 228    type bits:
 229       F E D C B A 9 8 7 6 5 4 3 2 1 0
 230                             | | | | |
 231                             | | +-+-+-- 0 = sharp-edged checkerboard
 232                             | |         1 = soft diamonds
 233                             | |         2 = radial waves
 234                             | |       3-7 = undefined
 235                             | +-- gradient #2 inverted?
 236                             +-- alternating columns inverted?
 237 */
 238static struct background_pattern {
 239    ush type;
 240    int rgb1_max, rgb1_min;     /* or bg_freq, bg_gray */
 241    int rgb2_max, rgb2_min;     /* or bg_bsat, bg_brot (both scaled by 10)*/
 242} bg[] = {
 243    {0+8,   2,0,  1,15},        /* checkered:  tan/black vs. white/ice blue */
 244    {0+24,  2,0,  1,0},         /* checkered:  tan/black vs. white/black */
 245    {0+8,   4,5,  0,2},         /* checkered:  gold/yellow vs. black/tan */
 246    {0+8,   4,5,  0,6},         /* checkered:  gold/yellow vs. black/blue */
 247    {0,     7,0,  8,9},         /* checkered:  deep blue/black vs. magenta */
 248    {0+8,  13,0,  5,14},        /* checkered:  orange/black vs. yellow */
 249    {0+8,  12,0, 10,11},        /* checkered:  orange/black vs. red */
 250    {1,     7,0,  8,0},         /* diamonds:  deep blue/black vs. magenta */
 251    {1,    12,0, 11,0},         /* diamonds:  orange vs. dark red */
 252    {1,    10,0,  7,0},         /* diamonds:  red vs. medium blue */
 253    {1,     4,0,  5,0},         /* diamonds:  gold vs. yellow */
 254    {1,     3,0,  0,0},         /* diamonds:  medium green vs. black */
 255    {2,    16, 100,  20,   0},  /* radial:  ~hard radial color-beams */
 256    {2,    18, 100,  10,   2},  /* radial:  soft, curved radial color-beams */
 257    {2,    16, 256, 100, 250},  /* radial:  very tight spiral */
 258    {2, 10000, 256,  11,   0}   /* radial:  dipole-moire' (almost fractal) */
 259};
 260static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
 261
 262
 263/* Windows-specific global variables (could go in struct, but messy...) */
 264static ulg wimage_rowbytes;
 265static uch *dib;
 266static uch *wimage_data;
 267static BITMAPINFOHEADER *bmih;
 268
 269static HWND global_hwnd;
 270static HINSTANCE global_hInst;
 271static int global_showmode;
 272
 273
 274
 275
 276int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
 277{
 278    char *args[1024];                 /* arbitrary limit, but should suffice */
 279    char **argv = args;
 280    char *p, *q, *bgstr = NULL;
 281    int argc = 0;
 282    int rc, alen, flen;
 283    int error = 0;
 284    int timing = FALSE;
 285    int have_bg = FALSE;
 286    double LUT_exponent;              /* just the lookup table */
 287    double CRT_exponent = 2.2;        /* just the monitor */
 288    double default_display_exponent;  /* whole display system */
 289    MSG msg;
 290
 291
 292    /* First initialize a few things, just to be sure--memset takes care of
 293     * default background color (black), booleans (FALSE), pointers (NULL),
 294     * etc. */
 295
 296    global_hInst = hInst;
 297    global_showmode = showmode;
 298    filename = (char *)NULL;
 299    memset(&rpng2_info, 0, sizeof(mainprog_info));
 300
 301#ifndef __CYGWIN__
 302    /* Next reenable console output, which normally goes to the bit bucket
 303     * for windowed apps.  Closing the console window will terminate the
 304     * app.  Thanks to David.Geldreich@realviz.com for supplying the magical
 305     * incantation. */
 306
 307    AllocConsole();
 308    freopen("CONOUT$", "a", stderr);
 309    freopen("CONOUT$", "a", stdout);
 310#endif
 311
 312    /* Set the default value for our display-system exponent, i.e., the
 313     * product of the CRT exponent and the exponent corresponding to
 314     * the frame-buffer's lookup table (LUT), if any.  This is not an
 315     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
 316     * ones), but it should cover 99% of the current possibilities.  And
 317     * yes, these ifdefs are completely wasted in a Windows program... */
 318
 319#if defined(NeXT)
 320    /* third-party utilities can modify the default LUT exponent */
 321    LUT_exponent = 1.0 / 2.2;
 322    /*
 323    if (some_next_function_that_returns_gamma(&next_gamma))
 324        LUT_exponent = 1.0 / next_gamma;
 325     */
 326#elif defined(sgi)
 327    LUT_exponent = 1.0 / 1.7;
 328    /* there doesn't seem to be any documented function to
 329     * get the "gamma" value, so we do it the hard way */
 330    infile = fopen("/etc/config/system.glGammaVal", "r");
 331    if (infile) {
 332        double sgi_gamma;
 333
 334        fgets(tmpline, 80, infile);
 335        fclose(infile);
 336        sgi_gamma = atof(tmpline);
 337        if (sgi_gamma > 0.0)
 338            LUT_exponent = 1.0 / sgi_gamma;
 339    }
 340#elif defined(Macintosh)
 341    LUT_exponent = 1.8 / 2.61;
 342    /*
 343    if (some_mac_function_that_returns_gamma(&mac_gamma))
 344        LUT_exponent = mac_gamma / 2.61;
 345     */
 346#else
 347    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
 348#endif
 349
 350    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
 351    default_display_exponent = LUT_exponent * CRT_exponent;
 352
 353
 354    /* If the user has set the SCREEN_GAMMA environment variable as suggested
 355     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
 356     * use the default value we just calculated.  Either way, the user may
 357     * override this via a command-line option. */
 358
 359    if ((p = getenv("SCREEN_GAMMA")) != NULL)
 360        rpng2_info.display_exponent = atof(p);
 361    else
 362        rpng2_info.display_exponent = default_display_exponent;
 363
 364
 365    /* Windows really hates command lines, so we have to set up our own argv.
 366     * Note that we do NOT bother with quoted arguments here, so don't use
 367     * filenames with spaces in 'em! */
 368
 369    argv[argc++] = PROGNAME;
 370    p = cmd;
 371    for (;;) {
 372        if (*p == ' ')
 373            while (*++p == ' ')
 374                ;
 375        /* now p points at the first non-space after some spaces */
 376        if (*p == '\0')
 377            break;    /* nothing after the spaces:  done */
 378        argv[argc++] = q = p;
 379        while (*q && *q != ' ')
 380            ++q;
 381        /* now q points at a space or the end of the string */
 382        if (*q == '\0')
 383            break;    /* last argv already terminated; quit */
 384        *q = '\0';    /* change space to terminator */
 385        p = q + 1;
 386    }
 387    argv[argc] = NULL;   /* terminate the argv array itself */
 388
 389
 390    /* Now parse the command line for options and the PNG filename. */
 391
 392    while (*++argv && !error) {
 393        if (!strncmp(*argv, "-gamma", 2)) {
 394            if (!*++argv)
 395                ++error;
 396            else {
 397                rpng2_info.display_exponent = atof(*argv);
 398                if (rpng2_info.display_exponent <= 0.0)
 399                    ++error;
 400            }
 401        } else if (!strncmp(*argv, "-bgcolor", 4)) {
 402            if (!*++argv)
 403                ++error;
 404            else {
 405                bgstr = *argv;
 406                if (strlen(bgstr) != 7 || bgstr[0] != '#')
 407                    ++error;
 408                else {
 409                    have_bg = TRUE;
 410                    bg_image = FALSE;
 411                }
 412            }
 413        } else if (!strncmp(*argv, "-bgpat", 4)) {
 414            if (!*++argv)
 415                ++error;
 416            else {
 417                pat = atoi(*argv) - 1;
 418                if (pat < 0 || pat >= num_bgpat)
 419                    ++error;
 420                else {
 421                    bg_image = TRUE;
 422                    have_bg = FALSE;
 423                }
 424            }
 425        } else if (!strncmp(*argv, "-timing", 2)) {
 426            timing = TRUE;
 427        } else {
 428            if (**argv != '-') {
 429                filename = *argv;
 430                if (argv[1])   /* shouldn't be any more args after filename */
 431                    ++error;
 432            } else
 433                ++error;   /* not expecting any other options */
 434        }
 435    }
 436
 437    if (!filename)
 438        ++error;
 439
 440
 441    /* print usage screen if any errors up to this point */
 442
 443    if (error) {
 444#ifndef __CYGWIN__
 445        int ch;
 446#endif
 447
 448        fprintf(stderr, "\n%s %s:  %s\n\n", PROGNAME, VERSION, appname);
 449        readpng2_version_info();
 450        fprintf(stderr, "\n"
 451          "Usage:  %s [-gamma exp] [-bgcolor bg | -bgpat pat] [-timing]\n"
 452          "        %*s file.png\n\n"
 453          "    exp \ttransfer-function exponent (``gamma'') of the display\n"
 454          "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
 455          "\t\t  to the product of the lookup-table exponent (varies)\n"
 456          "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
 457          "    bg  \tdesired background color in 7-character hex RGB format\n"
 458          "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
 459          "\t\t  used with transparent images; overrides -bgpat option\n"
 460          "    pat \tdesired background pattern number (1-%d); used with\n"
 461          "\t\t  transparent images; overrides -bgcolor option\n"
 462          "    -timing\tenables delay for every block read, to simulate modem\n"
 463          "\t\t  download of image (~36 Kbps)\n"
 464          "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
 465#ifndef __CYGWIN__
 466          "Press Q or Esc to quit this usage screen. ",
 467#else
 468          ,
 469#endif
 470          PROGNAME,
 471#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \
 472    !(defined(__CYGWIN__) || defined(__MINGW32__))
 473          (int)strlen(PROGNAME), " ",
 474#endif
 475          (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat);
 476        fflush(stderr);
 477#ifndef __CYGWIN__
 478        do
 479            ch = _getch();
 480        while (ch != 'q' && ch != 'Q' && ch != 0x1B);
 481#endif
 482        exit(1);
 483    }
 484
 485
 486    if (!(infile = fopen(filename, "rb"))) {
 487        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
 488        ++error;
 489    } else {
 490        incount = fread(inbuf, 1, INBUFSIZE, infile);
 491        if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
 492            fprintf(stderr, PROGNAME
 493              ":  [%s] is not a PNG file: incorrect signature\n",
 494              filename);
 495            ++error;
 496        } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
 497            switch (rc) {
 498                case 2:
 499                    fprintf(stderr, PROGNAME
 500                      ":  [%s] has bad IHDR (libpng longjmp)\n", filename);
 501                    break;
 502                case 4:
 503                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
 504                    break;
 505                default:
 506                    fprintf(stderr, PROGNAME
 507                      ":  unknown readpng2_init() error\n");
 508                    break;
 509            }
 510            ++error;
 511        }
 512        if (error)
 513            fclose(infile);
 514    }
 515
 516
 517    if (error) {
 518#ifndef __CYGWIN__
 519        int ch;
 520#endif
 521
 522        fprintf(stderr, PROGNAME ":  aborting.\n");
 523#ifndef __CYGWIN__
 524        do
 525            ch = _getch();
 526        while (ch != 'q' && ch != 'Q' && ch != 0x1B);
 527#endif
 528        exit(2);
 529    } else {
 530        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
 531#ifndef __CYGWIN__
 532        fprintf(stderr,
 533          "\n   [console window:  closing this window will terminate %s]\n\n",
 534          PROGNAME);
 535#endif
 536        fflush(stderr);
 537    }
 538
 539
 540    /* set the title-bar string, but make sure buffer doesn't overflow */
 541
 542    alen = strlen(appname);
 543    flen = strlen(filename);
 544    if (alen + flen + 3 > 1023)
 545        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
 546    else
 547        sprintf(titlebar, "%s:  %s", appname, filename);
 548
 549
 550    /* set some final rpng2_info variables before entering main data loop */
 551
 552    if (have_bg) {
 553        unsigned r, g, b;   /* this approach quiets compiler warnings */
 554
 555        sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
 556        rpng2_info.bg_red   = (uch)r;
 557        rpng2_info.bg_green = (uch)g;
 558        rpng2_info.bg_blue  = (uch)b;
 559    } else
 560        rpng2_info.need_bgcolor = TRUE;
 561
 562    rpng2_info.state = kPreInit;
 563    rpng2_info.mainprog_init = rpng2_win_init;
 564    rpng2_info.mainprog_display_row = rpng2_win_display_row;
 565    rpng2_info.mainprog_finish_display = rpng2_win_finish_display;
 566
 567
 568    /* OK, this is the fun part:  call readpng2_decode_data() at the start of
 569     * the loop to deal with our first buffer of data (read in above to verify
 570     * that the file is a PNG image), then loop through the file and continue
 571     * calling the same routine to handle each chunk of data.  It in turn
 572     * passes the data to libpng, which will invoke one or more of our call-
 573     * backs as decoded data become available.  We optionally call Sleep() for
 574     * one second per iteration to simulate downloading the image via an analog
 575     * modem. */
 576
 577    for (;;) {
 578        Trace((stderr, "about to call readpng2_decode_data()\n"))
 579        if (readpng2_decode_data(&rpng2_info, inbuf, incount))
 580            ++error;
 581        Trace((stderr, "done with readpng2_decode_data()\n"))
 582
 583        if (error || incount != INBUFSIZE || rpng2_info.state == kDone) {
 584            if (rpng2_info.state == kDone) {
 585                Trace((stderr, "done decoding PNG image\n"))
 586            } else if (ferror(infile)) {
 587                fprintf(stderr, PROGNAME
 588                  ":  error while reading PNG image file\n");
 589                exit(3);
 590            } else if (feof(infile)) {
 591                fprintf(stderr, PROGNAME ":  end of file reached "
 592                  "(unexpectedly) while reading PNG image file\n");
 593                exit(3);
 594            } else /* if (error) */ {
 595                /* will print error message below */
 596            }
 597            break;
 598        }
 599
 600        if (timing)
 601            Sleep(1000L);
 602
 603        incount = fread(inbuf, 1, INBUFSIZE, infile);
 604    }
 605
 606
 607    /* clean up PNG stuff and report any decoding errors */
 608
 609    fclose(infile);
 610    Trace((stderr, "about to call readpng2_cleanup()\n"))
 611    readpng2_cleanup(&rpng2_info);
 612
 613    if (error) {
 614        fprintf(stderr, PROGNAME ":  libpng error while decoding PNG image\n");
 615        exit(3);
 616    }
 617
 618
 619    /* wait for the user to tell us when to quit */
 620
 621    while (GetMessage(&msg, NULL, 0, 0)) {
 622        TranslateMessage(&msg);
 623        DispatchMessage(&msg);
 624    }
 625
 626
 627    /* we're done:  clean up all image and Windows resources and go away */
 628
 629    Trace((stderr, "about to call rpng2_win_cleanup()\n"))
 630    rpng2_win_cleanup();
 631
 632    return msg.wParam;
 633}
 634
 635
 636
 637
 638
 639/* this function is called by readpng2_info_callback() in readpng2.c, which
 640 * in turn is called by libpng after all of the pre-IDAT chunks have been
 641 * read and processed--i.e., we now have enough info to finish initializing */
 642
 643static void rpng2_win_init()
 644{
 645    ulg i;
 646    ulg rowbytes = rpng2_info.rowbytes;
 647
 648    Trace((stderr, "beginning rpng2_win_init()\n"))
 649    Trace((stderr, "  rowbytes = %d\n", rpng2_info.rowbytes))
 650    Trace((stderr, "  width  = %ld\n", rpng2_info.width))
 651    Trace((stderr, "  height = %ld\n", rpng2_info.height))
 652
 653    rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
 654    if (!rpng2_info.image_data) {
 655        readpng2_cleanup(&rpng2_info);
 656        return;
 657    }
 658
 659    rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
 660    if (!rpng2_info.row_pointers) {
 661        free(rpng2_info.image_data);
 662        rpng2_info.image_data = NULL;
 663        readpng2_cleanup(&rpng2_info);
 664        return;
 665    }
 666
 667    for (i = 0;  i < rpng2_info.height;  ++i)
 668        rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
 669
 670/*---------------------------------------------------------------------------
 671    Do the basic Windows initialization stuff, make the window, and fill it
 672    with the user-specified, file-specified or default background color.
 673  ---------------------------------------------------------------------------*/
 674
 675    if (rpng2_win_create_window()) {
 676        readpng2_cleanup(&rpng2_info);
 677        return;
 678    }
 679
 680    rpng2_info.state = kWindowInit;
 681}
 682
 683
 684
 685
 686
 687static int rpng2_win_create_window()
 688{
 689    uch bg_red   = rpng2_info.bg_red;
 690    uch bg_green = rpng2_info.bg_green;
 691    uch bg_blue  = rpng2_info.bg_blue;
 692    uch *dest;
 693    int extra_width, extra_height;
 694    ulg i, j;
 695    WNDCLASSEX wndclass;
 696    RECT rect;
 697
 698
 699/*---------------------------------------------------------------------------
 700    Allocate memory for the display-specific version of the image (round up
 701    to multiple of 4 for Windows DIB).
 702  ---------------------------------------------------------------------------*/
 703
 704    wimage_rowbytes = ((3*rpng2_info.width + 3L) >> 2) << 2;
 705
 706    if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
 707                              wimage_rowbytes*rpng2_info.height)))
 708    {
 709        return 4;   /* fail */
 710    }
 711
 712/*---------------------------------------------------------------------------
 713    Initialize the DIB.  Negative height means to use top-down BMP ordering
 714    (must be uncompressed, but that's what we want).  Bit count of 1, 4 or 8
 715    implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values
 716    directly => wimage_data begins immediately after BMP header.
 717  ---------------------------------------------------------------------------*/
 718
 719    memset(dib, 0, sizeof(BITMAPINFOHEADER));
 720    bmih = (BITMAPINFOHEADER *)dib;
 721    bmih->biSize = sizeof(BITMAPINFOHEADER);
 722    bmih->biWidth = rpng2_info.width;
 723    bmih->biHeight = -((long)rpng2_info.height);
 724    bmih->biPlanes = 1;
 725    bmih->biBitCount = 24;
 726    bmih->biCompression = 0;
 727    wimage_data = dib + sizeof(BITMAPINFOHEADER);
 728
 729/*---------------------------------------------------------------------------
 730    Fill window with the specified background color (default is black), but
 731    defer loading faked "background image" until window is displayed (may be
 732    slow to compute).  Data are in BGR order.
 733  ---------------------------------------------------------------------------*/
 734
 735    if (bg_image) {   /* just fill with black for now */
 736        memset(wimage_data, 0, wimage_rowbytes*rpng2_info.height);
 737    } else {
 738        for (j = 0;  j < rpng2_info.height;  ++j) {
 739            dest = wimage_data + j*wimage_rowbytes;
 740            for (i = rpng2_info.width;  i > 0;  --i) {
 741                *dest++ = bg_blue;
 742                *dest++ = bg_green;
 743                *dest++ = bg_red;
 744            }
 745        }
 746    }
 747
 748/*---------------------------------------------------------------------------
 749    Set the window parameters.
 750  ---------------------------------------------------------------------------*/
 751
 752    memset(&wndclass, 0, sizeof(wndclass));
 753
 754    wndclass.cbSize = sizeof(wndclass);
 755    wndclass.style = CS_HREDRAW | CS_VREDRAW;
 756    wndclass.lpfnWndProc = rpng2_win_wndproc;
 757    wndclass.hInstance = global_hInst;
 758    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
 759    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
 760    wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
 761    wndclass.lpszMenuName = NULL;
 762    wndclass.lpszClassName = progname;
 763    wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
 764
 765    RegisterClassEx(&wndclass);
 766
 767/*---------------------------------------------------------------------------
 768    Finally, create the window.
 769  ---------------------------------------------------------------------------*/
 770
 771    extra_width  = 2*(GetSystemMetrics(SM_CXBORDER) +
 772                      GetSystemMetrics(SM_CXDLGFRAME));
 773    extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
 774                      GetSystemMetrics(SM_CYDLGFRAME)) +
 775                      GetSystemMetrics(SM_CYCAPTION);
 776
 777    global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW,
 778      CW_USEDEFAULT, CW_USEDEFAULT, rpng2_info.width+extra_width,
 779      rpng2_info.height+extra_height, NULL, NULL, global_hInst, NULL);
 780
 781    ShowWindow(global_hwnd, global_showmode);
 782    UpdateWindow(global_hwnd);
 783
 784/*---------------------------------------------------------------------------
 785    Now compute the background image and display it.  If it fails (memory
 786    allocation), revert to a plain background color.
 787  ---------------------------------------------------------------------------*/
 788
 789    if (bg_image) {
 790        static const char *msg = "Computing background image...";
 791        int x, y, len = strlen(msg);
 792        HDC hdc = GetDC(global_hwnd);
 793        TEXTMETRIC tm;
 794
 795        GetTextMetrics(hdc, &tm);
 796        x = (rpng2_info.width - len*tm.tmAveCharWidth)/2;
 797        y = (rpng2_info.height - tm.tmHeight)/2;
 798        SetBkMode(hdc, TRANSPARENT);
 799        SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
 800        /* this can still begin out of bounds even if x is positive (???): */
 801        TextOut(hdc, ((x < 0)? 0 : x), ((y < 0)? 0 : y), msg, len);
 802        ReleaseDC(global_hwnd, hdc);
 803
 804        rpng2_win_load_bg_image();   /* resets bg_image if fails */
 805    }
 806
 807    if (!bg_image) {
 808        for (j = 0;  j < rpng2_info.height;  ++j) {
 809            dest = wimage_data + j*wimage_rowbytes;
 810            for (i = rpng2_info.width;  i > 0;  --i) {
 811                *dest++ = bg_blue;
 812                *dest++ = bg_green;
 813                *dest++ = bg_red;
 814            }
 815        }
 816    }
 817
 818    rect.left = 0L;
 819    rect.top = 0L;
 820    rect.right = (LONG)rpng2_info.width;       /* possibly off by one? */
 821    rect.bottom = (LONG)rpng2_info.height;     /* possibly off by one? */
 822    InvalidateRect(global_hwnd, &rect, FALSE);
 823    UpdateWindow(global_hwnd);                 /* similar to XFlush() */
 824
 825    return 0;
 826
 827} /* end function rpng2_win_create_window() */
 828
 829
 830
 831
 832
 833static int rpng2_win_load_bg_image()
 834{
 835    uch *src, *dest;
 836    uch r1, r2, g1, g2, b1, b2;
 837    uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
 838    int k, hmax, max;
 839    int xidx, yidx, yidx_max = (bgscale-1);
 840    int even_odd_vert, even_odd_horiz, even_odd;
 841    int invert_gradient2 = (bg[pat].type & 0x08);
 842    int invert_column;
 843    ulg i, row;
 844
 845/*---------------------------------------------------------------------------
 846    Allocate buffer for fake background image to be used with transparent
 847    images; if this fails, revert to plain background color.
 848  ---------------------------------------------------------------------------*/
 849
 850    bg_rowbytes = 3 * rpng2_info.width;
 851    bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
 852    if (!bg_data) {
 853        fprintf(stderr, PROGNAME
 854          ":  unable to allocate memory for background image\n");
 855        bg_image = 0;
 856        return 1;
 857    }
 858
 859/*---------------------------------------------------------------------------
 860    Vertical gradients (ramps) in NxN squares, alternating direction and
 861    colors (N == bgscale).
 862  ---------------------------------------------------------------------------*/
 863
 864    if ((bg[pat].type & 0x07) == 0) {
 865        uch r1_min  = rgb[bg[pat].rgb1_min].r;
 866        uch g1_min  = rgb[bg[pat].rgb1_min].g;
 867        uch b1_min  = rgb[bg[pat].rgb1_min].b;
 868        uch r2_min  = rgb[bg[pat].rgb2_min].r;
 869        uch g2_min  = rgb[bg[pat].rgb2_min].g;
 870        uch b2_min  = rgb[bg[pat].rgb2_min].b;
 871        int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
 872        int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
 873        int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
 874        int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
 875        int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
 876        int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
 877
 878        for (row = 0;  row < rpng2_info.height;  ++row) {
 879            yidx = row % bgscale;
 880            even_odd_vert = (row / bgscale) & 1;
 881
 882            r1 = r1_min + (r1_diff * yidx) / yidx_max;
 883            g1 = g1_min + (g1_diff * yidx) / yidx_max;
 884            b1 = b1_min + (b1_diff * yidx) / yidx_max;
 885            r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
 886            g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
 887            b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
 888
 889            r2 = r2_min + (r2_diff * yidx) / yidx_max;
 890            g2 = g2_min + (g2_diff * yidx) / yidx_max;
 891            b2 = b2_min + (b2_diff * yidx) / yidx_max;
 892            r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
 893            g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
 894            b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
 895
 896            dest = bg_data + row*bg_rowbytes;
 897            for (i = 0;  i < rpng2_info.width;  ++i) {
 898                even_odd_horiz = (i / bgscale) & 1;
 899                even_odd = even_odd_vert ^ even_odd_horiz;
 900                invert_column =
 901                  (even_odd_horiz && (bg[pat].type & 0x10));
 902                if (even_odd == 0) {         /* gradient #1 */
 903                    if (invert_column) {
 904                        *dest++ = r1_inv;
 905                        *dest++ = g1_inv;
 906                        *dest++ = b1_inv;
 907                    } else {
 908                        *dest++ = r1;
 909                        *dest++ = g1;
 910                        *dest++ = b1;
 911                    }
 912                } else {                     /* gradient #2 */
 913                    if ((invert_column && invert_gradient2) ||
 914                        (!invert_column && !invert_gradient2))
 915                    {
 916                        *dest++ = r2;        /* not inverted or */
 917                        *dest++ = g2;        /*  doubly inverted */
 918                        *dest++ = b2;
 919                    } else {
 920                        *dest++ = r2_inv;
 921                        *dest++ = g2_inv;    /* singly inverted */
 922                        *dest++ = b2_inv;
 923                    }
 924                }
 925            }
 926        }
 927
 928/*---------------------------------------------------------------------------
 929    Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
 930    M. Costello.
 931  ---------------------------------------------------------------------------*/
 932
 933    } else if ((bg[pat].type & 0x07) == 1) {
 934
 935        hmax = (bgscale-1)/2;   /* half the max weight of a color */
 936        max = 2*hmax;           /* the max weight of a color */
 937
 938        r1 = rgb[bg[pat].rgb1_max].r;
 939        g1 = rgb[bg[pat].rgb1_max].g;
 940        b1 = rgb[bg[pat].rgb1_max].b;
 941        r2 = rgb[bg[pat].rgb2_max].r;
 942        g2 = rgb[bg[pat].rgb2_max].g;
 943        b2 = rgb[bg[pat].rgb2_max].b;
 944
 945        for (row = 0;  row < rpng2_info.height;  ++row) {
 946            yidx = row % bgscale;
 947            if (yidx > hmax)
 948                yidx = bgscale-1 - yidx;
 949            dest = bg_data + row*bg_rowbytes;
 950            for (i = 0;  i < rpng2_info.width;  ++i) {
 951                xidx = i % bgscale;
 952                if (xidx > hmax)
 953                    xidx = bgscale-1 - xidx;
 954                k = xidx + yidx;
 955                *dest++ = (k*r1 + (max-k)*r2) / max;
 956                *dest++ = (k*g1 + (max-k)*g2) / max;
 957                *dest++ = (k*b1 + (max-k)*b2) / max;
 958            }
 959        }
 960
 961/*---------------------------------------------------------------------------
 962    Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
 963    soids will equal bgscale?].  This one is slow but very cool.  Code con-
 964    tributed by Pieter S. van der Meulen (originally in Smalltalk).
 965  ---------------------------------------------------------------------------*/
 966
 967    } else if ((bg[pat].type & 0x07) == 2) {
 968        uch ch;
 969        int ii, x, y, hw, hh, grayspot;
 970        double freq, rotate, saturate, gray, intensity;
 971        double angle=0.0, aoffset=0.0, maxDist, dist;
 972        double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
 973
 974        fprintf(stderr, "%s:  computing radial background...",
 975          PROGNAME);
 976        fflush(stderr);
 977
 978        hh = rpng2_info.height / 2;
 979        hw = rpng2_info.width / 2;
 980
 981        /* variables for radial waves:
 982         *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
 983         *   freq:  number of color beams originating from the center
 984         *   grayspot:  size of the graying center area (anti-alias)
 985         *   rotate:  rotation of the beams as a function of radius
 986         *   saturate:  saturation of beams' shape azimuthally
 987         */
 988        angle = CLIP(angle, 0.0, 360.0);
 989        grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
 990        freq = MAX((double)bg[pat].bg_freq, 0.0);
 991        saturate = (double)bg[pat].bg_bsat * 0.1;
 992        rotate = (double)bg[pat].bg_brot * 0.1;
 993        gray = 0.0;
 994        intensity = 0.0;
 995        maxDist = (double)((hw*hw) + (hh*hh));
 996
 997        for (row = 0;  row < rpng2_info.height;  ++row) {
 998            y = row - hh;
 999            dest = bg_data + row*bg_rowbytes;
1000            for (i = 0;  i < rpng2_info.width;  ++i) {
1001                x = i - hw;
1002                angle = (x == 0)? PI_2 : atan((double)y / (double)x);
1003                gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
1004                gray = MIN(1.0, gray);
1005                dist = (double)((x*x) + (y*y)) / maxDist;
1006                intensity = cos((angle+(rotate*dist*PI)) * freq) *
1007                  gray * saturate;
1008                intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
1009                hue = (angle + PI) * INV_PI_360 + aoffset;
1010                s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
1011                s = MIN(MAX(s,0.0), 1.0);
1012                v = MIN(MAX(intensity,0.0), 1.0);
1013
1014                if (s == 0.0) {
1015                    ch = (uch)(v * 255.0);
1016                    *dest++ = ch;
1017                    *dest++ = ch;
1018                    *dest++ = ch;
1019                } else {
1020                    if ((hue < 0.0) || (hue >= 360.0))
1021                        hue -= (((int)(hue / 360.0)) * 360.0);
1022                    hue /= 60.0;
1023                    ii = (int)hue;
1024                    f = hue - (double)ii;
1025                    p = (1.0 - s) * v;
1026                    q = (1.0 - (s * f)) * v;
1027                    t = (1.0 - (s * (1.0 - f))) * v;
1028                    if      (ii == 0) { red = v; green = t; blue = p; }
1029                    else if (ii == 1) { red = q; green = v; blue = p; }
1030                    else if (ii == 2) { red = p; green = v; blue = t; }
1031                    else if (ii == 3) { red = p; green = q; blue = v; }
1032                    else if (ii == 4) { red = t; green = p; blue = v; }
1033                    else if (ii == 5) { red = v; green = p; blue = q; }
1034                    *dest++ = (uch)(red * 255.0);
1035                    *dest++ = (uch)(green * 255.0);
1036                    *dest++ = (uch)(blue * 255.0);
1037                }
1038            }
1039        }
1040        fprintf(stderr, "done.\n");
1041        fflush(stderr);
1042    }
1043
1044/*---------------------------------------------------------------------------
1045    Blast background image to display buffer before beginning PNG decode;
1046    calling function will handle invalidation and UpdateWindow() call.
1047  ---------------------------------------------------------------------------*/
1048
1049    for (row = 0;  row < rpng2_info.height;  ++row) {
1050        src = bg_data + row*bg_rowbytes;
1051        dest = wimage_data + row*wimage_rowbytes;
1052        for (i = rpng2_info.width;  i > 0;  --i) {
1053            r1 = *src++;
1054            g1 = *src++;
1055            b1 = *src++;
1056            *dest++ = b1;
1057            *dest++ = g1;   /* note reverse order */
1058            *dest++ = r1;
1059        }
1060    }
1061
1062    return 0;
1063
1064} /* end function rpng2_win_load_bg_image() */
1065
1066
1067
1068
1069
1070static void rpng2_win_display_row(ulg row)
1071{
1072    uch bg_red   = rpng2_info.bg_red;
1073    uch bg_green = rpng2_info.bg_green;
1074    uch bg_blue  = rpng2_info.bg_blue;
1075    uch *src, *src2=NULL, *dest;
1076    uch r, g, b, a;
1077    ulg i;
1078    static int rows=0;
1079    static ulg firstrow;
1080
1081/*---------------------------------------------------------------------------
1082    rows and firstrow simply track how many rows (and which ones) have not
1083    yet been displayed; alternatively, we could call InvalidateRect() for
1084    every row and not bother with the records-keeping.
1085  ---------------------------------------------------------------------------*/
1086
1087    Trace((stderr, "beginning rpng2_win_display_row()\n"))
1088
1089    if (rows == 0)
1090        firstrow = row;   /* first row not yet displayed */
1091
1092    ++rows;   /* count of rows received but not yet displayed */
1093
1094/*---------------------------------------------------------------------------
1095    Aside from the use of the rpng2_info struct and the lack of an outer
1096    loop (over rows), this routine is identical to rpng_win_display_image()
1097    in the non-progressive version of the program.
1098  ---------------------------------------------------------------------------*/
1099
1100    src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1101    if (bg_image)
1102        src2 = bg_data + row*bg_rowbytes;
1103    dest = wimage_data + row*wimage_rowbytes;
1104
1105    if (rpng2_info.channels == 3) {
1106        for (i = rpng2_info.width;  i > 0;  --i) {
1107            r = *src++;
1108            g = *src++;
1109            b = *src++;
1110            *dest++ = b;
1111            *dest++ = g;   /* note reverse order */
1112            *dest++ = r;
1113        }
1114    } else /* if (rpng2_info.channels == 4) */ {
1115        for (i = rpng2_info.width;  i > 0;  --i) {
1116            r = *src++;
1117            g = *src++;
1118            b = *src++;
1119            a = *src++;
1120            if (bg_image) {
1121                bg_red   = *src2++;
1122                bg_green = *src2++;
1123                bg_blue  = *src2++;
1124            }
1125            if (a == 255) {
1126                *dest++ = b;
1127                *dest++ = g;
1128                *dest++ = r;
1129            } else if (a == 0) {
1130                *dest++ = bg_blue;
1131                *dest++ = bg_green;
1132                *dest++ = bg_red;
1133            } else {
1134                /* this macro (copied from png.h) composites the
1135                 * foreground and background values and puts the
1136                 * result into the first argument; there are no
1137                 * side effects with the first argument */
1138                alpha_composite(*dest++, b, a, bg_blue);
1139                alpha_composite(*dest++, g, a, bg_green);
1140                alpha_composite(*dest++, r, a, bg_red);
1141            }
1142        }
1143    }
1144
1145/*---------------------------------------------------------------------------
1146    Display after every 16 rows or when on last row.  (Region may include
1147    previously displayed lines due to interlacing--i.e., not contiguous.)
1148  ---------------------------------------------------------------------------*/
1149
1150    if ((rows & 0xf) == 0 || row == rpng2_info.height-1) {
1151        RECT rect;
1152
1153        rect.left = 0L;
1154        rect.top = (LONG)firstrow;
1155        rect.right = (LONG)rpng2_info.width;       /* possibly off by one? */
1156        rect.bottom = (LONG)row + 1L;              /* possibly off by one? */
1157        InvalidateRect(global_hwnd, &rect, FALSE);
1158        UpdateWindow(global_hwnd);                 /* similar to XFlush() */
1159        rows = 0;
1160    }
1161
1162} /* end function rpng2_win_display_row() */
1163
1164
1165
1166
1167
1168static void rpng2_win_finish_display()
1169{
1170    Trace((stderr, "beginning rpng2_win_finish_display()\n"))
1171
1172    /* last row has already been displayed by rpng2_win_display_row(), so
1173     * we have nothing to do here except set a flag and let the user know
1174     * that the image is done */
1175
1176    rpng2_info.state = kDone;
1177    printf(
1178#ifndef __CYGWIN__
1179      "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n"
1180#else
1181      "Done.  Press mouse button 1 (within image window) to quit.\n"
1182#endif
1183    );
1184    fflush(stdout);
1185}
1186
1187
1188
1189
1190
1191static void rpng2_win_cleanup()
1192{
1193    if (bg_image && bg_data) {
1194        free(bg_data);
1195        bg_data = NULL;
1196    }
1197
1198    if (rpng2_info.image_data) {
1199        free(rpng2_info.image_data);
1200        rpng2_info.image_data = NULL;
1201    }
1202
1203    if (rpng2_info.row_pointers) {
1204        free(rpng2_info.row_pointers);
1205        rpng2_info.row_pointers = NULL;
1206    }
1207
1208    if (dib) {
1209        free(dib);
1210        dib = NULL;
1211    }
1212}
1213
1214
1215
1216
1217
1218LRESULT CALLBACK rpng2_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
1219{
1220    HDC         hdc;
1221    PAINTSTRUCT ps;
1222    int rc;
1223
1224    switch (iMsg) {
1225        case WM_CREATE:
1226            /* one-time processing here, if any */
1227            return 0;
1228
1229        case WM_PAINT:
1230            hdc = BeginPaint(hwnd, &ps);
1231            rc = StretchDIBits(hdc, 0, 0, rpng2_info.width, rpng2_info.height,
1232                                    0, 0, rpng2_info.width, rpng2_info.height,
1233                                    wimage_data, (BITMAPINFO *)bmih,
1234                                    0, SRCCOPY);
1235            EndPaint(hwnd, &ps);
1236            return 0;
1237
1238        /* wait for the user to tell us when to quit */
1239        case WM_CHAR:
1240            switch (wP) {       /* only need one, so ignore repeat count */
1241                case 'q':
1242                case 'Q':
1243                case 0x1B:      /* Esc key */
1244                    PostQuitMessage(0);
1245            }
1246            return 0;
1247
1248        case WM_LBUTTONDOWN:    /* another way of quitting */
1249        case WM_DESTROY:
1250            PostQuitMessage(0);
1251            return 0;
1252    }
1253
1254    return DefWindowProc(hwnd, iMsg, wP, lP);
1255}