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