PageRenderTime 75ms CodeModel.GetById 25ms app.highlight 41ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Image_LibPNG154/contrib/gregbook/rpng2-win.c

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