Remove .gitignore do nothing is ignored.
[synfig.git] / gtkmm-osx / trunk / libpng-1.2.5 / 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:
16     - 8-bit support
17     - finish resizable checkerboard-gradient (sizes 4-128?)
18     - use %.1023s to simplify truncation of title-bar string?
19
20   ---------------------------------------------------------------------------
21
22    Changelog:
23     - 1.01:  initial public release
24     - 1.02:  modified to allow abbreviated options; fixed char/uchar mismatch
25     - 1.10:  added support for non-default visuals; fixed X pixel-conversion
26     - 1.11:  added -usleep option for demos; fixed command-line parsing bug
27     - 1.12:  added -pause option for demos and testing
28     - 1.20:  added runtime MMX-enabling/disabling and new -mmx* options
29     - 1.21:  fixed small X memory leak (thanks to Francois Petitjean)
30     - 1.22:  fixed XFreeGC() crash bug
31
32   ---------------------------------------------------------------------------
33
34       Copyright (c) 1998-2001 Greg Roelofs.  All rights reserved.
35
36       This software is provided "as is," without warranty of any kind,
37       express or implied.  In no event shall the author or contributors
38       be held liable for any damages arising in any way from the use of
39       this software.
40
41       Permission is granted to anyone to use this software for any purpose,
42       including commercial applications, and to alter it and redistribute
43       it freely, subject to the following restrictions:
44
45       1. Redistributions of source code must retain the above copyright
46          notice, disclaimer, and this list of conditions.
47       2. Redistributions in binary form must reproduce the above copyright
48          notice, disclaimer, and this list of conditions in the documenta-
49          tion and/or other materials provided with the distribution.
50       3. All advertising materials mentioning features or use of this
51          software must display the following acknowledgment:
52
53             This product includes software developed by Greg Roelofs
54             and contributors for the book, "PNG: The Definitive Guide,"
55             published by O'Reilly and Associates.
56
57   ---------------------------------------------------------------------------*/
58
59 #define PROGNAME  "rpng2-x"
60 #define LONGNAME  "Progressive PNG Viewer for X"
61 #define VERSION   "1.22 of 16 August 2001"
62
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <setjmp.h>       /* for jmpbuf declaration in readpng2.h */
67 #include <time.h>
68 #include <math.h>         /* only for PvdM background code */
69 #include <X11/Xlib.h>
70 #include <X11/Xutil.h>
71 #include <X11/Xos.h>
72 #include <X11/keysym.h>   /* defines XK_* macros */
73
74 #ifdef VMS
75 #  include <unistd.h>
76 #endif
77
78 /* all for PvdM background code: */
79 #ifndef PI
80 #  define PI             3.141592653589793238
81 #endif
82 #define PI_2             (PI*0.5)
83 #define INV_PI_360       (360.0 / PI)
84 #define MAX(a,b)         (a>b?a:b)
85 #define MIN(a,b)         (a<b?a:b)
86 #define CLIP(a,min,max)  MAX(min,MIN((a),max))
87 #define ABS(a)           ((a)<0?-(a):(a))
88 #define CLIP8P(c)        MAX(0,(MIN((c),255)))   /* 8-bit pos. integer (uch) */
89 #define ROUNDF(f)        ((int)(f + 0.5))
90
91 #define rgb1_max   bg_freq
92 #define rgb1_min   bg_gray
93 #define rgb2_max   bg_bsat
94 #define rgb2_min   bg_brot
95
96 /* #define DEBUG */     /* this enables the Trace() macros */
97
98 #include "readpng2.h"   /* typedefs, common macros, readpng2 prototypes */
99
100
101 /* could just include png.h, but this macro is the only thing we need
102  * (name and typedefs changed to local versions); note that side effects
103  * only happen with alpha (which could easily be avoided with
104  * "ush acopy = (alpha);") */
105
106 #define alpha_composite(composite, fg, alpha, bg) {               \
107     ush temp = ((ush)(fg)*(ush)(alpha) +                          \
108                 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
109     (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
110 }
111
112
113 #define INBUFSIZE 4096   /* with pseudo-timing on (1 sec delay/block), this
114                           *  block size corresponds roughly to a download
115                           *  speed 10% faster than theoretical 33.6K maximum
116                           *  (assuming 8 data bits, 1 stop bit and no other
117                           *  overhead) */
118
119 /* local prototypes */
120 static void rpng2_x_init(void);
121 static int  rpng2_x_create_window(void);
122 static int  rpng2_x_load_bg_image(void);
123 static void rpng2_x_display_row(ulg row);
124 static void rpng2_x_finish_display(void);
125 static void rpng2_x_cleanup(void);
126 static int  rpng2_x_msb(ulg u32val);
127
128
129 static char titlebar[1024], *window_name = titlebar;
130 static char *appname = LONGNAME;
131 static char *icon_name = PROGNAME;
132 static char *filename;
133 static FILE *infile;
134
135 static mainprog_info rpng2_info;
136
137 static uch inbuf[INBUFSIZE];
138 static int incount;
139
140 static int pat = 6;        /* must be less than num_bgpat */
141 static int bg_image = 0;
142 static int bgscale = 16;
143 static ulg bg_rowbytes;
144 static uch *bg_data;
145
146 int pause_after_pass = FALSE;
147 int demo_timing = FALSE;
148 ulg usleep_duration = 0L;
149
150 static struct rgb_color {
151     uch r, g, b;
152 } rgb[] = {
153     {  0,   0,   0},    /*  0:  black */
154     {255, 255, 255},    /*  1:  white */
155     {173, 132,  57},    /*  2:  tan */
156     { 64, 132,   0},    /*  3:  medium green */
157     {189, 117,   1},    /*  4:  gold */
158     {253, 249,   1},    /*  5:  yellow */
159     {  0,   0, 255},    /*  6:  blue */
160     {  0,   0, 120},    /*  7:  medium blue */
161     {255,   0, 255},    /*  8:  magenta */
162     { 64,   0,  64},    /*  9:  dark magenta */
163     {255,   0,   0},    /* 10:  red */
164     { 64,   0,   0},    /* 11:  dark red */
165     {255, 127,   0},    /* 12:  orange */
166     {192,  96,   0},    /* 13:  darker orange */
167     { 24,  60,   0},    /* 14:  dark green-yellow */
168     { 85, 125, 200}     /* 15:  ice blue */
169 };
170 /* not used for now, but should be for error-checking:
171 static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
172  */
173
174 /*
175     This whole struct is a fairly cheesy way to keep the number of
176     command-line options to a minimum.  The radial-waves background
177     type is a particularly poor fit to the integer elements of the
178     struct...but a few macros and a little fixed-point math will do
179     wonders for ya.
180
181     type bits:
182        F E D C B A 9 8 7 6 5 4 3 2 1 0
183                              | | | | |
184                              | | +-+-+-- 0 = sharp-edged checkerboard
185                              | |         1 = soft diamonds
186                              | |         2 = radial waves
187                              | |       3-7 = undefined
188                              | +-- gradient #2 inverted?
189                              +-- alternating columns inverted?
190  */
191 static struct background_pattern {
192     ush type;
193     int rgb1_max, rgb1_min;     /* or bg_freq, bg_gray */
194     int rgb2_max, rgb2_min;     /* or bg_bsat, bg_brot (both scaled by 10)*/
195 } bg[] = {
196     {0+8,   2,0,  1,15},        /* checkered:  tan/black vs. white/ice blue */
197     {0+24,  2,0,  1,0},         /* checkered:  tan/black vs. white/black */
198     {0+8,   4,5,  0,2},         /* checkered:  gold/yellow vs. black/tan */
199     {0+8,   4,5,  0,6},         /* checkered:  gold/yellow vs. black/blue */
200     {0,     7,0,  8,9},         /* checkered:  deep blue/black vs. magenta */
201     {0+8,  13,0,  5,14},        /* checkered:  orange/black vs. yellow */
202     {0+8,  12,0, 10,11},        /* checkered:  orange/black vs. red */
203     {1,     7,0,  8,0},         /* diamonds:  deep blue/black vs. magenta */
204     {1,    12,0, 11,0},         /* diamonds:  orange vs. dark red */
205     {1,    10,0,  7,0},         /* diamonds:  red vs. medium blue */
206     {1,     4,0,  5,0},         /* diamonds:  gold vs. yellow */
207     {1,     3,0,  0,0},         /* diamonds:  medium green vs. black */
208     {2,    16, 100,  20,   0},  /* radial:  ~hard radial color-beams */
209     {2,    18, 100,  10,   2},  /* radial:  soft, curved radial color-beams */
210     {2,    16, 256, 100, 250},  /* radial:  very tight spiral */
211     {2, 10000, 256,  11,   0}   /* radial:  dipole-moire' (almost fractal) */
212 };
213 static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
214
215
216 /* X-specific variables */
217 static char *displayname;
218 static XImage *ximage;
219 static Display *display;
220 static int depth;
221 static Visual *visual;
222 static XVisualInfo *visual_list;
223 static int RShift, GShift, BShift;
224 static ulg RMask, GMask, BMask;
225 static Window window;
226 static GC gc;
227 static Colormap colormap;
228
229 static int have_nondefault_visual = FALSE;
230 static int have_colormap = FALSE;
231 static int have_window = FALSE;
232 static int have_gc = FALSE;
233
234
235
236
237 int main(int argc, char **argv)
238 {
239 #ifdef sgi
240     char tmpline[80];
241 #endif
242     char *p, *bgstr = NULL;
243     int rc, alen, flen;
244     int error = 0;
245     int timing = FALSE;
246     int have_bg = FALSE;
247     double LUT_exponent;                /* just the lookup table */
248     double CRT_exponent = 2.2;          /* just the monitor */
249     double default_display_exponent;    /* whole display system */
250     XEvent e;
251     KeySym k;
252
253
254     /* First initialize a few things, just to be sure--memset takes care of
255      * default background color (black), booleans (FALSE), pointers (NULL),
256      * etc. */
257
258     displayname = (char *)NULL;
259     filename = (char *)NULL;
260     memset(&rpng2_info, 0, sizeof(mainprog_info));
261
262
263     /* Set the default value for our display-system exponent, i.e., the
264      * product of the CRT exponent and the exponent corresponding to
265      * the frame-buffer's lookup table (LUT), if any.  This is not an
266      * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
267      * ones), but it should cover 99% of the current possibilities. */
268
269 #if defined(NeXT)
270     /* third-party utilities can modify the default LUT exponent */
271     LUT_exponent = 1.0 / 2.2;
272     /*
273     if (some_next_function_that_returns_gamma(&next_gamma))
274         LUT_exponent = 1.0 / next_gamma;
275      */
276 #elif defined(sgi)
277     LUT_exponent = 1.0 / 1.7;
278     /* there doesn't seem to be any documented function to
279      * get the "gamma" value, so we do it the hard way */
280     infile = fopen("/etc/config/system.glGammaVal", "r");
281     if (infile) {
282         double sgi_gamma;
283
284         fgets(tmpline, 80, infile);
285         fclose(infile);
286         sgi_gamma = atof(tmpline);
287         if (sgi_gamma > 0.0)
288             LUT_exponent = 1.0 / sgi_gamma;
289     }
290 #elif defined(Macintosh)
291     LUT_exponent = 1.8 / 2.61;
292     /*
293     if (some_mac_function_that_returns_gamma(&mac_gamma))
294         LUT_exponent = mac_gamma / 2.61;
295      */
296 #else
297     LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
298 #endif
299
300     /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
301     default_display_exponent = LUT_exponent * CRT_exponent;
302
303
304     /* If the user has set the SCREEN_GAMMA environment variable as suggested
305      * (somewhat imprecisely) in the libpng documentation, use that; otherwise
306      * use the default value we just calculated.  Either way, the user may
307      * override this via a command-line option. */
308
309     if ((p = getenv("SCREEN_GAMMA")) != NULL)
310         rpng2_info.display_exponent = atof(p);
311     else
312         rpng2_info.display_exponent = default_display_exponent;
313
314
315     /* Now parse the command line for options and the PNG filename. */
316
317     while (*++argv && !error) {
318         if (!strncmp(*argv, "-display", 2)) {
319             if (!*++argv)
320                 ++error;
321             else
322                 displayname = *argv;
323         } else if (!strncmp(*argv, "-gamma", 2)) {
324             if (!*++argv)
325                 ++error;
326             else {
327                 rpng2_info.display_exponent = atof(*argv);
328                 if (rpng2_info.display_exponent <= 0.0)
329                     ++error;
330             }
331         } else if (!strncmp(*argv, "-bgcolor", 4)) {
332             if (!*++argv)
333                 ++error;
334             else {
335                 bgstr = *argv;
336                 if (strlen(bgstr) != 7 || bgstr[0] != '#')
337                     ++error;
338                 else {
339                     have_bg = TRUE;
340                     bg_image = FALSE;
341                 }
342             }
343         } else if (!strncmp(*argv, "-bgpat", 4)) {
344             if (!*++argv)
345                 ++error;
346             else {
347                 pat = atoi(*argv) - 1;
348                 if (pat < 0 || pat >= num_bgpat)
349                     ++error;
350                 else {
351                     bg_image = TRUE;
352                     have_bg = FALSE;
353                 }
354             }
355         } else if (!strncmp(*argv, "-usleep", 2)) {
356             if (!*++argv)
357                 ++error;
358             else {
359                 usleep_duration = (ulg)atol(*argv);
360                 demo_timing = TRUE;
361             }
362         } else if (!strncmp(*argv, "-pause", 2)) {
363             pause_after_pass = TRUE;
364         } else if (!strncmp(*argv, "-timing", 2)) {
365             timing = TRUE;
366 #if (defined(__i386__) || defined(_M_IX86))
367         } else if (!strncmp(*argv, "-nommxfilters", 7)) {
368             rpng2_info.nommxfilters = TRUE;
369         } else if (!strncmp(*argv, "-nommxcombine", 7)) {
370             rpng2_info.nommxcombine = TRUE;
371         } else if (!strncmp(*argv, "-nommxinterlace", 7)) {
372             rpng2_info.nommxinterlace = TRUE;
373         } else if (!strcmp(*argv, "-nommx")) {
374             rpng2_info.nommxfilters = TRUE;
375             rpng2_info.nommxcombine = TRUE;
376             rpng2_info.nommxinterlace = TRUE;
377 #endif
378         } else {
379             if (**argv != '-') {
380                 filename = *argv;
381                 if (argv[1])   /* shouldn't be any more args after filename */
382                     ++error;
383             } else
384                 ++error;   /* not expecting any other options */
385         }
386     }
387
388     if (!filename) {
389         ++error;
390     } else if (!(infile = fopen(filename, "rb"))) {
391         fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
392         ++error;
393     } else {
394         incount = fread(inbuf, 1, INBUFSIZE, infile);
395         if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
396             fprintf(stderr, PROGNAME
397               ":  [%s] is not a PNG file: incorrect signature\n",
398               filename);
399             ++error;
400         } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
401             switch (rc) {
402                 case 2:
403                     fprintf(stderr, PROGNAME
404                       ":  [%s] has bad IHDR (libpng longjmp)\n",
405                       filename);
406                     break;
407                 case 4:
408                     fprintf(stderr, PROGNAME ":  insufficient memory\n");
409                     break;
410                 default:
411                     fprintf(stderr, PROGNAME
412                       ":  unknown readpng2_init() error\n");
413                     break;
414             }
415             ++error;
416         } else {
417             display = XOpenDisplay(displayname);
418             if (!display) {
419                 readpng2_cleanup(&rpng2_info);
420                 fprintf(stderr, PROGNAME ":  can't open X display [%s]\n",
421                   displayname? displayname : "default");
422                 ++error;
423             }
424         }
425         if (error)
426             fclose(infile);
427     }
428
429
430     /* usage screen */
431
432     if (error) {
433         fprintf(stderr, "\n%s %s:  %s\n\n", PROGNAME, VERSION, appname);
434         readpng2_version_info();
435         fprintf(stderr, "\n"
436           "Usage:  %s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
437 #if (defined(__i386__) || defined(_M_IX86))
438           "        %*s [[-nommxfilters] [-nommxcombine] [-nommxinterlace] | -nommx]\n"
439 #endif
440           "        %*s [-usleep dur | -timing] [-pause] file.png\n\n"
441           "    xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
442           "    exp \ttransfer-function exponent (``gamma'') of the display\n"
443           "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
444           "\t\t  to the product of the lookup-table exponent (varies)\n"
445           "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
446           "    bg  \tdesired background color in 7-character hex RGB format\n"
447           "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
448           "\t\t  used with transparent images; overrides -bgpat\n"
449           "    pat \tdesired background pattern number (1-%d); used with\n"
450           "\t\t  transparent images; overrides -bgcolor\n"
451 #if (defined(__i386__) || defined(_M_IX86))
452           "    -nommx*\tdisable optimized MMX routines for decoding row filters,\n"
453           "\t\t  combining rows, and expanding interlacing, respectively\n"
454 #endif
455           "    dur \tduration in microseconds to wait after displaying each\n"
456           "\t\t  row (for demo purposes)\n"
457           "    -timing\tenables delay for every block read, to simulate modem\n"
458           "\t\t  download of image (~36 Kbps)\n"
459           "    -pause\tpauses after displaying each pass until key pressed\n"
460           "\nPress Q, Esc or mouse button 1 (within image window, after image\n"
461           "is displayed) to quit.\n"
462           "\n", PROGNAME,
463 #if (defined(__i386__) || defined(_M_IX86))
464           strlen(PROGNAME), " ",
465 #endif
466           strlen(PROGNAME), " ", default_display_exponent, num_bgpat);
467         exit(1);
468     }
469
470
471     /* set the title-bar string, but make sure buffer doesn't overflow */
472
473     alen = strlen(appname);
474     flen = strlen(filename);
475     if (alen + flen + 3 > 1023)
476         sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
477     else
478         sprintf(titlebar, "%s:  %s", appname, filename);
479
480
481     /* set some final rpng2_info variables before entering main data loop */
482
483     if (have_bg) {
484         unsigned r, g, b;   /* this approach quiets compiler warnings */
485
486         sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
487         rpng2_info.bg_red   = (uch)r;
488         rpng2_info.bg_green = (uch)g;
489         rpng2_info.bg_blue  = (uch)b;
490     } else
491         rpng2_info.need_bgcolor = TRUE;
492
493     rpng2_info.done = FALSE;
494     rpng2_info.mainprog_init = rpng2_x_init;
495     rpng2_info.mainprog_display_row = rpng2_x_display_row;
496     rpng2_info.mainprog_finish_display = rpng2_x_finish_display;
497
498
499     /* OK, this is the fun part:  call readpng2_decode_data() at the start of
500      * the loop to deal with our first buffer of data (read in above to verify
501      * that the file is a PNG image), then loop through the file and continue
502      * calling the same routine to handle each chunk of data.  It in turn
503      * passes the data to libpng, which will invoke one or more of our call-
504      * backs as decoded data become available.  We optionally call sleep() for
505      * one second per iteration to simulate downloading the image via an analog
506      * modem. */
507
508     for (;;) {
509         Trace((stderr, "about to call readpng2_decode_data()\n"))
510         if (readpng2_decode_data(&rpng2_info, inbuf, incount))
511             ++error;
512         Trace((stderr, "done with readpng2_decode_data()\n"))
513         if (error || feof(infile) || rpng2_info.done)
514             break;
515         if (timing)
516             sleep(1);
517         incount = fread(inbuf, 1, INBUFSIZE, infile);
518     }
519
520
521     /* clean up PNG stuff and report any decoding errors */
522
523     fclose(infile);
524     Trace((stderr, "about to call readpng2_cleanup()\n"))
525     readpng2_cleanup(&rpng2_info);
526
527     if (error) {
528         fprintf(stderr, PROGNAME ":  libpng error while decoding PNG image\n");
529         exit(3);
530     }
531
532
533     /* wait for the user to tell us when to quit */
534
535     do
536         XNextEvent(display, &e);
537     while (!(e.type == ButtonPress && e.xbutton.button == Button1) &&
538            !(e.type == KeyPress &&    /*  v--- or 1 for shifted keys */
539              ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape) ));
540
541
542     /* we're done:  clean up all image and X resources and go away */
543
544     Trace((stderr, "about to call rpng2_x_cleanup()\n"))
545     rpng2_x_cleanup();
546
547     return 0;
548 }
549
550
551
552
553
554 /* this function is called by readpng2_info_callback() in readpng2.c, which
555  * in turn is called by libpng after all of the pre-IDAT chunks have been
556  * read and processed--i.e., we now have enough info to finish initializing */
557
558 static void rpng2_x_init(void)
559 {
560     ulg i;
561     ulg rowbytes = rpng2_info.rowbytes;
562
563     Trace((stderr, "beginning rpng2_x_init()\n"))
564     Trace((stderr, "  rowbytes = %ld\n", rpng2_info.rowbytes))
565     Trace((stderr, "  width  = %ld\n", rpng2_info.width))
566     Trace((stderr, "  height = %ld\n", rpng2_info.height))
567
568     rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
569     if (!rpng2_info.image_data) {
570         readpng2_cleanup(&rpng2_info);
571         return;
572     }
573
574     rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
575     if (!rpng2_info.row_pointers) {
576         free(rpng2_info.image_data);
577         rpng2_info.image_data = NULL;
578         readpng2_cleanup(&rpng2_info);
579         return;
580     }
581
582     for (i = 0;  i < rpng2_info.height;  ++i)
583         rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
584
585
586     /* do the basic X initialization stuff, make the window, and fill it with
587      * the user-specified, file-specified or default background color or
588      * pattern */
589
590     if (rpng2_x_create_window()) {
591
592         /* GRR TEMPORARY HACK:  this is fundamentally no different from cases
593          * above; libpng should longjmp() back to us when png_ptr goes away.
594          * If we/it segfault instead, seems like a libpng bug... */
595
596         /* we're here via libpng callback, so if window fails, clean and bail */
597 printf("readpng2_cleanup.\n");
598         readpng2_cleanup(&rpng2_info);
599         rpng2_x_cleanup();
600         exit(2);
601     }
602 }
603
604
605
606
607
608 static int rpng2_x_create_window(void)
609 {
610     ulg bg_red   = rpng2_info.bg_red;
611     ulg bg_green = rpng2_info.bg_green;
612     ulg bg_blue  = rpng2_info.bg_blue;
613     ulg bg_pixel = 0L;
614     ulg attrmask;
615     int need_colormap = FALSE;
616     int screen, pad;
617     uch *xdata;
618     Window root;
619     XEvent e;
620     XGCValues gcvalues;
621     XSetWindowAttributes attr;
622     XSizeHints *size_hints;
623     XTextProperty windowName, *pWindowName = &windowName;
624     XTextProperty iconName, *pIconName = &iconName;
625     XVisualInfo visual_info;
626     XWMHints *wm_hints;
627
628
629     Trace((stderr, "beginning rpng2_x_create_window()\n"))
630
631     screen = DefaultScreen(display);
632     depth = DisplayPlanes(display, screen);
633     root = RootWindow(display, screen);
634
635 #ifdef DEBUG
636     XSynchronize(display, True);
637 #endif
638
639     if (depth != 16 && depth != 24 && depth != 32) {
640         int visuals_matched = 0;
641
642         Trace((stderr, "default depth is %d:  checking other visuals\n",
643           depth))
644
645         /* 24-bit first */
646         visual_info.screen = screen;
647         visual_info.depth = 24;
648         visual_list = XGetVisualInfo(display,
649           VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
650         if (visuals_matched == 0) {
651 /* GRR:  add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
652             fprintf(stderr, "default screen depth %d not supported, and no"
653               " 24-bit visuals found\n", depth);
654             return 2;
655         }
656         Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
657           visuals_matched))
658         visual = visual_list[0].visual;
659         depth = visual_list[0].depth;
660 /*
661         colormap_size = visual_list[0].colormap_size;
662         visual_class = visual->class;
663         visualID = XVisualIDFromVisual(visual);
664  */
665         have_nondefault_visual = TRUE;
666         need_colormap = TRUE;
667     } else {
668         XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
669         visual = visual_info.visual;
670     }
671
672     RMask = visual->red_mask;
673     GMask = visual->green_mask;
674     BMask = visual->blue_mask;
675
676 /* GRR:  add/check 8-bit support */
677     if (depth == 8 || need_colormap) {
678         colormap = XCreateColormap(display, root, visual, AllocNone);
679         if (!colormap) {
680             fprintf(stderr, "XCreateColormap() failed\n");
681             return 2;
682         }
683         have_colormap = TRUE;
684         if (depth == 8)
685             bg_image = FALSE;   /* gradient just wastes palette entries */
686     }
687     if (depth == 15 || depth == 16) {
688         RShift = 15 - rpng2_x_msb(RMask);    /* these are right-shifts */
689         GShift = 15 - rpng2_x_msb(GMask);
690         BShift = 15 - rpng2_x_msb(BMask);
691     } else if (depth > 16) {
692         RShift = rpng2_x_msb(RMask) - 7;     /* these are left-shifts */
693         GShift = rpng2_x_msb(GMask) - 7;
694         BShift = rpng2_x_msb(BMask) - 7;
695     }
696     if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
697         fprintf(stderr, "rpng2 internal logic error:  negative X shift(s)!\n");
698         return 2;
699     }
700
701 /*---------------------------------------------------------------------------
702     Finally, create the window.
703   ---------------------------------------------------------------------------*/
704
705     attr.backing_store = Always;
706     attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
707     attrmask = CWBackingStore | CWEventMask;
708     if (have_nondefault_visual) {
709         attr.colormap = colormap;
710         attr.background_pixel = 0;
711         attr.border_pixel = 1;
712         attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
713     }
714
715     window = XCreateWindow(display, root, 0, 0, rpng2_info.width,
716       rpng2_info.height, 0, depth, InputOutput, visual, attrmask, &attr);
717
718     if (window == None) {
719         fprintf(stderr, "XCreateWindow() failed\n");
720         return 2;
721     } else
722         have_window = TRUE;
723
724     if (depth == 8)
725         XSetWindowColormap(display, window, colormap);
726
727     if (!XStringListToTextProperty(&window_name, 1, pWindowName))
728         pWindowName = NULL;
729     if (!XStringListToTextProperty(&icon_name, 1, pIconName))
730         pIconName = NULL;
731
732     /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */
733
734     if ((size_hints = XAllocSizeHints()) != NULL) {
735         /* window will not be resizable */
736         size_hints->flags = PMinSize | PMaxSize;
737         size_hints->min_width = size_hints->max_width = (int)rpng2_info.width;
738         size_hints->min_height = size_hints->max_height =
739           (int)rpng2_info.height;
740     }
741
742     if ((wm_hints = XAllocWMHints()) != NULL) {
743         wm_hints->initial_state = NormalState;
744         wm_hints->input = True;
745      /* wm_hints->icon_pixmap = icon_pixmap; */
746         wm_hints->flags = StateHint | InputHint  /* | IconPixmapHint */ ;
747     }
748
749     XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
750       size_hints, wm_hints, NULL);
751
752     /* various properties and hints no longer needed; free memory */
753     if (pWindowName)
754        XFree(pWindowName->value);
755     if (pIconName)
756        XFree(pIconName->value);
757     if (size_hints)
758         XFree(size_hints);
759     if (wm_hints)
760        XFree(wm_hints);
761
762     XMapWindow(display, window);
763
764     gc = XCreateGC(display, window, 0, &gcvalues);
765     have_gc = TRUE;
766
767 /*---------------------------------------------------------------------------
768     Allocate memory for the X- and display-specific version of the image.
769   ---------------------------------------------------------------------------*/
770
771     if (depth == 24 || depth == 32) {
772         xdata = (uch *)malloc(4*rpng2_info.width*rpng2_info.height);
773         pad = 32;
774     } else if (depth == 16) {
775         xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height);
776         pad = 16;
777     } else /* depth == 8 */ {
778         xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height);
779         pad = 8;
780     }
781
782     if (!xdata) {
783         fprintf(stderr, PROGNAME ":  unable to allocate image memory\n");
784         return 4;
785     }
786
787     ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
788       (char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0);
789
790     if (!ximage) {
791         fprintf(stderr, PROGNAME ":  XCreateImage() failed\n");
792         free(xdata);
793         return 3;
794     }
795
796     /* to avoid testing the byte order every pixel (or doubling the size of
797      * the drawing routine with a giant if-test), we arbitrarily set the byte
798      * order to MSBFirst and let Xlib worry about inverting things on little-
799      * endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the
800      * most efficient approach (the giant if-test would be better), but in
801      * the interest of clarity, we'll take the easy way out... */
802
803     ximage->byte_order = MSBFirst;
804
805 /*---------------------------------------------------------------------------
806     Fill window with the specified background color (default is black) or
807     faked "background image" (but latter is disabled if 8-bit; gradients
808     just waste palette entries).
809   ---------------------------------------------------------------------------*/
810
811     if (bg_image)
812         rpng2_x_load_bg_image();    /* resets bg_image if fails */
813
814     if (!bg_image) {
815         if (depth == 24 || depth == 32) {
816             bg_pixel = (bg_red   << RShift) |
817                        (bg_green << GShift) |
818                        (bg_blue  << BShift);
819         } else if (depth == 16) {
820             bg_pixel = (((bg_red   << 8) >> RShift) & RMask) |
821                        (((bg_green << 8) >> GShift) & GMask) |
822                        (((bg_blue  << 8) >> BShift) & BMask);
823         } else /* depth == 8 */ {
824
825             /* GRR:  add 8-bit support */
826
827         }
828         XSetForeground(display, gc, bg_pixel);
829         XFillRectangle(display, window, gc, 0, 0, rpng2_info.width,
830           rpng2_info.height);
831     }
832
833 /*---------------------------------------------------------------------------
834     Wait for first Expose event to do any drawing, then flush and return.
835   ---------------------------------------------------------------------------*/
836
837     do
838         XNextEvent(display, &e);
839     while (e.type != Expose || e.xexpose.count);
840
841     XFlush(display);
842
843     return 0;
844
845 } /* end function rpng2_x_create_window() */
846
847
848
849
850
851 static int rpng2_x_load_bg_image(void)
852 {
853     uch *src;
854     char *dest;
855     uch r1, r2, g1, g2, b1, b2;
856     uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
857     int k, hmax, max;
858     int xidx, yidx, yidx_max = (bgscale-1);
859     int even_odd_vert, even_odd_horiz, even_odd;
860     int invert_gradient2 = (bg[pat].type & 0x08);
861     int invert_column;
862     int ximage_rowbytes = ximage->bytes_per_line;
863     ulg i, row;
864     ulg pixel;
865
866 /*---------------------------------------------------------------------------
867     Allocate buffer for fake background image to be used with transparent
868     images; if this fails, revert to plain background color.
869   ---------------------------------------------------------------------------*/
870
871     bg_rowbytes = 3 * rpng2_info.width;
872     bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
873     if (!bg_data) {
874         fprintf(stderr, PROGNAME
875           ":  unable to allocate memory for background image\n");
876         bg_image = 0;
877         return 1;
878     }
879
880 /*---------------------------------------------------------------------------
881     Vertical gradients (ramps) in NxN squares, alternating direction and
882     colors (N == bgscale).
883   ---------------------------------------------------------------------------*/
884
885     if ((bg[pat].type & 0x07) == 0) {
886         uch r1_min  = rgb[bg[pat].rgb1_min].r;
887         uch g1_min  = rgb[bg[pat].rgb1_min].g;
888         uch b1_min  = rgb[bg[pat].rgb1_min].b;
889         uch r2_min  = rgb[bg[pat].rgb2_min].r;
890         uch g2_min  = rgb[bg[pat].rgb2_min].g;
891         uch b2_min  = rgb[bg[pat].rgb2_min].b;
892         int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
893         int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
894         int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
895         int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
896         int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
897         int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
898
899         for (row = 0;  row < rpng2_info.height;  ++row) {
900             yidx = (int)(row % bgscale);
901             even_odd_vert = (int)((row / bgscale) & 1);
902
903             r1 = r1_min + (r1_diff * yidx) / yidx_max;
904             g1 = g1_min + (g1_diff * yidx) / yidx_max;
905             b1 = b1_min + (b1_diff * yidx) / yidx_max;
906             r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
907             g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
908             b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
909
910             r2 = r2_min + (r2_diff * yidx) / yidx_max;
911             g2 = g2_min + (g2_diff * yidx) / yidx_max;
912             b2 = b2_min + (b2_diff * yidx) / yidx_max;
913             r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
914             g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
915             b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
916
917             dest = (char *)bg_data + row*bg_rowbytes;
918             for (i = 0;  i < rpng2_info.width;  ++i) {
919                 even_odd_horiz = (int)((i / bgscale) & 1);
920                 even_odd = even_odd_vert ^ even_odd_horiz;
921                 invert_column =
922                   (even_odd_horiz && (bg[pat].type & 0x10));
923                 if (even_odd == 0) {        /* gradient #1 */
924                     if (invert_column) {
925                         *dest++ = r1_inv;
926                         *dest++ = g1_inv;
927                         *dest++ = b1_inv;
928                     } else {
929                         *dest++ = r1;
930                         *dest++ = g1;
931                         *dest++ = b1;
932                     }
933                 } else {                    /* gradient #2 */
934                     if ((invert_column && invert_gradient2) ||
935                         (!invert_column && !invert_gradient2))
936                     {
937                         *dest++ = r2;       /* not inverted or */
938                         *dest++ = g2;       /*  doubly inverted */
939                         *dest++ = b2;
940                     } else {
941                         *dest++ = r2_inv;
942                         *dest++ = g2_inv;   /* singly inverted */
943                         *dest++ = b2_inv;
944                     }
945                 }
946             }
947         }
948
949 /*---------------------------------------------------------------------------
950     Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
951     M. Costello.
952   ---------------------------------------------------------------------------*/
953
954     } else if ((bg[pat].type & 0x07) == 1) {
955
956         hmax = (bgscale-1)/2;   /* half the max weight of a color */
957         max = 2*hmax;           /* the max weight of a color */
958
959         r1 = rgb[bg[pat].rgb1_max].r;
960         g1 = rgb[bg[pat].rgb1_max].g;
961         b1 = rgb[bg[pat].rgb1_max].b;
962         r2 = rgb[bg[pat].rgb2_max].r;
963         g2 = rgb[bg[pat].rgb2_max].g;
964         b2 = rgb[bg[pat].rgb2_max].b;
965
966         for (row = 0;  row < rpng2_info.height;  ++row) {
967             yidx = (int)(row % bgscale);
968             if (yidx > hmax)
969                 yidx = bgscale-1 - yidx;
970             dest = (char *)bg_data + row*bg_rowbytes;
971             for (i = 0;  i < rpng2_info.width;  ++i) {
972                 xidx = (int)(i % bgscale);
973                 if (xidx > hmax)
974                     xidx = bgscale-1 - xidx;
975                 k = xidx + yidx;
976                 *dest++ = (k*r1 + (max-k)*r2) / max;
977                 *dest++ = (k*g1 + (max-k)*g2) / max;
978                 *dest++ = (k*b1 + (max-k)*b2) / max;
979             }
980         }
981
982 /*---------------------------------------------------------------------------
983     Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
984     soids will equal bgscale?].  This one is slow but very cool.  Code con-
985     tributed by Pieter S. van der Meulen (originally in Smalltalk).
986   ---------------------------------------------------------------------------*/
987
988     } else if ((bg[pat].type & 0x07) == 2) {
989         uch ch;
990         int ii, x, y, hw, hh, grayspot;
991         double freq, rotate, saturate, gray, intensity;
992         double angle=0.0, aoffset=0.0, maxDist, dist;
993         double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
994
995         fprintf(stderr, "%s:  computing radial background...",
996           PROGNAME);
997         fflush(stderr);
998
999         hh = (int)(rpng2_info.height / 2);
1000         hw = (int)(rpng2_info.width / 2);
1001
1002         /* variables for radial waves:
1003          *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
1004          *   freq:  number of color beams originating from the center
1005          *   grayspot:  size of the graying center area (anti-alias)
1006          *   rotate:  rotation of the beams as a function of radius
1007          *   saturate:  saturation of beams' shape azimuthally
1008          */
1009         angle = CLIP(angle, 0.0, 360.0);
1010         grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
1011         freq = MAX((double)bg[pat].bg_freq, 0.0);
1012         saturate = (double)bg[pat].bg_bsat * 0.1;
1013         rotate = (double)bg[pat].bg_brot * 0.1;
1014         gray = 0.0;
1015         intensity = 0.0;
1016         maxDist = (double)((hw*hw) + (hh*hh));
1017
1018         for (row = 0;  row < rpng2_info.height;  ++row) {
1019             y = (int)(row - hh);
1020             dest = (char *)bg_data + row*bg_rowbytes;
1021             for (i = 0;  i < rpng2_info.width;  ++i) {
1022                 x = (int)(i - hw);
1023                 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
1024                 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
1025                 gray = MIN(1.0, gray);
1026                 dist = (double)((x*x) + (y*y)) / maxDist;
1027                 intensity = cos((angle+(rotate*dist*PI)) * freq) *
1028                   gray * saturate;
1029                 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
1030                 hue = (angle + PI) * INV_PI_360 + aoffset;
1031                 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
1032                 s = MIN(MAX(s,0.0), 1.0);
1033                 v = MIN(MAX(intensity,0.0), 1.0);
1034
1035                 if (s == 0.0) {
1036                     ch = (uch)(v * 255.0);
1037                     *dest++ = ch;
1038                     *dest++ = ch;
1039                     *dest++ = ch;
1040                 } else {
1041                     if ((hue < 0.0) || (hue >= 360.0))
1042                         hue -= (((int)(hue / 360.0)) * 360.0);
1043                     hue /= 60.0;
1044                     ii = (int)hue;
1045                     f = hue - (double)ii;
1046                     p = (1.0 - s) * v;
1047                     q = (1.0 - (s * f)) * v;
1048                     t = (1.0 - (s * (1.0 - f))) * v;
1049                     if      (ii == 0) { red = v; green = t; blue = p; }
1050                     else if (ii == 1) { red = q; green = v; blue = p; }
1051                     else if (ii == 2) { red = p; green = v; blue = t; }
1052                     else if (ii == 3) { red = p; green = q; blue = v; }
1053                     else if (ii == 4) { red = t; green = p; blue = v; }
1054                     else if (ii == 5) { red = v; green = p; blue = q; }
1055                     *dest++ = (uch)(red * 255.0);
1056                     *dest++ = (uch)(green * 255.0);
1057                     *dest++ = (uch)(blue * 255.0);
1058                 }
1059             }
1060         }
1061         fprintf(stderr, "done.\n");
1062         fflush(stderr);
1063     }
1064
1065 /*---------------------------------------------------------------------------
1066     Blast background image to display buffer before beginning PNG decode.
1067   ---------------------------------------------------------------------------*/
1068
1069     if (depth == 24 || depth == 32) {
1070         ulg red, green, blue;
1071
1072         for (row = 0;  row < rpng2_info.height;  ++row) {
1073             src = bg_data + row*bg_rowbytes;
1074             dest = ximage->data + row*ximage_rowbytes;
1075             for (i = rpng2_info.width;  i > 0;  --i) {
1076                 red   = *src++;
1077                 green = *src++;
1078                 blue  = *src++;
1079                 pixel = (red   << RShift) |
1080                         (green << GShift) |
1081                         (blue  << BShift);
1082                 /* recall that we set ximage->byte_order = MSBFirst above */
1083                 /* GRR BUG:  this assumes bpp == 32, but may be 24: */
1084                 *dest++ = (char)((pixel >> 24) & 0xff);
1085                 *dest++ = (char)((pixel >> 16) & 0xff);
1086                 *dest++ = (char)((pixel >>  8) & 0xff);
1087                 *dest++ = (char)( pixel        & 0xff);
1088             }
1089         }
1090
1091     } else if (depth == 16) {
1092         ush red, green, blue;
1093
1094         for (row = 0;  row < rpng2_info.height;  ++row) {
1095             src = bg_data + row*bg_rowbytes;
1096             dest = ximage->data + row*ximage_rowbytes;
1097             for (i = rpng2_info.width;  i > 0;  --i) {
1098                 red   = ((ush)(*src) << 8);  ++src;
1099                 green = ((ush)(*src) << 8);  ++src;
1100                 blue  = ((ush)(*src) << 8);  ++src;
1101                 pixel = ((red   >> RShift) & RMask) |
1102                         ((green >> GShift) & GMask) |
1103                         ((blue  >> BShift) & BMask);
1104                 /* recall that we set ximage->byte_order = MSBFirst above */
1105                 *dest++ = (char)((pixel >>  8) & 0xff);
1106                 *dest++ = (char)( pixel        & 0xff);
1107             }
1108         }
1109
1110     } else /* depth == 8 */ {
1111
1112         /* GRR:  add 8-bit support */
1113
1114     }
1115
1116     XPutImage(display, window, gc, ximage, 0, 0, 0, 0, rpng2_info.width,
1117       rpng2_info.height);
1118
1119     return 0;
1120
1121 } /* end function rpng2_x_load_bg_image() */
1122
1123
1124
1125
1126
1127 static void rpng2_x_display_row(ulg row)
1128 {
1129     uch bg_red   = rpng2_info.bg_red;
1130     uch bg_green = rpng2_info.bg_green;
1131     uch bg_blue  = rpng2_info.bg_blue;
1132     uch *src, *src2=NULL;
1133     char *dest;
1134     uch r, g, b, a;
1135     int ximage_rowbytes = ximage->bytes_per_line;
1136     ulg i, pixel;
1137     static int rows=0, prevpass=(-1);
1138     static ulg firstrow;
1139
1140 /*---------------------------------------------------------------------------
1141     rows and firstrow simply track how many rows (and which ones) have not
1142     yet been displayed; alternatively, we could call XPutImage() for every
1143     row and not bother with the records-keeping.
1144   ---------------------------------------------------------------------------*/
1145
1146     Trace((stderr, "beginning rpng2_x_display_row()\n"))
1147
1148     if (rpng2_info.pass != prevpass) {
1149         if (pause_after_pass && rpng2_info.pass > 0) {
1150             XEvent e;
1151             KeySym k;
1152
1153             fprintf(stderr,
1154               "%s:  end of pass %d of 7; click in image window to continue\n",
1155               PROGNAME, prevpass + 1);
1156             do
1157                 XNextEvent(display, &e);
1158             while (!(e.type == ButtonPress && e.xbutton.button == Button1)
1159                    && !(e.type == KeyPress &&
1160                    ((k = XLookupKeysym(&e.xkey, 0)) == XK_q
1161                     || k == XK_Escape) )) ;
1162         }
1163         fprintf(stderr, "%s:  pass %d of 7\r", PROGNAME, rpng2_info.pass + 1);
1164         fflush(stderr);
1165         prevpass = rpng2_info.pass;
1166     }
1167
1168     if (rows == 0)
1169         firstrow = row;   /* first row that is not yet displayed */
1170
1171     ++rows;   /* count of rows received but not yet displayed */
1172
1173 /*---------------------------------------------------------------------------
1174     Aside from the use of the rpng2_info struct, the lack of an outer loop
1175     (over rows) and moving the XPutImage() call outside the "if (depth)"
1176     tests, this routine is identical to rpng_x_display_image() in the non-
1177     progressive version of the program.
1178   ---------------------------------------------------------------------------*/
1179
1180     if (depth == 24 || depth == 32) {
1181         ulg red, green, blue;
1182
1183         src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1184         if (bg_image)
1185             src2 = bg_data + row*bg_rowbytes;
1186         dest = ximage->data + row*ximage_rowbytes;
1187         if (rpng2_info.channels == 3) {
1188             for (i = rpng2_info.width;  i > 0;  --i) {
1189                 red   = *src++;
1190                 green = *src++;
1191                 blue  = *src++;
1192                 pixel = (red   << RShift) |
1193                         (green << GShift) |
1194                         (blue  << BShift);
1195                 /* recall that we set ximage->byte_order = MSBFirst above */
1196                 /* GRR BUG:  this assumes bpp == 32, but may be 24: */
1197                 *dest++ = (char)((pixel >> 24) & 0xff);
1198                 *dest++ = (char)((pixel >> 16) & 0xff);
1199                 *dest++ = (char)((pixel >>  8) & 0xff);
1200                 *dest++ = (char)( pixel        & 0xff);
1201             }
1202         } else /* if (rpng2_info.channels == 4) */ {
1203             for (i = rpng2_info.width;  i > 0;  --i) {
1204                 r = *src++;
1205                 g = *src++;
1206                 b = *src++;
1207                 a = *src++;
1208                 if (bg_image) {
1209                     bg_red   = *src2++;
1210                     bg_green = *src2++;
1211                     bg_blue  = *src2++;
1212                 }
1213                 if (a == 255) {
1214                     red   = r;
1215                     green = g;
1216                     blue  = b;
1217                 } else if (a == 0) {
1218                     red   = bg_red;
1219                     green = bg_green;
1220                     blue  = bg_blue;
1221                 } else {
1222                     /* this macro (from png.h) composites the foreground
1223                      * and background values and puts the result into the
1224                      * first argument */
1225                     alpha_composite(red,   r, a, bg_red);
1226                     alpha_composite(green, g, a, bg_green);
1227                     alpha_composite(blue,  b, a, bg_blue);
1228                 }
1229                 pixel = (red   << RShift) |
1230                         (green << GShift) |
1231                         (blue  << BShift);
1232                 /* recall that we set ximage->byte_order = MSBFirst above */
1233                 /* GRR BUG:  this assumes bpp == 32, but may be 24: */
1234                 *dest++ = (char)((pixel >> 24) & 0xff);
1235                 *dest++ = (char)((pixel >> 16) & 0xff);
1236                 *dest++ = (char)((pixel >>  8) & 0xff);
1237                 *dest++ = (char)( pixel        & 0xff);
1238             }
1239         }
1240
1241     } else if (depth == 16) {
1242         ush red, green, blue;
1243
1244         src = rpng2_info.row_pointers[row];
1245         if (bg_image)
1246             src2 = bg_data + row*bg_rowbytes;
1247         dest = ximage->data + row*ximage_rowbytes;
1248         if (rpng2_info.channels == 3) {
1249             for (i = rpng2_info.width;  i > 0;  --i) {
1250                 red   = ((ush)(*src) << 8);
1251                 ++src;
1252                 green = ((ush)(*src) << 8);
1253                 ++src;
1254                 blue  = ((ush)(*src) << 8);
1255                 ++src;
1256                 pixel = ((red   >> RShift) & RMask) |
1257                         ((green >> GShift) & GMask) |
1258                         ((blue  >> BShift) & BMask);
1259                 /* recall that we set ximage->byte_order = MSBFirst above */
1260                 *dest++ = (char)((pixel >>  8) & 0xff);
1261                 *dest++ = (char)( pixel        & 0xff);
1262             }
1263         } else /* if (rpng2_info.channels == 4) */ {
1264             for (i = rpng2_info.width;  i > 0;  --i) {
1265                 r = *src++;
1266                 g = *src++;
1267                 b = *src++;
1268                 a = *src++;
1269                 if (bg_image) {
1270                     bg_red   = *src2++;
1271                     bg_green = *src2++;
1272                     bg_blue  = *src2++;
1273                 }
1274                 if (a == 255) {
1275                     red   = ((ush)r << 8);
1276                     green = ((ush)g << 8);
1277                     blue  = ((ush)b << 8);
1278                 } else if (a == 0) {
1279                     red   = ((ush)bg_red   << 8);
1280                     green = ((ush)bg_green << 8);
1281                     blue  = ((ush)bg_blue  << 8);
1282                 } else {
1283                     /* this macro (from png.h) composites the foreground
1284                      * and background values and puts the result back into
1285                      * the first argument (== fg byte here:  safe) */
1286                     alpha_composite(r, r, a, bg_red);
1287                     alpha_composite(g, g, a, bg_green);
1288                     alpha_composite(b, b, a, bg_blue);
1289                     red   = ((ush)r << 8);
1290                     green = ((ush)g << 8);
1291                     blue  = ((ush)b << 8);
1292                 }
1293                 pixel = ((red   >> RShift) & RMask) |
1294                         ((green >> GShift) & GMask) |
1295                         ((blue  >> BShift) & BMask);
1296                 /* recall that we set ximage->byte_order = MSBFirst above */
1297                 *dest++ = (char)((pixel >>  8) & 0xff);
1298                 *dest++ = (char)( pixel        & 0xff);
1299             }
1300         }
1301
1302     } else /* depth == 8 */ {
1303
1304         /* GRR:  add 8-bit support */
1305
1306     }
1307
1308
1309 /*---------------------------------------------------------------------------
1310     Display after every 16 rows or when on one of last two rows.  (Region
1311     may include previously displayed lines due to interlacing--i.e., not
1312     contiguous.  Also, second-to-last row is final one in interlaced images
1313     with odd number of rows.)  For demos, flush (and delay) after every 16th
1314     row so "sparse" passes don't go twice as fast.
1315   ---------------------------------------------------------------------------*/
1316
1317     if (demo_timing && (row - firstrow >= 16 || row >= rpng2_info.height-2)) {
1318         XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1319           (int)firstrow, rpng2_info.width, row - firstrow + 1);
1320         XFlush(display);
1321         rows = 0;
1322         usleep(usleep_duration);
1323     } else
1324     if (!demo_timing && ((rows & 0xf) == 0 || row >= rpng2_info.height-2)) {
1325         XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1326           (int)firstrow, rpng2_info.width, row - firstrow + 1);
1327         XFlush(display);
1328         rows = 0;
1329     }
1330
1331 }
1332
1333
1334
1335
1336
1337 static void rpng2_x_finish_display(void)
1338 {
1339     Trace((stderr, "beginning rpng2_x_finish_display()\n"))
1340
1341     /* last row has already been displayed by rpng2_x_display_row(), so we
1342      * have nothing to do here except set a flag and let the user know that
1343      * the image is done */
1344
1345     rpng2_info.done = TRUE;
1346     printf(
1347       "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
1348     fflush(stdout);
1349 }
1350
1351
1352
1353
1354
1355 static void rpng2_x_cleanup(void)
1356 {
1357     if (bg_image && bg_data) {
1358         free(bg_data);
1359         bg_data = NULL;
1360     }
1361
1362     if (rpng2_info.image_data) {
1363         free(rpng2_info.image_data);
1364         rpng2_info.image_data = NULL;
1365     }
1366
1367     if (rpng2_info.row_pointers) {
1368         free(rpng2_info.row_pointers);
1369         rpng2_info.row_pointers = NULL;
1370     }
1371
1372     if (ximage) {
1373         if (ximage->data) {
1374             free(ximage->data);           /* we allocated it, so we free it */
1375             ximage->data = (char *)NULL;  /*  instead of XDestroyImage() */
1376         }
1377         XDestroyImage(ximage);
1378         ximage = NULL;
1379     }
1380
1381     if (have_gc)
1382         XFreeGC(display, gc);
1383
1384     if (have_window)
1385         XDestroyWindow(display, window);
1386
1387     if (have_colormap)
1388         XFreeColormap(display, colormap);
1389
1390     if (have_nondefault_visual)
1391         XFree(visual_list);
1392 }
1393
1394
1395
1396
1397
1398 static int rpng2_x_msb(ulg u32val)
1399 {
1400     int i;
1401
1402     for (i = 31;  i >= 0;  --i) {
1403         if (u32val & 0x80000000L)
1404             break;
1405         u32val <<= 1;
1406     }
1407     return i;
1408 }