Home | History | Annotate | Download | only in gregbook
      1 /*---------------------------------------------------------------------------
      2 
      3    rpng2 - progressive-model PNG display program                  rpng2-x.c
      4 
      5    This program decodes and displays PNG files progressively, as if it were
      6    a web browser (though the front end is only set up to read from files).
      7    It supports gamma correction, user-specified background colors, and user-
      8    specified background patterns (for transparent images).  This version is
      9    for the X Window System (tested by the author under Unix and by Martin
     10    Zinser under OpenVMS; may work under OS/2 with a little tweaking).
     11 
     12    Thanks to Adam Costello and Pieter S. van der Meulen for the "diamond"
     13    and "radial waves" patterns, respectively.
     14 
     15    to do (someday, maybe):
     16     - fix expose/redraw code:  don't draw entire row if only part exposed
     17     - 8-bit (colormapped) X support
     18     - finish resizable checkerboard-gradient (sizes 4-128?)
     19     - use %.1023s to simplify truncation of title-bar string?
     20 
     21   ---------------------------------------------------------------------------
     22 
     23    Changelog:
     24     - 1.01:  initial public release
     25     - 1.02:  modified to allow abbreviated options; fixed char/uchar mismatch
     26     - 1.10:  added support for non-default visuals; fixed X pixel-conversion
     27     - 1.11:  added -usleep option for demos; fixed command-line parsing bug
     28     - 1.12:  added -pause option for demos and testing
     29     - 1.20:  added runtime MMX-enabling/disabling and new -mmx* options
     30     - 1.21:  fixed some small X memory leaks (thanks to Franois Petitjean)
     31     - 1.22:  fixed XFreeGC() crash bug (thanks to Patrick Welche)
     32     - 1.23:  added -bgpat 0 mode (std white/gray checkerboard, 8x8 squares)
     33     - 1.30:  added -loop option for -bgpat (ifdef FEATURE_LOOP); fixed bpp =
     34               24; added support for X resources (thanks to Gerhard Niklasch)
     35     - 1.31:  added code to skip unused chunks (thanks to Glenn Randers-Pehrson)
     36     - 1.32:  added AMD64/EM64T support (__x86_64__); added basic expose/redraw
     37               handling
     38     - 2.00:  dual-licensed (added GNU GPL)
     39     - 2.01:  fixed 64-bit typo in readpng2.c; fixed -pause usage description
     40     - 2.02:  fixed improper display of usage screen on PNG error(s); fixed
     41               unexpected-EOF and file-read-error cases; fixed Trace() cut-and-
     42               paste bugs
     43 
     44   ---------------------------------------------------------------------------
     45 
     46       Copyright (c) 1998-2008 Greg Roelofs.  All rights reserved.
     47 
     48       This software is provided "as is," without warranty of any kind,
     49       express or implied.  In no event shall the author or contributors
     50       be held liable for any damages arising in any way from the use of
     51       this software.
     52 
     53       The contents of this file are DUAL-LICENSED.  You may modify and/or
     54       redistribute this software according to the terms of one of the
     55       following two licenses (at your option):
     56 
     57 
     58       LICENSE 1 ("BSD-like with advertising clause"):
     59 
     60       Permission is granted to anyone to use this software for any purpose,
     61       including commercial applications, and to alter it and redistribute
     62       it freely, subject to the following restrictions:
     63 
     64       1. Redistributions of source code must retain the above copyright
     65          notice, disclaimer, and this list of conditions.
     66       2. Redistributions in binary form must reproduce the above copyright
     67          notice, disclaimer, and this list of conditions in the documenta-
     68          tion and/or other materials provided with the distribution.
     69       3. All advertising materials mentioning features or use of this
     70          software must display the following acknowledgment:
     71 
     72             This product includes software developed by Greg Roelofs
     73             and contributors for the book, "PNG: The Definitive Guide,"
     74             published by O'Reilly and Associates.
     75 
     76 
     77       LICENSE 2 (GNU GPL v2 or later):
     78 
     79       This program is free software; you can redistribute it and/or modify
     80       it under the terms of the GNU General Public License as published by
     81       the Free Software Foundation; either version 2 of the License, or
     82       (at your option) any later version.
     83 
     84       This program is distributed in the hope that it will be useful,
     85       but WITHOUT ANY WARRANTY; without even the implied warranty of
     86       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     87       GNU General Public License for more details.
     88 
     89       You should have received a copy of the GNU General Public License
     90       along with this program; if not, write to the Free Software Foundation,
     91       Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     92 
     93   ---------------------------------------------------------------------------*/
     94 
     95 #define PROGNAME  "rpng2-x"
     96 #define LONGNAME  "Progressive PNG Viewer for X"
     97 #define VERSION   "2.02 of 16 March 2008"
     98 #define RESNAME   "rpng2"	/* our X resource application name */
     99 #define RESCLASS  "Rpng"	/* our X resource class name */
    100 
    101 #include <stdio.h>
    102 #include <stdlib.h>
    103 #include <ctype.h>
    104 #include <string.h>
    105 #include <setjmp.h>       /* for jmpbuf declaration in readpng2.h */
    106 #include <time.h>
    107 #include <math.h>         /* only for PvdM background code */
    108 #include <X11/Xlib.h>
    109 #include <X11/Xutil.h>
    110 #include <X11/Xos.h>
    111 #include <X11/keysym.h>   /* defines XK_* macros */
    112 
    113 #ifdef VMS
    114 #  include <unistd.h>
    115 #endif
    116 
    117 /* all for PvdM background code: */
    118 #ifndef PI
    119 #  define PI             3.141592653589793238
    120 #endif
    121 #define PI_2             (PI*0.5)
    122 #define INV_PI_360       (360.0 / PI)
    123 #define MAX(a,b)         (a>b?a:b)
    124 #define MIN(a,b)         (a<b?a:b)
    125 #define CLIP(a,min,max)  MAX(min,MIN((a),max))
    126 #define ABS(a)           ((a)<0?-(a):(a))
    127 #define CLIP8P(c)        MAX(0,(MIN((c),255)))   /* 8-bit pos. integer (uch) */
    128 #define ROUNDF(f)        ((int)(f + 0.5))
    129 
    130 #define QUIT(e,k) ((e.type == ButtonPress && e.xbutton.button == Button1) ||  \
    131                   (e.type == KeyPress &&   /*  v--- or 1 for shifted keys */  \
    132                   ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape)))
    133 
    134 #define NO_24BIT_MASKS	/* undef case not fully written--only for redisplay() */
    135 
    136 #define rgb1_max   bg_freq
    137 #define rgb1_min   bg_gray
    138 #define rgb2_max   bg_bsat
    139 #define rgb2_min   bg_brot
    140 
    141 /* #define DEBUG */     /* this enables the Trace() macros */
    142 
    143 #include "readpng2.h"   /* typedefs, common macros, readpng2 prototypes */
    144 
    145 
    146 /* could just include png.h, but this macro is the only thing we need
    147  * (name and typedefs changed to local versions); note that side effects
    148  * only happen with alpha (which could easily be avoided with
    149  * "ush acopy = (alpha);") */
    150 
    151 #define alpha_composite(composite, fg, alpha, bg) {               \
    152     ush temp = ((ush)(fg)*(ush)(alpha) +                          \
    153                 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
    154     (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
    155 }
    156 
    157 
    158 #define INBUFSIZE 4096   /* with pseudo-timing on (1 sec delay/block), this
    159                           *  block size corresponds roughly to a download
    160                           *  speed 10% faster than theoretical 33.6K maximum
    161                           *  (assuming 8 data bits, 1 stop bit and no other
    162                           *  overhead) */
    163 
    164 /* local prototypes */
    165 static void rpng2_x_init (void);
    166 static int  rpng2_x_create_window (void);
    167 static int  rpng2_x_load_bg_image (void);
    168 static void rpng2_x_display_row (ulg row);
    169 static void rpng2_x_finish_display (void);
    170 static void rpng2_x_redisplay_image (ulg startcol, ulg startrow,
    171                                      ulg width, ulg height);
    172 #ifdef FEATURE_LOOP
    173 static void rpng2_x_reload_bg_image (void);
    174 static int  is_number (char *p);
    175 #endif
    176 static void rpng2_x_cleanup (void);
    177 static int  rpng2_x_msb (ulg u32val);
    178 
    179 
    180 static char titlebar[1024], *window_name = titlebar;
    181 static char *appname = LONGNAME;
    182 static char *icon_name = PROGNAME;
    183 static char *res_name = RESNAME;
    184 static char *res_class = RESCLASS;
    185 static char *filename;
    186 static FILE *infile;
    187 
    188 static mainprog_info rpng2_info;
    189 
    190 static uch inbuf[INBUFSIZE];
    191 static int incount;
    192 
    193 static int pat = 6;        /* must be less than num_bgpat */
    194 static int bg_image = 0;
    195 static int bgscale, bgscale_default = 16;
    196 static ulg bg_rowbytes;
    197 static uch *bg_data;
    198 
    199 int pause_after_pass = FALSE;
    200 int demo_timing = FALSE;
    201 ulg usleep_duration = 0L;
    202 
    203 static struct rgb_color {
    204     uch r, g, b;
    205 } rgb[] = {
    206     {  0,   0,   0},    /*  0:  black */
    207     {255, 255, 255},    /*  1:  white */
    208     {173, 132,  57},    /*  2:  tan */
    209     { 64, 132,   0},    /*  3:  medium green */
    210     {189, 117,   1},    /*  4:  gold */
    211     {253, 249,   1},    /*  5:  yellow */
    212     {  0,   0, 255},    /*  6:  blue */
    213     {  0,   0, 120},    /*  7:  medium blue */
    214     {255,   0, 255},    /*  8:  magenta */
    215     { 64,   0,  64},    /*  9:  dark magenta */
    216     {255,   0,   0},    /* 10:  red */
    217     { 64,   0,   0},    /* 11:  dark red */
    218     {255, 127,   0},    /* 12:  orange */
    219     {192,  96,   0},    /* 13:  darker orange */
    220     { 24,  60,   0},    /* 14:  dark green-yellow */
    221     { 85, 125, 200},    /* 15:  ice blue */
    222     {192, 192, 192}     /* 16:  Netscape/Mosaic gray */
    223 };
    224 /* not used for now, but should be for error-checking:
    225 static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
    226  */
    227 
    228 /*
    229     This whole struct is a fairly cheesy way to keep the number of
    230     command-line options to a minimum.  The radial-waves background
    231     type is a particularly poor fit to the integer elements of the
    232     struct...but a few macros and a little fixed-point math will do
    233     wonders for ya.
    234 
    235     type bits:
    236        F E D C B A 9 8 7 6 5 4 3 2 1 0
    237                              | | | | |
    238                              | | +-+-+-- 0 = sharp-edged checkerboard
    239                              | |         1 = soft diamonds
    240                              | |         2 = radial waves
    241                              | |       3-7 = undefined
    242                              | +-- gradient #2 inverted?
    243                              +-- alternating columns inverted?
    244  */
    245 static struct background_pattern {
    246     ush type;
    247     int rgb1_max, rgb1_min;     /* or bg_freq, bg_gray */
    248     int rgb2_max, rgb2_min;     /* or bg_bsat, bg_brot (both scaled by 10)*/
    249 } bg[] = {
    250     {0,     1,1, 16,16},        /* checkered:  white vs. light gray (basic) */
    251     {0+8,   2,0,  1,15},        /* checkered:  tan/black vs. white/ice blue */
    252     {0+24,  2,0,  1,0},         /* checkered:  tan/black vs. white/black */
    253     {0+8,   4,5,  0,2},         /* checkered:  gold/yellow vs. black/tan */
    254     {0+8,   4,5,  0,6},         /* checkered:  gold/yellow vs. black/blue */
    255     {0,     7,0,  8,9},         /* checkered:  deep blue/black vs. magenta */
    256     {0+8,  13,0,  5,14},        /* checkered:  orange/black vs. yellow */
    257     {0+8,  12,0, 10,11},        /* checkered:  orange/black vs. red */
    258     {1,     7,0,  8,0},         /* diamonds:  deep blue/black vs. magenta */
    259     {1,    12,0, 11,0},         /* diamonds:  orange vs. dark red */
    260     {1,    10,0,  7,0},         /* diamonds:  red vs. medium blue */
    261     {1,     4,0,  5,0},         /* diamonds:  gold vs. yellow */
    262     {1,     3,0,  0,0},         /* diamonds:  medium green vs. black */
    263     {2,    16, 100,  20,   0},  /* radial:  ~hard radial color-beams */
    264     {2,    18, 100,  10,   2},  /* radial:  soft, curved radial color-beams */
    265     {2,    16, 256, 100, 250},  /* radial:  very tight spiral */
    266     {2, 10000, 256,  11,   0}   /* radial:  dipole-moire' (almost fractal) */
    267 };
    268 static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
    269 
    270 
    271 /* X-specific variables */
    272 static char *displayname;
    273 static XImage *ximage;
    274 static Display *display;
    275 static int depth;
    276 static Visual *visual;
    277 static XVisualInfo *visual_list;
    278 static int RShift, GShift, BShift;
    279 static ulg RMask, GMask, BMask;
    280 static Window window;
    281 static GC gc;
    282 static Colormap colormap;
    283 
    284 static int have_nondefault_visual = FALSE;
    285 static int have_colormap = FALSE;
    286 static int have_window = FALSE;
    287 static int have_gc = FALSE;
    288 
    289 
    290 
    291 
    292 int main(int argc, char **argv)
    293 {
    294 #ifdef sgi
    295     char tmpline[80];
    296 #endif
    297     char *p, *bgstr = NULL;
    298     int rc, alen, flen;
    299     int error = 0;
    300     int timing = FALSE;
    301     int have_bg = FALSE;
    302 #ifdef FEATURE_LOOP
    303     int loop = FALSE;
    304     long loop_interval = -1;		/* seconds (100,000 max) */
    305 #endif
    306     double LUT_exponent;                /* just the lookup table */
    307     double CRT_exponent = 2.2;          /* just the monitor */
    308     double default_display_exponent;    /* whole display system */
    309     XEvent e;
    310     KeySym k;
    311 
    312 
    313     /* First initialize a few things, just to be sure--memset takes care of
    314      * default background color (black), booleans (FALSE), pointers (NULL),
    315      * etc. */
    316 
    317     displayname = (char *)NULL;
    318     filename = (char *)NULL;
    319     memset(&rpng2_info, 0, sizeof(mainprog_info));
    320 
    321 
    322     /* Set the default value for our display-system exponent, i.e., the
    323      * product of the CRT exponent and the exponent corresponding to
    324      * the frame-buffer's lookup table (LUT), if any.  This is not an
    325      * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
    326      * ones), but it should cover 99% of the current possibilities. */
    327 
    328 #if defined(NeXT)
    329     /* third-party utilities can modify the default LUT exponent */
    330     LUT_exponent = 1.0 / 2.2;
    331     /*
    332     if (some_next_function_that_returns_gamma(&next_gamma))
    333         LUT_exponent = 1.0 / next_gamma;
    334      */
    335 #elif defined(sgi)
    336     LUT_exponent = 1.0 / 1.7;
    337     /* there doesn't seem to be any documented function to
    338      * get the "gamma" value, so we do it the hard way */
    339     infile = fopen("/etc/config/system.glGammaVal", "r");
    340     if (infile) {
    341         double sgi_gamma;
    342 
    343         fgets(tmpline, 80, infile);
    344         fclose(infile);
    345         sgi_gamma = atof(tmpline);
    346         if (sgi_gamma > 0.0)
    347             LUT_exponent = 1.0 / sgi_gamma;
    348     }
    349 #elif defined(Macintosh)
    350     LUT_exponent = 1.8 / 2.61;
    351     /*
    352     if (some_mac_function_that_returns_gamma(&mac_gamma))
    353         LUT_exponent = mac_gamma / 2.61;
    354      */
    355 #else
    356     LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
    357 #endif
    358 
    359     /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
    360     default_display_exponent = LUT_exponent * CRT_exponent;
    361 
    362 
    363     /* If the user has set the SCREEN_GAMMA environment variable as suggested
    364      * (somewhat imprecisely) in the libpng documentation, use that; otherwise
    365      * use the default value we just calculated.  Either way, the user may
    366      * override this via a command-line option. */
    367 
    368     if ((p = getenv("SCREEN_GAMMA")) != NULL)
    369         rpng2_info.display_exponent = atof(p);
    370     else
    371         rpng2_info.display_exponent = default_display_exponent;
    372 
    373 
    374     /* Now parse the command line for options and the PNG filename. */
    375 
    376     while (*++argv && !error) {
    377         if (!strncmp(*argv, "-display", 2)) {
    378             if (!*++argv)
    379                 ++error;
    380             else
    381                 displayname = *argv;
    382         } else if (!strncmp(*argv, "-gamma", 2)) {
    383             if (!*++argv)
    384                 ++error;
    385             else {
    386                 rpng2_info.display_exponent = atof(*argv);
    387                 if (rpng2_info.display_exponent <= 0.0)
    388                     ++error;
    389             }
    390         } else if (!strncmp(*argv, "-bgcolor", 4)) {
    391             if (!*++argv)
    392                 ++error;
    393             else {
    394                 bgstr = *argv;
    395                 if (strlen(bgstr) != 7 || bgstr[0] != '#')
    396                     ++error;
    397                 else {
    398                     have_bg = TRUE;
    399                     bg_image = FALSE;
    400                 }
    401             }
    402         } else if (!strncmp(*argv, "-bgpat", 4)) {
    403             if (!*++argv)
    404                 ++error;
    405             else {
    406                 pat = atoi(*argv);
    407                 if (pat >= 0 && pat < num_bgpat) {
    408                     bg_image = TRUE;
    409                     have_bg = FALSE;
    410                 } else
    411                     ++error;
    412             }
    413         } else if (!strncmp(*argv, "-usleep", 2)) {
    414             if (!*++argv)
    415                 ++error;
    416             else {
    417                 usleep_duration = (ulg)atol(*argv);
    418                 demo_timing = TRUE;
    419             }
    420         } else if (!strncmp(*argv, "-pause", 2)) {
    421             pause_after_pass = TRUE;
    422         } else if (!strncmp(*argv, "-timing", 2)) {
    423             timing = TRUE;
    424 #ifdef FEATURE_LOOP
    425         } else if (!strncmp(*argv, "-loop", 2)) {
    426             loop = TRUE;
    427             if (!argv[1] || !is_number(argv[1]))
    428                 loop_interval = 2;
    429             else {
    430                 ++argv;
    431                 loop_interval = atol(*argv);
    432                 if (loop_interval < 0)
    433                     loop_interval = 2;
    434                 else if (loop_interval > 100000)   /* bit more than one day */
    435                     loop_interval = 100000;
    436             }
    437 #endif
    438 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
    439         } else if (!strncmp(*argv, "-nommxfilters", 7)) {
    440             rpng2_info.nommxfilters = TRUE;
    441         } else if (!strncmp(*argv, "-nommxcombine", 7)) {
    442             rpng2_info.nommxcombine = TRUE;
    443         } else if (!strncmp(*argv, "-nommxinterlace", 7)) {
    444             rpng2_info.nommxinterlace = TRUE;
    445         } else if (!strcmp(*argv, "-nommx")) {
    446             rpng2_info.nommxfilters = TRUE;
    447             rpng2_info.nommxcombine = TRUE;
    448             rpng2_info.nommxinterlace = TRUE;
    449 #endif
    450         } else {
    451             if (**argv != '-') {
    452                 filename = *argv;
    453                 if (argv[1])   /* shouldn't be any more args after filename */
    454                     ++error;
    455             } else
    456                 ++error;   /* not expecting any other options */
    457         }
    458     }
    459 
    460     if (!filename)
    461         ++error;
    462 
    463 
    464     /* print usage screen if any errors up to this point */
    465 
    466     if (error) {
    467         fprintf(stderr, "\n%s %s:  %s\n\n", PROGNAME, VERSION, appname);
    468         readpng2_version_info();
    469         fprintf(stderr, "\n"
    470           "Usage:  %s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
    471 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
    472           "        %*s [[-nommxfilters] [-nommxcombine] [-nommxinterlace] | -nommx]\n"
    473 #endif
    474 #ifdef FEATURE_LOOP
    475           "        %*s [-usleep dur | -timing] [-pause] [-loop [sec]] file.png\n\n"
    476 #else
    477           "        %*s [-usleep dur | -timing] [-pause] file.png\n\n"
    478 #endif
    479           "    xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
    480           "    exp \ttransfer-function exponent (``gamma'') of the display\n"
    481           "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
    482           "\t\t  to the product of the lookup-table exponent (varies)\n"
    483           "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
    484           "    bg  \tdesired background color in 7-character hex RGB format\n"
    485           "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
    486           "\t\t  used with transparent images; overrides -bgpat\n"
    487           "    pat \tdesired background pattern number (0-%d); used with\n"
    488           "\t\t  transparent images; overrides -bgcolor\n"
    489 #ifdef FEATURE_LOOP
    490           "    -loop\tloops through background images after initial display\n"
    491           "\t\t  is complete (depends on -bgpat)\n"
    492           "    sec \tseconds to display each background image (default = 2)\n"
    493 #endif
    494 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
    495           "    -nommx*\tdisable optimized MMX routines for decoding row filters,\n"
    496           "\t\t  combining rows, and expanding interlacing, respectively\n"
    497 #endif
    498           "    dur \tduration in microseconds to wait after displaying each\n"
    499           "\t\t  row (for demo purposes)\n"
    500           "    -timing\tenables delay for every block read, to simulate modem\n"
    501           "\t\t  download of image (~36 Kbps)\n"
    502           "    -pause\tpauses after displaying each pass until mouse clicked\n"
    503           "\nPress Q, Esc or mouse button 1 (within image window, after image\n"
    504           "is displayed) to quit.\n"
    505           "\n", PROGNAME,
    506 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
    507           (int)strlen(PROGNAME), " ",
    508 #endif
    509           (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat-1);
    510         exit(1);
    511     }
    512 
    513 
    514     if (!(infile = fopen(filename, "rb"))) {
    515         fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
    516         ++error;
    517     } else {
    518         incount = fread(inbuf, 1, INBUFSIZE, infile);
    519         if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
    520             fprintf(stderr, PROGNAME
    521               ":  [%s] is not a PNG file: incorrect signature\n",
    522               filename);
    523             ++error;
    524         } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
    525             switch (rc) {
    526                 case 2:
    527                     fprintf(stderr, PROGNAME
    528                       ":  [%s] has bad IHDR (libpng longjmp)\n", filename);
    529                     break;
    530                 case 4:
    531                     fprintf(stderr, PROGNAME ":  insufficient memory\n");
    532                     break;
    533                 default:
    534                     fprintf(stderr, PROGNAME
    535                       ":  unknown readpng2_init() error\n");
    536                     break;
    537             }
    538             ++error;
    539         } else {
    540             Trace((stderr, "about to call XOpenDisplay()\n"))
    541             display = XOpenDisplay(displayname);
    542             if (!display) {
    543                 readpng2_cleanup(&rpng2_info);
    544                 fprintf(stderr, PROGNAME ":  can't open X display [%s]\n",
    545                   displayname? displayname : "default");
    546                 ++error;
    547             }
    548         }
    549         if (error)
    550             fclose(infile);
    551     }
    552 
    553 
    554     if (error) {
    555         fprintf(stderr, PROGNAME ":  aborting.\n");
    556         exit(2);
    557     }
    558 
    559 
    560     /* set the title-bar string, but make sure buffer doesn't overflow */
    561 
    562     alen = strlen(appname);
    563     flen = strlen(filename);
    564     if (alen + flen + 3 > 1023)
    565         sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
    566     else
    567         sprintf(titlebar, "%s:  %s", appname, filename);
    568 
    569 
    570     /* set some final rpng2_info variables before entering main data loop */
    571 
    572     if (have_bg) {
    573         unsigned r, g, b;   /* this approach quiets compiler warnings */
    574 
    575         sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
    576         rpng2_info.bg_red   = (uch)r;
    577         rpng2_info.bg_green = (uch)g;
    578         rpng2_info.bg_blue  = (uch)b;
    579     } else
    580         rpng2_info.need_bgcolor = TRUE;
    581 
    582     rpng2_info.state = kPreInit;
    583     rpng2_info.mainprog_init = rpng2_x_init;
    584     rpng2_info.mainprog_display_row = rpng2_x_display_row;
    585     rpng2_info.mainprog_finish_display = rpng2_x_finish_display;
    586 
    587 
    588     /* OK, this is the fun part:  call readpng2_decode_data() at the start of
    589      * the loop to deal with our first buffer of data (read in above to verify
    590      * that the file is a PNG image), then loop through the file and continue
    591      * calling the same routine to handle each chunk of data.  It in turn
    592      * passes the data to libpng, which will invoke one or more of our call-
    593      * backs as decoded data become available.  We optionally call sleep() for
    594      * one second per iteration to simulate downloading the image via an analog
    595      * modem. */
    596 
    597     for (;;) {
    598         Trace((stderr, "about to call readpng2_decode_data()\n"))
    599         if (readpng2_decode_data(&rpng2_info, inbuf, incount))
    600             ++error;
    601         Trace((stderr, "done with readpng2_decode_data()\n"))
    602 
    603         if (error || incount != INBUFSIZE || rpng2_info.state == kDone) {
    604             if (rpng2_info.state == kDone) {
    605                 Trace((stderr, "done decoding PNG image\n"))
    606             } else if (ferror(infile)) {
    607                 fprintf(stderr, PROGNAME
    608                   ":  error while reading PNG image file\n");
    609                 exit(3);
    610             } else if (feof(infile)) {
    611                 fprintf(stderr, PROGNAME ":  end of file reached "
    612                   "(unexpectedly) while reading PNG image file\n");
    613                 exit(3);
    614             } else /* if (error) */ {
    615                 // will print error message below
    616             }
    617             break;
    618         }
    619 
    620         if (timing)
    621             sleep(1);
    622 
    623         incount = fread(inbuf, 1, INBUFSIZE, infile);
    624     }
    625 
    626 
    627     /* clean up PNG stuff and report any decoding errors */
    628 
    629     fclose(infile);
    630     Trace((stderr, "about to call readpng2_cleanup()\n"))
    631     readpng2_cleanup(&rpng2_info);
    632 
    633     if (error) {
    634         fprintf(stderr, PROGNAME ":  libpng error while decoding PNG image\n");
    635         exit(3);
    636     }
    637 
    638 
    639 #ifdef FEATURE_LOOP
    640 
    641     if (loop && bg_image) {
    642         Trace((stderr, "entering -loop loop (FEATURE_LOOP)\n"))
    643         for (;;) {
    644             int i, use_sleep;
    645             struct timeval now, then;
    646 
    647             /* get current time and add loop_interval to get target time */
    648             if (gettimeofday(&then, NULL) == 0) {
    649                 then.tv_sec += loop_interval;
    650                 use_sleep = FALSE;
    651             } else
    652                 use_sleep = TRUE;
    653 
    654             /* do quick check for a quit event but don't wait for it */
    655             /* GRR BUG:  should also check for Expose events and redraw... */
    656             if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask, &e))
    657                 if (QUIT(e,k))
    658                     break;
    659 
    660             /* generate next background image */
    661             if (++pat >= num_bgpat)
    662                 pat = 0;
    663             rpng2_x_reload_bg_image();
    664 
    665             /* wait for timeout, using whatever means are available */
    666             if (use_sleep || gettimeofday(&now, NULL) != 0) {
    667                 for (i = loop_interval;  i > 0;  --i) {
    668                     sleep(1);
    669                     /* GRR BUG:  also need to check for Expose (and redraw!) */
    670                     if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask,
    671                         &e) && QUIT(e,k))
    672                         break;
    673                 }
    674             } else {
    675                 /* Y2038 BUG! */
    676                 if (now.tv_sec < then.tv_sec ||
    677                     (now.tv_sec == then.tv_sec && now.tv_usec < then.tv_usec))
    678                 {
    679                     int quit = FALSE;
    680                     long seconds_to_go = then.tv_sec - now.tv_sec;
    681                     long usleep_usec;
    682 
    683                     /* basically chew up most of remaining loop-interval with
    684                      *  calls to sleep(1) interleaved with checks for quit
    685                      *  events, but also recalc time-to-go periodically; when
    686                      *  done, clean up any remaining time with usleep() call
    687                      *  (could also use SIGALRM, but signals are a pain...) */
    688                     while (seconds_to_go-- > 1) {
    689                         int seconds_done = 0;
    690 
    691                         for (i = seconds_to_go;  i > 0 && !quit;  --i) {
    692                             sleep(1);
    693                             /* GRR BUG:  need to check for Expose and redraw */
    694                             if (XCheckMaskEvent(display, KeyPressMask |
    695                                 ButtonPressMask, &e) && QUIT(e,k))
    696                                 quit = TRUE;
    697                             if (++seconds_done > 1000)
    698                                 break;   /* time to redo seconds_to_go meas. */
    699                         }
    700                         if (quit)
    701                             break;
    702 
    703                         /* OK, more than 1000 seconds since last check:
    704                          *  correct the time-to-go measurement for drift */
    705                         if (gettimeofday(&now, NULL) == 0) {
    706                             if (now.tv_sec >= then.tv_sec)
    707                                 break;
    708                             seconds_to_go = then.tv_sec - now.tv_sec;
    709                         } else
    710                             ++seconds_to_go;  /* restore what we subtracted */
    711                     }
    712                     if (quit)
    713                         break;   /* breaks outer do-loop, skips redisplay */
    714 
    715                     /* since difference between "now" and "then" is already
    716                      *  eaten up to within a couple of seconds, don't need to
    717                      *  worry about overflow--but might have overshot (neg.) */
    718                     if (gettimeofday(&now, NULL) == 0) {
    719                         usleep_usec = 1000000L*(then.tv_sec - now.tv_sec) +
    720                           then.tv_usec - now.tv_usec;
    721                         if (usleep_usec > 0)
    722                             usleep((ulg)usleep_usec);
    723                     }
    724                 }
    725             }
    726 
    727             /* composite image against new background and display (note that
    728              *  we do not take into account the time spent doing this...) */
    729             rpng2_x_redisplay_image (0, 0, rpng2_info.width, rpng2_info.height);
    730         }
    731 
    732     } else /* FALL THROUGH and do the normal thing */
    733 
    734 #endif /* FEATURE_LOOP */
    735 
    736     /* wait for the user to tell us when to quit */
    737 
    738     if (rpng2_info.state >= kWindowInit) {
    739         Trace((stderr, "entering final wait-for-quit-event loop\n"))
    740         do {
    741             XNextEvent(display, &e);
    742             if (e.type == Expose) {
    743                 XExposeEvent *ex = (XExposeEvent *)&e;
    744                 rpng2_x_redisplay_image (ex->x, ex->y, ex->width, ex->height);
    745             }
    746         } while (!QUIT(e,k));
    747     } else {
    748         fprintf(stderr, PROGNAME ":  init callback never called:  probable "
    749           "libpng error while decoding PNG metadata\n");
    750         exit(4);
    751     }
    752 
    753 
    754     /* we're done:  clean up all image and X resources and go away */
    755 
    756     Trace((stderr, "about to call rpng2_x_cleanup()\n"))
    757     rpng2_x_cleanup();
    758 
    759     return 0;
    760 }
    761 
    762 
    763 
    764 
    765 
    766 /* this function is called by readpng2_info_callback() in readpng2.c, which
    767  * in turn is called by libpng after all of the pre-IDAT chunks have been
    768  * read and processed--i.e., we now have enough info to finish initializing */
    769 
    770 static void rpng2_x_init(void)
    771 {
    772     ulg i;
    773     ulg rowbytes = rpng2_info.rowbytes;
    774 
    775     Trace((stderr, "beginning rpng2_x_init()\n"))
    776     Trace((stderr, "  rowbytes = %d\n", rpng2_info.rowbytes))
    777     Trace((stderr, "  width  = %ld\n", rpng2_info.width))
    778     Trace((stderr, "  height = %ld\n", rpng2_info.height))
    779 
    780     rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
    781     if (!rpng2_info.image_data) {
    782         readpng2_cleanup(&rpng2_info);
    783         return;
    784     }
    785 
    786     rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
    787     if (!rpng2_info.row_pointers) {
    788         free(rpng2_info.image_data);
    789         rpng2_info.image_data = NULL;
    790         readpng2_cleanup(&rpng2_info);
    791         return;
    792     }
    793 
    794     for (i = 0;  i < rpng2_info.height;  ++i)
    795         rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
    796 
    797 
    798     /* do the basic X initialization stuff, make the window, and fill it with
    799      * the user-specified, file-specified or default background color or
    800      * pattern */
    801 
    802     if (rpng2_x_create_window()) {
    803 
    804         /* GRR TEMPORARY HACK:  this is fundamentally no different from cases
    805          * above; libpng should longjmp() back to us when png_ptr goes away.
    806          * If we/it segfault instead, seems like a libpng bug... */
    807 
    808         /* we're here via libpng callback, so if window fails, clean and bail */
    809         readpng2_cleanup(&rpng2_info);
    810         rpng2_x_cleanup();
    811         exit(2);
    812     }
    813 
    814     rpng2_info.state = kWindowInit;
    815 }
    816 
    817 
    818 
    819 
    820 
    821 static int rpng2_x_create_window(void)
    822 {
    823     ulg bg_red   = rpng2_info.bg_red;
    824     ulg bg_green = rpng2_info.bg_green;
    825     ulg bg_blue  = rpng2_info.bg_blue;
    826     ulg bg_pixel = 0L;
    827     ulg attrmask;
    828     int need_colormap = FALSE;
    829     int screen, pad;
    830     uch *xdata;
    831     Window root;
    832     XEvent e;
    833     XGCValues gcvalues;
    834     XSetWindowAttributes attr;
    835     XTextProperty windowName, *pWindowName = &windowName;
    836     XTextProperty iconName, *pIconName = &iconName;
    837     XVisualInfo visual_info;
    838     XSizeHints *size_hints;
    839     XWMHints *wm_hints;
    840     XClassHint *class_hints;
    841 
    842 
    843     Trace((stderr, "beginning rpng2_x_create_window()\n"))
    844 
    845     screen = DefaultScreen(display);
    846     depth = DisplayPlanes(display, screen);
    847     root = RootWindow(display, screen);
    848 
    849 #ifdef DEBUG
    850     XSynchronize(display, True);
    851 #endif
    852 
    853     if (depth != 16 && depth != 24 && depth != 32) {
    854         int visuals_matched = 0;
    855 
    856         Trace((stderr, "default depth is %d:  checking other visuals\n",
    857           depth))
    858 
    859         /* 24-bit first */
    860         visual_info.screen = screen;
    861         visual_info.depth = 24;
    862         visual_list = XGetVisualInfo(display,
    863           VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
    864         if (visuals_matched == 0) {
    865 /* GRR:  add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
    866             fprintf(stderr, "default screen depth %d not supported, and no"
    867               " 24-bit visuals found\n", depth);
    868             return 2;
    869         }
    870         Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
    871           visuals_matched))
    872         visual = visual_list[0].visual;
    873         depth = visual_list[0].depth;
    874 /*
    875         colormap_size = visual_list[0].colormap_size;
    876         visual_class = visual->class;
    877         visualID = XVisualIDFromVisual(visual);
    878  */
    879         have_nondefault_visual = TRUE;
    880         need_colormap = TRUE;
    881     } else {
    882         XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
    883         visual = visual_info.visual;
    884     }
    885 
    886     RMask = visual->red_mask;
    887     GMask = visual->green_mask;
    888     BMask = visual->blue_mask;
    889 
    890 /* GRR:  add/check 8-bit support */
    891     if (depth == 8 || need_colormap) {
    892         colormap = XCreateColormap(display, root, visual, AllocNone);
    893         if (!colormap) {
    894             fprintf(stderr, "XCreateColormap() failed\n");
    895             return 2;
    896         }
    897         have_colormap = TRUE;
    898         if (depth == 8)
    899             bg_image = FALSE;   /* gradient just wastes palette entries */
    900     }
    901     if (depth == 15 || depth == 16) {
    902         RShift = 15 - rpng2_x_msb(RMask);    /* these are right-shifts */
    903         GShift = 15 - rpng2_x_msb(GMask);
    904         BShift = 15 - rpng2_x_msb(BMask);
    905     } else if (depth > 16) {
    906         RShift = rpng2_x_msb(RMask) - 7;     /* these are left-shifts */
    907         GShift = rpng2_x_msb(GMask) - 7;
    908         BShift = rpng2_x_msb(BMask) - 7;
    909     }
    910     if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
    911         fprintf(stderr, "rpng2 internal logic error:  negative X shift(s)!\n");
    912         return 2;
    913     }
    914 
    915 /*---------------------------------------------------------------------------
    916     Finally, create the window.
    917   ---------------------------------------------------------------------------*/
    918 
    919     attr.backing_store = Always;
    920     attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
    921     attrmask = CWBackingStore | CWEventMask;
    922     if (have_nondefault_visual) {
    923         attr.colormap = colormap;
    924         attr.background_pixel = 0;
    925         attr.border_pixel = 1;
    926         attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
    927     }
    928 
    929     window = XCreateWindow(display, root, 0, 0, rpng2_info.width,
    930       rpng2_info.height, 0, depth, InputOutput, visual, attrmask, &attr);
    931 
    932     if (window == None) {
    933         fprintf(stderr, "XCreateWindow() failed\n");
    934         return 2;
    935     } else
    936         have_window = TRUE;
    937 
    938     if (depth == 8)
    939         XSetWindowColormap(display, window, colormap);
    940 
    941     if (!XStringListToTextProperty(&window_name, 1, pWindowName))
    942         pWindowName = NULL;
    943     if (!XStringListToTextProperty(&icon_name, 1, pIconName))
    944         pIconName = NULL;
    945 
    946     /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */
    947 
    948     if ((size_hints = XAllocSizeHints()) != NULL) {
    949         /* window will not be resizable */
    950         size_hints->flags = PMinSize | PMaxSize;
    951         size_hints->min_width = size_hints->max_width = (int)rpng2_info.width;
    952         size_hints->min_height = size_hints->max_height =
    953           (int)rpng2_info.height;
    954     }
    955 
    956     if ((wm_hints = XAllocWMHints()) != NULL) {
    957         wm_hints->initial_state = NormalState;
    958         wm_hints->input = True;
    959      /* wm_hints->icon_pixmap = icon_pixmap; */
    960         wm_hints->flags = StateHint | InputHint  /* | IconPixmapHint */ ;
    961     }
    962 
    963     if ((class_hints = XAllocClassHint()) != NULL) {
    964         class_hints->res_name = res_name;
    965         class_hints->res_class = res_class;
    966     }
    967 
    968     XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
    969       size_hints, wm_hints, class_hints);
    970 
    971     /* various properties and hints no longer needed; free memory */
    972     if (pWindowName)
    973        XFree(pWindowName->value);
    974     if (pIconName)
    975        XFree(pIconName->value);
    976     if (size_hints)
    977         XFree(size_hints);
    978     if (wm_hints)
    979        XFree(wm_hints);
    980     if (class_hints)
    981        XFree(class_hints);
    982 
    983     XMapWindow(display, window);
    984 
    985     gc = XCreateGC(display, window, 0, &gcvalues);
    986     have_gc = TRUE;
    987 
    988 /*---------------------------------------------------------------------------
    989     Allocate memory for the X- and display-specific version of the image.
    990   ---------------------------------------------------------------------------*/
    991 
    992     if (depth == 24 || depth == 32) {
    993         xdata = (uch *)malloc(4*rpng2_info.width*rpng2_info.height);
    994         pad = 32;
    995     } else if (depth == 16) {
    996         xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height);
    997         pad = 16;
    998     } else /* depth == 8 */ {
    999         xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height);
   1000         pad = 8;
   1001     }
   1002 
   1003     if (!xdata) {
   1004         fprintf(stderr, PROGNAME ":  unable to allocate image memory\n");
   1005         return 4;
   1006     }
   1007 
   1008     ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
   1009       (char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0);
   1010 
   1011     if (!ximage) {
   1012         fprintf(stderr, PROGNAME ":  XCreateImage() failed\n");
   1013         free(xdata);
   1014         return 3;
   1015     }
   1016 
   1017     /* to avoid testing the byte order every pixel (or doubling the size of
   1018      * the drawing routine with a giant if-test), we arbitrarily set the byte
   1019      * order to MSBFirst and let Xlib worry about inverting things on little-
   1020      * endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the
   1021      * most efficient approach (the giant if-test would be better), but in
   1022      * the interest of clarity, we'll take the easy way out... */
   1023 
   1024     ximage->byte_order = MSBFirst;
   1025 
   1026 /*---------------------------------------------------------------------------
   1027     Fill window with the specified background color (default is black) or
   1028     faked "background image" (but latter is disabled if 8-bit; gradients
   1029     just waste palette entries).
   1030   ---------------------------------------------------------------------------*/
   1031 
   1032     if (bg_image)
   1033         rpng2_x_load_bg_image();    /* resets bg_image if fails */
   1034 
   1035     if (!bg_image) {
   1036         if (depth == 24 || depth == 32) {
   1037             bg_pixel = (bg_red   << RShift) |
   1038                        (bg_green << GShift) |
   1039                        (bg_blue  << BShift);
   1040         } else if (depth == 16) {
   1041             bg_pixel = (((bg_red   << 8) >> RShift) & RMask) |
   1042                        (((bg_green << 8) >> GShift) & GMask) |
   1043                        (((bg_blue  << 8) >> BShift) & BMask);
   1044         } else /* depth == 8 */ {
   1045 
   1046             /* GRR:  add 8-bit support */
   1047 
   1048         }
   1049         XSetForeground(display, gc, bg_pixel);
   1050         XFillRectangle(display, window, gc, 0, 0, rpng2_info.width,
   1051           rpng2_info.height);
   1052     }
   1053 
   1054 /*---------------------------------------------------------------------------
   1055     Wait for first Expose event to do any drawing, then flush and return.
   1056   ---------------------------------------------------------------------------*/
   1057 
   1058     do
   1059         XNextEvent(display, &e);
   1060     while (e.type != Expose || e.xexpose.count);
   1061 
   1062     XFlush(display);
   1063 
   1064     return 0;
   1065 
   1066 } /* end function rpng2_x_create_window() */
   1067 
   1068 
   1069 
   1070 
   1071 
   1072 static int rpng2_x_load_bg_image(void)
   1073 {
   1074     uch *src;
   1075     char *dest;
   1076     uch r1, r2, g1, g2, b1, b2;
   1077     uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
   1078     int k, hmax, max;
   1079     int xidx, yidx, yidx_max;
   1080     int even_odd_vert, even_odd_horiz, even_odd;
   1081     int invert_gradient2 = (bg[pat].type & 0x08);
   1082     int invert_column;
   1083     int ximage_rowbytes = ximage->bytes_per_line;
   1084     ulg i, row;
   1085     ulg pixel;
   1086 
   1087 /*---------------------------------------------------------------------------
   1088     Allocate buffer for fake background image to be used with transparent
   1089     images; if this fails, revert to plain background color.
   1090   ---------------------------------------------------------------------------*/
   1091 
   1092     bg_rowbytes = 3 * rpng2_info.width;
   1093     bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
   1094     if (!bg_data) {
   1095         fprintf(stderr, PROGNAME
   1096           ":  unable to allocate memory for background image\n");
   1097         bg_image = 0;
   1098         return 1;
   1099     }
   1100 
   1101     bgscale = (pat == 0)? 8 : bgscale_default;
   1102     yidx_max = bgscale - 1;
   1103 
   1104 /*---------------------------------------------------------------------------
   1105     Vertical gradients (ramps) in NxN squares, alternating direction and
   1106     colors (N == bgscale).
   1107   ---------------------------------------------------------------------------*/
   1108 
   1109     if ((bg[pat].type & 0x07) == 0) {
   1110         uch r1_min  = rgb[bg[pat].rgb1_min].r;
   1111         uch g1_min  = rgb[bg[pat].rgb1_min].g;
   1112         uch b1_min  = rgb[bg[pat].rgb1_min].b;
   1113         uch r2_min  = rgb[bg[pat].rgb2_min].r;
   1114         uch g2_min  = rgb[bg[pat].rgb2_min].g;
   1115         uch b2_min  = rgb[bg[pat].rgb2_min].b;
   1116         int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
   1117         int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
   1118         int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
   1119         int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
   1120         int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
   1121         int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
   1122 
   1123         for (row = 0;  row < rpng2_info.height;  ++row) {
   1124             yidx = (int)(row % bgscale);
   1125             even_odd_vert = (int)((row / bgscale) & 1);
   1126 
   1127             r1 = r1_min + (r1_diff * yidx) / yidx_max;
   1128             g1 = g1_min + (g1_diff * yidx) / yidx_max;
   1129             b1 = b1_min + (b1_diff * yidx) / yidx_max;
   1130             r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
   1131             g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
   1132             b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
   1133 
   1134             r2 = r2_min + (r2_diff * yidx) / yidx_max;
   1135             g2 = g2_min + (g2_diff * yidx) / yidx_max;
   1136             b2 = b2_min + (b2_diff * yidx) / yidx_max;
   1137             r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
   1138             g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
   1139             b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
   1140 
   1141             dest = (char *)bg_data + row*bg_rowbytes;
   1142             for (i = 0;  i < rpng2_info.width;  ++i) {
   1143                 even_odd_horiz = (int)((i / bgscale) & 1);
   1144                 even_odd = even_odd_vert ^ even_odd_horiz;
   1145                 invert_column =
   1146                   (even_odd_horiz && (bg[pat].type & 0x10));
   1147                 if (even_odd == 0) {        /* gradient #1 */
   1148                     if (invert_column) {
   1149                         *dest++ = r1_inv;
   1150                         *dest++ = g1_inv;
   1151                         *dest++ = b1_inv;
   1152                     } else {
   1153                         *dest++ = r1;
   1154                         *dest++ = g1;
   1155                         *dest++ = b1;
   1156                     }
   1157                 } else {                    /* gradient #2 */
   1158                     if ((invert_column && invert_gradient2) ||
   1159                         (!invert_column && !invert_gradient2))
   1160                     {
   1161                         *dest++ = r2;       /* not inverted or */
   1162                         *dest++ = g2;       /*  doubly inverted */
   1163                         *dest++ = b2;
   1164                     } else {
   1165                         *dest++ = r2_inv;
   1166                         *dest++ = g2_inv;   /* singly inverted */
   1167                         *dest++ = b2_inv;
   1168                     }
   1169                 }
   1170             }
   1171         }
   1172 
   1173 /*---------------------------------------------------------------------------
   1174     Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
   1175     M. Costello.
   1176   ---------------------------------------------------------------------------*/
   1177 
   1178     } else if ((bg[pat].type & 0x07) == 1) {
   1179 
   1180         hmax = (bgscale-1)/2;   /* half the max weight of a color */
   1181         max = 2*hmax;           /* the max weight of a color */
   1182 
   1183         r1 = rgb[bg[pat].rgb1_max].r;
   1184         g1 = rgb[bg[pat].rgb1_max].g;
   1185         b1 = rgb[bg[pat].rgb1_max].b;
   1186         r2 = rgb[bg[pat].rgb2_max].r;
   1187         g2 = rgb[bg[pat].rgb2_max].g;
   1188         b2 = rgb[bg[pat].rgb2_max].b;
   1189 
   1190         for (row = 0;  row < rpng2_info.height;  ++row) {
   1191             yidx = (int)(row % bgscale);
   1192             if (yidx > hmax)
   1193                 yidx = bgscale-1 - yidx;
   1194             dest = (char *)bg_data + row*bg_rowbytes;
   1195             for (i = 0;  i < rpng2_info.width;  ++i) {
   1196                 xidx = (int)(i % bgscale);
   1197                 if (xidx > hmax)
   1198                     xidx = bgscale-1 - xidx;
   1199                 k = xidx + yidx;
   1200                 *dest++ = (k*r1 + (max-k)*r2) / max;
   1201                 *dest++ = (k*g1 + (max-k)*g2) / max;
   1202                 *dest++ = (k*b1 + (max-k)*b2) / max;
   1203             }
   1204         }
   1205 
   1206 /*---------------------------------------------------------------------------
   1207     Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
   1208     soids will equal bgscale?].  This one is slow but very cool.  Code con-
   1209     tributed by Pieter S. van der Meulen (originally in Smalltalk).
   1210   ---------------------------------------------------------------------------*/
   1211 
   1212     } else if ((bg[pat].type & 0x07) == 2) {
   1213         uch ch;
   1214         int ii, x, y, hw, hh, grayspot;
   1215         double freq, rotate, saturate, gray, intensity;
   1216         double angle=0.0, aoffset=0.0, maxDist, dist;
   1217         double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
   1218 
   1219         fprintf(stderr, "%s:  computing radial background...",
   1220           PROGNAME);
   1221         fflush(stderr);
   1222 
   1223         hh = (int)(rpng2_info.height / 2);
   1224         hw = (int)(rpng2_info.width / 2);
   1225 
   1226         /* variables for radial waves:
   1227          *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
   1228          *   freq:  number of color beams originating from the center
   1229          *   grayspot:  size of the graying center area (anti-alias)
   1230          *   rotate:  rotation of the beams as a function of radius
   1231          *   saturate:  saturation of beams' shape azimuthally
   1232          */
   1233         angle = CLIP(angle, 0.0, 360.0);
   1234         grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
   1235         freq = MAX((double)bg[pat].bg_freq, 0.0);
   1236         saturate = (double)bg[pat].bg_bsat * 0.1;
   1237         rotate = (double)bg[pat].bg_brot * 0.1;
   1238         gray = 0.0;
   1239         intensity = 0.0;
   1240         maxDist = (double)((hw*hw) + (hh*hh));
   1241 
   1242         for (row = 0;  row < rpng2_info.height;  ++row) {
   1243             y = (int)(row - hh);
   1244             dest = (char *)bg_data + row*bg_rowbytes;
   1245             for (i = 0;  i < rpng2_info.width;  ++i) {
   1246                 x = (int)(i - hw);
   1247                 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
   1248                 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
   1249                 gray = MIN(1.0, gray);
   1250                 dist = (double)((x*x) + (y*y)) / maxDist;
   1251                 intensity = cos((angle+(rotate*dist*PI)) * freq) *
   1252                   gray * saturate;
   1253                 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
   1254                 hue = (angle + PI) * INV_PI_360 + aoffset;
   1255                 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
   1256                 s = MIN(MAX(s,0.0), 1.0);
   1257                 v = MIN(MAX(intensity,0.0), 1.0);
   1258 
   1259                 if (s == 0.0) {
   1260                     ch = (uch)(v * 255.0);
   1261                     *dest++ = ch;
   1262                     *dest++ = ch;
   1263                     *dest++ = ch;
   1264                 } else {
   1265                     if ((hue < 0.0) || (hue >= 360.0))
   1266                         hue -= (((int)(hue / 360.0)) * 360.0);
   1267                     hue /= 60.0;
   1268                     ii = (int)hue;
   1269                     f = hue - (double)ii;
   1270                     p = (1.0 - s) * v;
   1271                     q = (1.0 - (s * f)) * v;
   1272                     t = (1.0 - (s * (1.0 - f))) * v;
   1273                     if      (ii == 0) { red = v; green = t; blue = p; }
   1274                     else if (ii == 1) { red = q; green = v; blue = p; }
   1275                     else if (ii == 2) { red = p; green = v; blue = t; }
   1276                     else if (ii == 3) { red = p; green = q; blue = v; }
   1277                     else if (ii == 4) { red = t; green = p; blue = v; }
   1278                     else if (ii == 5) { red = v; green = p; blue = q; }
   1279                     *dest++ = (uch)(red * 255.0);
   1280                     *dest++ = (uch)(green * 255.0);
   1281                     *dest++ = (uch)(blue * 255.0);
   1282                 }
   1283             }
   1284         }
   1285         fprintf(stderr, "done.\n");
   1286         fflush(stderr);
   1287     }
   1288 
   1289 /*---------------------------------------------------------------------------
   1290     Blast background image to display buffer before beginning PNG decode.
   1291   ---------------------------------------------------------------------------*/
   1292 
   1293     if (depth == 24 || depth == 32) {
   1294         ulg red, green, blue;
   1295         int bpp = ximage->bits_per_pixel;
   1296 
   1297         for (row = 0;  row < rpng2_info.height;  ++row) {
   1298             src = bg_data + row*bg_rowbytes;
   1299             dest = ximage->data + row*ximage_rowbytes;
   1300             if (bpp == 32) {	/* slightly optimized version */
   1301                 for (i = rpng2_info.width;  i > 0;  --i) {
   1302                     red   = *src++;
   1303                     green = *src++;
   1304                     blue  = *src++;
   1305                     pixel = (red   << RShift) |
   1306                             (green << GShift) |
   1307                             (blue  << BShift);
   1308                     /* recall that we set ximage->byte_order = MSBFirst above */
   1309                     *dest++ = (char)((pixel >> 24) & 0xff);
   1310                     *dest++ = (char)((pixel >> 16) & 0xff);
   1311                     *dest++ = (char)((pixel >>  8) & 0xff);
   1312                     *dest++ = (char)( pixel        & 0xff);
   1313                 }
   1314             } else {
   1315                 for (i = rpng2_info.width;  i > 0;  --i) {
   1316                     red   = *src++;
   1317                     green = *src++;
   1318                     blue  = *src++;
   1319                     pixel = (red   << RShift) |
   1320                             (green << GShift) |
   1321                             (blue  << BShift);
   1322                     /* recall that we set ximage->byte_order = MSBFirst above */
   1323                     /* GRR BUG?  this assumes bpp == 24 & bits are packed low */
   1324                     /*           (probably need to use RShift, RMask, etc.) */
   1325                     *dest++ = (char)((pixel >> 16) & 0xff);
   1326                     *dest++ = (char)((pixel >>  8) & 0xff);
   1327                     *dest++ = (char)( pixel        & 0xff);
   1328                 }
   1329             }
   1330         }
   1331 
   1332     } else if (depth == 16) {
   1333         ush red, green, blue;
   1334 
   1335         for (row = 0;  row < rpng2_info.height;  ++row) {
   1336             src = bg_data + row*bg_rowbytes;
   1337             dest = ximage->data + row*ximage_rowbytes;
   1338             for (i = rpng2_info.width;  i > 0;  --i) {
   1339                 red   = ((ush)(*src) << 8);  ++src;
   1340                 green = ((ush)(*src) << 8);  ++src;
   1341                 blue  = ((ush)(*src) << 8);  ++src;
   1342                 pixel = ((red   >> RShift) & RMask) |
   1343                         ((green >> GShift) & GMask) |
   1344                         ((blue  >> BShift) & BMask);
   1345                 /* recall that we set ximage->byte_order = MSBFirst above */
   1346                 *dest++ = (char)((pixel >>  8) & 0xff);
   1347                 *dest++ = (char)( pixel        & 0xff);
   1348             }
   1349         }
   1350 
   1351     } else /* depth == 8 */ {
   1352 
   1353         /* GRR:  add 8-bit support */
   1354 
   1355     }
   1356 
   1357     XPutImage(display, window, gc, ximage, 0, 0, 0, 0, rpng2_info.width,
   1358       rpng2_info.height);
   1359 
   1360     return 0;
   1361 
   1362 } /* end function rpng2_x_load_bg_image() */
   1363 
   1364 
   1365 
   1366 
   1367 
   1368 static void rpng2_x_display_row(ulg row)
   1369 {
   1370     uch bg_red   = rpng2_info.bg_red;
   1371     uch bg_green = rpng2_info.bg_green;
   1372     uch bg_blue  = rpng2_info.bg_blue;
   1373     uch *src, *src2=NULL;
   1374     char *dest;
   1375     uch r, g, b, a;
   1376     int ximage_rowbytes = ximage->bytes_per_line;
   1377     ulg i, pixel;
   1378     static int rows=0, prevpass=(-1);
   1379     static ulg firstrow;
   1380 
   1381 /*---------------------------------------------------------------------------
   1382     rows and firstrow simply track how many rows (and which ones) have not
   1383     yet been displayed; alternatively, we could call XPutImage() for every
   1384     row and not bother with the records-keeping.
   1385   ---------------------------------------------------------------------------*/
   1386 
   1387     Trace((stderr, "beginning rpng2_x_display_row()\n"))
   1388 
   1389     if (rpng2_info.pass != prevpass) {
   1390         if (pause_after_pass && rpng2_info.pass > 0) {
   1391             XEvent e;
   1392             KeySym k;
   1393 
   1394             fprintf(stderr,
   1395               "%s:  end of pass %d of 7; click in image window to continue\n",
   1396               PROGNAME, prevpass + 1);
   1397             do
   1398                 XNextEvent(display, &e);
   1399             while (!QUIT(e,k));
   1400         }
   1401         fprintf(stderr, "%s:  pass %d of 7\r", PROGNAME, rpng2_info.pass + 1);
   1402         fflush(stderr);
   1403         prevpass = rpng2_info.pass;
   1404     }
   1405 
   1406     if (rows == 0)
   1407         firstrow = row;   /* first row that is not yet displayed */
   1408 
   1409     ++rows;   /* count of rows received but not yet displayed */
   1410 
   1411 /*---------------------------------------------------------------------------
   1412     Aside from the use of the rpng2_info struct, the lack of an outer loop
   1413     (over rows) and moving the XPutImage() call outside the "if (depth)"
   1414     tests, this routine is identical to rpng_x_display_image() in the non-
   1415     progressive version of the program.
   1416   ---------------------------------------------------------------------------*/
   1417 
   1418     if (depth == 24 || depth == 32) {
   1419         ulg red, green, blue;
   1420         int bpp = ximage->bits_per_pixel;
   1421 
   1422         src = rpng2_info.image_data + row*rpng2_info.rowbytes;
   1423         if (bg_image)
   1424             src2 = bg_data + row*bg_rowbytes;
   1425         dest = ximage->data + row*ximage_rowbytes;
   1426         if (rpng2_info.channels == 3) {
   1427             for (i = rpng2_info.width;  i > 0;  --i) {
   1428                 red   = *src++;
   1429                 green = *src++;
   1430                 blue  = *src++;
   1431                 pixel = (red   << RShift) |
   1432                         (green << GShift) |
   1433                         (blue  << BShift);
   1434                 /* recall that we set ximage->byte_order = MSBFirst above */
   1435                 if (bpp == 32) {
   1436                     *dest++ = (char)((pixel >> 24) & 0xff);
   1437                     *dest++ = (char)((pixel >> 16) & 0xff);
   1438                     *dest++ = (char)((pixel >>  8) & 0xff);
   1439                     *dest++ = (char)( pixel        & 0xff);
   1440                 } else {
   1441                     /* GRR BUG?  this assumes bpp == 24 & bits are packed low */
   1442                     /*           (probably need to use RShift, RMask, etc.) */
   1443                     *dest++ = (char)((pixel >> 16) & 0xff);
   1444                     *dest++ = (char)((pixel >>  8) & 0xff);
   1445                     *dest++ = (char)( pixel        & 0xff);
   1446                 }
   1447             }
   1448         } else /* if (rpng2_info.channels == 4) */ {
   1449             for (i = rpng2_info.width;  i > 0;  --i) {
   1450                 r = *src++;
   1451                 g = *src++;
   1452                 b = *src++;
   1453                 a = *src++;
   1454                 if (bg_image) {
   1455                     bg_red   = *src2++;
   1456                     bg_green = *src2++;
   1457                     bg_blue  = *src2++;
   1458                 }
   1459                 if (a == 255) {
   1460                     red   = r;
   1461                     green = g;
   1462                     blue  = b;
   1463                 } else if (a == 0) {
   1464                     red   = bg_red;
   1465                     green = bg_green;
   1466                     blue  = bg_blue;
   1467                 } else {
   1468                     /* this macro (from png.h) composites the foreground
   1469                      * and background values and puts the result into the
   1470                      * first argument */
   1471                     alpha_composite(red,   r, a, bg_red);
   1472                     alpha_composite(green, g, a, bg_green);
   1473                     alpha_composite(blue,  b, a, bg_blue);
   1474                 }
   1475                 pixel = (red   << RShift) |
   1476                         (green << GShift) |
   1477                         (blue  << BShift);
   1478                 /* recall that we set ximage->byte_order = MSBFirst above */
   1479                 if (bpp == 32) {
   1480                     *dest++ = (char)((pixel >> 24) & 0xff);
   1481                     *dest++ = (char)((pixel >> 16) & 0xff);
   1482                     *dest++ = (char)((pixel >>  8) & 0xff);
   1483                     *dest++ = (char)( pixel        & 0xff);
   1484                 } else {
   1485                     /* GRR BUG?  this assumes bpp == 24 & bits are packed low */
   1486                     /*           (probably need to use RShift, RMask, etc.) */
   1487                     *dest++ = (char)((pixel >> 16) & 0xff);
   1488                     *dest++ = (char)((pixel >>  8) & 0xff);
   1489                     *dest++ = (char)( pixel        & 0xff);
   1490                 }
   1491             }
   1492         }
   1493 
   1494     } else if (depth == 16) {
   1495         ush red, green, blue;
   1496 
   1497         src = rpng2_info.row_pointers[row];
   1498         if (bg_image)
   1499             src2 = bg_data + row*bg_rowbytes;
   1500         dest = ximage->data + row*ximage_rowbytes;
   1501         if (rpng2_info.channels == 3) {
   1502             for (i = rpng2_info.width;  i > 0;  --i) {
   1503                 red   = ((ush)(*src) << 8);
   1504                 ++src;
   1505                 green = ((ush)(*src) << 8);
   1506                 ++src;
   1507                 blue  = ((ush)(*src) << 8);
   1508                 ++src;
   1509                 pixel = ((red   >> RShift) & RMask) |
   1510                         ((green >> GShift) & GMask) |
   1511                         ((blue  >> BShift) & BMask);
   1512                 /* recall that we set ximage->byte_order = MSBFirst above */
   1513                 *dest++ = (char)((pixel >>  8) & 0xff);
   1514                 *dest++ = (char)( pixel        & 0xff);
   1515             }
   1516         } else /* if (rpng2_info.channels == 4) */ {
   1517             for (i = rpng2_info.width;  i > 0;  --i) {
   1518                 r = *src++;
   1519                 g = *src++;
   1520                 b = *src++;
   1521                 a = *src++;
   1522                 if (bg_image) {
   1523                     bg_red   = *src2++;
   1524                     bg_green = *src2++;
   1525                     bg_blue  = *src2++;
   1526                 }
   1527                 if (a == 255) {
   1528                     red   = ((ush)r << 8);
   1529                     green = ((ush)g << 8);
   1530                     blue  = ((ush)b << 8);
   1531                 } else if (a == 0) {
   1532                     red   = ((ush)bg_red   << 8);
   1533                     green = ((ush)bg_green << 8);
   1534                     blue  = ((ush)bg_blue  << 8);
   1535                 } else {
   1536                     /* this macro (from png.h) composites the foreground
   1537                      * and background values and puts the result back into
   1538                      * the first argument (== fg byte here:  safe) */
   1539                     alpha_composite(r, r, a, bg_red);
   1540                     alpha_composite(g, g, a, bg_green);
   1541                     alpha_composite(b, b, a, bg_blue);
   1542                     red   = ((ush)r << 8);
   1543                     green = ((ush)g << 8);
   1544                     blue  = ((ush)b << 8);
   1545                 }
   1546                 pixel = ((red   >> RShift) & RMask) |
   1547                         ((green >> GShift) & GMask) |
   1548                         ((blue  >> BShift) & BMask);
   1549                 /* recall that we set ximage->byte_order = MSBFirst above */
   1550                 *dest++ = (char)((pixel >>  8) & 0xff);
   1551                 *dest++ = (char)( pixel        & 0xff);
   1552             }
   1553         }
   1554 
   1555     } else /* depth == 8 */ {
   1556 
   1557         /* GRR:  add 8-bit support */
   1558 
   1559     }
   1560 
   1561 
   1562 /*---------------------------------------------------------------------------
   1563     Display after every 16 rows or when on one of last two rows.  (Region
   1564     may include previously displayed lines due to interlacing--i.e., not
   1565     contiguous.  Also, second-to-last row is final one in interlaced images
   1566     with odd number of rows.)  For demos, flush (and delay) after every 16th
   1567     row so "sparse" passes don't go twice as fast.
   1568   ---------------------------------------------------------------------------*/
   1569 
   1570     if (demo_timing && (row - firstrow >= 16 || row >= rpng2_info.height-2)) {
   1571         XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
   1572           (int)firstrow, rpng2_info.width, row - firstrow + 1);
   1573         XFlush(display);
   1574         rows = 0;
   1575         usleep(usleep_duration);
   1576     } else
   1577     if (!demo_timing && ((rows & 0xf) == 0 || row >= rpng2_info.height-2)) {
   1578         XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
   1579           (int)firstrow, rpng2_info.width, row - firstrow + 1);
   1580         XFlush(display);
   1581         rows = 0;
   1582     }
   1583 
   1584 }
   1585 
   1586 
   1587 
   1588 
   1589 
   1590 static void rpng2_x_finish_display(void)
   1591 {
   1592     Trace((stderr, "beginning rpng2_x_finish_display()\n"))
   1593 
   1594     /* last row has already been displayed by rpng2_x_display_row(), so we
   1595      * have nothing to do here except set a flag and let the user know that
   1596      * the image is done */
   1597 
   1598     rpng2_info.state = kDone;
   1599     printf(
   1600       "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
   1601     fflush(stdout);
   1602 }
   1603 
   1604 
   1605 
   1606 
   1607 
   1608 static void rpng2_x_redisplay_image(ulg startcol, ulg startrow,
   1609                                     ulg width, ulg height)
   1610 {
   1611     uch bg_red   = rpng2_info.bg_red;
   1612     uch bg_green = rpng2_info.bg_green;
   1613     uch bg_blue  = rpng2_info.bg_blue;
   1614     uch *src, *src2=NULL;
   1615     char *dest;
   1616     uch r, g, b, a;
   1617     ulg i, row, lastrow = 0;
   1618     ulg pixel;
   1619     int ximage_rowbytes = ximage->bytes_per_line;
   1620 
   1621 
   1622     Trace((stderr, "beginning display loop (image_channels == %d)\n",
   1623       rpng2_info.channels))
   1624     Trace((stderr, "   (width = %ld, rowbytes = %d, ximage_rowbytes = %d)\n",
   1625       rpng2_info.width, rpng2_info.rowbytes, ximage_rowbytes))
   1626     Trace((stderr, "   (bpp = %d)\n", ximage->bits_per_pixel))
   1627     Trace((stderr, "   (byte_order = %s)\n", ximage->byte_order == MSBFirst?
   1628       "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown")))
   1629 
   1630 /*---------------------------------------------------------------------------
   1631     Aside from the use of the rpng2_info struct and of src2 (for background
   1632     image), this routine is identical to rpng_x_display_image() in the non-
   1633     progressive version of the program--for the simple reason that redisplay
   1634     of the image against a new background happens after the image is fully
   1635     decoded and therefore is, by definition, non-progressive.
   1636   ---------------------------------------------------------------------------*/
   1637 
   1638     if (depth == 24 || depth == 32) {
   1639         ulg red, green, blue;
   1640         int bpp = ximage->bits_per_pixel;
   1641 
   1642         for (lastrow = row = startrow;  row < startrow+height;  ++row) {
   1643             src = rpng2_info.image_data + row*rpng2_info.rowbytes;
   1644             if (bg_image)
   1645                 src2 = bg_data + row*bg_rowbytes;
   1646             dest = ximage->data + row*ximage_rowbytes;
   1647             if (rpng2_info.channels == 3) {
   1648                 for (i = rpng2_info.width;  i > 0;  --i) {
   1649                     red   = *src++;
   1650                     green = *src++;
   1651                     blue  = *src++;
   1652 #ifdef NO_24BIT_MASKS
   1653                     pixel = (red   << RShift) |
   1654                             (green << GShift) |
   1655                             (blue  << BShift);
   1656                     /* recall that we set ximage->byte_order = MSBFirst above */
   1657                     if (bpp == 32) {
   1658                         *dest++ = (char)((pixel >> 24) & 0xff);
   1659                         *dest++ = (char)((pixel >> 16) & 0xff);
   1660                         *dest++ = (char)((pixel >>  8) & 0xff);
   1661                         *dest++ = (char)( pixel        & 0xff);
   1662                     } else {
   1663                         /* this assumes bpp == 24 & bits are packed low */
   1664                         /* (probably need to use RShift, RMask, etc.) */
   1665                         *dest++ = (char)((pixel >> 16) & 0xff);
   1666                         *dest++ = (char)((pixel >>  8) & 0xff);
   1667                         *dest++ = (char)( pixel        & 0xff);
   1668                     }
   1669 #else
   1670                     red   = (RShift < 0)? red   << (-RShift) : red   >> RShift;
   1671                     green = (GShift < 0)? green << (-GShift) : green >> GShift;
   1672                     blue  = (BShift < 0)? blue  << (-BShift) : blue  >> BShift;
   1673                     pixel = (red & RMask) | (green & GMask) | (blue & BMask);
   1674                     /* recall that we set ximage->byte_order = MSBFirst above */
   1675                     if (bpp == 32) {
   1676                         *dest++ = (char)((pixel >> 24) & 0xff);
   1677                         *dest++ = (char)((pixel >> 16) & 0xff);
   1678                         *dest++ = (char)((pixel >>  8) & 0xff);
   1679                         *dest++ = (char)( pixel        & 0xff);
   1680                     } else {
   1681                         /* GRR BUG */
   1682                         /* this assumes bpp == 24 & bits are packed low */
   1683                         /* (probably need to use RShift/RMask/etc. here, too) */
   1684                         *dest++ = (char)((pixel >> 16) & 0xff);
   1685                         *dest++ = (char)((pixel >>  8) & 0xff);
   1686                         *dest++ = (char)( pixel        & 0xff);
   1687                     }
   1688 #endif
   1689                 }
   1690 
   1691             } else /* if (rpng2_info.channels == 4) */ {
   1692                 for (i = rpng2_info.width;  i > 0;  --i) {
   1693                     r = *src++;
   1694                     g = *src++;
   1695                     b = *src++;
   1696                     a = *src++;
   1697                     if (bg_image) {
   1698                         bg_red   = *src2++;
   1699                         bg_green = *src2++;
   1700                         bg_blue  = *src2++;
   1701                     }
   1702                     if (a == 255) {
   1703                         red   = r;
   1704                         green = g;
   1705                         blue  = b;
   1706                     } else if (a == 0) {
   1707                         red   = bg_red;
   1708                         green = bg_green;
   1709                         blue  = bg_blue;
   1710                     } else {
   1711                         /* this macro (from png.h) composites the foreground
   1712                          * and background values and puts the result into the
   1713                          * first argument */
   1714                         alpha_composite(red,   r, a, bg_red);
   1715                         alpha_composite(green, g, a, bg_green);
   1716                         alpha_composite(blue,  b, a, bg_blue);
   1717                     }
   1718 #ifdef NO_24BIT_MASKS
   1719                     pixel = (red   << RShift) |
   1720                             (green << GShift) |
   1721                             (blue  << BShift);
   1722                     /* recall that we set ximage->byte_order = MSBFirst above */
   1723                     if (bpp == 32) {
   1724                         *dest++ = (char)((pixel >> 24) & 0xff);
   1725                         *dest++ = (char)((pixel >> 16) & 0xff);
   1726                         *dest++ = (char)((pixel >>  8) & 0xff);
   1727                         *dest++ = (char)( pixel        & 0xff);
   1728                     } else {
   1729                         /* this assumes bpp == 24 & bits are packed low */
   1730                         /* (probably need to use RShift, RMask, etc.) */
   1731                         *dest++ = (char)((pixel >> 16) & 0xff);
   1732                         *dest++ = (char)((pixel >>  8) & 0xff);
   1733                         *dest++ = (char)( pixel        & 0xff);
   1734                     }
   1735 #else
   1736                     red   = (RShift < 0)? red   << (-RShift) : red   >> RShift;
   1737                     green = (GShift < 0)? green << (-GShift) : green >> GShift;
   1738                     blue  = (BShift < 0)? blue  << (-BShift) : blue  >> BShift;
   1739                     pixel = (red & RMask) | (green & GMask) | (blue & BMask);
   1740                     /* recall that we set ximage->byte_order = MSBFirst above */
   1741                     if (bpp == 32) {
   1742                         *dest++ = (char)((pixel >> 24) & 0xff);
   1743                         *dest++ = (char)((pixel >> 16) & 0xff);
   1744                         *dest++ = (char)((pixel >>  8) & 0xff);
   1745                         *dest++ = (char)( pixel        & 0xff);
   1746                     } else {
   1747                         /* GRR BUG */
   1748                         /* this assumes bpp == 24 & bits are packed low */
   1749                         /* (probably need to use RShift/RMask/etc. here, too) */
   1750                         *dest++ = (char)((pixel >> 16) & 0xff);
   1751                         *dest++ = (char)((pixel >>  8) & 0xff);
   1752                         *dest++ = (char)( pixel        & 0xff);
   1753                     }
   1754 #endif
   1755                 }
   1756             }
   1757             /* display after every 16 lines */
   1758             if (((row+1) & 0xf) == 0) {
   1759                 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
   1760                   (int)lastrow, rpng2_info.width, 16);
   1761                 XFlush(display);
   1762                 lastrow = row + 1;
   1763             }
   1764         }
   1765 
   1766     } else if (depth == 16) {
   1767         ush red, green, blue;
   1768 
   1769         for (lastrow = row = startrow;  row < startrow+height;  ++row) {
   1770             src = rpng2_info.row_pointers[row];
   1771             if (bg_image)
   1772                 src2 = bg_data + row*bg_rowbytes;
   1773             dest = ximage->data + row*ximage_rowbytes;
   1774             if (rpng2_info.channels == 3) {
   1775                 for (i = rpng2_info.width;  i > 0;  --i) {
   1776                     red   = ((ush)(*src) << 8);
   1777                     ++src;
   1778                     green = ((ush)(*src) << 8);
   1779                     ++src;
   1780                     blue  = ((ush)(*src) << 8);
   1781                     ++src;
   1782                     pixel = ((red   >> RShift) & RMask) |
   1783                             ((green >> GShift) & GMask) |
   1784                             ((blue  >> BShift) & BMask);
   1785                     /* recall that we set ximage->byte_order = MSBFirst above */
   1786                     *dest++ = (char)((pixel >>  8) & 0xff);
   1787                     *dest++ = (char)( pixel        & 0xff);
   1788                 }
   1789             } else /* if (rpng2_info.channels == 4) */ {
   1790                 for (i = rpng2_info.width;  i > 0;  --i) {
   1791                     r = *src++;
   1792                     g = *src++;
   1793                     b = *src++;
   1794                     a = *src++;
   1795                     if (bg_image) {
   1796                         bg_red   = *src2++;
   1797                         bg_green = *src2++;
   1798                         bg_blue  = *src2++;
   1799                     }
   1800                     if (a == 255) {
   1801                         red   = ((ush)r << 8);
   1802                         green = ((ush)g << 8);
   1803                         blue  = ((ush)b << 8);
   1804                     } else if (a == 0) {
   1805                         red   = ((ush)bg_red   << 8);
   1806                         green = ((ush)bg_green << 8);
   1807                         blue  = ((ush)bg_blue  << 8);
   1808                     } else {
   1809                         /* this macro (from png.h) composites the foreground
   1810                          * and background values and puts the result back into
   1811                          * the first argument (== fg byte here:  safe) */
   1812                         alpha_composite(r, r, a, bg_red);
   1813                         alpha_composite(g, g, a, bg_green);
   1814                         alpha_composite(b, b, a, bg_blue);
   1815                         red   = ((ush)r << 8);
   1816                         green = ((ush)g << 8);
   1817                         blue  = ((ush)b << 8);
   1818                     }
   1819                     pixel = ((red   >> RShift) & RMask) |
   1820                             ((green >> GShift) & GMask) |
   1821                             ((blue  >> BShift) & BMask);
   1822                     /* recall that we set ximage->byte_order = MSBFirst above */
   1823                     *dest++ = (char)((pixel >>  8) & 0xff);
   1824                     *dest++ = (char)( pixel        & 0xff);
   1825                 }
   1826             }
   1827             /* display after every 16 lines */
   1828             if (((row+1) & 0xf) == 0) {
   1829                 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
   1830                   (int)lastrow, rpng2_info.width, 16);
   1831                 XFlush(display);
   1832                 lastrow = row + 1;
   1833             }
   1834         }
   1835 
   1836     } else /* depth == 8 */ {
   1837 
   1838         /* GRR:  add 8-bit support */
   1839 
   1840     }
   1841 
   1842     Trace((stderr, "calling final XPutImage()\n"))
   1843     if (lastrow < startrow+height) {
   1844         XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
   1845           (int)lastrow, rpng2_info.width, rpng2_info.height-lastrow);
   1846         XFlush(display);
   1847     }
   1848 
   1849 } /* end function rpng2_x_redisplay_image() */
   1850 
   1851 
   1852 
   1853 
   1854 
   1855 #ifdef FEATURE_LOOP
   1856 
   1857 static void rpng2_x_reload_bg_image(void)
   1858 {
   1859     char *dest;
   1860     uch r1, r2, g1, g2, b1, b2;
   1861     uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
   1862     int k, hmax, max;
   1863     int xidx, yidx, yidx_max;
   1864     int even_odd_vert, even_odd_horiz, even_odd;
   1865     int invert_gradient2 = (bg[pat].type & 0x08);
   1866     int invert_column;
   1867     ulg i, row;
   1868 
   1869 
   1870     bgscale = (pat == 0)? 8 : bgscale_default;
   1871     yidx_max = bgscale - 1;
   1872 
   1873 /*---------------------------------------------------------------------------
   1874     Vertical gradients (ramps) in NxN squares, alternating direction and
   1875     colors (N == bgscale).
   1876   ---------------------------------------------------------------------------*/
   1877 
   1878     if ((bg[pat].type & 0x07) == 0) {
   1879         uch r1_min  = rgb[bg[pat].rgb1_min].r;
   1880         uch g1_min  = rgb[bg[pat].rgb1_min].g;
   1881         uch b1_min  = rgb[bg[pat].rgb1_min].b;
   1882         uch r2_min  = rgb[bg[pat].rgb2_min].r;
   1883         uch g2_min  = rgb[bg[pat].rgb2_min].g;
   1884         uch b2_min  = rgb[bg[pat].rgb2_min].b;
   1885         int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
   1886         int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
   1887         int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
   1888         int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
   1889         int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
   1890         int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
   1891 
   1892         for (row = 0;  row < rpng2_info.height;  ++row) {
   1893             yidx = (int)(row % bgscale);
   1894             even_odd_vert = (int)((row / bgscale) & 1);
   1895 
   1896             r1 = r1_min + (r1_diff * yidx) / yidx_max;
   1897             g1 = g1_min + (g1_diff * yidx) / yidx_max;
   1898             b1 = b1_min + (b1_diff * yidx) / yidx_max;
   1899             r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
   1900             g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
   1901             b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
   1902 
   1903             r2 = r2_min + (r2_diff * yidx) / yidx_max;
   1904             g2 = g2_min + (g2_diff * yidx) / yidx_max;
   1905             b2 = b2_min + (b2_diff * yidx) / yidx_max;
   1906             r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
   1907             g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
   1908             b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
   1909 
   1910             dest = (char *)bg_data + row*bg_rowbytes;
   1911             for (i = 0;  i < rpng2_info.width;  ++i) {
   1912                 even_odd_horiz = (int)((i / bgscale) & 1);
   1913                 even_odd = even_odd_vert ^ even_odd_horiz;
   1914                 invert_column =
   1915                   (even_odd_horiz && (bg[pat].type & 0x10));
   1916                 if (even_odd == 0) {        /* gradient #1 */
   1917                     if (invert_column) {
   1918                         *dest++ = r1_inv;
   1919                         *dest++ = g1_inv;
   1920                         *dest++ = b1_inv;
   1921                     } else {
   1922                         *dest++ = r1;
   1923                         *dest++ = g1;
   1924                         *dest++ = b1;
   1925                     }
   1926                 } else {                    /* gradient #2 */
   1927                     if ((invert_column && invert_gradient2) ||
   1928                         (!invert_column && !invert_gradient2))
   1929                     {
   1930                         *dest++ = r2;       /* not inverted or */
   1931                         *dest++ = g2;       /*  doubly inverted */
   1932                         *dest++ = b2;
   1933                     } else {
   1934                         *dest++ = r2_inv;
   1935                         *dest++ = g2_inv;   /* singly inverted */
   1936                         *dest++ = b2_inv;
   1937                     }
   1938                 }
   1939             }
   1940         }
   1941 
   1942 /*---------------------------------------------------------------------------
   1943     Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
   1944     M. Costello.
   1945   ---------------------------------------------------------------------------*/
   1946 
   1947     } else if ((bg[pat].type & 0x07) == 1) {
   1948 
   1949         hmax = (bgscale-1)/2;   /* half the max weight of a color */
   1950         max = 2*hmax;           /* the max weight of a color */
   1951 
   1952         r1 = rgb[bg[pat].rgb1_max].r;
   1953         g1 = rgb[bg[pat].rgb1_max].g;
   1954         b1 = rgb[bg[pat].rgb1_max].b;
   1955         r2 = rgb[bg[pat].rgb2_max].r;
   1956         g2 = rgb[bg[pat].rgb2_max].g;
   1957         b2 = rgb[bg[pat].rgb2_max].b;
   1958 
   1959         for (row = 0;  row < rpng2_info.height;  ++row) {
   1960             yidx = (int)(row % bgscale);
   1961             if (yidx > hmax)
   1962                 yidx = bgscale-1 - yidx;
   1963             dest = (char *)bg_data + row*bg_rowbytes;
   1964             for (i = 0;  i < rpng2_info.width;  ++i) {
   1965                 xidx = (int)(i % bgscale);
   1966                 if (xidx > hmax)
   1967                     xidx = bgscale-1 - xidx;
   1968                 k = xidx + yidx;
   1969                 *dest++ = (k*r1 + (max-k)*r2) / max;
   1970                 *dest++ = (k*g1 + (max-k)*g2) / max;
   1971                 *dest++ = (k*b1 + (max-k)*b2) / max;
   1972             }
   1973         }
   1974 
   1975 /*---------------------------------------------------------------------------
   1976     Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
   1977     soids will equal bgscale?].  This one is slow but very cool.  Code con-
   1978     tributed by Pieter S. van der Meulen (originally in Smalltalk).
   1979   ---------------------------------------------------------------------------*/
   1980 
   1981     } else if ((bg[pat].type & 0x07) == 2) {
   1982         uch ch;
   1983         int ii, x, y, hw, hh, grayspot;
   1984         double freq, rotate, saturate, gray, intensity;
   1985         double angle=0.0, aoffset=0.0, maxDist, dist;
   1986         double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
   1987 
   1988         hh = (int)(rpng2_info.height / 2);
   1989         hw = (int)(rpng2_info.width / 2);
   1990 
   1991         /* variables for radial waves:
   1992          *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
   1993          *   freq:  number of color beams originating from the center
   1994          *   grayspot:  size of the graying center area (anti-alias)
   1995          *   rotate:  rotation of the beams as a function of radius
   1996          *   saturate:  saturation of beams' shape azimuthally
   1997          */
   1998         angle = CLIP(angle, 0.0, 360.0);
   1999         grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
   2000         freq = MAX((double)bg[pat].bg_freq, 0.0);
   2001         saturate = (double)bg[pat].bg_bsat * 0.1;
   2002         rotate = (double)bg[pat].bg_brot * 0.1;
   2003         gray = 0.0;
   2004         intensity = 0.0;
   2005         maxDist = (double)((hw*hw) + (hh*hh));
   2006 
   2007         for (row = 0;  row < rpng2_info.height;  ++row) {
   2008             y = (int)(row - hh);
   2009             dest = (char *)bg_data + row*bg_rowbytes;
   2010             for (i = 0;  i < rpng2_info.width;  ++i) {
   2011                 x = (int)(i - hw);
   2012                 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
   2013                 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
   2014                 gray = MIN(1.0, gray);
   2015                 dist = (double)((x*x) + (y*y)) / maxDist;
   2016                 intensity = cos((angle+(rotate*dist*PI)) * freq) *
   2017                   gray * saturate;
   2018                 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
   2019                 hue = (angle + PI) * INV_PI_360 + aoffset;
   2020                 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
   2021                 s = MIN(MAX(s,0.0), 1.0);
   2022                 v = MIN(MAX(intensity,0.0), 1.0);
   2023 
   2024                 if (s == 0.0) {
   2025                     ch = (uch)(v * 255.0);
   2026                     *dest++ = ch;
   2027                     *dest++ = ch;
   2028                     *dest++ = ch;
   2029                 } else {
   2030                     if ((hue < 0.0) || (hue >= 360.0))
   2031                         hue -= (((int)(hue / 360.0)) * 360.0);
   2032                     hue /= 60.0;
   2033                     ii = (int)hue;
   2034                     f = hue - (double)ii;
   2035                     p = (1.0 - s) * v;
   2036                     q = (1.0 - (s * f)) * v;
   2037                     t = (1.0 - (s * (1.0 - f))) * v;
   2038                     if      (ii == 0) { red = v; green = t; blue = p; }
   2039                     else if (ii == 1) { red = q; green = v; blue = p; }
   2040                     else if (ii == 2) { red = p; green = v; blue = t; }
   2041                     else if (ii == 3) { red = p; green = q; blue = v; }
   2042                     else if (ii == 4) { red = t; green = p; blue = v; }
   2043                     else if (ii == 5) { red = v; green = p; blue = q; }
   2044                     *dest++ = (uch)(red * 255.0);
   2045                     *dest++ = (uch)(green * 255.0);
   2046                     *dest++ = (uch)(blue * 255.0);
   2047                 }
   2048             }
   2049         }
   2050     }
   2051 
   2052 } /* end function rpng2_x_reload_bg_image() */
   2053 
   2054 
   2055 
   2056 
   2057 
   2058 static int is_number(char *p)
   2059 {
   2060     while (*p) {
   2061         if (!isdigit(*p))
   2062             return FALSE;
   2063         ++p;
   2064     }
   2065     return TRUE;
   2066 }
   2067 
   2068 #endif /* FEATURE_LOOP */
   2069 
   2070 
   2071 
   2072 
   2073 
   2074 static void rpng2_x_cleanup(void)
   2075 {
   2076     if (bg_image && bg_data) {
   2077         free(bg_data);
   2078         bg_data = NULL;
   2079     }
   2080 
   2081     if (rpng2_info.image_data) {
   2082         free(rpng2_info.image_data);
   2083         rpng2_info.image_data = NULL;
   2084     }
   2085 
   2086     if (rpng2_info.row_pointers) {
   2087         free(rpng2_info.row_pointers);
   2088         rpng2_info.row_pointers = NULL;
   2089     }
   2090 
   2091     if (ximage) {
   2092         if (ximage->data) {
   2093             free(ximage->data);           /* we allocated it, so we free it */
   2094             ximage->data = (char *)NULL;  /*  instead of XDestroyImage() */
   2095         }
   2096         XDestroyImage(ximage);
   2097         ximage = NULL;
   2098     }
   2099 
   2100     if (have_gc)
   2101         XFreeGC(display, gc);
   2102 
   2103     if (have_window)
   2104         XDestroyWindow(display, window);
   2105 
   2106     if (have_colormap)
   2107         XFreeColormap(display, colormap);
   2108 
   2109     if (have_nondefault_visual)
   2110         XFree(visual_list);
   2111 }
   2112 
   2113 
   2114 
   2115 
   2116 
   2117 static int rpng2_x_msb(ulg u32val)
   2118 {
   2119     int i;
   2120 
   2121     for (i = 31;  i >= 0;  --i) {
   2122         if (u32val & 0x80000000L)
   2123             break;
   2124         u32val <<= 1;
   2125     }
   2126     return i;
   2127 }
   2128