Blame contrib/gregbook/rpng2-win.c

Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
   rpng2 - progressive-model PNG display program                rpng2-win.c
Packit 0ba690
Packit 0ba690
   This program decodes and displays PNG files progressively, as if it were
Packit 0ba690
   a web browser (though the front end is only set up to read from files).
Packit 0ba690
   It supports gamma correction, user-specified background colors, and user-
Packit 0ba690
   specified background patterns (for transparent images).  This version is
Packit 0ba690
   for 32-bit Windows; it may compile under 16-bit Windows with a little
Packit 0ba690
   tweaking (or maybe not).  Thanks to Adam Costello and Pieter S. van der
Packit 0ba690
   Meulen for the "diamond" and "radial waves" patterns, respectively.
Packit 0ba690
Packit 0ba690
   to do (someday, maybe):
Packit 0ba690
    - handle quoted command-line args (especially filenames with spaces)
Packit 0ba690
    - finish resizable checkerboard-gradient (sizes 4-128?)
Packit 0ba690
    - use %.1023s to simplify truncation of title-bar string?
Packit 0ba690
    - have minimum window width:  oh well
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
   Changelog:
Packit 0ba690
    - 1.01:  initial public release
Packit 0ba690
    - 1.02:  fixed cut-and-paste error in usage screen (oops...)
Packit 0ba690
    - 1.03:  modified to allow abbreviated options
Packit 0ba690
    - 1.04:  removed bogus extra argument from usage fprintf() [Glenn R-P?];
Packit 0ba690
              fixed command-line parsing bug
Packit 0ba690
    - 1.10:  enabled "message window"/console (thanks to David Geldreich)
Packit 0ba690
    - 1.20:  added runtime MMX-enabling/disabling and new -mmx* options
Packit 0ba690
    - 1.21:  made minor tweak to usage screen to fit within 25-line console
Packit 0ba690
    - 1.22:  added AMD64/EM64T support (__x86_64__)
Packit 0ba690
    - 2.00:  dual-licensed (added GNU GPL)
Packit 0ba690
    - 2.01:  fixed 64-bit typo in readpng2.c
Packit 0ba690
    - 2.02:  fixed improper display of usage screen on PNG error(s); fixed
Packit 0ba690
              unexpected-EOF and file-read-error cases
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
      Copyright (c) 1998-2008 Greg Roelofs.  All rights reserved.
Packit 0ba690
Packit 0ba690
      This software is provided "as is," without warranty of any kind,
Packit 0ba690
      express or implied.  In no event shall the author or contributors
Packit 0ba690
      be held liable for any damages arising in any way from the use of
Packit 0ba690
      this software.
Packit 0ba690
Packit 0ba690
      The contents of this file are DUAL-LICENSED.  You may modify and/or
Packit 0ba690
      redistribute this software according to the terms of one of the
Packit 0ba690
      following two licenses (at your option):
Packit 0ba690
Packit 0ba690
Packit 0ba690
      LICENSE 1 ("BSD-like with advertising clause"):
Packit 0ba690
Packit 0ba690
      Permission is granted to anyone to use this software for any purpose,
Packit 0ba690
      including commercial applications, and to alter it and redistribute
Packit 0ba690
      it freely, subject to the following restrictions:
Packit 0ba690
Packit 0ba690
      1. Redistributions of source code must retain the above copyright
Packit 0ba690
         notice, disclaimer, and this list of conditions.
Packit 0ba690
      2. Redistributions in binary form must reproduce the above copyright
Packit 0ba690
         notice, disclaimer, and this list of conditions in the documenta-
Packit 0ba690
         tion and/or other materials provided with the distribution.
Packit 0ba690
      3. All advertising materials mentioning features or use of this
Packit 0ba690
         software must display the following acknowledgment:
Packit 0ba690
Packit 0ba690
            This product includes software developed by Greg Roelofs
Packit 0ba690
            and contributors for the book, "PNG: The Definitive Guide,"
Packit 0ba690
            published by O'Reilly and Associates.
Packit 0ba690
Packit 0ba690
Packit 0ba690
      LICENSE 2 (GNU GPL v2 or later):
Packit 0ba690
Packit 0ba690
      This program is free software; you can redistribute it and/or modify
Packit 0ba690
      it under the terms of the GNU General Public License as published by
Packit 0ba690
      the Free Software Foundation; either version 2 of the License, or
Packit 0ba690
      (at your option) any later version.
Packit 0ba690
Packit 0ba690
      This program is distributed in the hope that it will be useful,
Packit 0ba690
      but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 0ba690
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 0ba690
      GNU General Public License for more details.
Packit 0ba690
Packit 0ba690
      You should have received a copy of the GNU General Public License
Packit 0ba690
      along with this program; if not, write to the Free Software Foundation,
Packit 0ba690
      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
#define PROGNAME  "rpng2-win"
Packit 0ba690
#define LONGNAME  "Progressive PNG Viewer for Windows"
Packit 0ba690
#define VERSION   "2.02 of 16 March 2008"
Packit 0ba690
Packit 0ba690
#include <stdio.h>
Packit 0ba690
#include <stdlib.h>
Packit 0ba690
#include <string.h>
Packit 0ba690
#include <setjmp.h>    /* for jmpbuf declaration in readpng2.h */
Packit 0ba690
#include <time.h>
Packit 0ba690
#include <math.h>      /* only for PvdM background code */
Packit 0ba690
#include <windows.h>
Packit 0ba690
#include <conio.h>     /* only for _getch() */
Packit 0ba690
Packit 0ba690
/* all for PvdM background code: */
Packit 0ba690
#ifndef PI
Packit 0ba690
#  define PI             3.141592653589793238
Packit 0ba690
#endif
Packit 0ba690
#define PI_2             (PI*0.5)
Packit 0ba690
#define INV_PI_360       (360.0 / PI)
Packit 0ba690
#define MAX(a,b)         (a>b?a:b)
Packit 0ba690
#define MIN(a,b)         (a
Packit 0ba690
#define CLIP(a,min,max)  MAX(min,MIN((a),max))
Packit 0ba690
#define ABS(a)           ((a)<0?-(a):(a))
Packit 0ba690
#define CLIP8P(c)        MAX(0,(MIN((c),255)))   /* 8-bit pos. integer (uch) */
Packit 0ba690
#define ROUNDF(f)        ((int)(f + 0.5))
Packit 0ba690
Packit 0ba690
#define rgb1_max   bg_freq
Packit 0ba690
#define rgb1_min   bg_gray
Packit 0ba690
#define rgb2_max   bg_bsat
Packit 0ba690
#define rgb2_min   bg_brot
Packit 0ba690
Packit 0ba690
/* #define DEBUG */     /* this enables the Trace() macros */
Packit 0ba690
Packit 0ba690
#include "readpng2.h"   /* typedefs, common macros, readpng2 prototypes */
Packit 0ba690
Packit 0ba690
Packit 0ba690
/* could just include png.h, but this macro is the only thing we need
Packit 0ba690
 * (name and typedefs changed to local versions); note that side effects
Packit 0ba690
 * only happen with alpha (which could easily be avoided with
Packit 0ba690
 * "ush acopy = (alpha);") */
