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