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