Packit 0ba690
Packit 0ba690
#define alpha_composite(composite, fg, alpha, bg) {               \
Packit 0ba690
    ush temp = ((ush)(fg)*(ush)(alpha) +                          \
Packit 0ba690
                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
Packit 0ba690
    (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
#define INBUFSIZE 4096   /* with pseudo-timing on (1 sec delay/block), this
Packit 0ba690
                          *  block size corresponds roughly to a download
Packit 0ba690
                          *  speed 10% faster than theoretical 33.6K maximum
Packit 0ba690
                          *  (assuming 8 data bits, 1 stop bit and no other
Packit 0ba690
                          *  overhead) */
Packit 0ba690
Packit 0ba690
/* local prototypes */
Packit 0ba690
static void       rpng2_win_init(void);
Packit 0ba690
static int        rpng2_win_create_window(void);
Packit 0ba690
static int        rpng2_win_load_bg_image(void);
Packit 0ba690
static void       rpng2_win_display_row(ulg row);
Packit 0ba690
static void       rpng2_win_finish_display(void);
Packit 0ba690
static void       rpng2_win_cleanup(void);
Packit 0ba690
LRESULT CALLBACK  rpng2_win_wndproc(HWND, UINT, WPARAM, LPARAM);
Packit 0ba690
Packit 0ba690
Packit 0ba690
static char titlebar[1024];
Packit 0ba690
static char *progname = PROGNAME;
Packit 0ba690
static char *appname = LONGNAME;
Packit 0ba690
static char *filename;
Packit 0ba690
static FILE *infile;
Packit 0ba690
Packit 0ba690
static mainprog_info rpng2_info;
Packit 0ba690
Packit 0ba690
static uch inbuf[INBUFSIZE];
Packit 0ba690
static int incount;
Packit 0ba690
Packit 0ba690
static int pat = 6;         /* must be less than num_bgpat */
Packit 0ba690
static int bg_image = 0;
Packit 0ba690
static int bgscale = 16;
Packit 0ba690
static ulg bg_rowbytes;
Packit 0ba690
static uch *bg_data;
Packit 0ba690
Packit 0ba690
static struct rgb_color {
Packit 0ba690
    uch r, g, b;
Packit 0ba690
} rgb[] = {
Packit 0ba690
    {  0,   0,   0},    /*  0:  black */
Packit 0ba690
    {255, 255, 255},    /*  1:  white */
Packit 0ba690
    {173, 132,  57},    /*  2:  tan */
Packit 0ba690
    { 64, 132,   0},    /*  3:  medium green */
Packit 0ba690
    {189, 117,   1},    /*  4:  gold */
Packit 0ba690
    {253, 249,   1},    /*  5:  yellow */
Packit 0ba690
    {  0,   0, 255},    /*  6:  blue */
Packit 0ba690
    {  0,   0, 120},    /*  7:  medium blue */
Packit 0ba690
    {255,   0, 255},    /*  8:  magenta */
Packit 0ba690
    { 64,   0,  64},    /*  9:  dark magenta */
Packit 0ba690
    {255,   0,   0},    /* 10:  red */
Packit 0ba690
    { 64,   0,   0},    /* 11:  dark red */
Packit 0ba690
    {255, 127,   0},    /* 12:  orange */
Packit 0ba690
    {192,  96,   0},    /* 13:  darker orange */
Packit 0ba690
    { 24,  60,   0},    /* 14:  dark green-yellow */
Packit 0ba690
    { 85, 125, 200}     /* 15:  ice blue */
Packit 0ba690
};
Packit 0ba690
/* not used for now, but should be for error-checking:
Packit 0ba690
static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
Packit 0ba690
 */
Packit 0ba690
Packit 0ba690
/*
Packit 0ba690
    This whole struct is a fairly cheesy way to keep the number of
Packit 0ba690
    command-line options to a minimum.  The radial-waves background
Packit 0ba690
    type is a particularly poor fit to the integer elements of the
Packit 0ba690
    struct...but a few macros and a little fixed-point math will do
Packit 0ba690
    wonders for ya.
Packit 0ba690
Packit 0ba690
    type bits:
Packit 0ba690
       F E D C B A 9 8 7 6 5 4 3 2 1 0
Packit 0ba690
                             | | | | |
Packit 0ba690
                             | | +-+-+-- 0 = sharp-edged checkerboard
Packit 0ba690
                             | |         1 = soft diamonds
Packit 0ba690
                             | |         2 = radial waves
Packit 0ba690
                             | |       3-7 = undefined
Packit 0ba690
                             | +-- gradient #2 inverted?
Packit 0ba690
                             +-- alternating columns inverted?
Packit 0ba690
 */
Packit 0ba690
static struct background_pattern {
Packit 0ba690
    ush type;
Packit 0ba690
    int rgb1_max, rgb1_min;     /* or bg_freq, bg_gray */
Packit 0ba690
    int rgb2_max, rgb2_min;     /* or bg_bsat, bg_brot (both scaled by 10)*/
Packit 0ba690
} bg[] = {
Packit 0ba690
    {0+8,   2,0,  1,15},        /* checkered:  tan/black vs. white/ice blue */
Packit 0ba690
    {0+24,  2,0,  1,0},         /* checkered:  tan/black vs. white/black */
Packit 0ba690
    {0+8,   4,5,  0,2},         /* checkered:  gold/yellow vs. black/tan */
Packit 0ba690
    {0+8,   4,5,  0,6},         /* checkered:  gold/yellow vs. black/blue */
Packit 0ba690
    {0,     7,0,  8,9},         /* checkered:  deep blue/black vs. magenta */
Packit 0ba690
    {0+8,  13,0,  5,14},        /* checkered:  orange/black vs. yellow */
Packit 0ba690
    {0+8,  12,0, 10,11},        /* checkered:  orange/black vs. red */
Packit 0ba690
    {1,     7,0,  8,0},         /* diamonds:  deep blue/black vs. magenta */
Packit 0ba690
    {1,    12,0, 11,0},         /* diamonds:  orange vs. dark red */
Packit 0ba690
    {1,    10,0,  7,0},         /* diamonds:  red vs. medium blue */
Packit 0ba690
    {1,     4,0,  5,0},         /* diamonds:  gold vs. yellow */
Packit 0ba690
    {1,     3,0,  0,0},         /* diamonds:  medium green vs. black */
Packit 0ba690
    {2,    16, 100,  20,   0},  /* radial:  ~hard radial color-beams */
Packit 0ba690
    {2,    18, 100,  10,   2},  /* radial:  soft, curved radial color-beams */
Packit 0ba690
    {2,    16, 256, 100, 250},  /* radial:  very tight spiral */
Packit 0ba690
    {2, 10000, 256,  11,   0}   /* radial:  dipole-moire' (almost fractal) */
Packit 0ba690
};
Packit 0ba690
static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
Packit 0ba690
Packit 0ba690
Packit 0ba690
/* Windows-specific global variables (could go in struct, but messy...) */
Packit 0ba690
static ulg wimage_rowbytes;
Packit 0ba690
static uch *dib;
Packit 0ba690
static uch *wimage_data;
Packit 0ba690
static BITMAPINFOHEADER *bmih;
Packit 0ba690
Packit 0ba690
static HWND global_hwnd;
Packit 0ba690
static HINSTANCE global_hInst;
Packit 0ba690
static int global_showmode;
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
Packit 0ba690
{
Packit 0ba690
    char *args[1024];                 /* arbitrary limit, but should suffice */
Packit 0ba690
    char **argv = args;
Packit 0ba690
    char *p, *q, *bgstr = NULL;
Packit 0ba690
    int argc = 0;
Packit 0ba690
    int rc, alen, flen;
Packit 0ba690
    int error = 0;
Packit 0ba690
    int timing = FALSE;
Packit 0ba690
    int have_bg = FALSE;
Packit 0ba690
    double LUT_exponent;              /* just the lookup table */
Packit 0ba690
    double CRT_exponent = 2.2;        /* just the monitor */
Packit 0ba690
    double default_display_exponent;  /* whole display system */
Packit 0ba690
    MSG msg;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* First initialize a few things, just to be sure--memset takes care of
Packit 0ba690
     * default background color (black), booleans (FALSE), pointers (NULL),
Packit 0ba690
     * etc. */
Packit 0ba690
Packit 0ba690
    global_hInst = hInst;
Packit 0ba690
    global_showmode = showmode;
Packit 0ba690
    filename = (char *)NULL;
Packit 0ba690
    memset(&rpng2_info, 0, sizeof(mainprog_info));
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* Next reenable console output, which normally goes to the bit bucket
Packit 0ba690
     * for windowed apps.  Closing the console window will terminate the
Packit 0ba690
     * app.  Thanks to David.Geldreich@realviz.com for supplying the magical
Packit 0ba690
     * incantation. */
Packit 0ba690
Packit 0ba690
    AllocConsole();
Packit 0ba690
    freopen("CONOUT$", "a", stderr);
Packit 0ba690
    freopen("CONOUT$", "a", stdout);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* Set the default value for our display-system exponent, i.e., the
Packit 0ba690
     * product of the CRT exponent and the exponent corresponding to
Packit 0ba690
     * the frame-buffer's lookup table (LUT), if any.  This is not an
Packit 0ba690
     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
Packit 0ba690
     * ones), but it should cover 99% of the current possibilities.  And
Packit 0ba690
     * yes, these ifdefs are completely wasted in a Windows program... */
Packit 0ba690
Packit 0ba690
#if defined(NeXT)
Packit 0ba690
    /* third-party utilities can modify the default LUT exponent */
Packit 0ba690
    LUT_exponent = 1.0 / 2.2;
Packit 0ba690
    /*
Packit 0ba690
    if (some_next_function_that_returns_gamma(&next_gamma))
Packit 0ba690
        LUT_exponent = 1.0 / next_gamma;
Packit 0ba690
     */
Packit 0ba690
#elif defined(sgi)
Packit 0ba690
    LUT_exponent = 1.0 / 1.7;
Packit 0ba690
    /* there doesn't seem to be any documented function to
Packit 0ba690
     * get the "gamma" value, so we do it the hard way */
Packit 0ba690
    infile = fopen("/etc/config/system.glGammaVal", "r");
Packit 0ba690
    if (infile) {
Packit 0ba690
        double sgi_gamma;
Packit 0ba690
Packit 0ba690
        fgets(tmpline, 80, infile);
Packit 0ba690
        fclose(infile);
Packit 0ba690
        sgi_gamma = atof(tmpline);
Packit 0ba690
        if (sgi_gamma > 0.0)
Packit 0ba690
            LUT_exponent = 1.0 / sgi_gamma;
Packit 0ba690
    }
Packit 0ba690
#elif defined(Macintosh)
Packit 0ba690
    LUT_exponent = 1.8 / 2.61;
Packit 0ba690
    /*
Packit 0ba690
    if (some_mac_function_that_returns_gamma(&mac_gamma))
Packit 0ba690
        LUT_exponent = mac_gamma / 2.61;
Packit 0ba690
     */
Packit 0ba690
#else
Packit 0ba690
    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
Packit 0ba690
#endif
Packit 0ba690
Packit 0ba690
    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
Packit 0ba690
    default_display_exponent = LUT_exponent * CRT_exponent;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* If the user has set the SCREEN_GAMMA environment variable as suggested
Packit 0ba690
     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
Packit 0ba690
     * use the default value we just calculated.  Either way, the user may
Packit 0ba690
     * override this via a command-line option. */
Packit 0ba690
Packit 0ba690
    if ((p = getenv("SCREEN_GAMMA")) != NULL)
Packit 0ba690
        rpng2_info.display_exponent = atof(p);
Packit 0ba690
    else
Packit 0ba690
        rpng2_info.display_exponent = default_display_exponent;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* Windows really hates command lines, so we have to set up our own argv.
Packit 0ba690
     * Note that we do NOT bother with quoted arguments here, so don't use
Packit 0ba690
     * filenames with spaces in 'em! */
Packit 0ba690
Packit 0ba690
    argv[argc++] = PROGNAME;
Packit 0ba690
    p = cmd;
Packit 0ba690
    for (;;) {
Packit 0ba690
        if (*p == ' ')
Packit 0ba690
            while (*++p == ' ')
Packit 0ba690
                ;
Packit 0ba690
        /* now p points at the first non-space after some spaces */
Packit 0ba690
        if (*p == '\0')
Packit 0ba690
            break;    /* nothing after the spaces:  done */
Packit 0ba690
        argv[argc++] = q = p;
Packit 0ba690
        while (*q && *q != ' ')
Packit 0ba690
            ++q;
Packit 0ba690
        /* now q points at a space or the end of the string */
Packit 0ba690
        if (*q == '\0')
Packit 0ba690
            break;    /* last argv already terminated; quit */
Packit 0ba690
        *q = '\0';    /* change space to terminator */
Packit 0ba690
        p = q + 1;
Packit 0ba690
    }
Packit 0ba690
    argv[argc] = NULL;   /* terminate the argv array itself */
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* Now parse the command line for options and the PNG filename. */
Packit 0ba690
Packit 0ba690
    while (*++argv && !error) {
Packit 0ba690
        if (!strncmp(*argv, "-gamma", 2)) {
Packit 0ba690
            if (!*++argv)
Packit 0ba690
                ++error;
Packit 0ba690
            else {
Packit 0ba690
                rpng2_info.display_exponent = atof(*argv);
Packit 0ba690
                if (rpng2_info.display_exponent <= 0.0)
Packit 0ba690
                    ++error;
Packit 0ba690
            }
Packit 0ba690
        } else if (!strncmp(*argv, "-bgcolor", 4)) {
Packit 0ba690
            if (!*++argv)
Packit 0ba690
                ++error;
Packit 0ba690
            else {
Packit 0ba690
                bgstr = *argv;
Packit 0ba690
                if (strlen(bgstr) != 7 || bgstr[0] != '#')
Packit 0ba690
                    ++error;
Packit 0ba690
                else {
Packit 0ba690
                    have_bg = TRUE;
Packit 0ba690
                    bg_image = FALSE;
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        } else if (!strncmp(*argv, "-bgpat", 4)) {
Packit 0ba690
            if (!*++argv)
Packit 0ba690
                ++error;
Packit 0ba690
            else {
Packit 0ba690
                pat = atoi(*argv) - 1;
Packit 0ba690
                if (pat < 0 || pat >= num_bgpat)
Packit 0ba690
                    ++error;
Packit 0ba690
                else {
Packit 0ba690
                    bg_image = TRUE;
Packit 0ba690
                    have_bg = FALSE;
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        } else if (!strncmp(*argv, "-timing", 2)) {
Packit 0ba690
            timing = TRUE;
Packit 0ba690
#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
Packit 0ba690
        } else if (!strncmp(*argv, "-nommxfilters", 7)) {
Packit 0ba690
            rpng2_info.nommxfilters = TRUE;
Packit 0ba690
        } else if (!strncmp(*argv, "-nommxcombine", 7)) {
Packit 0ba690
            rpng2_info.nommxcombine = TRUE;
Packit 0ba690
        } else if (!strncmp(*argv, "-nommxinterlace", 7)) {
Packit 0ba690
            rpng2_info.nommxinterlace = TRUE;
Packit 0ba690
        } else if (!strcmp(*argv, "-nommx")) {
Packit 0ba690
            rpng2_info.nommxfilters = TRUE;
Packit 0ba690
            rpng2_info.nommxcombine = TRUE;
Packit 0ba690
            rpng2_info.nommxinterlace = TRUE;
Packit 0ba690
#endif
Packit 0ba690
        } else {
Packit 0ba690
            if (**argv != '-') {
Packit 0ba690
                filename = *argv;
Packit 0ba690
                if (argv[1])   /* shouldn't be any more args after filename */
Packit 0ba690
                    ++error;
Packit 0ba690
            } else
Packit 0ba690
                ++error;   /* not expecting any other options */
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (!filename)
Packit 0ba690
        ++error;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* print usage screen if any errors up to this point */
Packit 0ba690
Packit 0ba690
    if (error) {
Packit 0ba690
        int ch;
Packit 0ba690
Packit 0ba690
        fprintf(stderr, "\n%s %s:  %s\n\n", PROGNAME, VERSION, appname);
Packit 0ba690
        readpng2_version_info();
Packit 0ba690
        fprintf(stderr, "\n"
Packit 0ba690
          "Usage:  %s [-gamma exp] [-bgcolor bg | -bgpat pat] [-timing]\n"
Packit 0ba690
#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
Packit 0ba690
          "        %*s [[-nommxfilters] [-nommxcombine] [-nommxinterlace] | -nommx]\n"
Packit 0ba690
#endif
Packit 0ba690
          "        %*s file.png\n\n"
Packit 0ba690
          "    exp \ttransfer-function exponent (``gamma'') of the display\n"
Packit 0ba690
          "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
Packit 0ba690
          "\t\t  to the product of the lookup-table exponent (varies)\n"
Packit 0ba690
          "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
Packit 0ba690
          "    bg  \tdesired background color in 7-character hex RGB format\n"
Packit 0ba690
          "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
Packit 0ba690
          "\t\t  used with transparent images; overrides -bgpat option\n"
Packit 0ba690
          "    pat \tdesired background pattern number (1-%d); used with\n"
Packit 0ba690
          "\t\t  transparent images; overrides -bgcolor option\n"
Packit 0ba690
          "    -timing\tenables delay for every block read, to simulate modem\n"
Packit 0ba690
          "\t\t  download of image (~36 Kbps)\n"
Packit 0ba690
#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
Packit 0ba690
          "    -nommx*\tdisable optimized MMX routines for decoding row filters,\n"
Packit 0ba690
          "\t\t  combining rows, and expanding interlacing, respectively\n"
Packit 0ba690
#endif
Packit 0ba690
          "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
Packit 0ba690
          "Press Q or Esc to quit this usage screen. ",
Packit 0ba690
          PROGNAME,
Packit 0ba690
#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
Packit 0ba690
          (int)strlen(PROGNAME), " ",
Packit 0ba690
#endif
Packit 0ba690
          (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat);
Packit 0ba690
        fflush(stderr);
Packit 0ba690
        do
Packit 0ba690
            ch = _getch();
Packit 0ba690
        while (ch != 'q' && ch != 'Q' && ch != 0x1B);
Packit 0ba690
        exit(1);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    if (!(infile = fopen(filename, "rb"))) {
Packit 0ba690
        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
Packit 0ba690
        ++error;
Packit 0ba690
    } else {
Packit 0ba690
        incount = fread(inbuf, 1, INBUFSIZE, infile);
Packit 0ba690
        if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
Packit 0ba690
            fprintf(stderr, PROGNAME
Packit 0ba690
              ":  [%s] is not a PNG file: incorrect signature\n",
Packit 0ba690
              filename);
Packit 0ba690
            ++error;
Packit 0ba690
        } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
Packit 0ba690
            switch (rc) {
Packit 0ba690
                case 2:
Packit 0ba690
                    fprintf(stderr, PROGNAME
Packit 0ba690
                      ":  [%s] has bad IHDR (libpng longjmp)\n", filename);
Packit 0ba690
                    break;
Packit 0ba690
                case 4:
Packit 0ba690
                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
Packit 0ba690
                    break;
Packit 0ba690
                default:
Packit 0ba690
                    fprintf(stderr, PROGNAME
Packit 0ba690
                      ":  unknown readpng2_init() error\n");
Packit 0ba690
                    break;
Packit 0ba690
            }
Packit 0ba690
            ++error;
Packit 0ba690
        }
Packit 0ba690
        if (error)
Packit 0ba690
            fclose(infile);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    if (error) {
Packit 0ba690
        int ch;
Packit 0ba690
Packit 0ba690
        fprintf(stderr, PROGNAME ":  aborting.\n");
Packit 0ba690
        do
Packit 0ba690
            ch = _getch();
Packit 0ba690
        while (ch != 'q' && ch != 'Q' && ch != 0x1B);
Packit 0ba690
        exit(2);
Packit 0ba690
    } else {
Packit 0ba690
        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
Packit 0ba690
        fprintf(stderr,
Packit 0ba690
          "\n   [console window:  closing this window will terminate %s]\n\n",
Packit 0ba690
          PROGNAME);
Packit 0ba690
        fflush(stderr);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* set the title-bar string, but make sure buffer doesn't overflow */
Packit 0ba690
Packit 0ba690
    alen = strlen(appname);
Packit 0ba690
    flen = strlen(filename);
Packit 0ba690
    if (alen + flen + 3 > 1023)
Packit 0ba690
        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
Packit 0ba690
    else
Packit 0ba690
        sprintf(titlebar, "%s:  %s", appname, filename);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* set some final rpng2_info variables before entering main data loop */
Packit 0ba690
Packit 0ba690
    if (have_bg) {
Packit 0ba690
        unsigned r, g, b;   /* this approach quiets compiler warnings */
Packit 0ba690
Packit 0ba690
        sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
Packit 0ba690
        rpng2_info.bg_red   = (uch)r;
Packit 0ba690
        rpng2_info.bg_green = (uch)g;
Packit 0ba690
        rpng2_info.bg_blue  = (uch)b;
Packit 0ba690
    } else
Packit 0ba690
        rpng2_info.need_bgcolor = TRUE;
Packit 0ba690
Packit 0ba690
    rpng2_info.state = kPreInit;
Packit 0ba690
    rpng2_info.mainprog_init = rpng2_win_init;
Packit 0ba690
    rpng2_info.mainprog_display_row = rpng2_win_display_row;
Packit 0ba690
    rpng2_info.mainprog_finish_display = rpng2_win_finish_display;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* OK, this is the fun part:  call readpng2_decode_data() at the start of
Packit 0ba690
     * the loop to deal with our first buffer of data (read in above to verify
Packit 0ba690
     * that the file is a PNG image), then loop through the file and continue
Packit 0ba690
     * calling the same routine to handle each chunk of data.  It in turn
Packit 0ba690
     * passes the data to libpng, which will invoke one or more of our call-
Packit 0ba690
     * backs as decoded data become available.  We optionally call Sleep() for
Packit 0ba690
     * one second per iteration to simulate downloading the image via an analog
Packit 0ba690
     * modem. */
Packit 0ba690
Packit 0ba690
    for (;;) {
Packit 0ba690
        Trace((stderr, "about to call readpng2_decode_data()\n"))
Packit 0ba690
        if (readpng2_decode_data(&rpng2_info, inbuf, incount))
Packit 0ba690
            ++error;
Packit 0ba690
        Trace((stderr, "done with readpng2_decode_data()\n"))
Packit 0ba690
Packit 0ba690
        if (error || incount != INBUFSIZE || rpng2_info.state == kDone) {
Packit 0ba690
            if (rpng2_info.state == kDone) {
Packit 0ba690
                Trace((stderr, "done decoding PNG image\n"))
Packit 0ba690
            } else if (ferror(infile)) {
Packit 0ba690
                fprintf(stderr, PROGNAME
Packit 0ba690
                  ":  error while reading PNG image file\n");
Packit 0ba690
                exit(3);
Packit 0ba690
            } else if (feof(infile)) {
Packit 0ba690
                fprintf(stderr, PROGNAME ":  end of file reached "
Packit 0ba690
                  "(unexpectedly) while reading PNG image file\n");
Packit 0ba690
                exit(3);
Packit 0ba690
            } else /* if (error) */ {
Packit 0ba690
                // will print error message below
Packit 0ba690
            }
Packit 0ba690
            break;
Packit 0ba690
        }
Packit 0ba690
Packit 0ba690
        if (timing)
Packit 0ba690
            Sleep(1000L);
Packit 0ba690
Packit 0ba690
        incount = fread(inbuf, 1, INBUFSIZE, infile);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* clean up PNG stuff and report any decoding errors */
Packit 0ba690
Packit 0ba690
    fclose(infile);
Packit 0ba690
    Trace((stderr, "about to call readpng2_cleanup()\n"))
Packit 0ba690
    readpng2_cleanup(&rpng2_info);
Packit 0ba690
Packit 0ba690
    if (error) {
Packit 0ba690
        fprintf(stderr, PROGNAME ":  libpng error while decoding PNG image\n");
Packit 0ba690
        exit(3);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* wait for the user to tell us when to quit */
Packit 0ba690
Packit 0ba690
    while (GetMessage(&msg, NULL, 0, 0)) {
Packit 0ba690
        TranslateMessage(&msg;;
Packit 0ba690
        DispatchMessage(&msg;;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* we're done:  clean up all image and Windows resources and go away */
Packit 0ba690
Packit 0ba690
    Trace((stderr, "about to call rpng2_win_cleanup()\n"))
Packit 0ba690
    rpng2_win_cleanup();
Packit 0ba690
Packit 0ba690
    return msg.wParam;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
/* this function is called by readpng2_info_callback() in readpng2.c, which
Packit 0ba690
 * in turn is called by libpng after all of the pre-IDAT chunks have been
Packit 0ba690
 * read and processed--i.e., we now have enough info to finish initializing */
Packit 0ba690
Packit 0ba690
static void rpng2_win_init()
Packit 0ba690
{
Packit 0ba690
    ulg i;
Packit 0ba690
    ulg rowbytes = rpng2_info.rowbytes;
Packit 0ba690
Packit 0ba690
    Trace((stderr, "beginning rpng2_win_init()\n"))
Packit 0ba690
    Trace((stderr, "  rowbytes = %d\n", rpng2_info.rowbytes))
Packit 0ba690
    Trace((stderr, "  width  = %ld\n", rpng2_info.width))
Packit 0ba690
    Trace((stderr, "  height = %ld\n", rpng2_info.height))
Packit 0ba690
Packit 0ba690
    rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
Packit 0ba690
    if (!rpng2_info.image_data) {
Packit 0ba690
        readpng2_cleanup(&rpng2_info);
Packit 0ba690
        return;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
Packit 0ba690
    if (!rpng2_info.row_pointers) {
Packit 0ba690
        free(rpng2_info.image_data);
Packit 0ba690
        rpng2_info.image_data = NULL;
Packit 0ba690
        readpng2_cleanup(&rpng2_info);
Packit 0ba690
        return;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    for (i = 0;  i < rpng2_info.height;  ++i)
Packit 0ba690
        rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Do the basic Windows initialization stuff, make the window, and fill it
Packit 0ba690
    with the user-specified, file-specified or default background color.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    if (rpng2_win_create_window()) {
Packit 0ba690
        readpng2_cleanup(&rpng2_info);
Packit 0ba690
        return;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    rpng2_info.state = kWindowInit;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static int rpng2_win_create_window()
Packit 0ba690
{
Packit 0ba690
    uch bg_red   = rpng2_info.bg_red;
Packit 0ba690
    uch bg_green = rpng2_info.bg_green;
Packit 0ba690
    uch bg_blue  = rpng2_info.bg_blue;
Packit 0ba690
    uch *dest;
Packit 0ba690
    int extra_width, extra_height;
Packit 0ba690
    ulg i, j;
Packit 0ba690
    WNDCLASSEX wndclass;
Packit 0ba690
    RECT rect;
Packit 0ba690
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Allocate memory for the display-specific version of the image (round up
Packit 0ba690
    to multiple of 4 for Windows DIB).
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    wimage_rowbytes = ((3*rpng2_info.width + 3L) >> 2) << 2;
Packit 0ba690
Packit 0ba690
    if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
Packit 0ba690
                              wimage_rowbytes*rpng2_info.height)))
Packit 0ba690
    {
Packit 0ba690
        return 4;   /* fail */
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Initialize the DIB.  Negative height means to use top-down BMP ordering
Packit 0ba690
    (must be uncompressed, but that's what we want).  Bit count of 1, 4 or 8
Packit 0ba690
    implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values
Packit 0ba690
    directly => wimage_data begins immediately after BMP header.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    memset(dib, 0, sizeof(BITMAPINFOHEADER));
Packit 0ba690
    bmih = (BITMAPINFOHEADER *)dib;
Packit 0ba690
    bmih->biSize = sizeof(BITMAPINFOHEADER);
Packit 0ba690
    bmih->biWidth = rpng2_info.width;
Packit 0ba690
    bmih->biHeight = -((long)rpng2_info.height);
Packit 0ba690
    bmih->biPlanes = 1;
Packit 0ba690
    bmih->biBitCount = 24;
Packit 0ba690
    bmih->biCompression = 0;
Packit 0ba690
    wimage_data = dib + sizeof(BITMAPINFOHEADER);
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Fill window with the specified background color (default is black), but
Packit 0ba690
    defer loading faked "background image" until window is displayed (may be
Packit 0ba690
    slow to compute).  Data are in BGR order.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    if (bg_image) {   /* just fill with black for now */
Packit 0ba690
        memset(wimage_data, 0, wimage_rowbytes*rpng2_info.height);
Packit 0ba690
    } else {
Packit 0ba690
        for (j = 0;  j < rpng2_info.height;  ++j) {
Packit 0ba690
            dest = wimage_data + j*wimage_rowbytes;
Packit 0ba690
            for (i = rpng2_info.width;  i > 0;  --i) {
Packit 0ba690
                *dest++ = bg_blue;
Packit 0ba690
                *dest++ = bg_green;
Packit 0ba690
                *dest++ = bg_red;
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Set the window parameters.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    memset(&wndclass, 0, sizeof(wndclass));
Packit 0ba690
Packit 0ba690
    wndclass.cbSize = sizeof(wndclass);
Packit 0ba690
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
Packit 0ba690
    wndclass.lpfnWndProc = rpng2_win_wndproc;
Packit 0ba690
    wndclass.hInstance = global_hInst;
Packit 0ba690
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
Packit 0ba690
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
Packit 0ba690
    wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
Packit 0ba690
    wndclass.lpszMenuName = NULL;
Packit 0ba690
    wndclass.lpszClassName = progname;
Packit 0ba690
    wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
Packit 0ba690
Packit 0ba690
    RegisterClassEx(&wndclass);
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Finally, create the window.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    extra_width  = 2*(GetSystemMetrics(SM_CXBORDER) +
Packit 0ba690
                      GetSystemMetrics(SM_CXDLGFRAME));
Packit 0ba690
    extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
Packit 0ba690
                      GetSystemMetrics(SM_CYDLGFRAME)) +
Packit 0ba690
                      GetSystemMetrics(SM_CYCAPTION);
Packit 0ba690
Packit 0ba690
    global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW,
Packit 0ba690
      CW_USEDEFAULT, CW_USEDEFAULT, rpng2_info.width+extra_width,
Packit 0ba690
      rpng2_info.height+extra_height, NULL, NULL, global_hInst, NULL);
Packit 0ba690
Packit 0ba690
    ShowWindow(global_hwnd, global_showmode);
Packit 0ba690
    UpdateWindow(global_hwnd);
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Now compute the background image and display it.  If it fails (memory
Packit 0ba690
    allocation), revert to a plain background color.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    if (bg_image) {
Packit 0ba690
        static const char *msg = "Computing background image...";
Packit 0ba690
        int x, y, len = strlen(msg);
Packit 0ba690
        HDC hdc = GetDC(global_hwnd);
Packit 0ba690
        TEXTMETRIC tm;
Packit 0ba690
Packit 0ba690
        GetTextMetrics(hdc, &tm;;
Packit 0ba690
        x = (rpng2_info.width - len*tm.tmAveCharWidth)/2;
Packit 0ba690
        y = (rpng2_info.height - tm.tmHeight)/2;
Packit 0ba690
        SetBkMode(hdc, TRANSPARENT);
Packit 0ba690
        SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
Packit 0ba690
        /* this can still begin out of bounds even if x is positive (???): */
Packit 0ba690
        TextOut(hdc, ((x < 0)? 0 : x), ((y < 0)? 0 : y), msg, len);
Packit 0ba690
        ReleaseDC(global_hwnd, hdc);
Packit 0ba690
Packit 0ba690
        rpng2_win_load_bg_image();   /* resets bg_image if fails */
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (!bg_image) {
Packit 0ba690
        for (j = 0;  j < rpng2_info.height;  ++j) {
Packit 0ba690
            dest = wimage_data + j*wimage_rowbytes;
Packit 0ba690
            for (i = rpng2_info.width;  i > 0;  --i) {
Packit 0ba690
                *dest++ = bg_blue;
Packit 0ba690
                *dest++ = bg_green;
Packit 0ba690
                *dest++ = bg_red;
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    rect.left = 0L;
Packit 0ba690
    rect.top = 0L;
Packit 0ba690
    rect.right = (LONG)rpng2_info.width;       /* possibly off by one? */
Packit 0ba690
    rect.bottom = (LONG)rpng2_info.height;     /* possibly off by one? */
Packit 0ba690
    InvalidateRect(global_hwnd, &rect, FALSE);
Packit 0ba690
    UpdateWindow(global_hwnd);                 /* similar to XFlush() */
Packit 0ba690
Packit 0ba690
    return 0;
Packit 0ba690
Packit 0ba690
} /* end function rpng2_win_create_window() */
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static int rpng2_win_load_bg_image()
Packit 0ba690
{
Packit 0ba690
    uch *src, *dest;
Packit 0ba690
    uch r1, r2, g1, g2, b1, b2;
Packit 0ba690
    uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
Packit 0ba690
    int k, hmax, max;
Packit 0ba690
    int xidx, yidx, yidx_max = (bgscale-1);
Packit 0ba690
    int even_odd_vert, even_odd_horiz, even_odd;
Packit 0ba690
    int invert_gradient2 = (bg[pat].type & 0x08);
Packit 0ba690
    int invert_column;
Packit 0ba690
    ulg i, row;
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Allocate buffer for fake background image to be used with transparent
Packit 0ba690
    images; if this fails, revert to plain background color.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    bg_rowbytes = 3 * rpng2_info.width;
Packit 0ba690
    bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
Packit 0ba690
    if (!bg_data) {
Packit 0ba690
        fprintf(stderr, PROGNAME
Packit 0ba690
          ":  unable to allocate memory for background image\n");
Packit 0ba690
        bg_image = 0;
Packit 0ba690
        return 1;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Vertical gradients (ramps) in NxN squares, alternating direction and
Packit 0ba690
    colors (N == bgscale).
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    if ((bg[pat].type & 0x07) == 0) {
Packit 0ba690
        uch r1_min  = rgb[bg[pat].rgb1_min].r;
Packit 0ba690
        uch g1_min  = rgb[bg[pat].rgb1_min].g;
Packit 0ba690
        uch b1_min  = rgb[bg[pat].rgb1_min].b;
Packit 0ba690
        uch r2_min  = rgb[bg[pat].rgb2_min].r;
Packit 0ba690
        uch g2_min  = rgb[bg[pat].rgb2_min].g;
Packit 0ba690
        uch b2_min  = rgb[bg[pat].rgb2_min].b;
Packit 0ba690
        int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
Packit 0ba690
        int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
Packit 0ba690
        int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
Packit 0ba690
        int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
Packit 0ba690
        int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
Packit 0ba690
        int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
Packit 0ba690
Packit 0ba690
        for (row = 0;  row < rpng2_info.height;  ++row) {
Packit 0ba690
            yidx = row % bgscale;
Packit 0ba690
            even_odd_vert = (row / bgscale) & 1;
Packit 0ba690
Packit 0ba690
            r1 = r1_min + (r1_diff * yidx) / yidx_max;
Packit 0ba690
            g1 = g1_min + (g1_diff * yidx) / yidx_max;
Packit 0ba690
            b1 = b1_min + (b1_diff * yidx) / yidx_max;
Packit 0ba690
            r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
Packit 0ba690
            g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
Packit 0ba690
            b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
Packit 0ba690
Packit 0ba690
            r2 = r2_min + (r2_diff * yidx) / yidx_max;
Packit 0ba690
            g2 = g2_min + (g2_diff * yidx) / yidx_max;
Packit 0ba690
            b2 = b2_min + (b2_diff * yidx) / yidx_max;
Packit 0ba690
            r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
Packit 0ba690
            g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
Packit 0ba690
            b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
Packit 0ba690
Packit 0ba690
            dest = bg_data + row*bg_rowbytes;
Packit 0ba690
            for (i = 0;  i < rpng2_info.width;  ++i) {
Packit 0ba690
                even_odd_horiz = (i / bgscale) & 1;
Packit 0ba690
                even_odd = even_odd_vert ^ even_odd_horiz;
Packit 0ba690
                invert_column =
Packit 0ba690
                  (even_odd_horiz && (bg[pat].type & 0x10));
Packit 0ba690
                if (even_odd == 0) {         /* gradient #1 */
Packit 0ba690
                    if (invert_column) {
Packit 0ba690
                        *dest++ = r1_inv;
Packit 0ba690
                        *dest++ = g1_inv;
Packit 0ba690
                        *dest++ = b1_inv;
Packit 0ba690
                    } else {
Packit 0ba690
                        *dest++ = r1;
Packit 0ba690
                        *dest++ = g1;
Packit 0ba690
                        *dest++ = b1;
Packit 0ba690
                    }
Packit 0ba690
                } else {                     /* gradient #2 */
Packit 0ba690
                    if ((invert_column && invert_gradient2) ||
Packit 0ba690
                        (!invert_column && !invert_gradient2))
Packit 0ba690
                    {
Packit 0ba690
                        *dest++ = r2;        /* not inverted or */
Packit 0ba690
                        *dest++ = g2;        /*  doubly inverted */
Packit 0ba690
                        *dest++ = b2;
Packit 0ba690
                    } else {
Packit 0ba690
                        *dest++ = r2_inv;
Packit 0ba690
                        *dest++ = g2_inv;    /* singly inverted */
Packit 0ba690
                        *dest++ = b2_inv;
Packit 0ba690
                    }
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
Packit 0ba690
    M. Costello.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    } else if ((bg[pat].type & 0x07) == 1) {
Packit 0ba690
Packit 0ba690
        hmax = (bgscale-1)/2;   /* half the max weight of a color */
Packit 0ba690
        max = 2*hmax;           /* the max weight of a color */
Packit 0ba690
Packit 0ba690
        r1 = rgb[bg[pat].rgb1_max].r;
Packit 0ba690
        g1 = rgb[bg[pat].rgb1_max].g;
Packit 0ba690
        b1 = rgb[bg[pat].rgb1_max].b;
Packit 0ba690
        r2 = rgb[bg[pat].rgb2_max].r;
Packit 0ba690
        g2 = rgb[bg[pat].rgb2_max].g;
Packit 0ba690
        b2 = rgb[bg[pat].rgb2_max].b;
Packit 0ba690
Packit 0ba690
        for (row = 0;  row < rpng2_info.height;  ++row) {
Packit 0ba690
            yidx = row % bgscale;
Packit 0ba690
            if (yidx > hmax)
Packit 0ba690
                yidx = bgscale-1 - yidx;
Packit 0ba690
            dest = bg_data + row*bg_rowbytes;
Packit 0ba690
            for (i = 0;  i < rpng2_info.width;  ++i) {
Packit 0ba690
                xidx = i % bgscale;
Packit 0ba690
                if (xidx > hmax)
Packit 0ba690
                    xidx = bgscale-1 - xidx;
Packit 0ba690
                k = xidx + yidx;
Packit 0ba690
                *dest++ = (k*r1 + (max-k)*r2) / max;
Packit 0ba690
                *dest++ = (k*g1 + (max-k)*g2) / max;
Packit 0ba690
                *dest++ = (k*b1 + (max-k)*b2) / max;
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
Packit 0ba690
    soids will equal bgscale?].  This one is slow but very cool.  Code con-
Packit 0ba690
    tributed by Pieter S. van der Meulen (originally in Smalltalk).
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    } else if ((bg[pat].type & 0x07) == 2) {
Packit 0ba690
        uch ch;
Packit 0ba690
        int ii, x, y, hw, hh, grayspot;
Packit 0ba690
        double freq, rotate, saturate, gray, intensity;
Packit 0ba690
        double angle=0.0, aoffset=0.0, maxDist, dist;
Packit 0ba690
        double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
Packit 0ba690
Packit 0ba690
        fprintf(stderr, "%s:  computing radial background...",
Packit 0ba690
          PROGNAME);
Packit 0ba690
        fflush(stderr);
Packit 0ba690
Packit 0ba690
        hh = rpng2_info.height / 2;
Packit 0ba690
        hw = rpng2_info.width / 2;
Packit 0ba690
Packit 0ba690
        /* variables for radial waves:
Packit 0ba690
         *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
Packit 0ba690
         *   freq:  number of color beams originating from the center
Packit 0ba690
         *   grayspot:  size of the graying center area (anti-alias)
Packit 0ba690
         *   rotate:  rotation of the beams as a function of radius
Packit 0ba690
         *   saturate:  saturation of beams' shape azimuthally
Packit 0ba690
         */
Packit 0ba690
        angle = CLIP(angle, 0.0, 360.0);
Packit 0ba690
        grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
Packit 0ba690
        freq = MAX((double)bg[pat].bg_freq, 0.0);
Packit 0ba690
        saturate = (double)bg[pat].bg_bsat * 0.1;
Packit 0ba690
        rotate = (double)bg[pat].bg_brot * 0.1;
Packit 0ba690
        gray = 0.0;
Packit 0ba690
        intensity = 0.0;
Packit 0ba690
        maxDist = (double)((hw*hw) + (hh*hh));
Packit 0ba690
Packit 0ba690
        for (row = 0;  row < rpng2_info.height;  ++row) {
Packit 0ba690
            y = row - hh;
Packit 0ba690
            dest = bg_data + row*bg_rowbytes;
Packit 0ba690
            for (i = 0;  i < rpng2_info.width;  ++i) {
Packit 0ba690
                x = i - hw;
Packit 0ba690
                angle = (x == 0)? PI_2 : atan((double)y / (double)x);
Packit 0ba690
                gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
Packit 0ba690
                gray = MIN(1.0, gray);
Packit 0ba690
                dist = (double)((x*x) + (y*y)) / maxDist;
Packit 0ba690
                intensity = cos((angle+(rotate*dist*PI)) * freq) *
Packit 0ba690
                  gray * saturate;
Packit 0ba690
                intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
Packit 0ba690
                hue = (angle + PI) * INV_PI_360 + aoffset;
Packit 0ba690
                s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
Packit 0ba690
                s = MIN(MAX(s,0.0), 1.0);
Packit 0ba690
                v = MIN(MAX(intensity,0.0), 1.0);
Packit 0ba690
Packit 0ba690
                if (s == 0.0) {
Packit 0ba690
                    ch = (uch)(v * 255.0);
Packit 0ba690
                    *dest++ = ch;
Packit 0ba690
                    *dest++ = ch;
Packit 0ba690
                    *dest++ = ch;
Packit 0ba690
                } else {
Packit 0ba690
                    if ((hue < 0.0) || (hue >= 360.0))
Packit 0ba690
                        hue -= (((int)(hue / 360.0)) * 360.0);
Packit 0ba690
                    hue /= 60.0;
Packit 0ba690
                    ii = (int)hue;
Packit 0ba690
                    f = hue - (double)ii;
Packit 0ba690
                    p = (1.0 - s) * v;
Packit 0ba690
                    q = (1.0 - (s * f)) * v;
Packit 0ba690
                    t = (1.0 - (s * (1.0 - f))) * v;
Packit 0ba690
                    if      (ii == 0) { red = v; green = t; blue = p; }
Packit 0ba690
                    else if (ii == 1) { red = q; green = v; blue = p; }
Packit 0ba690
                    else if (ii == 2) { red = p; green = v; blue = t; }
Packit 0ba690
                    else if (ii == 3) { red = p; green = q; blue = v; }
Packit 0ba690
                    else if (ii == 4) { red = t; green = p; blue = v; }
Packit 0ba690
                    else if (ii == 5) { red = v; green = p; blue = q; }
Packit 0ba690
                    *dest++ = (uch)(red * 255.0);
Packit 0ba690
                    *dest++ = (uch)(green * 255.0);
Packit 0ba690
                    *dest++ = (uch)(blue * 255.0);
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
        fprintf(stderr, "done.\n");
Packit 0ba690
        fflush(stderr);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Blast background image to display buffer before beginning PNG decode;
Packit 0ba690
    calling function will handle invalidation and UpdateWindow() call.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    for (row = 0;  row < rpng2_info.height;  ++row) {
Packit 0ba690
        src = bg_data + row*bg_rowbytes;
Packit 0ba690
        dest = wimage_data + row*wimage_rowbytes;
Packit 0ba690
        for (i = rpng2_info.width;  i > 0;  --i) {
Packit 0ba690
            r1 = *src++;
Packit 0ba690
            g1 = *src++;
Packit 0ba690
            b1 = *src++;
Packit 0ba690
            *dest++ = b1;
Packit 0ba690
            *dest++ = g1;   /* note reverse order */
Packit 0ba690
            *dest++ = r1;
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    return 0;
Packit 0ba690
Packit 0ba690
} /* end function rpng2_win_load_bg_image() */
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static void rpng2_win_display_row(ulg row)
Packit 0ba690
{
Packit 0ba690
    uch bg_red   = rpng2_info.bg_red;
Packit 0ba690
    uch bg_green = rpng2_info.bg_green;
Packit 0ba690
    uch bg_blue  = rpng2_info.bg_blue;
Packit 0ba690
    uch *src, *src2=NULL, *dest;
Packit 0ba690
    uch r, g, b, a;
Packit 0ba690
    ulg i;
Packit 0ba690
    static int rows=0;
Packit 0ba690
    static ulg firstrow;
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    rows and firstrow simply track how many rows (and which ones) have not
Packit 0ba690
    yet been displayed; alternatively, we could call InvalidateRect() for
Packit 0ba690
    every row and not bother with the records-keeping.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    Trace((stderr, "beginning rpng2_win_display_row()\n"))
Packit 0ba690
Packit 0ba690
    if (rows == 0)
Packit 0ba690
        firstrow = row;   /* first row not yet displayed */
Packit 0ba690
Packit 0ba690
    ++rows;   /* count of rows received but not yet displayed */
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Aside from the use of the rpng2_info struct and the lack of an outer
Packit 0ba690
    loop (over rows), this routine is identical to rpng_win_display_image()
Packit 0ba690
    in the non-progressive version of the program.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    src = rpng2_info.image_data + row*rpng2_info.rowbytes;
Packit 0ba690
    if (bg_image)
Packit 0ba690
        src2 = bg_data + row*bg_rowbytes;
Packit 0ba690
    dest = wimage_data + row*wimage_rowbytes;
Packit 0ba690
Packit 0ba690
    if (rpng2_info.channels == 3) {
Packit 0ba690
        for (i = rpng2_info.width;  i > 0;  --i) {
Packit 0ba690
            r = *src++;
Packit 0ba690
            g = *src++;
Packit 0ba690
            b = *src++;
Packit 0ba690
            *dest++ = b;
Packit 0ba690
            *dest++ = g;   /* note reverse order */
Packit 0ba690
            *dest++ = r;
Packit 0ba690
        }
Packit 0ba690
    } else /* if (rpng2_info.channels == 4) */ {
Packit 0ba690
        for (i = rpng2_info.width;  i > 0;  --i) {
Packit 0ba690
            r = *src++;
Packit 0ba690
            g = *src++;
Packit 0ba690
            b = *src++;
Packit 0ba690
            a = *src++;
Packit 0ba690
            if (bg_image) {
Packit 0ba690
                bg_red   = *src2++;
Packit 0ba690
                bg_green = *src2++;
Packit 0ba690
                bg_blue  = *src2++;
Packit 0ba690
            }
Packit 0ba690
            if (a == 255) {
Packit 0ba690
                *dest++ = b;
Packit 0ba690
                *dest++ = g;
Packit 0ba690
                *dest++ = r;
Packit 0ba690
            } else if (a == 0) {
Packit 0ba690
                *dest++ = bg_blue;
Packit 0ba690
                *dest++ = bg_green;
Packit 0ba690
                *dest++ = bg_red;
Packit 0ba690
            } else {
Packit 0ba690
                /* this macro (copied from png.h) composites the
Packit 0ba690
                 * foreground and background values and puts the
Packit 0ba690
                 * result into the first argument; there are no
Packit 0ba690
                 * side effects with the first argument */
Packit 0ba690
                alpha_composite(*dest++, b, a, bg_blue);
Packit 0ba690
                alpha_composite(*dest++, g, a, bg_green);
Packit 0ba690
                alpha_composite(*dest++, r, a, bg_red);
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Display after every 16 rows or when on last row.  (Region may include
Packit 0ba690
    previously displayed lines due to interlacing--i.e., not contiguous.)
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    if ((rows & 0xf) == 0 || row == rpng2_info.height-1) {
Packit 0ba690
        RECT rect;
Packit 0ba690
Packit 0ba690
        rect.left = 0L;
Packit 0ba690
        rect.top = (LONG)firstrow;
Packit 0ba690
        rect.right = (LONG)rpng2_info.width;       /* possibly off by one? */
Packit 0ba690
        rect.bottom = (LONG)row + 1L;              /* possibly off by one? */
Packit 0ba690
        InvalidateRect(global_hwnd, &rect, FALSE);
Packit 0ba690
        UpdateWindow(global_hwnd);                 /* similar to XFlush() */
Packit 0ba690
        rows = 0;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
} /* end function rpng2_win_display_row() */
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static void rpng2_win_finish_display()
Packit 0ba690
{
Packit 0ba690
    Trace((stderr, "beginning rpng2_win_finish_display()\n"))
Packit 0ba690
Packit 0ba690
    /* last row has already been displayed by rpng2_win_display_row(), so
Packit 0ba690
     * we have nothing to do here except set a flag and let the user know
Packit 0ba690
     * that the image is done */
Packit 0ba690
Packit 0ba690
    rpng2_info.state = kDone;
Packit 0ba690
    printf(
Packit 0ba690
      "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
Packit 0ba690
    fflush(stdout);
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static void rpng2_win_cleanup()
Packit 0ba690
{
Packit 0ba690
    if (bg_image && bg_data) {
Packit 0ba690
        free(bg_data);
Packit 0ba690
        bg_data = NULL;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (rpng2_info.image_data) {
Packit 0ba690
        free(rpng2_info.image_data);
Packit 0ba690
        rpng2_info.image_data = NULL;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (rpng2_info.row_pointers) {
Packit 0ba690
        free(rpng2_info.row_pointers);
Packit 0ba690
        rpng2_info.row_pointers = NULL;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (dib) {
Packit 0ba690
        free(dib);
Packit 0ba690
        dib = NULL;
Packit 0ba690
    }
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
LRESULT CALLBACK rpng2_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
Packit 0ba690
{
Packit 0ba690
    HDC         hdc;
Packit 0ba690
    PAINTSTRUCT ps;
Packit 0ba690
    int rc;
Packit 0ba690
Packit 0ba690
    switch (iMsg) {
Packit 0ba690
        case WM_CREATE:
Packit 0ba690
            /* one-time processing here, if any */
Packit 0ba690
            return 0;
Packit 0ba690
Packit 0ba690
        case WM_PAINT:
Packit 0ba690
            hdc = BeginPaint(hwnd, &ps);
Packit 0ba690
            rc = StretchDIBits(hdc, 0, 0, rpng2_info.width, rpng2_info.height,
Packit 0ba690
                                    0, 0, rpng2_info.width, rpng2_info.height,
Packit 0ba690
                                    wimage_data, (BITMAPINFO *)bmih,
Packit 0ba690
                                    0, SRCCOPY);
Packit 0ba690
            EndPaint(hwnd, &ps);
Packit 0ba690
            return 0;
Packit 0ba690
Packit 0ba690
        /* wait for the user to tell us when to quit */
Packit 0ba690
        case WM_CHAR:
Packit 0ba690
            switch (wP) {       /* only need one, so ignore repeat count */
Packit 0ba690
                case 'q':
Packit 0ba690
                case 'Q':
Packit 0ba690
                case 0x1B:      /* Esc key */
Packit 0ba690
                    PostQuitMessage(0);
Packit 0ba690
            }
Packit 0ba690
            return 0;
Packit 0ba690
Packit 0ba690
        case WM_LBUTTONDOWN:    /* another way of quitting */
Packit 0ba690
        case WM_DESTROY:
Packit 0ba690
            PostQuitMessage(0);
Packit 0ba690
            return 0;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    return DefWindowProc(hwnd, iMsg, wP, lP);
Packit 0ba690
}