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