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