all repos — mgba @ 81a52403a3583039f4e571f1516cd0efe4872c4b

mGBA Game Boy Advance Emulator

src/third-party/libpng/contrib/gregbook/rpng2-x.c (view raw)

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