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