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