Home | History | Annotate | Download | only in gregbook
      1 /*---------------------------------------------------------------------------
      2 
      3    rpng - simple PNG display program                             rpng-win.c
      4 
      5    This program decodes and displays PNG images, with gamma correction and
      6    optionally with a user-specified background color (in case the image has
      7    transparency).  It is very nearly the most basic PNG viewer possible.
      8    This version is for 32-bit Windows; it may compile under 16-bit Windows
      9    with a little tweaking (or maybe not).
     10 
     11    to do:
     12     - handle quoted command-line args (especially filenames with spaces)
     13     - have minimum window width:  oh well
     14     - use %.1023s to simplify truncation of title-bar string?
     15 
     16   ---------------------------------------------------------------------------
     17 
     18    Changelog:
     19     - 1.00:  initial public release
     20     - 1.01:  modified to allow abbreviated options; fixed long/ulong mis-
     21               match; switched to png_jmpbuf() macro
     22     - 1.02:  added extra set of parentheses to png_jmpbuf() macro; fixed
     23               command-line parsing bug
     24     - 1.10:  enabled "message window"/console (thanks to David Geldreich)
     25     - 2.00:  dual-licensed (added GNU GPL)
     26     - 2.01:  fixed improper display of usage screen on PNG error(s)
     27     - 2.02:  check for integer overflow (Glenn R-P)
     28 
     29   ---------------------------------------------------------------------------
     30 
     31       Copyright (c) 1998-2008, 2017 Greg Roelofs.  All rights reserved.
     32 
     33       This software is provided "as is," without warranty of any kind,
     34       express or implied.  In no event shall the author or contributors
     35       be held liable for any damages arising in any way from the use of
     36       this software.
     37 
     38       The contents of this file are DUAL-LICENSED.  You may modify and/or
     39       redistribute this software according to the terms of one of the
     40       following two licenses (at your option):
     41 
     42 
     43       LICENSE 1 ("BSD-like with advertising clause"):
     44 
     45       Permission is granted to anyone to use this software for any purpose,
     46       including commercial applications, and to alter it and redistribute
     47       it freely, subject to the following restrictions:
     48 
     49       1. Redistributions of source code must retain the above copyright
     50          notice, disclaimer, and this list of conditions.
     51       2. Redistributions in binary form must reproduce the above copyright
     52          notice, disclaimer, and this list of conditions in the documenta-
     53          tion and/or other materials provided with the distribution.
     54       3. All advertising materials mentioning features or use of this
     55          software must display the following acknowledgment:
     56 
     57             This product includes software developed by Greg Roelofs
     58             and contributors for the book, "PNG: The Definitive Guide,"
     59             published by O'Reilly and Associates.
     60 
     61 
     62       LICENSE 2 (GNU GPL v2 or later):
     63 
     64       This program is free software; you can redistribute it and/or modify
     65       it under the terms of the GNU General Public License as published by
     66       the Free Software Foundation; either version 2 of the License, or
     67       (at your option) any later version.
     68 
     69       This program is distributed in the hope that it will be useful,
     70       but WITHOUT ANY WARRANTY; without even the implied warranty of
     71       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     72       GNU General Public License for more details.
     73 
     74       You should have received a copy of the GNU General Public License
     75       along with this program; if not, write to the Free Software Foundation,
     76       Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     77 
     78   ---------------------------------------------------------------------------*/
     79 
     80 #define PROGNAME  "rpng-win"
     81 #define LONGNAME  "Simple PNG Viewer for Windows"
     82 #define VERSION   "2.01 of 16 March 2008"
     83 
     84 #include <stdio.h>
     85 #include <stdlib.h>
     86 #include <string.h>
     87 #include <time.h>
     88 #include <windows.h>
     89 #ifdef __CYGWIN__
     90 /* getch replacement. Turns out, we don't really need this,
     91  * but leave it here if we ever enable any of the uses of
     92  * _getch in the main code
     93  */
     94 #include <unistd.h>
     95 #include <termio.h>
     96 #include <sys/ioctl.h>
     97 int repl_getch( void )
     98 {
     99   char ch;
    100   int fd = fileno(stdin);
    101   struct termio old_tty, new_tty;
    102 
    103   ioctl(fd, TCGETA, &old_tty);
    104   new_tty = old_tty;
    105   new_tty.c_lflag &= ~(ICANON | ECHO | ISIG);
    106   ioctl(fd, TCSETA, &new_tty);
    107   fread(&ch, 1, sizeof(ch), stdin);
    108   ioctl(fd, TCSETA, &old_tty);
    109 
    110   return ch;
    111 }
    112 #define _getch repl_getch
    113 #else
    114 #include <conio.h>      /* only for _getch() */
    115 #endif
    116 
    117 /* #define DEBUG  :  this enables the Trace() macros */
    118 
    119 #include "readpng.h"    /* typedefs, common macros, readpng prototypes */
    120 
    121 
    122 /* could just include png.h, but this macro is the only thing we need
    123  * (name and typedefs changed to local versions); note that side effects
    124  * only happen with alpha (which could easily be avoided with
    125  * "ush acopy = (alpha);") */
    126 
    127 #define alpha_composite(composite, fg, alpha, bg) {               \
    128     ush temp = ((ush)(fg)*(ush)(alpha) +                          \
    129                 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
    130     (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
    131 }
    132 
    133 
    134 /* local prototypes */
    135 static int        rpng_win_create_window(HINSTANCE hInst, int showmode);
    136 static int        rpng_win_display_image(void);
    137 static void       rpng_win_cleanup(void);
    138 LRESULT CALLBACK  rpng_win_wndproc(HWND, UINT, WPARAM, LPARAM);
    139 
    140 
    141 static char titlebar[1024];
    142 static char *progname = PROGNAME;
    143 static char *appname = LONGNAME;
    144 static char *filename;
    145 static FILE *infile;
    146 
    147 static char *bgstr;
    148 static uch bg_red=0, bg_green=0, bg_blue=0;
    149 
    150 static double display_exponent;
    151 
    152 static ulg image_width, image_height, image_rowbytes;
    153 static int image_channels;
    154 static uch *image_data;
    155 
    156 /* Windows-specific variables */
    157 static ulg wimage_rowbytes;
    158 static uch *dib;
    159 static uch *wimage_data;
    160 static BITMAPINFOHEADER *bmih;
    161 
    162 static HWND global_hwnd;
    163 
    164 
    165 
    166 
    167 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
    168 {
    169     char *args[1024];                 /* arbitrary limit, but should suffice */
    170     char *p, *q, **argv = args;
    171     int argc = 0;
    172     int rc, alen, flen;
    173     int error = 0;
    174     int have_bg = FALSE;
    175     double LUT_exponent;              /* just the lookup table */
    176     double CRT_exponent = 2.2;        /* just the monitor */
    177     double default_display_exponent;  /* whole display system */
    178     MSG msg;
    179 
    180 
    181     filename = (char *)NULL;
    182 
    183 #ifndef __CYGWIN__
    184     /* First reenable console output, which normally goes to the bit bucket
    185      * for windowed apps.  Closing the console window will terminate the
    186      * app.  Thanks to David.Geldreich at realviz.com for supplying the magical
    187      * incantation. */
    188 
    189     AllocConsole();
    190     freopen("CONOUT$", "a", stderr);
    191     freopen("CONOUT$", "a", stdout);
    192 #endif
    193 
    194 
    195     /* Next set the default value for our display-system exponent, i.e.,
    196      * the product of the CRT exponent and the exponent corresponding to
    197      * the frame-buffer's lookup table (LUT), if any.  This is not an
    198      * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
    199      * ones), but it should cover 99% of the current possibilities.  And
    200      * yes, these ifdefs are completely wasted in a Windows program... */
    201 
    202 #if defined(NeXT)
    203     LUT_exponent = 1.0 / 2.2;
    204     /*
    205     if (some_next_function_that_returns_gamma(&next_gamma))
    206         LUT_exponent = 1.0 / next_gamma;
    207      */
    208 #elif defined(sgi)
    209     LUT_exponent = 1.0 / 1.7;
    210     /* there doesn't seem to be any documented function to get the
    211      * "gamma" value, so we do it the hard way */
    212     infile = fopen("/etc/config/system.glGammaVal", "r");
    213     if (infile) {
    214         double sgi_gamma;
    215 
    216         fgets(tmpline, 80, infile);
    217         fclose(infile);
    218         sgi_gamma = atof(tmpline);
    219         if (sgi_gamma > 0.0)
    220             LUT_exponent = 1.0 / sgi_gamma;
    221     }
    222 #elif defined(Macintosh)
    223     LUT_exponent = 1.8 / 2.61;
    224     /*
    225     if (some_mac_function_that_returns_gamma(&mac_gamma))
    226         LUT_exponent = mac_gamma / 2.61;
    227      */
    228 #else
    229     LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
    230 #endif
    231 
    232     /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
    233     default_display_exponent = LUT_exponent * CRT_exponent;
    234 
    235 
    236     /* If the user has set the SCREEN_GAMMA environment variable as suggested
    237      * (somewhat imprecisely) in the libpng documentation, use that; otherwise
    238      * use the default value we just calculated.  Either way, the user may
    239      * override this via a command-line option. */
    240 
    241     if ((p = getenv("SCREEN_GAMMA")) != NULL)
    242         display_exponent = atof(p);
    243     else
    244         display_exponent = default_display_exponent;
    245 
    246 
    247     /* Windows really hates command lines, so we have to set up our own argv.
    248      * Note that we do NOT bother with quoted arguments here, so don't use
    249      * filenames with spaces in 'em! */
    250 
    251     argv[argc++] = PROGNAME;
    252     p = cmd;
    253     for (;;) {
    254         if (*p == ' ')
    255             while (*++p == ' ')
    256                 ;
    257         /* now p points at the first non-space after some spaces */
    258         if (*p == '\0')
    259             break;    /* nothing after the spaces:  done */
    260         argv[argc++] = q = p;
    261         while (*q && *q != ' ')
    262             ++q;
    263         /* now q points at a space or the end of the string */
    264         if (*q == '\0')
    265             break;    /* last argv already terminated; quit */
    266         *q = '\0';    /* change space to terminator */
    267         p = q + 1;
    268     }
    269     argv[argc] = NULL;   /* terminate the argv array itself */
    270 
    271 
    272     /* Now parse the command line for options and the PNG filename. */
    273 
    274     while (*++argv && !error) {
    275         if (!strncmp(*argv, "-gamma", 2)) {
    276             if (!*++argv)
    277                 ++error;
    278             else {
    279                 display_exponent = atof(*argv);
    280                 if (display_exponent <= 0.0)
    281                     ++error;
    282             }
    283         } else if (!strncmp(*argv, "-bgcolor", 2)) {
    284             if (!*++argv)
    285                 ++error;
    286             else {
    287                 bgstr = *argv;
    288                 if (strlen(bgstr) != 7 || bgstr[0] != '#')
    289                     ++error;
    290                 else
    291                     have_bg = TRUE;
    292             }
    293         } else {
    294             if (**argv != '-') {
    295                 filename = *argv;
    296                 if (argv[1])   /* shouldn't be any more args after filename */
    297                     ++error;
    298             } else
    299                 ++error;   /* not expecting any other options */
    300         }
    301     }
    302 
    303     if (!filename)
    304         ++error;
    305 
    306 
    307     /* print usage screen if any errors up to this point */
    308 
    309     if (error) {
    310 #ifndef __CYGWIN__
    311         int ch;
    312 #endif
    313 
    314         fprintf(stderr, "\n%s %s:  %s\n\n", PROGNAME, VERSION, appname);
    315         readpng_version_info();
    316         fprintf(stderr, "\n"
    317           "Usage:  %s [-gamma exp] [-bgcolor bg] file.png\n"
    318           "    exp \ttransfer-function exponent (``gamma'') of the display\n"
    319           "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
    320           "\t\t  to the product of the lookup-table exponent (varies)\n"
    321           "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
    322           "    bg  \tdesired background color in 7-character hex RGB format\n"
    323           "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
    324           "\t\t  used with transparent images\n"
    325           "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
    326 #ifndef __CYGWIN__
    327           "Press Q or Esc to quit this usage screen.\n"
    328 #endif
    329           "\n", PROGNAME, default_display_exponent);
    330 #ifndef __CYGWIN__
    331         do
    332             ch = _getch();
    333         while (ch != 'q' && ch != 'Q' && ch != 0x1B);
    334 #endif
    335         exit(1);
    336     }
    337 
    338 
    339     if (!(infile = fopen(filename, "rb"))) {
    340         fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
    341         ++error;
    342     } else {
    343         if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) {
    344             switch (rc) {
    345                 case 1:
    346                     fprintf(stderr, PROGNAME
    347                       ":  [%s] is not a PNG file: incorrect signature\n",
    348                       filename);
    349                     break;
    350                 case 2:
    351                     fprintf(stderr, PROGNAME
    352                       ":  [%s] has bad IHDR (libpng longjmp)\n", filename);
    353                     break;
    354                 case 4:
    355                     fprintf(stderr, PROGNAME ":  insufficient memory\n");
    356                     break;
    357                 default:
    358                     fprintf(stderr, PROGNAME
    359                       ":  unknown readpng_init() error\n");
    360                     break;
    361             }
    362             ++error;
    363         }
    364         if (error)
    365             fclose(infile);
    366     }
    367 
    368 
    369     if (error) {
    370 #ifndef __CYGWIN__
    371         int ch;
    372 #endif
    373 
    374         fprintf(stderr, PROGNAME ":  aborting.\n");
    375 #ifndef __CYGWIN__
    376         do
    377             ch = _getch();
    378         while (ch != 'q' && ch != 'Q' && ch != 0x1B);
    379 #endif
    380         exit(2);
    381     } else {
    382         fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
    383 #ifndef __CYGWIN__
    384         fprintf(stderr,
    385           "\n   [console window:  closing this window will terminate %s]\n\n",
    386           PROGNAME);
    387 #endif
    388     }
    389 
    390 
    391     /* set the title-bar string, but make sure buffer doesn't overflow */
    392 
    393     alen = strlen(appname);
    394     flen = strlen(filename);
    395     if (alen + flen + 3 > 1023)
    396         sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
    397     else
    398         sprintf(titlebar, "%s:  %s", appname, filename);
    399 
    400 
    401     /* if the user didn't specify a background color on the command line,
    402      * check for one in the PNG file--if not, the initialized values of 0
    403      * (black) will be used */
    404 
    405     if (have_bg) {
    406         unsigned r, g, b;   /* this approach quiets compiler warnings */
    407 
    408         sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
    409         bg_red   = (uch)r;
    410         bg_green = (uch)g;
    411         bg_blue  = (uch)b;
    412     } else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) {
    413         readpng_cleanup(TRUE);
    414         fprintf(stderr, PROGNAME
    415           ":  libpng error while checking for background color\n");
    416         exit(2);
    417     }
    418 
    419 
    420     /* do the basic Windows initialization stuff, make the window and fill it
    421      * with the background color */
    422 
    423     if (rpng_win_create_window(hInst, showmode))
    424         exit(2);
    425 
    426 
    427     /* decode the image, all at once */
    428 
    429     Trace((stderr, "calling readpng_get_image()\n"))
    430     image_data = readpng_get_image(display_exponent, &image_channels,
    431       &image_rowbytes);
    432     Trace((stderr, "done with readpng_get_image()\n"))
    433 
    434 
    435     /* done with PNG file, so clean up to minimize memory usage (but do NOT
    436      * nuke image_data!) */
    437 
    438     readpng_cleanup(FALSE);
    439     fclose(infile);
    440 
    441     if (!image_data) {
    442         fprintf(stderr, PROGNAME ":  unable to decode PNG image\n");
    443         exit(3);
    444     }
    445 
    446 
    447     /* display image (composite with background if requested) */
    448 
    449     Trace((stderr, "calling rpng_win_display_image()\n"))
    450     if (rpng_win_display_image()) {
    451         free(image_data);
    452         exit(4);
    453     }
    454     Trace((stderr, "done with rpng_win_display_image()\n"))
    455 
    456 
    457     /* wait for the user to tell us when to quit */
    458 
    459     printf(
    460 #ifndef __CYGWIN__
    461       "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n"
    462 #else
    463       "Done.  Press mouse button 1 (within image window) to quit.\n"
    464 #endif
    465     );
    466     fflush(stdout);
    467 
    468     while (GetMessage(&msg, NULL, 0, 0)) {
    469         TranslateMessage(&msg);
    470         DispatchMessage(&msg);
    471     }
    472 
    473 
    474     /* OK, we're done:  clean up all image and Windows resources and go away */
    475 
    476     rpng_win_cleanup();
    477 
    478     return msg.wParam;
    479 }
    480 
    481 
    482 
    483 
    484 
    485 static int rpng_win_create_window(HINSTANCE hInst, int showmode)
    486 {
    487     uch *dest;
    488     int extra_width, extra_height;
    489     ulg i, j;
    490     WNDCLASSEX wndclass;
    491 
    492 
    493 /*---------------------------------------------------------------------------
    494     Allocate memory for the display-specific version of the image (round up
    495     to multiple of 4 for Windows DIB).
    496   ---------------------------------------------------------------------------*/
    497 
    498     wimage_rowbytes = ((3*image_width + 3L) >> 2) << 2;
    499 
    500     /* Guard against integer overflow */
    501     if (image_height > ((size_t)(-1))/wimage_rowbytes) {
    502         fprintf(stderr, PROGNAME ":  image_data buffer would be too large\n");
    503         return 4;   /* fail */
    504     }
    505 
    506     if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
    507                               wimage_rowbytes*image_height)))
    508     {
    509         return 4;   /* fail */
    510     }
    511 
    512 /*---------------------------------------------------------------------------
    513     Initialize the DIB.  Negative height means to use top-down BMP ordering
    514     (must be uncompressed, but that's what we want).  Bit count of 1, 4 or 8
    515     implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values
    516     directly => wimage_data begins immediately after BMP header.
    517   ---------------------------------------------------------------------------*/
    518 
    519     memset(dib, 0, sizeof(BITMAPINFOHEADER));
    520     bmih = (BITMAPINFOHEADER *)dib;
    521     bmih->biSize = sizeof(BITMAPINFOHEADER);
    522     bmih->biWidth = image_width;
    523     bmih->biHeight = -((long)image_height);
    524     bmih->biPlanes = 1;
    525     bmih->biBitCount = 24;
    526     bmih->biCompression = 0;
    527     wimage_data = dib + sizeof(BITMAPINFOHEADER);
    528 
    529 /*---------------------------------------------------------------------------
    530     Fill in background color (black by default); data are in BGR order.
    531   ---------------------------------------------------------------------------*/
    532 
    533     for (j = 0;  j < image_height;  ++j) {
    534         dest = wimage_data + j*wimage_rowbytes;
    535         for (i = image_width;  i > 0;  --i) {
    536             *dest++ = bg_blue;
    537             *dest++ = bg_green;
    538             *dest++ = bg_red;
    539         }
    540     }
    541 
    542 /*---------------------------------------------------------------------------
    543     Set the window parameters.
    544   ---------------------------------------------------------------------------*/
    545 
    546     memset(&wndclass, 0, sizeof(wndclass));
    547 
    548     wndclass.cbSize = sizeof(wndclass);
    549     wndclass.style = CS_HREDRAW | CS_VREDRAW;
    550     wndclass.lpfnWndProc = rpng_win_wndproc;
    551     wndclass.hInstance = hInst;
    552     wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    553     wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    554     wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
    555     wndclass.lpszMenuName = NULL;
    556     wndclass.lpszClassName = progname;
    557     wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    558 
    559     RegisterClassEx(&wndclass);
    560 
    561 /*---------------------------------------------------------------------------
    562     Finally, create the window.
    563   ---------------------------------------------------------------------------*/
    564 
    565     extra_width  = 2*(GetSystemMetrics(SM_CXBORDER) +
    566                       GetSystemMetrics(SM_CXDLGFRAME));
    567     extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
    568                       GetSystemMetrics(SM_CYDLGFRAME)) +
    569                       GetSystemMetrics(SM_CYCAPTION);
    570 
    571     global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW,
    572       CW_USEDEFAULT, CW_USEDEFAULT, image_width+extra_width,
    573       image_height+extra_height, NULL, NULL, hInst, NULL);
    574 
    575     ShowWindow(global_hwnd, showmode);
    576     UpdateWindow(global_hwnd);
    577 
    578     return 0;
    579 
    580 } /* end function rpng_win_create_window() */
    581 
    582 
    583 
    584 
    585 
    586 static int rpng_win_display_image()
    587 {
    588     uch *src, *dest;
    589     uch r, g, b, a;
    590     ulg i, row, lastrow;
    591     RECT rect;
    592 
    593 
    594     Trace((stderr, "beginning display loop (image_channels == %d)\n",
    595       image_channels))
    596     Trace((stderr, "(width = %ld, rowbytes = %ld, wimage_rowbytes = %d)\n",
    597       image_width, image_rowbytes, wimage_rowbytes))
    598 
    599 
    600 /*---------------------------------------------------------------------------
    601     Blast image data to buffer.  This whole routine takes place before the
    602     message loop begins, so there's no real point in any pseudo-progressive
    603     display...
    604   ---------------------------------------------------------------------------*/
    605 
    606     for (lastrow = row = 0;  row < image_height;  ++row) {
    607         src = image_data + row*image_rowbytes;
    608         dest = wimage_data + row*wimage_rowbytes;
    609         if (image_channels == 3) {
    610             for (i = image_width;  i > 0;  --i) {
    611                 r = *src++;
    612                 g = *src++;
    613                 b = *src++;
    614                 *dest++ = b;
    615                 *dest++ = g;   /* note reverse order */
    616                 *dest++ = r;
    617             }
    618         } else /* if (image_channels == 4) */ {
    619             for (i = image_width;  i > 0;  --i) {
    620                 r = *src++;
    621                 g = *src++;
    622                 b = *src++;
    623                 a = *src++;
    624                 if (a == 255) {
    625                     *dest++ = b;
    626                     *dest++ = g;
    627                     *dest++ = r;
    628                 } else if (a == 0) {
    629                     *dest++ = bg_blue;
    630                     *dest++ = bg_green;
    631                     *dest++ = bg_red;
    632                 } else {
    633                     /* this macro (copied from png.h) composites the
    634                      * foreground and background values and puts the
    635                      * result into the first argument; there are no
    636                      * side effects with the first argument */
    637                     alpha_composite(*dest++, b, a, bg_blue);
    638                     alpha_composite(*dest++, g, a, bg_green);
    639                     alpha_composite(*dest++, r, a, bg_red);
    640                 }
    641             }
    642         }
    643         /* display after every 16 lines */
    644         if (((row+1) & 0xf) == 0) {
    645             rect.left = 0L;
    646             rect.top = (LONG)lastrow;
    647             rect.right = (LONG)image_width;      /* possibly off by one? */
    648             rect.bottom = (LONG)lastrow + 16L;   /* possibly off by one? */
    649             InvalidateRect(global_hwnd, &rect, FALSE);
    650             UpdateWindow(global_hwnd);     /* similar to XFlush() */
    651             lastrow = row + 1;
    652         }
    653     }
    654 
    655     Trace((stderr, "calling final image-flush routine\n"))
    656     if (lastrow < image_height) {
    657         rect.left = 0L;
    658         rect.top = (LONG)lastrow;
    659         rect.right = (LONG)image_width;      /* possibly off by one? */
    660         rect.bottom = (LONG)image_height;    /* possibly off by one? */
    661         InvalidateRect(global_hwnd, &rect, FALSE);
    662         UpdateWindow(global_hwnd);     /* similar to XFlush() */
    663     }
    664 
    665 /*
    666     last param determines whether or not background is wiped before paint
    667     InvalidateRect(global_hwnd, NULL, TRUE);
    668     UpdateWindow(global_hwnd);
    669  */
    670 
    671     return 0;
    672 }
    673 
    674 
    675 
    676 
    677 
    678 static void rpng_win_cleanup()
    679 {
    680     if (image_data) {
    681         free(image_data);
    682         image_data = NULL;
    683     }
    684 
    685     if (dib) {
    686         free(dib);
    687         dib = NULL;
    688     }
    689 }
    690 
    691 
    692 
    693 
    694 
    695 LRESULT CALLBACK rpng_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
    696 {
    697     HDC         hdc;
    698     PAINTSTRUCT ps;
    699     int rc;
    700 
    701     switch (iMsg) {
    702         case WM_CREATE:
    703             /* one-time processing here, if any */
    704             return 0;
    705 
    706         case WM_PAINT:
    707             hdc = BeginPaint(hwnd, &ps);
    708                     /*                    dest                          */
    709             rc = StretchDIBits(hdc, 0, 0, image_width, image_height,
    710                     /*                    source                        */
    711                                     0, 0, image_width, image_height,
    712                                     wimage_data, (BITMAPINFO *)bmih,
    713                     /*              iUsage: no clue                     */
    714                                     0, SRCCOPY);
    715             EndPaint(hwnd, &ps);
    716             return 0;
    717 
    718         /* wait for the user to tell us when to quit */
    719         case WM_CHAR:
    720             switch (wP) {      /* only need one, so ignore repeat count */
    721                 case 'q':
    722                 case 'Q':
    723                 case 0x1B:     /* Esc key */
    724                     PostQuitMessage(0);
    725             }
    726             return 0;
    727 
    728         case WM_LBUTTONDOWN:   /* another way of quitting */
    729         case WM_DESTROY:
    730             PostQuitMessage(0);
    731             return 0;
    732     }
    733 
    734     return DefWindowProc(hwnd, iMsg, wP, lP);
    735 }
    736