873af2335432694120f4470871efa8d1d597fbe6
[platform/upstream/libpng.git] / contrib / gregbook / rpng2-x.c
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 */
165 static void rpng2_x_init (void);
166 static int  rpng2_x_create_window (void);
167 static int  rpng2_x_load_bg_image (void);
168 static void rpng2_x_display_row (ulg row);
169 static void rpng2_x_finish_display (void);
170 static void rpng2_x_redisplay_image (ulg startcol, ulg startrow,
171                                      ulg width, ulg height);
172 #ifdef FEATURE_LOOP
173 static void rpng2_x_reload_bg_image (void);
174 static int  is_number (char *p);
175 #endif
176 static void rpng2_x_cleanup (void);
177 static int  rpng2_x_msb (ulg u32val);
178
179
180 static char titlebar[1024], *window_name = titlebar;
181 static char *appname = LONGNAME;
182 static char *icon_name = PROGNAME;
183 static char *res_name = RESNAME;
184 static char *res_class = RESCLASS;
185 static char *filename;
186 static FILE *infile;
187
188 static mainprog_info rpng2_info;
189
190 static uch inbuf[INBUFSIZE];
191 static int incount;
192
193 static int pat = 6;        /* must be less than num_bgpat */
194 static int bg_image = 0;
195 static int bgscale, bgscale_default = 16;
196 static ulg bg_rowbytes;
197 static uch *bg_data;
198
199 int pause_after_pass = FALSE;
200 int demo_timing = FALSE;
201 ulg usleep_duration = 0L;
202
203 static 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:
225 static 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  */
245 static 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 };
268 static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
269
270
271 /* X-specific variables */
272 static char *displayname;
273 static XImage *ximage;
274 static Display *display;
275 static int depth;
276 static Visual *visual;
277 static XVisualInfo *visual_list;
278 static int RShift, GShift, BShift;
279 static ulg RMask, GMask, BMask;
280 static Window window;
281 static GC gc;
282 static Colormap colormap;
283
284 static int have_nondefault_visual = FALSE;
285 static int have_colormap = FALSE;
286 static int have_window = FALSE;
287 static int have_gc = FALSE;
288
289
290
291
292 int 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
770 static 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
821 static 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
1072 static 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
1368 static 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
1590 static 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
1608 static 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
1857 static 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
2058 static 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
2074 static 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
2117 static 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 }