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 
main(int argc,char ** argv)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           (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat-1);
489         exit(1);
490     }
491 
492 
493     if (!(infile = fopen(filename, "rb"))) {
494         fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
495         ++error;
496     } else {
497         incount = fread(inbuf, 1, INBUFSIZE, infile);
498         if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
499             fprintf(stderr, PROGNAME
500               ":  [%s] is not a PNG file: incorrect signature\n",
501               filename);
502             ++error;
503         } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
504             switch (rc) {
505                 case 2:
506                     fprintf(stderr, PROGNAME
507                       ":  [%s] has bad IHDR (libpng longjmp)\n", filename);
508                     break;
509                 case 4:
510                     fprintf(stderr, PROGNAME ":  insufficient memory\n");
511                     break;
512                 default:
513                     fprintf(stderr, PROGNAME
514                       ":  unknown readpng2_init() error\n");
515                     break;
516             }
517             ++error;
518         } else {
519             Trace((stderr, "about to call XOpenDisplay()\n"))
520             display = XOpenDisplay(displayname);
521             if (!display) {
522                 readpng2_cleanup(&rpng2_info);
523                 fprintf(stderr, PROGNAME ":  can't open X display [%s]\n",
524                   displayname? displayname : "default");
525                 ++error;
526             }
527         }
528         if (error)
529             fclose(infile);
530     }
531 
532 
533     if (error) {
534         fprintf(stderr, PROGNAME ":  aborting.\n");
535         exit(2);
536     }
537 
538 
539     /* set the title-bar string, but make sure buffer doesn't overflow */
540 
541     alen = strlen(appname);
542     flen = strlen(filename);
543     if (alen + flen + 3 > 1023)
544         sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
545     else
546         sprintf(titlebar, "%s:  %s", appname, filename);
547 
548 
549     /* set some final rpng2_info variables before entering main data loop */
550 
551     if (have_bg) {
552         unsigned r, g, b;   /* this approach quiets compiler warnings */
553 
554         sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
555         rpng2_info.bg_red   = (uch)r;
556         rpng2_info.bg_green = (uch)g;
557         rpng2_info.bg_blue  = (uch)b;
558     } else
559         rpng2_info.need_bgcolor = TRUE;
560 
561     rpng2_info.state = kPreInit;
562     rpng2_info.mainprog_init = rpng2_x_init;
563     rpng2_info.mainprog_display_row = rpng2_x_display_row;
564     rpng2_info.mainprog_finish_display = rpng2_x_finish_display;
565 
566 
567     /* OK, this is the fun part:  call readpng2_decode_data() at the start of
568      * the loop to deal with our first buffer of data (read in above to verify
569      * that the file is a PNG image), then loop through the file and continue
570      * calling the same routine to handle each chunk of data.  It in turn
571      * passes the data to libpng, which will invoke one or more of our call-
572      * backs as decoded data become available.  We optionally call sleep() for
573      * one second per iteration to simulate downloading the image via an analog
574      * modem. */
575 
576     for (;;) {
577         Trace((stderr, "about to call readpng2_decode_data()\n"))
578         if (readpng2_decode_data(&rpng2_info, inbuf, incount))
579             ++error;
580         Trace((stderr, "done with readpng2_decode_data()\n"))
581 
582         if (error || incount != INBUFSIZE || rpng2_info.state == kDone) {
583             if (rpng2_info.state == kDone) {
584                 Trace((stderr, "done decoding PNG image\n"))
585             } else if (ferror(infile)) {
586                 fprintf(stderr, PROGNAME
587                   ":  error while reading PNG image file\n");
588                 exit(3);
589             } else if (feof(infile)) {
590                 fprintf(stderr, PROGNAME ":  end of file reached "
591                   "(unexpectedly) while reading PNG image file\n");
592                 exit(3);
593             } else /* if (error) */ {
594                 /* will print error message below */
595             }
596             break;
597         }
598 
599         if (timing)
600             sleep(1);
601 
602         incount = fread(inbuf, 1, INBUFSIZE, infile);
603     }
604 
605 
606     /* clean up PNG stuff and report any decoding errors */
607 
608     fclose(infile);
609     Trace((stderr, "about to call readpng2_cleanup()\n"))
610     readpng2_cleanup(&rpng2_info);
611 
612     if (error) {
613         fprintf(stderr, PROGNAME ":  libpng error while decoding PNG image\n");
614         exit(3);
615     }
616 
617 
618 #ifdef FEATURE_LOOP
619 
620     if (loop && bg_image) {
621         Trace((stderr, "entering -loop loop (FEATURE_LOOP)\n"))
622         for (;;) {
623             int i, use_sleep;
624             struct timeval now, then;
625 
626             /* get current time and add loop_interval to get target time */
627             if (gettimeofday(&then, NULL) == 0) {
628                 then.tv_sec += loop_interval;
629                 use_sleep = FALSE;
630             } else
631                 use_sleep = TRUE;
632 
633             /* do quick check for a quit event but don't wait for it */
634             /* GRR BUG:  should also check for Expose events and redraw... */
635             if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask, &e))
636                 if (QUIT(e,k))
637                     break;
638 
639             /* generate next background image */
640             if (++pat >= num_bgpat)
641                 pat = 0;
642             rpng2_x_reload_bg_image();
643 
644             /* wait for timeout, using whatever means are available */
645             if (use_sleep || gettimeofday(&now, NULL) != 0) {
646                 for (i = loop_interval;  i > 0;  --i) {
647                     sleep(1);
648                     /* GRR BUG:  also need to check for Expose (and redraw!) */
649                     if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask,
650                         &e) && QUIT(e,k))
651                         break;
652                 }
653             } else {
654                 /* Y2038 BUG! */
655                 if (now.tv_sec < then.tv_sec ||
656                     (now.tv_sec == then.tv_sec && now.tv_usec < then.tv_usec))
657                 {
658                     int quit = FALSE;
659                     long seconds_to_go = then.tv_sec - now.tv_sec;
660                     long usleep_usec;
661 
662                     /* basically chew up most of remaining loop-interval with
663                      *  calls to sleep(1) interleaved with checks for quit
664                      *  events, but also recalc time-to-go periodically; when
665                      *  done, clean up any remaining time with usleep() call
666                      *  (could also use SIGALRM, but signals are a pain...) */
667                     while (seconds_to_go-- > 1) {
668                         int seconds_done = 0;
669 
670                         for (i = seconds_to_go;  i > 0 && !quit;  --i) {
671                             sleep(1);
672                             /* GRR BUG:  need to check for Expose and redraw */
673                             if (XCheckMaskEvent(display, KeyPressMask |
674                                 ButtonPressMask, &e) && QUIT(e,k))
675                                 quit = TRUE;
676                             if (++seconds_done > 1000)
677                                 break;   /* time to redo seconds_to_go meas. */
678                         }
679                         if (quit)
680                             break;
681 
682                         /* OK, more than 1000 seconds since last check:
683                          *  correct the time-to-go measurement for drift */
684                         if (gettimeofday(&now, NULL) == 0) {
685                             if (now.tv_sec >= then.tv_sec)
686                                 break;
687                             seconds_to_go = then.tv_sec - now.tv_sec;
688                         } else
689                             ++seconds_to_go;  /* restore what we subtracted */
690                     }
691                     if (quit)
692                         break;   /* breaks outer do-loop, skips redisplay */
693 
694                     /* since difference between "now" and "then" is already
695                      *  eaten up to within a couple of seconds, don't need to
696                      *  worry about overflow--but might have overshot (neg.) */
697                     if (gettimeofday(&now, NULL) == 0) {
698                         usleep_usec = 1000000L*(then.tv_sec - now.tv_sec) +
699                           then.tv_usec - now.tv_usec;
700                         if (usleep_usec > 0)
701                             usleep((ulg)usleep_usec);
702                     }
703                 }
704             }
705 
706             /* composite image against new background and display (note that
707              *  we do not take into account the time spent doing this...) */
708             rpng2_x_redisplay_image (0, 0, rpng2_info.width, rpng2_info.height);
709         }
710 
711     } else /* FALL THROUGH and do the normal thing */
712 
713 #endif /* FEATURE_LOOP */
714 
715     /* wait for the user to tell us when to quit */
716 
717     if (rpng2_info.state >= kWindowInit) {
718         Trace((stderr, "entering final wait-for-quit-event loop\n"))
719         do {
720             XNextEvent(display, &e);
721             if (e.type == Expose) {
722                 XExposeEvent *ex = (XExposeEvent *)&e;
723                 rpng2_x_redisplay_image (ex->x, ex->y, ex->width, ex->height);
724             }
725         } while (!QUIT(e,k));
726     } else {
727         fprintf(stderr, PROGNAME ":  init callback never called:  probable "
728           "libpng error while decoding PNG metadata\n");
729         exit(4);
730     }
731 
732 
733     /* we're done:  clean up all image and X resources and go away */
734 
735     Trace((stderr, "about to call rpng2_x_cleanup()\n"))
736     rpng2_x_cleanup();
737 
738     return 0;
739 }
740 
741 
742 
743 
744 
745 /* this function is called by readpng2_info_callback() in readpng2.c, which
746  * in turn is called by libpng after all of the pre-IDAT chunks have been
747  * read and processed--i.e., we now have enough info to finish initializing */
748 
rpng2_x_init(void)749 static void rpng2_x_init(void)
750 {
751     ulg i;
752     ulg rowbytes = rpng2_info.rowbytes;
753 
754     Trace((stderr, "beginning rpng2_x_init()\n"))
755     Trace((stderr, "  rowbytes = %d\n", rpng2_info.rowbytes))
756     Trace((stderr, "  width  = %ld\n", rpng2_info.width))
757     Trace((stderr, "  height = %ld\n", rpng2_info.height))
758 
759     rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
760     if (!rpng2_info.image_data) {
761         readpng2_cleanup(&rpng2_info);
762         return;
763     }
764 
765     rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
766     if (!rpng2_info.row_pointers) {
767         free(rpng2_info.image_data);
768         rpng2_info.image_data = NULL;
769         readpng2_cleanup(&rpng2_info);
770         return;
771     }
772 
773     for (i = 0;  i < rpng2_info.height;  ++i)
774         rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
775 
776 
777     /* do the basic X initialization stuff, make the window, and fill it with
778      * the user-specified, file-specified or default background color or
779      * pattern */
780 
781     if (rpng2_x_create_window()) {
782 
783         /* GRR TEMPORARY HACK:  this is fundamentally no different from cases
784          * above; libpng should call our error handler to longjmp() back to us
785          * when png_ptr goes away.  If we/it segfault instead, seems like a
786          * libpng bug... */
787 
788         /* we're here via libpng callback, so if window fails, clean and bail */
789         readpng2_cleanup(&rpng2_info);
790         rpng2_x_cleanup();
791         exit(2);
792     }
793 
794     rpng2_info.state = kWindowInit;
795 }
796 
797 
798 
799 
800 
rpng2_x_create_window(void)801 static int rpng2_x_create_window(void)
802 {
803     ulg bg_red   = rpng2_info.bg_red;
804     ulg bg_green = rpng2_info.bg_green;
805     ulg bg_blue  = rpng2_info.bg_blue;
806     ulg bg_pixel = 0L;
807     ulg attrmask;
808     int need_colormap = FALSE;
809     int screen, pad;
810     uch *xdata;
811     Window root;
812     XEvent e;
813     XGCValues gcvalues;
814     XSetWindowAttributes attr;
815     XTextProperty windowName, *pWindowName = &windowName;
816     XTextProperty iconName, *pIconName = &iconName;
817     XVisualInfo visual_info;
818     XSizeHints *size_hints;
819     XWMHints *wm_hints;
820     XClassHint *class_hints;
821 
822 
823     Trace((stderr, "beginning rpng2_x_create_window()\n"))
824 
825     screen = DefaultScreen(display);
826     depth = DisplayPlanes(display, screen);
827     root = RootWindow(display, screen);
828 
829 #ifdef DEBUG
830     XSynchronize(display, True);
831 #endif
832 
833     if (depth != 16 && depth != 24 && depth != 32) {
834         int visuals_matched = 0;
835 
836         Trace((stderr, "default depth is %d:  checking other visuals\n",
837           depth))
838 
839         /* 24-bit first */
840         visual_info.screen = screen;
841         visual_info.depth = 24;
842         visual_list = XGetVisualInfo(display,
843           VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
844         if (visuals_matched == 0) {
845 /* GRR:  add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
846             fprintf(stderr, "default screen depth %d not supported, and no"
847               " 24-bit visuals found\n", depth);
848             return 2;
849         }
850         Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
851           visuals_matched))
852         visual = visual_list[0].visual;
853         depth = visual_list[0].depth;
854 /*
855         colormap_size = visual_list[0].colormap_size;
856         visual_class = visual->class;
857         visualID = XVisualIDFromVisual(visual);
858  */
859         have_nondefault_visual = TRUE;
860         need_colormap = TRUE;
861     } else {
862         XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
863         visual = visual_info.visual;
864     }
865 
866     RMask = visual->red_mask;
867     GMask = visual->green_mask;
868     BMask = visual->blue_mask;
869 
870 /* GRR:  add/check 8-bit support */
871     if (depth == 8 || need_colormap) {
872         colormap = XCreateColormap(display, root, visual, AllocNone);
873         if (!colormap) {
874             fprintf(stderr, "XCreateColormap() failed\n");
875             return 2;
876         }
877         have_colormap = TRUE;
878         if (depth == 8)
879             bg_image = FALSE;   /* gradient just wastes palette entries */
880     }
881     if (depth == 15 || depth == 16) {
882         RShift = 15 - rpng2_x_msb(RMask);    /* these are right-shifts */
883         GShift = 15 - rpng2_x_msb(GMask);
884         BShift = 15 - rpng2_x_msb(BMask);
885     } else if (depth > 16) {
886         RShift = rpng2_x_msb(RMask) - 7;     /* these are left-shifts */
887         GShift = rpng2_x_msb(GMask) - 7;
888         BShift = rpng2_x_msb(BMask) - 7;
889     }
890     if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
891         fprintf(stderr, "rpng2 internal logic error:  negative X shift(s)!\n");
892         return 2;
893     }
894 
895 /*---------------------------------------------------------------------------
896     Finally, create the window.
897   ---------------------------------------------------------------------------*/
898 
899     attr.backing_store = Always;
900     attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
901     attrmask = CWBackingStore | CWEventMask;
902     if (have_nondefault_visual) {
903         attr.colormap = colormap;
904         attr.background_pixel = 0;
905         attr.border_pixel = 1;
906         attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
907     }
908 
909     window = XCreateWindow(display, root, 0, 0, rpng2_info.width,
910       rpng2_info.height, 0, depth, InputOutput, visual, attrmask, &attr);
911 
912     if (window == None) {
913         fprintf(stderr, "XCreateWindow() failed\n");
914         return 2;
915     } else
916         have_window = TRUE;
917 
918     if (depth == 8)
919         XSetWindowColormap(display, window, colormap);
920 
921     if (!XStringListToTextProperty(&window_name, 1, pWindowName))
922         pWindowName = NULL;
923     if (!XStringListToTextProperty(&icon_name, 1, pIconName))
924         pIconName = NULL;
925 
926     /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */
927 
928     if ((size_hints = XAllocSizeHints()) != NULL) {
929         /* window will not be resizable */
930         size_hints->flags = PMinSize | PMaxSize;
931         size_hints->min_width = size_hints->max_width = (int)rpng2_info.width;
932         size_hints->min_height = size_hints->max_height =
933           (int)rpng2_info.height;
934     }
935 
936     if ((wm_hints = XAllocWMHints()) != NULL) {
937         wm_hints->initial_state = NormalState;
938         wm_hints->input = True;
939      /* wm_hints->icon_pixmap = icon_pixmap; */
940         wm_hints->flags = StateHint | InputHint  /* | IconPixmapHint */ ;
941     }
942 
943     if ((class_hints = XAllocClassHint()) != NULL) {
944         class_hints->res_name = res_name;
945         class_hints->res_class = res_class;
946     }
947 
948     XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
949       size_hints, wm_hints, class_hints);
950 
951     /* various properties and hints no longer needed; free memory */
952     if (pWindowName)
953        XFree(pWindowName->value);
954     if (pIconName)
955        XFree(pIconName->value);
956     if (size_hints)
957         XFree(size_hints);
958     if (wm_hints)
959        XFree(wm_hints);
960     if (class_hints)
961        XFree(class_hints);
962 
963     XMapWindow(display, window);
964 
965     gc = XCreateGC(display, window, 0, &gcvalues);
966     have_gc = TRUE;
967 
968 /*---------------------------------------------------------------------------
969     Allocate memory for the X- and display-specific version of the image.
970   ---------------------------------------------------------------------------*/
971 
972     if (depth == 24 || depth == 32) {
973         xdata = (uch *)malloc(4*rpng2_info.width*rpng2_info.height);
974         pad = 32;
975     } else if (depth == 16) {
976         xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height);
977         pad = 16;
978     } else /* depth == 8 */ {
979         xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height);
980         pad = 8;
981     }
982 
983     if (!xdata) {
984         fprintf(stderr, PROGNAME ":  unable to allocate image memory\n");
985         return 4;
986     }
987 
988     ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
989       (char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0);
990 
991     if (!ximage) {
992         fprintf(stderr, PROGNAME ":  XCreateImage() failed\n");
993         free(xdata);
994         return 3;
995     }
996 
997     /* to avoid testing the byte order every pixel (or doubling the size of
998      * the drawing routine with a giant if-test), we arbitrarily set the byte
999      * order to MSBFirst and let Xlib worry about inverting things on little-
1000      * endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the
1001      * most efficient approach (the giant if-test would be better), but in
1002      * the interest of clarity, we'll take the easy way out... */
1003 
1004     ximage->byte_order = MSBFirst;
1005 
1006 /*---------------------------------------------------------------------------
1007     Fill window with the specified background color (default is black) or
1008     faked "background image" (but latter is disabled if 8-bit; gradients
1009     just waste palette entries).
1010   ---------------------------------------------------------------------------*/
1011 
1012     if (bg_image)
1013         rpng2_x_load_bg_image();    /* resets bg_image if fails */
1014 
1015     if (!bg_image) {
1016         if (depth == 24 || depth == 32) {
1017             bg_pixel = (bg_red   << RShift) |
1018                        (bg_green << GShift) |
1019                        (bg_blue  << BShift);
1020         } else if (depth == 16) {
1021             bg_pixel = (((bg_red   << 8) >> RShift) & RMask) |
1022                        (((bg_green << 8) >> GShift) & GMask) |
1023                        (((bg_blue  << 8) >> BShift) & BMask);
1024         } else /* depth == 8 */ {
1025 
1026             /* GRR:  add 8-bit support */
1027 
1028         }
1029         XSetForeground(display, gc, bg_pixel);
1030         XFillRectangle(display, window, gc, 0, 0, rpng2_info.width,
1031           rpng2_info.height);
1032     }
1033 
1034 /*---------------------------------------------------------------------------
1035     Wait for first Expose event to do any drawing, then flush and return.
1036   ---------------------------------------------------------------------------*/
1037 
1038     do
1039         XNextEvent(display, &e);
1040     while (e.type != Expose || e.xexpose.count);
1041 
1042     XFlush(display);
1043 
1044     return 0;
1045 
1046 } /* end function rpng2_x_create_window() */
1047 
1048 
1049 
1050 
1051 
rpng2_x_load_bg_image(void)1052 static int rpng2_x_load_bg_image(void)
1053 {
1054     uch *src;
1055     char *dest;
1056     uch r1, r2, g1, g2, b1, b2;
1057     uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
1058     int k, hmax, max;
1059     int xidx, yidx, yidx_max;
1060     int even_odd_vert, even_odd_horiz, even_odd;
1061     int invert_gradient2 = (bg[pat].type & 0x08);
1062     int invert_column;
1063     int ximage_rowbytes = ximage->bytes_per_line;
1064     ulg i, row;
1065     ulg pixel;
1066 
1067 /*---------------------------------------------------------------------------
1068     Allocate buffer for fake background image to be used with transparent
1069     images; if this fails, revert to plain background color.
1070   ---------------------------------------------------------------------------*/
1071 
1072     bg_rowbytes = 3 * rpng2_info.width;
1073     bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
1074     if (!bg_data) {
1075         fprintf(stderr, PROGNAME
1076           ":  unable to allocate memory for background image\n");
1077         bg_image = 0;
1078         return 1;
1079     }
1080 
1081     bgscale = (pat == 0)? 8 : bgscale_default;
1082     yidx_max = bgscale - 1;
1083 
1084 /*---------------------------------------------------------------------------
1085     Vertical gradients (ramps) in NxN squares, alternating direction and
1086     colors (N == bgscale).
1087   ---------------------------------------------------------------------------*/
1088 
1089     if ((bg[pat].type & 0x07) == 0) {
1090         uch r1_min  = rgb[bg[pat].rgb1_min].r;
1091         uch g1_min  = rgb[bg[pat].rgb1_min].g;
1092         uch b1_min  = rgb[bg[pat].rgb1_min].b;
1093         uch r2_min  = rgb[bg[pat].rgb2_min].r;
1094         uch g2_min  = rgb[bg[pat].rgb2_min].g;
1095         uch b2_min  = rgb[bg[pat].rgb2_min].b;
1096         int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
1097         int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
1098         int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
1099         int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
1100         int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
1101         int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
1102 
1103         for (row = 0;  row < rpng2_info.height;  ++row) {
1104             yidx = (int)(row % bgscale);
1105             even_odd_vert = (int)((row / bgscale) & 1);
1106 
1107             r1 = r1_min + (r1_diff * yidx) / yidx_max;
1108             g1 = g1_min + (g1_diff * yidx) / yidx_max;
1109             b1 = b1_min + (b1_diff * yidx) / yidx_max;
1110             r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
1111             g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
1112             b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
1113 
1114             r2 = r2_min + (r2_diff * yidx) / yidx_max;
1115             g2 = g2_min + (g2_diff * yidx) / yidx_max;
1116             b2 = b2_min + (b2_diff * yidx) / yidx_max;
1117             r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
1118             g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
1119             b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
1120 
1121             dest = (char *)bg_data + row*bg_rowbytes;
1122             for (i = 0;  i < rpng2_info.width;  ++i) {
1123                 even_odd_horiz = (int)((i / bgscale) & 1);
1124                 even_odd = even_odd_vert ^ even_odd_horiz;
1125                 invert_column =
1126                   (even_odd_horiz && (bg[pat].type & 0x10));
1127                 if (even_odd == 0) {        /* gradient #1 */
1128                     if (invert_column) {
1129                         *dest++ = r1_inv;
1130                         *dest++ = g1_inv;
1131                         *dest++ = b1_inv;
1132                     } else {
1133                         *dest++ = r1;
1134                         *dest++ = g1;
1135                         *dest++ = b1;
1136                     }
1137                 } else {                    /* gradient #2 */
1138                     if ((invert_column && invert_gradient2) ||
1139                         (!invert_column && !invert_gradient2))
1140                     {
1141                         *dest++ = r2;       /* not inverted or */
1142                         *dest++ = g2;       /*  doubly inverted */
1143                         *dest++ = b2;
1144                     } else {
1145                         *dest++ = r2_inv;
1146                         *dest++ = g2_inv;   /* singly inverted */
1147                         *dest++ = b2_inv;
1148                     }
1149                 }
1150             }
1151         }
1152 
1153 /*---------------------------------------------------------------------------
1154     Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
1155     M. Costello.
1156   ---------------------------------------------------------------------------*/
1157 
1158     } else if ((bg[pat].type & 0x07) == 1) {
1159 
1160         hmax = (bgscale-1)/2;   /* half the max weight of a color */
1161         max = 2*hmax;           /* the max weight of a color */
1162 
1163         r1 = rgb[bg[pat].rgb1_max].r;
1164         g1 = rgb[bg[pat].rgb1_max].g;
1165         b1 = rgb[bg[pat].rgb1_max].b;
1166         r2 = rgb[bg[pat].rgb2_max].r;
1167         g2 = rgb[bg[pat].rgb2_max].g;
1168         b2 = rgb[bg[pat].rgb2_max].b;
1169 
1170         for (row = 0;  row < rpng2_info.height;  ++row) {
1171             yidx = (int)(row % bgscale);
1172             if (yidx > hmax)
1173                 yidx = bgscale-1 - yidx;
1174             dest = (char *)bg_data + row*bg_rowbytes;
1175             for (i = 0;  i < rpng2_info.width;  ++i) {
1176                 xidx = (int)(i % bgscale);
1177                 if (xidx > hmax)
1178                     xidx = bgscale-1 - xidx;
1179                 k = xidx + yidx;
1180                 *dest++ = (k*r1 + (max-k)*r2) / max;
1181                 *dest++ = (k*g1 + (max-k)*g2) / max;
1182                 *dest++ = (k*b1 + (max-k)*b2) / max;
1183             }
1184         }
1185 
1186 /*---------------------------------------------------------------------------
1187     Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
1188     soids will equal bgscale?].  This one is slow but very cool.  Code con-
1189     tributed by Pieter S. van der Meulen (originally in Smalltalk).
1190   ---------------------------------------------------------------------------*/
1191 
1192     } else if ((bg[pat].type & 0x07) == 2) {
1193         uch ch;
1194         int ii, x, y, hw, hh, grayspot;
1195         double freq, rotate, saturate, gray, intensity;
1196         double angle=0.0, aoffset=0.0, maxDist, dist;
1197         double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
1198 
1199         fprintf(stderr, "%s:  computing radial background...",
1200           PROGNAME);
1201         fflush(stderr);
1202 
1203         hh = (int)(rpng2_info.height / 2);
1204         hw = (int)(rpng2_info.width / 2);
1205 
1206         /* variables for radial waves:
1207          *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
1208          *   freq:  number of color beams originating from the center
1209          *   grayspot:  size of the graying center area (anti-alias)
1210          *   rotate:  rotation of the beams as a function of radius
1211          *   saturate:  saturation of beams' shape azimuthally
1212          */
1213         angle = CLIP(angle, 0.0, 360.0);
1214         grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
1215         freq = MAX((double)bg[pat].bg_freq, 0.0);
1216         saturate = (double)bg[pat].bg_bsat * 0.1;
1217         rotate = (double)bg[pat].bg_brot * 0.1;
1218         gray = 0.0;
1219         intensity = 0.0;
1220         maxDist = (double)((hw*hw) + (hh*hh));
1221 
1222         for (row = 0;  row < rpng2_info.height;  ++row) {
1223             y = (int)(row - hh);
1224             dest = (char *)bg_data + row*bg_rowbytes;
1225             for (i = 0;  i < rpng2_info.width;  ++i) {
1226                 x = (int)(i - hw);
1227                 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
1228                 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
1229                 gray = MIN(1.0, gray);
1230                 dist = (double)((x*x) + (y*y)) / maxDist;
1231                 intensity = cos((angle+(rotate*dist*PI)) * freq) *
1232                   gray * saturate;
1233                 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
1234                 hue = (angle + PI) * INV_PI_360 + aoffset;
1235                 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
1236                 s = MIN(MAX(s,0.0), 1.0);
1237                 v = MIN(MAX(intensity,0.0), 1.0);
1238 
1239                 if (s == 0.0) {
1240                     ch = (uch)(v * 255.0);
1241                     *dest++ = ch;
1242                     *dest++ = ch;
1243                     *dest++ = ch;
1244                 } else {
1245                     if ((hue < 0.0) || (hue >= 360.0))
1246                         hue -= (((int)(hue / 360.0)) * 360.0);
1247                     hue /= 60.0;
1248                     ii = (int)hue;
1249                     f = hue - (double)ii;
1250                     p = (1.0 - s) * v;
1251                     q = (1.0 - (s * f)) * v;
1252                     t = (1.0 - (s * (1.0 - f))) * v;
1253                     if      (ii == 0) { red = v; green = t; blue = p; }
1254                     else if (ii == 1) { red = q; green = v; blue = p; }
1255                     else if (ii == 2) { red = p; green = v; blue = t; }
1256                     else if (ii == 3) { red = p; green = q; blue = v; }
1257                     else if (ii == 4) { red = t; green = p; blue = v; }
1258                     else if (ii == 5) { red = v; green = p; blue = q; }
1259                     *dest++ = (uch)(red * 255.0);
1260                     *dest++ = (uch)(green * 255.0);
1261                     *dest++ = (uch)(blue * 255.0);
1262                 }
1263             }
1264         }
1265         fprintf(stderr, "done.\n");
1266         fflush(stderr);
1267     }
1268 
1269 /*---------------------------------------------------------------------------
1270     Blast background image to display buffer before beginning PNG decode.
1271   ---------------------------------------------------------------------------*/
1272 
1273     if (depth == 24 || depth == 32) {
1274         ulg red, green, blue;
1275         int bpp = ximage->bits_per_pixel;
1276 
1277         for (row = 0;  row < rpng2_info.height;  ++row) {
1278             src = bg_data + row*bg_rowbytes;
1279             dest = ximage->data + row*ximage_rowbytes;
1280             if (bpp == 32) {    /* slightly optimized version */
1281                 for (i = rpng2_info.width;  i > 0;  --i) {
1282                     red   = *src++;
1283                     green = *src++;
1284                     blue  = *src++;
1285                     pixel = (red   << RShift) |
1286                             (green << GShift) |
1287                             (blue  << BShift);
1288                     /* recall that we set ximage->byte_order = MSBFirst above */
1289                     *dest++ = (char)((pixel >> 24) & 0xff);
1290                     *dest++ = (char)((pixel >> 16) & 0xff);
1291                     *dest++ = (char)((pixel >>  8) & 0xff);
1292                     *dest++ = (char)( pixel        & 0xff);
1293                 }
1294             } else {
1295                 for (i = rpng2_info.width;  i > 0;  --i) {
1296                     red   = *src++;
1297                     green = *src++;
1298                     blue  = *src++;
1299                     pixel = (red   << RShift) |
1300                             (green << GShift) |
1301                             (blue  << BShift);
1302                     /* recall that we set ximage->byte_order = MSBFirst above */
1303                     /* GRR BUG?  this assumes bpp == 24 & bits are packed low */
1304                     /*           (probably need to use RShift, RMask, etc.) */
1305                     *dest++ = (char)((pixel >> 16) & 0xff);
1306                     *dest++ = (char)((pixel >>  8) & 0xff);
1307                     *dest++ = (char)( pixel        & 0xff);
1308                 }
1309             }
1310         }
1311 
1312     } else if (depth == 16) {
1313         ush red, green, blue;
1314 
1315         for (row = 0;  row < rpng2_info.height;  ++row) {
1316             src = bg_data + row*bg_rowbytes;
1317             dest = ximage->data + row*ximage_rowbytes;
1318             for (i = rpng2_info.width;  i > 0;  --i) {
1319                 red   = ((ush)(*src) << 8);  ++src;
1320                 green = ((ush)(*src) << 8);  ++src;
1321                 blue  = ((ush)(*src) << 8);  ++src;
1322                 pixel = ((red   >> RShift) & RMask) |
1323                         ((green >> GShift) & GMask) |
1324                         ((blue  >> BShift) & BMask);
1325                 /* recall that we set ximage->byte_order = MSBFirst above */
1326                 *dest++ = (char)((pixel >>  8) & 0xff);
1327                 *dest++ = (char)( pixel        & 0xff);
1328             }
1329         }
1330 
1331     } else /* depth == 8 */ {
1332 
1333         /* GRR:  add 8-bit support */
1334 
1335     }
1336 
1337     XPutImage(display, window, gc, ximage, 0, 0, 0, 0, rpng2_info.width,
1338       rpng2_info.height);
1339 
1340     return 0;
1341 
1342 } /* end function rpng2_x_load_bg_image() */
1343 
1344 
1345 
1346 
1347 
rpng2_x_display_row(ulg row)1348 static void rpng2_x_display_row(ulg row)
1349 {
1350     uch bg_red   = rpng2_info.bg_red;
1351     uch bg_green = rpng2_info.bg_green;
1352     uch bg_blue  = rpng2_info.bg_blue;
1353     uch *src, *src2=NULL;
1354     char *dest;
1355     uch r, g, b, a;
1356     int ximage_rowbytes = ximage->bytes_per_line;
1357     ulg i, pixel;
1358     static int rows=0, prevpass=(-1);
1359     static ulg firstrow;
1360 
1361 /*---------------------------------------------------------------------------
1362     rows and firstrow simply track how many rows (and which ones) have not
1363     yet been displayed; alternatively, we could call XPutImage() for every
1364     row and not bother with the records-keeping.
1365   ---------------------------------------------------------------------------*/
1366 
1367     Trace((stderr, "beginning rpng2_x_display_row()\n"))
1368 
1369     if (rpng2_info.pass != prevpass) {
1370         if (pause_after_pass && rpng2_info.pass > 0) {
1371             XEvent e;
1372             KeySym k;
1373 
1374             fprintf(stderr,
1375               "%s:  end of pass %d of 7; click in image window to continue\n",
1376               PROGNAME, prevpass + 1);
1377             do
1378                 XNextEvent(display, &e);
1379             while (!QUIT(e,k));
1380         }
1381         fprintf(stderr, "%s:  pass %d of 7\r", PROGNAME, rpng2_info.pass + 1);
1382         fflush(stderr);
1383         prevpass = rpng2_info.pass;
1384     }
1385 
1386     if (rows == 0)
1387         firstrow = row;   /* first row that is not yet displayed */
1388 
1389     ++rows;   /* count of rows received but not yet displayed */
1390 
1391 /*---------------------------------------------------------------------------
1392     Aside from the use of the rpng2_info struct, the lack of an outer loop
1393     (over rows) and moving the XPutImage() call outside the "if (depth)"
1394     tests, this routine is identical to rpng_x_display_image() in the non-
1395     progressive version of the program.
1396   ---------------------------------------------------------------------------*/
1397 
1398     if (depth == 24 || depth == 32) {
1399         ulg red, green, blue;
1400         int bpp = ximage->bits_per_pixel;
1401 
1402         src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1403         if (bg_image)
1404             src2 = bg_data + row*bg_rowbytes;
1405         dest = ximage->data + row*ximage_rowbytes;
1406         if (rpng2_info.channels == 3) {
1407             for (i = rpng2_info.width;  i > 0;  --i) {
1408                 red   = *src++;
1409                 green = *src++;
1410                 blue  = *src++;
1411                 pixel = (red   << RShift) |
1412                         (green << GShift) |
1413                         (blue  << BShift);
1414                 /* recall that we set ximage->byte_order = MSBFirst above */
1415                 if (bpp == 32) {
1416                     *dest++ = (char)((pixel >> 24) & 0xff);
1417                     *dest++ = (char)((pixel >> 16) & 0xff);
1418                     *dest++ = (char)((pixel >>  8) & 0xff);
1419                     *dest++ = (char)( pixel        & 0xff);
1420                 } else {
1421                     /* GRR BUG?  this assumes bpp == 24 & bits are packed low */
1422                     /*           (probably need to use RShift, RMask, etc.) */
1423                     *dest++ = (char)((pixel >> 16) & 0xff);
1424                     *dest++ = (char)((pixel >>  8) & 0xff);
1425                     *dest++ = (char)( pixel        & 0xff);
1426                 }
1427             }
1428         } else /* if (rpng2_info.channels == 4) */ {
1429             for (i = rpng2_info.width;  i > 0;  --i) {
1430                 r = *src++;
1431                 g = *src++;
1432                 b = *src++;
1433                 a = *src++;
1434                 if (bg_image) {
1435                     bg_red   = *src2++;
1436                     bg_green = *src2++;
1437                     bg_blue  = *src2++;
1438                 }
1439                 if (a == 255) {
1440                     red   = r;
1441                     green = g;
1442                     blue  = b;
1443                 } else if (a == 0) {
1444                     red   = bg_red;
1445                     green = bg_green;
1446                     blue  = bg_blue;
1447                 } else {
1448                     /* this macro (from png.h) composites the foreground
1449                      * and background values and puts the result into the
1450                      * first argument */
1451                     alpha_composite(red,   r, a, bg_red);
1452                     alpha_composite(green, g, a, bg_green);
1453                     alpha_composite(blue,  b, a, bg_blue);
1454                 }
1455                 pixel = (red   << RShift) |
1456                         (green << GShift) |
1457                         (blue  << BShift);
1458                 /* recall that we set ximage->byte_order = MSBFirst above */
1459                 if (bpp == 32) {
1460                     *dest++ = (char)((pixel >> 24) & 0xff);
1461                     *dest++ = (char)((pixel >> 16) & 0xff);
1462                     *dest++ = (char)((pixel >>  8) & 0xff);
1463                     *dest++ = (char)( pixel        & 0xff);
1464                 } else {
1465                     /* GRR BUG?  this assumes bpp == 24 & bits are packed low */
1466                     /*           (probably need to use RShift, RMask, etc.) */
1467                     *dest++ = (char)((pixel >> 16) & 0xff);
1468                     *dest++ = (char)((pixel >>  8) & 0xff);
1469                     *dest++ = (char)( pixel        & 0xff);
1470                 }
1471             }
1472         }
1473 
1474     } else if (depth == 16) {
1475         ush red, green, blue;
1476 
1477         src = rpng2_info.row_pointers[row];
1478         if (bg_image)
1479             src2 = bg_data + row*bg_rowbytes;
1480         dest = ximage->data + row*ximage_rowbytes;
1481         if (rpng2_info.channels == 3) {
1482             for (i = rpng2_info.width;  i > 0;  --i) {
1483                 red   = ((ush)(*src) << 8);
1484                 ++src;
1485                 green = ((ush)(*src) << 8);
1486                 ++src;
1487                 blue  = ((ush)(*src) << 8);
1488                 ++src;
1489                 pixel = ((red   >> RShift) & RMask) |
1490                         ((green >> GShift) & GMask) |
1491                         ((blue  >> BShift) & BMask);
1492                 /* recall that we set ximage->byte_order = MSBFirst above */
1493                 *dest++ = (char)((pixel >>  8) & 0xff);
1494                 *dest++ = (char)( pixel        & 0xff);
1495             }
1496         } else /* if (rpng2_info.channels == 4) */ {
1497             for (i = rpng2_info.width;  i > 0;  --i) {
1498                 r = *src++;
1499                 g = *src++;
1500                 b = *src++;
1501                 a = *src++;
1502                 if (bg_image) {
1503                     bg_red   = *src2++;
1504                     bg_green = *src2++;
1505                     bg_blue  = *src2++;
1506                 }
1507                 if (a == 255) {
1508                     red   = ((ush)r << 8);
1509                     green = ((ush)g << 8);
1510                     blue  = ((ush)b << 8);
1511                 } else if (a == 0) {
1512                     red   = ((ush)bg_red   << 8);
1513                     green = ((ush)bg_green << 8);
1514                     blue  = ((ush)bg_blue  << 8);
1515                 } else {
1516                     /* this macro (from png.h) composites the foreground
1517                      * and background values and puts the result back into
1518                      * the first argument (== fg byte here:  safe) */
1519                     alpha_composite(r, r, a, bg_red);
1520                     alpha_composite(g, g, a, bg_green);
1521                     alpha_composite(b, b, a, bg_blue);
1522                     red   = ((ush)r << 8);
1523                     green = ((ush)g << 8);
1524                     blue  = ((ush)b << 8);
1525                 }
1526                 pixel = ((red   >> RShift) & RMask) |
1527                         ((green >> GShift) & GMask) |
1528                         ((blue  >> BShift) & BMask);
1529                 /* recall that we set ximage->byte_order = MSBFirst above */
1530                 *dest++ = (char)((pixel >>  8) & 0xff);
1531                 *dest++ = (char)( pixel        & 0xff);
1532             }
1533         }
1534 
1535     } else /* depth == 8 */ {
1536 
1537         /* GRR:  add 8-bit support */
1538 
1539     }
1540 
1541 
1542 /*---------------------------------------------------------------------------
1543     Display after every 16 rows or when on one of last two rows.  (Region
1544     may include previously displayed lines due to interlacing--i.e., not
1545     contiguous.  Also, second-to-last row is final one in interlaced images
1546     with odd number of rows.)  For demos, flush (and delay) after every 16th
1547     row so "sparse" passes don't go twice as fast.
1548   ---------------------------------------------------------------------------*/
1549 
1550     if (demo_timing && (row - firstrow >= 16 || row >= rpng2_info.height-2)) {
1551         XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1552           (int)firstrow, rpng2_info.width, row - firstrow + 1);
1553         XFlush(display);
1554         rows = 0;
1555         usleep(usleep_duration);
1556     } else
1557     if (!demo_timing && ((rows & 0xf) == 0 || row >= rpng2_info.height-2)) {
1558         XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1559           (int)firstrow, rpng2_info.width, row - firstrow + 1);
1560         XFlush(display);
1561         rows = 0;
1562     }
1563 
1564 }
1565 
1566 
1567 
1568 
1569 
rpng2_x_finish_display(void)1570 static void rpng2_x_finish_display(void)
1571 {
1572     Trace((stderr, "beginning rpng2_x_finish_display()\n"))
1573 
1574     /* last row has already been displayed by rpng2_x_display_row(), so we
1575      * have nothing to do here except set a flag and let the user know that
1576      * the image is done */
1577 
1578     rpng2_info.state = kDone;
1579     printf(
1580       "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
1581     fflush(stdout);
1582 }
1583 
1584 
1585 
1586 
1587 
rpng2_x_redisplay_image(ulg startcol,ulg startrow,ulg width,ulg height)1588 static void rpng2_x_redisplay_image(ulg startcol, ulg startrow,
1589                                     ulg width, ulg height)
1590 {
1591     uch bg_red   = rpng2_info.bg_red;
1592     uch bg_green = rpng2_info.bg_green;
1593     uch bg_blue  = rpng2_info.bg_blue;
1594     uch *src, *src2=NULL;
1595     char *dest;
1596     uch r, g, b, a;
1597     ulg i, row, lastrow = 0;
1598     ulg pixel;
1599     int ximage_rowbytes = ximage->bytes_per_line;
1600 
1601 
1602     Trace((stderr, "beginning display loop (image_channels == %d)\n",
1603       rpng2_info.channels))
1604     Trace((stderr, "   (width = %ld, rowbytes = %d, ximage_rowbytes = %d)\n",
1605       rpng2_info.width, rpng2_info.rowbytes, ximage_rowbytes))
1606     Trace((stderr, "   (bpp = %d)\n", ximage->bits_per_pixel))
1607     Trace((stderr, "   (byte_order = %s)\n", ximage->byte_order == MSBFirst?
1608       "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown")))
1609 
1610 /*---------------------------------------------------------------------------
1611     Aside from the use of the rpng2_info struct and of src2 (for background
1612     image), this routine is identical to rpng_x_display_image() in the non-
1613     progressive version of the program--for the simple reason that redisplay
1614     of the image against a new background happens after the image is fully
1615     decoded and therefore is, by definition, non-progressive.
1616   ---------------------------------------------------------------------------*/
1617 
1618     if (depth == 24 || depth == 32) {
1619         ulg red, green, blue;
1620         int bpp = ximage->bits_per_pixel;
1621 
1622         for (lastrow = row = startrow;  row < startrow+height;  ++row) {
1623             src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1624             if (bg_image)
1625                 src2 = bg_data + row*bg_rowbytes;
1626             dest = ximage->data + row*ximage_rowbytes;
1627             if (rpng2_info.channels == 3) {
1628                 for (i = rpng2_info.width;  i > 0;  --i) {
1629                     red   = *src++;
1630                     green = *src++;
1631                     blue  = *src++;
1632 #ifdef NO_24BIT_MASKS
1633                     pixel = (red   << RShift) |
1634                             (green << GShift) |
1635                             (blue  << BShift);
1636                     /* recall that we set ximage->byte_order = MSBFirst above */
1637                     if (bpp == 32) {
1638                         *dest++ = (char)((pixel >> 24) & 0xff);
1639                         *dest++ = (char)((pixel >> 16) & 0xff);
1640                         *dest++ = (char)((pixel >>  8) & 0xff);
1641                         *dest++ = (char)( pixel        & 0xff);
1642                     } else {
1643                         /* this assumes bpp == 24 & bits are packed low */
1644                         /* (probably need to use RShift, RMask, etc.) */
1645                         *dest++ = (char)((pixel >> 16) & 0xff);
1646                         *dest++ = (char)((pixel >>  8) & 0xff);
1647                         *dest++ = (char)( pixel        & 0xff);
1648                     }
1649 #else
1650                     red   = (RShift < 0)? red   << (-RShift) : red   >> RShift;
1651                     green = (GShift < 0)? green << (-GShift) : green >> GShift;
1652                     blue  = (BShift < 0)? blue  << (-BShift) : blue  >> BShift;
1653                     pixel = (red & RMask) | (green & GMask) | (blue & BMask);
1654                     /* recall that we set ximage->byte_order = MSBFirst above */
1655                     if (bpp == 32) {
1656                         *dest++ = (char)((pixel >> 24) & 0xff);
1657                         *dest++ = (char)((pixel >> 16) & 0xff);
1658                         *dest++ = (char)((pixel >>  8) & 0xff);
1659                         *dest++ = (char)( pixel        & 0xff);
1660                     } else {
1661                         /* GRR BUG */
1662                         /* this assumes bpp == 24 & bits are packed low */
1663                         /* (probably need to use RShift/RMask/etc. here, too) */
1664                         *dest++ = (char)((pixel >> 16) & 0xff);
1665                         *dest++ = (char)((pixel >>  8) & 0xff);
1666                         *dest++ = (char)( pixel        & 0xff);
1667                     }
1668 #endif
1669                 }
1670 
1671             } else /* if (rpng2_info.channels == 4) */ {
1672                 for (i = rpng2_info.width;  i > 0;  --i) {
1673                     r = *src++;
1674                     g = *src++;
1675                     b = *src++;
1676                     a = *src++;
1677                     if (bg_image) {
1678                         bg_red   = *src2++;
1679                         bg_green = *src2++;
1680                         bg_blue  = *src2++;
1681                     }
1682                     if (a == 255) {
1683                         red   = r;
1684                         green = g;
1685                         blue  = b;
1686                     } else if (a == 0) {
1687                         red   = bg_red;
1688                         green = bg_green;
1689                         blue  = bg_blue;
1690                     } else {
1691                         /* this macro (from png.h) composites the foreground
1692                          * and background values and puts the result into the
1693                          * first argument */
1694                         alpha_composite(red,   r, a, bg_red);
1695                         alpha_composite(green, g, a, bg_green);
1696                         alpha_composite(blue,  b, a, bg_blue);
1697                     }
1698 #ifdef NO_24BIT_MASKS
1699                     pixel = (red   << RShift) |
1700                             (green << GShift) |
1701                             (blue  << BShift);
1702                     /* recall that we set ximage->byte_order = MSBFirst above */
1703                     if (bpp == 32) {
1704                         *dest++ = (char)((pixel >> 24) & 0xff);
1705                         *dest++ = (char)((pixel >> 16) & 0xff);
1706                         *dest++ = (char)((pixel >>  8) & 0xff);
1707                         *dest++ = (char)( pixel        & 0xff);
1708                     } else {
1709                         /* this assumes bpp == 24 & bits are packed low */
1710                         /* (probably need to use RShift, RMask, etc.) */
1711                         *dest++ = (char)((pixel >> 16) & 0xff);
1712                         *dest++ = (char)((pixel >>  8) & 0xff);
1713                         *dest++ = (char)( pixel        & 0xff);
1714                     }
1715 #else
1716                     red   = (RShift < 0)? red   << (-RShift) : red   >> RShift;
1717                     green = (GShift < 0)? green << (-GShift) : green >> GShift;
1718                     blue  = (BShift < 0)? blue  << (-BShift) : blue  >> BShift;
1719                     pixel = (red & RMask) | (green & GMask) | (blue & BMask);
1720                     /* recall that we set ximage->byte_order = MSBFirst above */
1721                     if (bpp == 32) {
1722                         *dest++ = (char)((pixel >> 24) & 0xff);
1723                         *dest++ = (char)((pixel >> 16) & 0xff);
1724                         *dest++ = (char)((pixel >>  8) & 0xff);
1725                         *dest++ = (char)( pixel        & 0xff);
1726                     } else {
1727                         /* GRR BUG */
1728                         /* this assumes bpp == 24 & bits are packed low */
1729                         /* (probably need to use RShift/RMask/etc. here, too) */
1730                         *dest++ = (char)((pixel >> 16) & 0xff);
1731                         *dest++ = (char)((pixel >>  8) & 0xff);
1732                         *dest++ = (char)( pixel        & 0xff);
1733                     }
1734 #endif
1735                 }
1736             }
1737             /* display after every 16 lines */
1738             if (((row+1) & 0xf) == 0) {
1739                 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1740                   (int)lastrow, rpng2_info.width, 16);
1741                 XFlush(display);
1742                 lastrow = row + 1;
1743             }
1744         }
1745 
1746     } else if (depth == 16) {
1747         ush red, green, blue;
1748 
1749         for (lastrow = row = startrow;  row < startrow+height;  ++row) {
1750             src = rpng2_info.row_pointers[row];
1751             if (bg_image)
1752                 src2 = bg_data + row*bg_rowbytes;
1753             dest = ximage->data + row*ximage_rowbytes;
1754             if (rpng2_info.channels == 3) {
1755                 for (i = rpng2_info.width;  i > 0;  --i) {
1756                     red   = ((ush)(*src) << 8);
1757                     ++src;
1758                     green = ((ush)(*src) << 8);
1759                     ++src;
1760                     blue  = ((ush)(*src) << 8);
1761                     ++src;
1762                     pixel = ((red   >> RShift) & RMask) |
1763                             ((green >> GShift) & GMask) |
1764                             ((blue  >> BShift) & BMask);
1765                     /* recall that we set ximage->byte_order = MSBFirst above */
1766                     *dest++ = (char)((pixel >>  8) & 0xff);
1767                     *dest++ = (char)( pixel        & 0xff);
1768                 }
1769             } else /* if (rpng2_info.channels == 4) */ {
1770                 for (i = rpng2_info.width;  i > 0;  --i) {
1771                     r = *src++;
1772                     g = *src++;
1773                     b = *src++;
1774                     a = *src++;
1775                     if (bg_image) {
1776                         bg_red   = *src2++;
1777                         bg_green = *src2++;
1778                         bg_blue  = *src2++;
1779                     }
1780                     if (a == 255) {
1781                         red   = ((ush)r << 8);
1782                         green = ((ush)g << 8);
1783                         blue  = ((ush)b << 8);
1784                     } else if (a == 0) {
1785                         red   = ((ush)bg_red   << 8);
1786                         green = ((ush)bg_green << 8);
1787                         blue  = ((ush)bg_blue  << 8);
1788                     } else {
1789                         /* this macro (from png.h) composites the foreground
1790                          * and background values and puts the result back into
1791                          * the first argument (== fg byte here:  safe) */
1792                         alpha_composite(r, r, a, bg_red);
1793                         alpha_composite(g, g, a, bg_green);
1794                         alpha_composite(b, b, a, bg_blue);
1795                         red   = ((ush)r << 8);
1796                         green = ((ush)g << 8);
1797                         blue  = ((ush)b << 8);
1798                     }
1799                     pixel = ((red   >> RShift) & RMask) |
1800                             ((green >> GShift) & GMask) |
1801                             ((blue  >> BShift) & BMask);
1802                     /* recall that we set ximage->byte_order = MSBFirst above */
1803                     *dest++ = (char)((pixel >>  8) & 0xff);
1804                     *dest++ = (char)( pixel        & 0xff);
1805                 }
1806             }
1807             /* display after every 16 lines */
1808             if (((row+1) & 0xf) == 0) {
1809                 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1810                   (int)lastrow, rpng2_info.width, 16);
1811                 XFlush(display);
1812                 lastrow = row + 1;
1813             }
1814         }
1815 
1816     } else /* depth == 8 */ {
1817 
1818         /* GRR:  add 8-bit support */
1819 
1820     }
1821 
1822     Trace((stderr, "calling final XPutImage()\n"))
1823     if (lastrow < startrow+height) {
1824         XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1825           (int)lastrow, rpng2_info.width, rpng2_info.height-lastrow);
1826         XFlush(display);
1827     }
1828 
1829 } /* end function rpng2_x_redisplay_image() */
1830 
1831 
1832 
1833 
1834 
1835 #ifdef FEATURE_LOOP
1836 
rpng2_x_reload_bg_image(void)1837 static void rpng2_x_reload_bg_image(void)
1838 {
1839     char *dest;
1840     uch r1, r2, g1, g2, b1, b2;
1841     uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
1842     int k, hmax, max;
1843     int xidx, yidx, yidx_max;
1844     int even_odd_vert, even_odd_horiz, even_odd;
1845     int invert_gradient2 = (bg[pat].type & 0x08);
1846     int invert_column;
1847     ulg i, row;
1848 
1849 
1850     bgscale = (pat == 0)? 8 : bgscale_default;
1851     yidx_max = bgscale - 1;
1852 
1853 /*---------------------------------------------------------------------------
1854     Vertical gradients (ramps) in NxN squares, alternating direction and
1855     colors (N == bgscale).
1856   ---------------------------------------------------------------------------*/
1857 
1858     if ((bg[pat].type & 0x07) == 0) {
1859         uch r1_min  = rgb[bg[pat].rgb1_min].r;
1860         uch g1_min  = rgb[bg[pat].rgb1_min].g;
1861         uch b1_min  = rgb[bg[pat].rgb1_min].b;
1862         uch r2_min  = rgb[bg[pat].rgb2_min].r;
1863         uch g2_min  = rgb[bg[pat].rgb2_min].g;
1864         uch b2_min  = rgb[bg[pat].rgb2_min].b;
1865         int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
1866         int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
1867         int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
1868         int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
1869         int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
1870         int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
1871 
1872         for (row = 0;  row < rpng2_info.height;  ++row) {
1873             yidx = (int)(row % bgscale);
1874             even_odd_vert = (int)((row / bgscale) & 1);
1875 
1876             r1 = r1_min + (r1_diff * yidx) / yidx_max;
1877             g1 = g1_min + (g1_diff * yidx) / yidx_max;
1878             b1 = b1_min + (b1_diff * yidx) / yidx_max;
1879             r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
1880             g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
1881             b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
1882 
1883             r2 = r2_min + (r2_diff * yidx) / yidx_max;
1884             g2 = g2_min + (g2_diff * yidx) / yidx_max;
1885             b2 = b2_min + (b2_diff * yidx) / yidx_max;
1886             r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
1887             g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
1888             b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
1889 
1890             dest = (char *)bg_data + row*bg_rowbytes;
1891             for (i = 0;  i < rpng2_info.width;  ++i) {
1892                 even_odd_horiz = (int)((i / bgscale) & 1);
1893                 even_odd = even_odd_vert ^ even_odd_horiz;
1894                 invert_column =
1895                   (even_odd_horiz && (bg[pat].type & 0x10));
1896                 if (even_odd == 0) {        /* gradient #1 */
1897                     if (invert_column) {
1898                         *dest++ = r1_inv;
1899                         *dest++ = g1_inv;
1900                         *dest++ = b1_inv;
1901                     } else {
1902                         *dest++ = r1;
1903                         *dest++ = g1;
1904                         *dest++ = b1;
1905                     }
1906                 } else {                    /* gradient #2 */
1907                     if ((invert_column && invert_gradient2) ||
1908                         (!invert_column && !invert_gradient2))
1909                     {
1910                         *dest++ = r2;       /* not inverted or */
1911                         *dest++ = g2;       /*  doubly inverted */
1912                         *dest++ = b2;
1913                     } else {
1914                         *dest++ = r2_inv;
1915                         *dest++ = g2_inv;   /* singly inverted */
1916                         *dest++ = b2_inv;
1917                     }
1918                 }
1919             }
1920         }
1921 
1922 /*---------------------------------------------------------------------------
1923     Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
1924     M. Costello.
1925   ---------------------------------------------------------------------------*/
1926 
1927     } else if ((bg[pat].type & 0x07) == 1) {
1928 
1929         hmax = (bgscale-1)/2;   /* half the max weight of a color */
1930         max = 2*hmax;           /* the max weight of a color */
1931 
1932         r1 = rgb[bg[pat].rgb1_max].r;
1933         g1 = rgb[bg[pat].rgb1_max].g;
1934         b1 = rgb[bg[pat].rgb1_max].b;
1935         r2 = rgb[bg[pat].rgb2_max].r;
1936         g2 = rgb[bg[pat].rgb2_max].g;
1937         b2 = rgb[bg[pat].rgb2_max].b;
1938 
1939         for (row = 0;  row < rpng2_info.height;  ++row) {
1940             yidx = (int)(row % bgscale);
1941             if (yidx > hmax)
1942                 yidx = bgscale-1 - yidx;
1943             dest = (char *)bg_data + row*bg_rowbytes;
1944             for (i = 0;  i < rpng2_info.width;  ++i) {
1945                 xidx = (int)(i % bgscale);
1946                 if (xidx > hmax)
1947                     xidx = bgscale-1 - xidx;
1948                 k = xidx + yidx;
1949                 *dest++ = (k*r1 + (max-k)*r2) / max;
1950                 *dest++ = (k*g1 + (max-k)*g2) / max;
1951                 *dest++ = (k*b1 + (max-k)*b2) / max;
1952             }
1953         }
1954 
1955 /*---------------------------------------------------------------------------
1956     Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
1957     soids will equal bgscale?].  This one is slow but very cool.  Code con-
1958     tributed by Pieter S. van der Meulen (originally in Smalltalk).
1959   ---------------------------------------------------------------------------*/
1960 
1961     } else if ((bg[pat].type & 0x07) == 2) {
1962         uch ch;
1963         int ii, x, y, hw, hh, grayspot;
1964         double freq, rotate, saturate, gray, intensity;
1965         double angle=0.0, aoffset=0.0, maxDist, dist;
1966         double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
1967 
1968         hh = (int)(rpng2_info.height / 2);
1969         hw = (int)(rpng2_info.width / 2);
1970 
1971         /* variables for radial waves:
1972          *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
1973          *   freq:  number of color beams originating from the center
1974          *   grayspot:  size of the graying center area (anti-alias)
1975          *   rotate:  rotation of the beams as a function of radius
1976          *   saturate:  saturation of beams' shape azimuthally
1977          */
1978         angle = CLIP(angle, 0.0, 360.0);
1979         grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
1980         freq = MAX((double)bg[pat].bg_freq, 0.0);
1981         saturate = (double)bg[pat].bg_bsat * 0.1;
1982         rotate = (double)bg[pat].bg_brot * 0.1;
1983         gray = 0.0;
1984         intensity = 0.0;
1985         maxDist = (double)((hw*hw) + (hh*hh));
1986 
1987         for (row = 0;  row < rpng2_info.height;  ++row) {
1988             y = (int)(row - hh);
1989             dest = (char *)bg_data + row*bg_rowbytes;
1990             for (i = 0;  i < rpng2_info.width;  ++i) {
1991                 x = (int)(i - hw);
1992                 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
1993                 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
1994                 gray = MIN(1.0, gray);
1995                 dist = (double)((x*x) + (y*y)) / maxDist;
1996                 intensity = cos((angle+(rotate*dist*PI)) * freq) *
1997                   gray * saturate;
1998                 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
1999                 hue = (angle + PI) * INV_PI_360 + aoffset;
2000                 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
2001                 s = MIN(MAX(s,0.0), 1.0);
2002                 v = MIN(MAX(intensity,0.0), 1.0);
2003 
2004                 if (s == 0.0) {
2005                     ch = (uch)(v * 255.0);
2006                     *dest++ = ch;
2007                     *dest++ = ch;
2008                     *dest++ = ch;
2009                 } else {
2010                     if ((hue < 0.0) || (hue >= 360.0))
2011                         hue -= (((int)(hue / 360.0)) * 360.0);
2012                     hue /= 60.0;
2013                     ii = (int)hue;
2014                     f = hue - (double)ii;
2015                     p = (1.0 - s) * v;
2016                     q = (1.0 - (s * f)) * v;
2017                     t = (1.0 - (s * (1.0 - f))) * v;
2018                     if      (ii == 0) { red = v; green = t; blue = p; }
2019                     else if (ii == 1) { red = q; green = v; blue = p; }
2020                     else if (ii == 2) { red = p; green = v; blue = t; }
2021                     else if (ii == 3) { red = p; green = q; blue = v; }
2022                     else if (ii == 4) { red = t; green = p; blue = v; }
2023                     else if (ii == 5) { red = v; green = p; blue = q; }
2024                     *dest++ = (uch)(red * 255.0);
2025                     *dest++ = (uch)(green * 255.0);
2026                     *dest++ = (uch)(blue * 255.0);
2027                 }
2028             }
2029         }
2030     }
2031 
2032 } /* end function rpng2_x_reload_bg_image() */
2033 
2034 
2035 
2036 
2037 
is_number(char * p)2038 static int is_number(char *p)
2039 {
2040     while (*p) {
2041         if (!isdigit(*p))
2042             return FALSE;
2043         ++p;
2044     }
2045     return TRUE;
2046 }
2047 
2048 #endif /* FEATURE_LOOP */
2049 
2050 
2051 
2052 
2053 
rpng2_x_cleanup(void)2054 static void rpng2_x_cleanup(void)
2055 {
2056     if (bg_image && bg_data) {
2057         free(bg_data);
2058         bg_data = NULL;
2059     }
2060 
2061     if (rpng2_info.image_data) {
2062         free(rpng2_info.image_data);
2063         rpng2_info.image_data = NULL;
2064     }
2065 
2066     if (rpng2_info.row_pointers) {
2067         free(rpng2_info.row_pointers);
2068         rpng2_info.row_pointers = NULL;
2069     }
2070 
2071     if (ximage) {
2072         if (ximage->data) {
2073             free(ximage->data);           /* we allocated it, so we free it */
2074             ximage->data = (char *)NULL;  /*  instead of XDestroyImage() */
2075         }
2076         XDestroyImage(ximage);
2077         ximage = NULL;
2078     }
2079 
2080     if (have_gc)
2081         XFreeGC(display, gc);
2082 
2083     if (have_window)
2084         XDestroyWindow(display, window);
2085 
2086     if (have_colormap)
2087         XFreeColormap(display, colormap);
2088 
2089     if (have_nondefault_visual)
2090         XFree(visual_list);
2091 }
2092 
2093 
2094 
2095 
2096 
rpng2_x_msb(ulg u32val)2097 static int rpng2_x_msb(ulg u32val)
2098 {
2099     int i;
2100 
2101     for (i = 31;  i >= 0;  --i) {
2102         if (u32val & 0x80000000L)
2103             break;
2104         u32val <<= 1;
2105     }
2106     return i;
2107 }
2108