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