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