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