all repos — mgba @ 9de8f084ba55460b02d300c1dd8b8e6c56f691d5

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