(root)/
libpng-1.6.40/
contrib/
gregbook/
rpng-win.c
       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  }