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