all repos — mgba @ 9de8f084ba55460b02d300c1dd8b8e6c56f691d5

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