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