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