• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
main(int argc,char ** argv)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 
rpng2_x_init(void)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 
rpng2_x_create_window(void)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 
rpng2_x_load_bg_image(void)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 
rpng2_x_display_row(ulg row)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 
rpng2_x_finish_display(void)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 
rpng2_x_redisplay_image(ulg startcol,ulg startrow,ulg width,ulg height)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 
rpng2_x_reload_bg_image(void)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 
is_number(char * p)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 
rpng2_x_cleanup(void)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 
rpng2_x_msb(ulg u32val)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 }
2135