1 /*---------------------------------------------------------------------------
3 rpng2 - progressive-model PNG display program rpng2-x.c
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).
12 Thanks to Adam Costello and Pieter S. van der Meulen for the "diamond"
13 and "radial waves" patterns, respectively.
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?
21 ---------------------------------------------------------------------------
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
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-
43 - 2.03: deleted runtime MMX-enabling/disabling and obsolete -mmx* options
45 ---------------------------------------------------------------------------
47 Copyright (c) 1998-2008 Greg Roelofs. All rights reserved.
49 This software is provided "as is," without warranty of any kind,
50 express or implied. In no event shall the author or contributors
51 be held liable for any damages arising in any way from the use of
54 The contents of this file are DUAL-LICENSED. You may modify and/or
55 redistribute this software according to the terms of one of the
56 following two licenses (at your option):
59 LICENSE 1 ("BSD-like with advertising clause"):
61 Permission is granted to anyone to use this software for any purpose,
62 including commercial applications, and to alter it and redistribute
63 it freely, subject to the following restrictions:
65 1. Redistributions of source code must retain the above copyright
66 notice, disclaimer, and this list of conditions.
67 2. Redistributions in binary form must reproduce the above copyright
68 notice, disclaimer, and this list of conditions in the documenta-
69 tion and/or other materials provided with the distribution.
70 3. All advertising materials mentioning features or use of this
71 software must display the following acknowledgment:
73 This product includes software developed by Greg Roelofs
74 and contributors for the book, "PNG: The Definitive Guide,"
75 published by O'Reilly and Associates.
78 LICENSE 2 (GNU GPL v2 or later):
80 This program is free software; you can redistribute it and/or modify
81 it under the terms of the GNU General Public License as published by
82 the Free Software Foundation; either version 2 of the License, or
83 (at your option) any later version.
85 This program is distributed in the hope that it will be useful,
86 but WITHOUT ANY WARRANTY; without even the implied warranty of
87 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
88 GNU General Public License for more details.
90 You should have received a copy of the GNU General Public License
91 along with this program; if not, write to the Free Software Foundation,
92 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
94 ---------------------------------------------------------------------------*/
96 #define PROGNAME "rpng2-x"
97 #define LONGNAME "Progressive PNG Viewer for X"
98 #define VERSION "2.03 of 25 February 2010"
99 #define RESNAME "rpng2" /* our X resource application name */
100 #define RESCLASS "Rpng" /* our X resource class name */
106 #include <setjmp.h> /* for jmpbuf declaration in readpng2.h */
108 #include <math.h> /* only for PvdM background code */
109 #include <X11/Xlib.h>
110 #include <X11/Xutil.h>
112 #include <X11/keysym.h> /* defines XK_* macros */
118 /* all for PvdM background code: */
120 # define PI 3.141592653589793238
122 #define PI_2 (PI*0.5)
123 #define INV_PI_360 (360.0 / PI)
124 #define MAX(a,b) (a>b?a:b)
125 #define MIN(a,b) (a<b?a:b)
126 #define CLIP(a,min,max) MAX(min,MIN((a),max))
127 #define ABS(a) ((a)<0?-(a):(a))
128 #define CLIP8P(c) MAX(0,(MIN((c),255))) /* 8-bit pos. integer (uch) */
129 #define ROUNDF(f) ((int)(f + 0.5))
131 #define QUIT(e,k) ((e.type == ButtonPress && e.xbutton.button == Button1) || \
132 (e.type == KeyPress && /* v--- or 1 for shifted keys */ \
133 ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape)))
135 #define NO_24BIT_MASKS /* undef case not fully written--only for redisplay() */
137 #define rgb1_max bg_freq
138 #define rgb1_min bg_gray
139 #define rgb2_max bg_bsat
140 #define rgb2_min bg_brot
142 /* #define DEBUG */ /* this enables the Trace() macros */
144 #include "readpng2.h" /* typedefs, common macros, readpng2 prototypes */
147 /* could just include png.h, but this macro is the only thing we need
148 * (name and typedefs changed to local versions); note that side effects
149 * only happen with alpha (which could easily be avoided with
150 * "ush acopy = (alpha);") */
152 #define alpha_composite(composite, fg, alpha, bg) { \
153 ush temp = ((ush)(fg)*(ush)(alpha) + \
154 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \
155 (composite) = (uch)((temp + (temp >> 8)) >> 8); \
159 #define INBUFSIZE 4096 /* with pseudo-timing on (1 sec delay/block), this
160 * block size corresponds roughly to a download
161 * speed 10% faster than theoretical 33.6K maximum
162 * (assuming 8 data bits, 1 stop bit and no other
165 /* local prototypes */
166 static void rpng2_x_init (void);
167 static int rpng2_x_create_window (void);
168 static int rpng2_x_load_bg_image (void);
169 static void rpng2_x_display_row (ulg row);
170 static void rpng2_x_finish_display (void);
171 static void rpng2_x_redisplay_image (ulg startcol, ulg startrow,
172 ulg width, ulg height);
174 static void rpng2_x_reload_bg_image (void);
175 static int is_number (char *p);
177 static void rpng2_x_cleanup (void);
178 static int rpng2_x_msb (ulg u32val);
181 static char titlebar[1024], *window_name = titlebar;
182 static char *appname = LONGNAME;
183 static char *icon_name = PROGNAME;
184 static char *res_name = RESNAME;
185 static char *res_class = RESCLASS;
186 static char *filename;
189 static mainprog_info rpng2_info;
191 static uch inbuf[INBUFSIZE];
194 static int pat = 6; /* must be less than num_bgpat */
195 static int bg_image = 0;
196 static int bgscale, bgscale_default = 16;
197 static ulg bg_rowbytes;
200 int pause_after_pass = FALSE;
201 int demo_timing = FALSE;
202 ulg usleep_duration = 0L;
204 static struct rgb_color {
207 { 0, 0, 0}, /* 0: black */
208 {255, 255, 255}, /* 1: white */
209 {173, 132, 57}, /* 2: tan */
210 { 64, 132, 0}, /* 3: medium green */
211 {189, 117, 1}, /* 4: gold */
212 {253, 249, 1}, /* 5: yellow */
213 { 0, 0, 255}, /* 6: blue */
214 { 0, 0, 120}, /* 7: medium blue */
215 {255, 0, 255}, /* 8: magenta */
216 { 64, 0, 64}, /* 9: dark magenta */
217 {255, 0, 0}, /* 10: red */
218 { 64, 0, 0}, /* 11: dark red */
219 {255, 127, 0}, /* 12: orange */
220 {192, 96, 0}, /* 13: darker orange */
221 { 24, 60, 0}, /* 14: dark green-yellow */
222 { 85, 125, 200}, /* 15: ice blue */
223 {192, 192, 192} /* 16: Netscape/Mosaic gray */
225 /* not used for now, but should be for error-checking:
226 static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
230 This whole struct is a fairly cheesy way to keep the number of
231 command-line options to a minimum. The radial-waves background
232 type is a particularly poor fit to the integer elements of the
233 struct...but a few macros and a little fixed-point math will do
237 F E D C B A 9 8 7 6 5 4 3 2 1 0
239 | | +-+-+-- 0 = sharp-edged checkerboard
240 | | 1 = soft diamonds
243 | +-- gradient #2 inverted?
244 +-- alternating columns inverted?
246 static struct background_pattern {
248 int rgb1_max, rgb1_min; /* or bg_freq, bg_gray */
249 int rgb2_max, rgb2_min; /* or bg_bsat, bg_brot (both scaled by 10)*/
251 {0, 1,1, 16,16}, /* checkered: white vs. light gray (basic) */
252 {0+8, 2,0, 1,15}, /* checkered: tan/black vs. white/ice blue */
253 {0+24, 2,0, 1,0}, /* checkered: tan/black vs. white/black */
254 {0+8, 4,5, 0,2}, /* checkered: gold/yellow vs. black/tan */
255 {0+8, 4,5, 0,6}, /* checkered: gold/yellow vs. black/blue */
256 {0, 7,0, 8,9}, /* checkered: deep blue/black vs. magenta */
257 {0+8, 13,0, 5,14}, /* checkered: orange/black vs. yellow */
258 {0+8, 12,0, 10,11}, /* checkered: orange/black vs. red */
259 {1, 7,0, 8,0}, /* diamonds: deep blue/black vs. magenta */
260 {1, 12,0, 11,0}, /* diamonds: orange vs. dark red */
261 {1, 10,0, 7,0}, /* diamonds: red vs. medium blue */
262 {1, 4,0, 5,0}, /* diamonds: gold vs. yellow */
263 {1, 3,0, 0,0}, /* diamonds: medium green vs. black */
264 {2, 16, 100, 20, 0}, /* radial: ~hard radial color-beams */
265 {2, 18, 100, 10, 2}, /* radial: soft, curved radial color-beams */
266 {2, 16, 256, 100, 250}, /* radial: very tight spiral */
267 {2, 10000, 256, 11, 0} /* radial: dipole-moire' (almost fractal) */
269 static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
272 /* X-specific variables */
273 static char *displayname;
274 static XImage *ximage;
275 static Display *display;
277 static Visual *visual;
278 static XVisualInfo *visual_list;
279 static int RShift, GShift, BShift;
280 static ulg RMask, GMask, BMask;
281 static Window window;
283 static Colormap colormap;
285 static int have_nondefault_visual = FALSE;
286 static int have_colormap = FALSE;
287 static int have_window = FALSE;
288 static int have_gc = FALSE;
293 int main(int argc, char **argv)
298 char *p, *bgstr = NULL;
305 long loop_interval = -1; /* seconds (100,000 max) */
307 double LUT_exponent; /* just the lookup table */
308 double CRT_exponent = 2.2; /* just the monitor */
309 double default_display_exponent; /* whole display system */
314 /* First initialize a few things, just to be sure--memset takes care of
315 * default background color (black), booleans (FALSE), pointers (NULL),
318 displayname = (char *)NULL;
319 filename = (char *)NULL;
320 memset(&rpng2_info, 0, sizeof(mainprog_info));
323 /* Set the default value for our display-system exponent, i.e., the
324 * product of the CRT exponent and the exponent corresponding to
325 * the frame-buffer's lookup table (LUT), if any. This is not an
326 * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
327 * ones), but it should cover 99% of the current possibilities. */
330 /* third-party utilities can modify the default LUT exponent */
331 LUT_exponent = 1.0 / 2.2;
333 if (some_next_function_that_returns_gamma(&next_gamma))
334 LUT_exponent = 1.0 / next_gamma;
337 LUT_exponent = 1.0 / 1.7;
338 /* there doesn't seem to be any documented function to
339 * get the "gamma" value, so we do it the hard way */
340 infile = fopen("/etc/config/system.glGammaVal", "r");
344 fgets(tmpline, 80, infile);
346 sgi_gamma = atof(tmpline);
348 LUT_exponent = 1.0 / sgi_gamma;
350 #elif defined(Macintosh)
351 LUT_exponent = 1.8 / 2.61;
353 if (some_mac_function_that_returns_gamma(&mac_gamma))
354 LUT_exponent = mac_gamma / 2.61;
357 LUT_exponent = 1.0; /* assume no LUT: most PCs */
360 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
361 default_display_exponent = LUT_exponent * CRT_exponent;
364 /* If the user has set the SCREEN_GAMMA environment variable as suggested
365 * (somewhat imprecisely) in the libpng documentation, use that; otherwise
366 * use the default value we just calculated. Either way, the user may
367 * override this via a command-line option. */
369 if ((p = getenv("SCREEN_GAMMA")) != NULL)
370 rpng2_info.display_exponent = atof(p);
372 rpng2_info.display_exponent = default_display_exponent;
375 /* Now parse the command line for options and the PNG filename. */
377 while (*++argv && !error) {
378 if (!strncmp(*argv, "-display", 2)) {
383 } else if (!strncmp(*argv, "-gamma", 2)) {
387 rpng2_info.display_exponent = atof(*argv);
388 if (rpng2_info.display_exponent <= 0.0)
391 } else if (!strncmp(*argv, "-bgcolor", 4)) {
396 if (strlen(bgstr) != 7 || bgstr[0] != '#')
403 } else if (!strncmp(*argv, "-bgpat", 4)) {
408 if (pat >= 0 && pat < num_bgpat) {
414 } else if (!strncmp(*argv, "-usleep", 2)) {
418 usleep_duration = (ulg)atol(*argv);
421 } else if (!strncmp(*argv, "-pause", 2)) {
422 pause_after_pass = TRUE;
423 } else if (!strncmp(*argv, "-timing", 2)) {
426 } else if (!strncmp(*argv, "-loop", 2)) {
428 if (!argv[1] || !is_number(argv[1]))
432 loop_interval = atol(*argv);
433 if (loop_interval < 0)
435 else if (loop_interval > 100000) /* bit more than one day */
436 loop_interval = 100000;
442 if (argv[1]) /* shouldn't be any more args after filename */
445 ++error; /* not expecting any other options */
453 /* print usage screen if any errors up to this point */
456 fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname);
457 readpng2_version_info();
459 "Usage: %s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
461 " %*s [-usleep dur | -timing] [-pause] [-loop [sec]] file.png\n\n"
463 " %*s [-usleep dur | -timing] [-pause] file.png\n\n"
465 " xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
466 " exp \ttransfer-function exponent (``gamma'') of the display\n"
467 "\t\t system in floating-point format (e.g., ``%.1f''); equal\n"
468 "\t\t to the product of the lookup-table exponent (varies)\n"
469 "\t\t and the CRT exponent (usually 2.2); must be positive\n"
470 " bg \tdesired background color in 7-character hex RGB format\n"
471 "\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n"
472 "\t\t used with transparent images; overrides -bgpat\n"
473 " pat \tdesired background pattern number (0-%d); used with\n"
474 "\t\t transparent images; overrides -bgcolor\n"
476 " -loop\tloops through background images after initial display\n"
477 "\t\t is complete (depends on -bgpat)\n"
478 " sec \tseconds to display each background image (default = 2)\n"
480 " dur \tduration in microseconds to wait after displaying each\n"
481 "\t\t row (for demo purposes)\n"
482 " -timing\tenables delay for every block read, to simulate modem\n"
483 "\t\t download of image (~36 Kbps)\n"
484 " -pause\tpauses after displaying each pass until mouse clicked\n"
485 "\nPress Q, Esc or mouse button 1 (within image window, after image\n"
486 "is displayed) to quit.\n"
488 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
489 (int)strlen(PROGNAME), " ",
491 (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat-1);
496 if (!(infile = fopen(filename, "rb"))) {
497 fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename);
500 incount = fread(inbuf, 1, INBUFSIZE, infile);
501 if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
502 fprintf(stderr, PROGNAME
503 ": [%s] is not a PNG file: incorrect signature\n",
506 } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
509 fprintf(stderr, PROGNAME
510 ": [%s] has bad IHDR (libpng longjmp)\n", filename);
513 fprintf(stderr, PROGNAME ": insufficient memory\n");
516 fprintf(stderr, PROGNAME
517 ": unknown readpng2_init() error\n");
522 Trace((stderr, "about to call XOpenDisplay()\n"))
523 display = XOpenDisplay(displayname);
525 readpng2_cleanup(&rpng2_info);
526 fprintf(stderr, PROGNAME ": can't open X display [%s]\n",
527 displayname? displayname : "default");
537 fprintf(stderr, PROGNAME ": aborting.\n");
542 /* set the title-bar string, but make sure buffer doesn't overflow */
544 alen = strlen(appname);
545 flen = strlen(filename);
546 if (alen + flen + 3 > 1023)
547 sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023));
549 sprintf(titlebar, "%s: %s", appname, filename);
552 /* set some final rpng2_info variables before entering main data loop */
555 unsigned r, g, b; /* this approach quiets compiler warnings */
557 sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
558 rpng2_info.bg_red = (uch)r;
559 rpng2_info.bg_green = (uch)g;
560 rpng2_info.bg_blue = (uch)b;
562 rpng2_info.need_bgcolor = TRUE;
564 rpng2_info.state = kPreInit;
565 rpng2_info.mainprog_init = rpng2_x_init;
566 rpng2_info.mainprog_display_row = rpng2_x_display_row;
567 rpng2_info.mainprog_finish_display = rpng2_x_finish_display;
570 /* OK, this is the fun part: call readpng2_decode_data() at the start of
571 * the loop to deal with our first buffer of data (read in above to verify
572 * that the file is a PNG image), then loop through the file and continue
573 * calling the same routine to handle each chunk of data. It in turn
574 * passes the data to libpng, which will invoke one or more of our call-
575 * backs as decoded data become available. We optionally call sleep() for
576 * one second per iteration to simulate downloading the image via an analog
580 Trace((stderr, "about to call readpng2_decode_data()\n"))
581 if (readpng2_decode_data(&rpng2_info, inbuf, incount))
583 Trace((stderr, "done with readpng2_decode_data()\n"))
585 if (error || incount != INBUFSIZE || rpng2_info.state == kDone) {
586 if (rpng2_info.state == kDone) {
587 Trace((stderr, "done decoding PNG image\n"))
588 } else if (ferror(infile)) {
589 fprintf(stderr, PROGNAME
590 ": error while reading PNG image file\n");
592 } else if (feof(infile)) {
593 fprintf(stderr, PROGNAME ": end of file reached "
594 "(unexpectedly) while reading PNG image file\n");
596 } else /* if (error) */ {
597 // will print error message below
605 incount = fread(inbuf, 1, INBUFSIZE, infile);
609 /* clean up PNG stuff and report any decoding errors */
612 Trace((stderr, "about to call readpng2_cleanup()\n"))
613 readpng2_cleanup(&rpng2_info);
616 fprintf(stderr, PROGNAME ": libpng error while decoding PNG image\n");
623 if (loop && bg_image) {
624 Trace((stderr, "entering -loop loop (FEATURE_LOOP)\n"))
627 struct timeval now, then;
629 /* get current time and add loop_interval to get target time */
630 if (gettimeofday(&then, NULL) == 0) {
631 then.tv_sec += loop_interval;
636 /* do quick check for a quit event but don't wait for it */
637 /* GRR BUG: should also check for Expose events and redraw... */
638 if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask, &e))
642 /* generate next background image */
643 if (++pat >= num_bgpat)
645 rpng2_x_reload_bg_image();
647 /* wait for timeout, using whatever means are available */
648 if (use_sleep || gettimeofday(&now, NULL) != 0) {
649 for (i = loop_interval; i > 0; --i) {
651 /* GRR BUG: also need to check for Expose (and redraw!) */
652 if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask,
658 if (now.tv_sec < then.tv_sec ||
659 (now.tv_sec == then.tv_sec && now.tv_usec < then.tv_usec))
662 long seconds_to_go = then.tv_sec - now.tv_sec;
665 /* basically chew up most of remaining loop-interval with
666 * calls to sleep(1) interleaved with checks for quit
667 * events, but also recalc time-to-go periodically; when
668 * done, clean up any remaining time with usleep() call
669 * (could also use SIGALRM, but signals are a pain...) */
670 while (seconds_to_go-- > 1) {
671 int seconds_done = 0;
673 for (i = seconds_to_go; i > 0 && !quit; --i) {
675 /* GRR BUG: need to check for Expose and redraw */
676 if (XCheckMaskEvent(display, KeyPressMask |
677 ButtonPressMask, &e) && QUIT(e,k))
679 if (++seconds_done > 1000)
680 break; /* time to redo seconds_to_go meas. */
685 /* OK, more than 1000 seconds since last check:
686 * correct the time-to-go measurement for drift */
687 if (gettimeofday(&now, NULL) == 0) {
688 if (now.tv_sec >= then.tv_sec)
690 seconds_to_go = then.tv_sec - now.tv_sec;
692 ++seconds_to_go; /* restore what we subtracted */
695 break; /* breaks outer do-loop, skips redisplay */
697 /* since difference between "now" and "then" is already
698 * eaten up to within a couple of seconds, don't need to
699 * worry about overflow--but might have overshot (neg.) */
700 if (gettimeofday(&now, NULL) == 0) {
701 usleep_usec = 1000000L*(then.tv_sec - now.tv_sec) +
702 then.tv_usec - now.tv_usec;
704 usleep((ulg)usleep_usec);
709 /* composite image against new background and display (note that
710 * we do not take into account the time spent doing this...) */
711 rpng2_x_redisplay_image (0, 0, rpng2_info.width, rpng2_info.height);
714 } else /* FALL THROUGH and do the normal thing */
716 #endif /* FEATURE_LOOP */
718 /* wait for the user to tell us when to quit */
720 if (rpng2_info.state >= kWindowInit) {
721 Trace((stderr, "entering final wait-for-quit-event loop\n"))
723 XNextEvent(display, &e);
724 if (e.type == Expose) {
725 XExposeEvent *ex = (XExposeEvent *)&e;
726 rpng2_x_redisplay_image (ex->x, ex->y, ex->width, ex->height);
728 } while (!QUIT(e,k));
730 fprintf(stderr, PROGNAME ": init callback never called: probable "
731 "libpng error while decoding PNG metadata\n");
736 /* we're done: clean up all image and X resources and go away */
738 Trace((stderr, "about to call rpng2_x_cleanup()\n"))
748 /* this function is called by readpng2_info_callback() in readpng2.c, which
749 * in turn is called by libpng after all of the pre-IDAT chunks have been
750 * read and processed--i.e., we now have enough info to finish initializing */
752 static void rpng2_x_init(void)
755 ulg rowbytes = rpng2_info.rowbytes;
757 Trace((stderr, "beginning rpng2_x_init()\n"))
758 Trace((stderr, " rowbytes = %d\n", rpng2_info.rowbytes))
759 Trace((stderr, " width = %ld\n", rpng2_info.width))
760 Trace((stderr, " height = %ld\n", rpng2_info.height))
762 rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
763 if (!rpng2_info.image_data) {
764 readpng2_cleanup(&rpng2_info);
768 rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
769 if (!rpng2_info.row_pointers) {
770 free(rpng2_info.image_data);
771 rpng2_info.image_data = NULL;
772 readpng2_cleanup(&rpng2_info);
776 for (i = 0; i < rpng2_info.height; ++i)
777 rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
780 /* do the basic X initialization stuff, make the window, and fill it with
781 * the user-specified, file-specified or default background color or
784 if (rpng2_x_create_window()) {
786 /* GRR TEMPORARY HACK: this is fundamentally no different from cases
787 * above; libpng should longjmp() back to us when png_ptr goes away.
788 * If we/it segfault instead, seems like a libpng bug... */
790 /* we're here via libpng callback, so if window fails, clean and bail */
791 readpng2_cleanup(&rpng2_info);
796 rpng2_info.state = kWindowInit;
803 static int rpng2_x_create_window(void)
805 ulg bg_red = rpng2_info.bg_red;
806 ulg bg_green = rpng2_info.bg_green;
807 ulg bg_blue = rpng2_info.bg_blue;
810 int need_colormap = FALSE;
816 XSetWindowAttributes attr;
817 XTextProperty windowName, *pWindowName = &windowName;
818 XTextProperty iconName, *pIconName = &iconName;
819 XVisualInfo visual_info;
820 XSizeHints *size_hints;
822 XClassHint *class_hints;
825 Trace((stderr, "beginning rpng2_x_create_window()\n"))
827 screen = DefaultScreen(display);
828 depth = DisplayPlanes(display, screen);
829 root = RootWindow(display, screen);
832 XSynchronize(display, True);
835 if (depth != 16 && depth != 24 && depth != 32) {
836 int visuals_matched = 0;
838 Trace((stderr, "default depth is %d: checking other visuals\n",
842 visual_info.screen = screen;
843 visual_info.depth = 24;
844 visual_list = XGetVisualInfo(display,
845 VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
846 if (visuals_matched == 0) {
847 /* GRR: add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
848 fprintf(stderr, "default screen depth %d not supported, and no"
849 " 24-bit visuals found\n", depth);
852 Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
854 visual = visual_list[0].visual;
855 depth = visual_list[0].depth;
857 colormap_size = visual_list[0].colormap_size;
858 visual_class = visual->class;
859 visualID = XVisualIDFromVisual(visual);
861 have_nondefault_visual = TRUE;
862 need_colormap = TRUE;
864 XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
865 visual = visual_info.visual;
868 RMask = visual->red_mask;
869 GMask = visual->green_mask;
870 BMask = visual->blue_mask;
872 /* GRR: add/check 8-bit support */
873 if (depth == 8 || need_colormap) {
874 colormap = XCreateColormap(display, root, visual, AllocNone);
876 fprintf(stderr, "XCreateColormap() failed\n");
879 have_colormap = TRUE;
881 bg_image = FALSE; /* gradient just wastes palette entries */
883 if (depth == 15 || depth == 16) {
884 RShift = 15 - rpng2_x_msb(RMask); /* these are right-shifts */
885 GShift = 15 - rpng2_x_msb(GMask);
886 BShift = 15 - rpng2_x_msb(BMask);
887 } else if (depth > 16) {
888 RShift = rpng2_x_msb(RMask) - 7; /* these are left-shifts */
889 GShift = rpng2_x_msb(GMask) - 7;
890 BShift = rpng2_x_msb(BMask) - 7;
892 if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
893 fprintf(stderr, "rpng2 internal logic error: negative X shift(s)!\n");
897 /*---------------------------------------------------------------------------
898 Finally, create the window.
899 ---------------------------------------------------------------------------*/
901 attr.backing_store = Always;
902 attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
903 attrmask = CWBackingStore | CWEventMask;
904 if (have_nondefault_visual) {
905 attr.colormap = colormap;
906 attr.background_pixel = 0;
907 attr.border_pixel = 1;
908 attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
911 window = XCreateWindow(display, root, 0, 0, rpng2_info.width,
912 rpng2_info.height, 0, depth, InputOutput, visual, attrmask, &attr);
914 if (window == None) {
915 fprintf(stderr, "XCreateWindow() failed\n");
921 XSetWindowColormap(display, window, colormap);
923 if (!XStringListToTextProperty(&window_name, 1, pWindowName))
925 if (!XStringListToTextProperty(&icon_name, 1, pIconName))
928 /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */
930 if ((size_hints = XAllocSizeHints()) != NULL) {
931 /* window will not be resizable */
932 size_hints->flags = PMinSize | PMaxSize;
933 size_hints->min_width = size_hints->max_width = (int)rpng2_info.width;
934 size_hints->min_height = size_hints->max_height =
935 (int)rpng2_info.height;
938 if ((wm_hints = XAllocWMHints()) != NULL) {
939 wm_hints->initial_state = NormalState;
940 wm_hints->input = True;
941 /* wm_hints->icon_pixmap = icon_pixmap; */
942 wm_hints->flags = StateHint | InputHint /* | IconPixmapHint */ ;
945 if ((class_hints = XAllocClassHint()) != NULL) {
946 class_hints->res_name = res_name;
947 class_hints->res_class = res_class;
950 XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
951 size_hints, wm_hints, class_hints);
953 /* various properties and hints no longer needed; free memory */
955 XFree(pWindowName->value);
957 XFree(pIconName->value);
965 XMapWindow(display, window);
967 gc = XCreateGC(display, window, 0, &gcvalues);
970 /*---------------------------------------------------------------------------
971 Allocate memory for the X- and display-specific version of the image.
972 ---------------------------------------------------------------------------*/
974 if (depth == 24 || depth == 32) {
975 xdata = (uch *)malloc(4*rpng2_info.width*rpng2_info.height);
977 } else if (depth == 16) {
978 xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height);
980 } else /* depth == 8 */ {
981 xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height);
986 fprintf(stderr, PROGNAME ": unable to allocate image memory\n");
990 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
991 (char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0);
994 fprintf(stderr, PROGNAME ": XCreateImage() failed\n");
999 /* to avoid testing the byte order every pixel (or doubling the size of
1000 * the drawing routine with a giant if-test), we arbitrarily set the byte
1001 * order to MSBFirst and let Xlib worry about inverting things on little-
1002 * endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the
1003 * most efficient approach (the giant if-test would be better), but in
1004 * the interest of clarity, we'll take the easy way out... */
1006 ximage->byte_order = MSBFirst;
1008 /*---------------------------------------------------------------------------
1009 Fill window with the specified background color (default is black) or
1010 faked "background image" (but latter is disabled if 8-bit; gradients
1011 just waste palette entries).
1012 ---------------------------------------------------------------------------*/
1015 rpng2_x_load_bg_image(); /* resets bg_image if fails */
1018 if (depth == 24 || depth == 32) {
1019 bg_pixel = (bg_red << RShift) |
1020 (bg_green << GShift) |
1021 (bg_blue << BShift);
1022 } else if (depth == 16) {
1023 bg_pixel = (((bg_red << 8) >> RShift) & RMask) |
1024 (((bg_green << 8) >> GShift) & GMask) |
1025 (((bg_blue << 8) >> BShift) & BMask);
1026 } else /* depth == 8 */ {
1028 /* GRR: add 8-bit support */
1031 XSetForeground(display, gc, bg_pixel);
1032 XFillRectangle(display, window, gc, 0, 0, rpng2_info.width,
1036 /*---------------------------------------------------------------------------
1037 Wait for first Expose event to do any drawing, then flush and return.
1038 ---------------------------------------------------------------------------*/
1041 XNextEvent(display, &e);
1042 while (e.type != Expose || e.xexpose.count);
1048 } /* end function rpng2_x_create_window() */
1054 static int rpng2_x_load_bg_image(void)
1058 uch r1, r2, g1, g2, b1, b2;
1059 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
1061 int xidx, yidx, yidx_max;
1062 int even_odd_vert, even_odd_horiz, even_odd;
1063 int invert_gradient2 = (bg[pat].type & 0x08);
1065 int ximage_rowbytes = ximage->bytes_per_line;
1069 /*---------------------------------------------------------------------------
1070 Allocate buffer for fake background image to be used with transparent
1071 images; if this fails, revert to plain background color.
1072 ---------------------------------------------------------------------------*/
1074 bg_rowbytes = 3 * rpng2_info.width;
1075 bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
1077 fprintf(stderr, PROGNAME
1078 ": unable to allocate memory for background image\n");
1083 bgscale = (pat == 0)? 8 : bgscale_default;
1084 yidx_max = bgscale - 1;
1086 /*---------------------------------------------------------------------------
1087 Vertical gradients (ramps) in NxN squares, alternating direction and
1088 colors (N == bgscale).
1089 ---------------------------------------------------------------------------*/
1091 if ((bg[pat].type & 0x07) == 0) {
1092 uch r1_min = rgb[bg[pat].rgb1_min].r;
1093 uch g1_min = rgb[bg[pat].rgb1_min].g;
1094 uch b1_min = rgb[bg[pat].rgb1_min].b;
1095 uch r2_min = rgb[bg[pat].rgb2_min].r;
1096 uch g2_min = rgb[bg[pat].rgb2_min].g;
1097 uch b2_min = rgb[bg[pat].rgb2_min].b;
1098 int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
1099 int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
1100 int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
1101 int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
1102 int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
1103 int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
1105 for (row = 0; row < rpng2_info.height; ++row) {
1106 yidx = (int)(row % bgscale);
1107 even_odd_vert = (int)((row / bgscale) & 1);
1109 r1 = r1_min + (r1_diff * yidx) / yidx_max;
1110 g1 = g1_min + (g1_diff * yidx) / yidx_max;
1111 b1 = b1_min + (b1_diff * yidx) / yidx_max;
1112 r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
1113 g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
1114 b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
1116 r2 = r2_min + (r2_diff * yidx) / yidx_max;
1117 g2 = g2_min + (g2_diff * yidx) / yidx_max;
1118 b2 = b2_min + (b2_diff * yidx) / yidx_max;
1119 r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
1120 g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
1121 b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
1123 dest = (char *)bg_data + row*bg_rowbytes;
1124 for (i = 0; i < rpng2_info.width; ++i) {
1125 even_odd_horiz = (int)((i / bgscale) & 1);
1126 even_odd = even_odd_vert ^ even_odd_horiz;
1128 (even_odd_horiz && (bg[pat].type & 0x10));
1129 if (even_odd == 0) { /* gradient #1 */
1130 if (invert_column) {
1139 } else { /* gradient #2 */
1140 if ((invert_column && invert_gradient2) ||
1141 (!invert_column && !invert_gradient2))
1143 *dest++ = r2; /* not inverted or */
1144 *dest++ = g2; /* doubly inverted */
1148 *dest++ = g2_inv; /* singly inverted */
1155 /*---------------------------------------------------------------------------
1156 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
1158 ---------------------------------------------------------------------------*/
1160 } else if ((bg[pat].type & 0x07) == 1) {
1162 hmax = (bgscale-1)/2; /* half the max weight of a color */
1163 max = 2*hmax; /* the max weight of a color */
1165 r1 = rgb[bg[pat].rgb1_max].r;
1166 g1 = rgb[bg[pat].rgb1_max].g;
1167 b1 = rgb[bg[pat].rgb1_max].b;
1168 r2 = rgb[bg[pat].rgb2_max].r;
1169 g2 = rgb[bg[pat].rgb2_max].g;
1170 b2 = rgb[bg[pat].rgb2_max].b;
1172 for (row = 0; row < rpng2_info.height; ++row) {
1173 yidx = (int)(row % bgscale);
1175 yidx = bgscale-1 - yidx;
1176 dest = (char *)bg_data + row*bg_rowbytes;
1177 for (i = 0; i < rpng2_info.width; ++i) {
1178 xidx = (int)(i % bgscale);
1180 xidx = bgscale-1 - xidx;
1182 *dest++ = (k*r1 + (max-k)*r2) / max;
1183 *dest++ = (k*g1 + (max-k)*g2) / max;
1184 *dest++ = (k*b1 + (max-k)*b2) / max;
1188 /*---------------------------------------------------------------------------
1189 Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
1190 soids will equal bgscale?]. This one is slow but very cool. Code con-
1191 tributed by Pieter S. van der Meulen (originally in Smalltalk).
1192 ---------------------------------------------------------------------------*/
1194 } else if ((bg[pat].type & 0x07) == 2) {
1196 int ii, x, y, hw, hh, grayspot;
1197 double freq, rotate, saturate, gray, intensity;
1198 double angle=0.0, aoffset=0.0, maxDist, dist;
1199 double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
1201 fprintf(stderr, "%s: computing radial background...",
1205 hh = (int)(rpng2_info.height / 2);
1206 hw = (int)(rpng2_info.width / 2);
1208 /* variables for radial waves:
1209 * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED]
1210 * freq: number of color beams originating from the center
1211 * grayspot: size of the graying center area (anti-alias)
1212 * rotate: rotation of the beams as a function of radius
1213 * saturate: saturation of beams' shape azimuthally
1215 angle = CLIP(angle, 0.0, 360.0);
1216 grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
1217 freq = MAX((double)bg[pat].bg_freq, 0.0);
1218 saturate = (double)bg[pat].bg_bsat * 0.1;
1219 rotate = (double)bg[pat].bg_brot * 0.1;
1222 maxDist = (double)((hw*hw) + (hh*hh));
1224 for (row = 0; row < rpng2_info.height; ++row) {
1225 y = (int)(row - hh);
1226 dest = (char *)bg_data + row*bg_rowbytes;
1227 for (i = 0; i < rpng2_info.width; ++i) {
1229 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
1230 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
1231 gray = MIN(1.0, gray);
1232 dist = (double)((x*x) + (y*y)) / maxDist;
1233 intensity = cos((angle+(rotate*dist*PI)) * freq) *
1235 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
1236 hue = (angle + PI) * INV_PI_360 + aoffset;
1237 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
1238 s = MIN(MAX(s,0.0), 1.0);
1239 v = MIN(MAX(intensity,0.0), 1.0);
1242 ch = (uch)(v * 255.0);
1247 if ((hue < 0.0) || (hue >= 360.0))
1248 hue -= (((int)(hue / 360.0)) * 360.0);
1251 f = hue - (double)ii;
1253 q = (1.0 - (s * f)) * v;
1254 t = (1.0 - (s * (1.0 - f))) * v;
1255 if (ii == 0) { red = v; green = t; blue = p; }
1256 else if (ii == 1) { red = q; green = v; blue = p; }
1257 else if (ii == 2) { red = p; green = v; blue = t; }
1258 else if (ii == 3) { red = p; green = q; blue = v; }
1259 else if (ii == 4) { red = t; green = p; blue = v; }
1260 else if (ii == 5) { red = v; green = p; blue = q; }
1261 *dest++ = (uch)(red * 255.0);
1262 *dest++ = (uch)(green * 255.0);
1263 *dest++ = (uch)(blue * 255.0);
1267 fprintf(stderr, "done.\n");
1271 /*---------------------------------------------------------------------------
1272 Blast background image to display buffer before beginning PNG decode.
1273 ---------------------------------------------------------------------------*/
1275 if (depth == 24 || depth == 32) {
1276 ulg red, green, blue;
1277 int bpp = ximage->bits_per_pixel;
1279 for (row = 0; row < rpng2_info.height; ++row) {
1280 src = bg_data + row*bg_rowbytes;
1281 dest = ximage->data + row*ximage_rowbytes;
1282 if (bpp == 32) { /* slightly optimized version */
1283 for (i = rpng2_info.width; i > 0; --i) {
1287 pixel = (red << RShift) |
1290 /* recall that we set ximage->byte_order = MSBFirst above */
1291 *dest++ = (char)((pixel >> 24) & 0xff);
1292 *dest++ = (char)((pixel >> 16) & 0xff);
1293 *dest++ = (char)((pixel >> 8) & 0xff);
1294 *dest++ = (char)( pixel & 0xff);
1297 for (i = rpng2_info.width; i > 0; --i) {
1301 pixel = (red << RShift) |
1304 /* recall that we set ximage->byte_order = MSBFirst above */
1305 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1306 /* (probably need to use RShift, RMask, etc.) */
1307 *dest++ = (char)((pixel >> 16) & 0xff);
1308 *dest++ = (char)((pixel >> 8) & 0xff);
1309 *dest++ = (char)( pixel & 0xff);
1314 } else if (depth == 16) {
1315 ush red, green, blue;
1317 for (row = 0; row < rpng2_info.height; ++row) {
1318 src = bg_data + row*bg_rowbytes;
1319 dest = ximage->data + row*ximage_rowbytes;
1320 for (i = rpng2_info.width; i > 0; --i) {
1321 red = ((ush)(*src) << 8); ++src;
1322 green = ((ush)(*src) << 8); ++src;
1323 blue = ((ush)(*src) << 8); ++src;
1324 pixel = ((red >> RShift) & RMask) |
1325 ((green >> GShift) & GMask) |
1326 ((blue >> BShift) & BMask);
1327 /* recall that we set ximage->byte_order = MSBFirst above */
1328 *dest++ = (char)((pixel >> 8) & 0xff);
1329 *dest++ = (char)( pixel & 0xff);
1333 } else /* depth == 8 */ {
1335 /* GRR: add 8-bit support */
1339 XPutImage(display, window, gc, ximage, 0, 0, 0, 0, rpng2_info.width,
1344 } /* end function rpng2_x_load_bg_image() */
1350 static void rpng2_x_display_row(ulg row)
1352 uch bg_red = rpng2_info.bg_red;
1353 uch bg_green = rpng2_info.bg_green;
1354 uch bg_blue = rpng2_info.bg_blue;
1355 uch *src, *src2=NULL;
1358 int ximage_rowbytes = ximage->bytes_per_line;
1360 static int rows=0, prevpass=(-1);
1361 static ulg firstrow;
1363 /*---------------------------------------------------------------------------
1364 rows and firstrow simply track how many rows (and which ones) have not
1365 yet been displayed; alternatively, we could call XPutImage() for every
1366 row and not bother with the records-keeping.
1367 ---------------------------------------------------------------------------*/
1369 Trace((stderr, "beginning rpng2_x_display_row()\n"))
1371 if (rpng2_info.pass != prevpass) {
1372 if (pause_after_pass && rpng2_info.pass > 0) {
1377 "%s: end of pass %d of 7; click in image window to continue\n",
1378 PROGNAME, prevpass + 1);
1380 XNextEvent(display, &e);
1383 fprintf(stderr, "%s: pass %d of 7\r", PROGNAME, rpng2_info.pass + 1);
1385 prevpass = rpng2_info.pass;
1389 firstrow = row; /* first row that is not yet displayed */
1391 ++rows; /* count of rows received but not yet displayed */
1393 /*---------------------------------------------------------------------------
1394 Aside from the use of the rpng2_info struct, the lack of an outer loop
1395 (over rows) and moving the XPutImage() call outside the "if (depth)"
1396 tests, this routine is identical to rpng_x_display_image() in the non-
1397 progressive version of the program.
1398 ---------------------------------------------------------------------------*/
1400 if (depth == 24 || depth == 32) {
1401 ulg red, green, blue;
1402 int bpp = ximage->bits_per_pixel;
1404 src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1406 src2 = bg_data + row*bg_rowbytes;
1407 dest = ximage->data + row*ximage_rowbytes;
1408 if (rpng2_info.channels == 3) {
1409 for (i = rpng2_info.width; i > 0; --i) {
1413 pixel = (red << RShift) |
1416 /* recall that we set ximage->byte_order = MSBFirst above */
1418 *dest++ = (char)((pixel >> 24) & 0xff);
1419 *dest++ = (char)((pixel >> 16) & 0xff);
1420 *dest++ = (char)((pixel >> 8) & 0xff);
1421 *dest++ = (char)( pixel & 0xff);
1423 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1424 /* (probably need to use RShift, RMask, etc.) */
1425 *dest++ = (char)((pixel >> 16) & 0xff);
1426 *dest++ = (char)((pixel >> 8) & 0xff);
1427 *dest++ = (char)( pixel & 0xff);
1430 } else /* if (rpng2_info.channels == 4) */ {
1431 for (i = rpng2_info.width; i > 0; --i) {
1445 } else if (a == 0) {
1450 /* this macro (from png.h) composites the foreground
1451 * and background values and puts the result into the
1453 alpha_composite(red, r, a, bg_red);
1454 alpha_composite(green, g, a, bg_green);
1455 alpha_composite(blue, b, a, bg_blue);
1457 pixel = (red << RShift) |
1460 /* recall that we set ximage->byte_order = MSBFirst above */
1462 *dest++ = (char)((pixel >> 24) & 0xff);
1463 *dest++ = (char)((pixel >> 16) & 0xff);
1464 *dest++ = (char)((pixel >> 8) & 0xff);
1465 *dest++ = (char)( pixel & 0xff);
1467 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1468 /* (probably need to use RShift, RMask, etc.) */
1469 *dest++ = (char)((pixel >> 16) & 0xff);
1470 *dest++ = (char)((pixel >> 8) & 0xff);
1471 *dest++ = (char)( pixel & 0xff);
1476 } else if (depth == 16) {
1477 ush red, green, blue;
1479 src = rpng2_info.row_pointers[row];
1481 src2 = bg_data + row*bg_rowbytes;
1482 dest = ximage->data + row*ximage_rowbytes;
1483 if (rpng2_info.channels == 3) {
1484 for (i = rpng2_info.width; i > 0; --i) {
1485 red = ((ush)(*src) << 8);
1487 green = ((ush)(*src) << 8);
1489 blue = ((ush)(*src) << 8);
1491 pixel = ((red >> RShift) & RMask) |
1492 ((green >> GShift) & GMask) |
1493 ((blue >> BShift) & BMask);
1494 /* recall that we set ximage->byte_order = MSBFirst above */
1495 *dest++ = (char)((pixel >> 8) & 0xff);
1496 *dest++ = (char)( pixel & 0xff);
1498 } else /* if (rpng2_info.channels == 4) */ {
1499 for (i = rpng2_info.width; i > 0; --i) {
1510 red = ((ush)r << 8);
1511 green = ((ush)g << 8);
1512 blue = ((ush)b << 8);
1513 } else if (a == 0) {
1514 red = ((ush)bg_red << 8);
1515 green = ((ush)bg_green << 8);
1516 blue = ((ush)bg_blue << 8);
1518 /* this macro (from png.h) composites the foreground
1519 * and background values and puts the result back into
1520 * the first argument (== fg byte here: safe) */
1521 alpha_composite(r, r, a, bg_red);
1522 alpha_composite(g, g, a, bg_green);
1523 alpha_composite(b, b, a, bg_blue);
1524 red = ((ush)r << 8);
1525 green = ((ush)g << 8);
1526 blue = ((ush)b << 8);
1528 pixel = ((red >> RShift) & RMask) |
1529 ((green >> GShift) & GMask) |
1530 ((blue >> BShift) & BMask);
1531 /* recall that we set ximage->byte_order = MSBFirst above */
1532 *dest++ = (char)((pixel >> 8) & 0xff);
1533 *dest++ = (char)( pixel & 0xff);
1537 } else /* depth == 8 */ {
1539 /* GRR: add 8-bit support */
1544 /*---------------------------------------------------------------------------
1545 Display after every 16 rows or when on one of last two rows. (Region
1546 may include previously displayed lines due to interlacing--i.e., not
1547 contiguous. Also, second-to-last row is final one in interlaced images
1548 with odd number of rows.) For demos, flush (and delay) after every 16th
1549 row so "sparse" passes don't go twice as fast.
1550 ---------------------------------------------------------------------------*/
1552 if (demo_timing && (row - firstrow >= 16 || row >= rpng2_info.height-2)) {
1553 XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1554 (int)firstrow, rpng2_info.width, row - firstrow + 1);
1557 usleep(usleep_duration);
1559 if (!demo_timing && ((rows & 0xf) == 0 || row >= rpng2_info.height-2)) {
1560 XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1561 (int)firstrow, rpng2_info.width, row - firstrow + 1);
1572 static void rpng2_x_finish_display(void)
1574 Trace((stderr, "beginning rpng2_x_finish_display()\n"))
1576 /* last row has already been displayed by rpng2_x_display_row(), so we
1577 * have nothing to do here except set a flag and let the user know that
1578 * the image is done */
1580 rpng2_info.state = kDone;
1582 "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
1590 static void rpng2_x_redisplay_image(ulg startcol, ulg startrow,
1591 ulg width, ulg height)
1593 uch bg_red = rpng2_info.bg_red;
1594 uch bg_green = rpng2_info.bg_green;
1595 uch bg_blue = rpng2_info.bg_blue;
1596 uch *src, *src2=NULL;
1599 ulg i, row, lastrow = 0;
1601 int ximage_rowbytes = ximage->bytes_per_line;
1604 Trace((stderr, "beginning display loop (image_channels == %d)\n",
1605 rpng2_info.channels))
1606 Trace((stderr, " (width = %ld, rowbytes = %d, ximage_rowbytes = %d)\n",
1607 rpng2_info.width, rpng2_info.rowbytes, ximage_rowbytes))
1608 Trace((stderr, " (bpp = %d)\n", ximage->bits_per_pixel))
1609 Trace((stderr, " (byte_order = %s)\n", ximage->byte_order == MSBFirst?
1610 "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown")))
1612 /*---------------------------------------------------------------------------
1613 Aside from the use of the rpng2_info struct and of src2 (for background
1614 image), this routine is identical to rpng_x_display_image() in the non-
1615 progressive version of the program--for the simple reason that redisplay
1616 of the image against a new background happens after the image is fully
1617 decoded and therefore is, by definition, non-progressive.
1618 ---------------------------------------------------------------------------*/
1620 if (depth == 24 || depth == 32) {
1621 ulg red, green, blue;
1622 int bpp = ximage->bits_per_pixel;
1624 for (lastrow = row = startrow; row < startrow+height; ++row) {
1625 src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1627 src2 = bg_data + row*bg_rowbytes;
1628 dest = ximage->data + row*ximage_rowbytes;
1629 if (rpng2_info.channels == 3) {
1630 for (i = rpng2_info.width; i > 0; --i) {
1634 #ifdef NO_24BIT_MASKS
1635 pixel = (red << RShift) |
1638 /* recall that we set ximage->byte_order = MSBFirst above */
1640 *dest++ = (char)((pixel >> 24) & 0xff);
1641 *dest++ = (char)((pixel >> 16) & 0xff);
1642 *dest++ = (char)((pixel >> 8) & 0xff);
1643 *dest++ = (char)( pixel & 0xff);
1645 /* this assumes bpp == 24 & bits are packed low */
1646 /* (probably need to use RShift, RMask, etc.) */
1647 *dest++ = (char)((pixel >> 16) & 0xff);
1648 *dest++ = (char)((pixel >> 8) & 0xff);
1649 *dest++ = (char)( pixel & 0xff);
1652 red = (RShift < 0)? red << (-RShift) : red >> RShift;
1653 green = (GShift < 0)? green << (-GShift) : green >> GShift;
1654 blue = (BShift < 0)? blue << (-BShift) : blue >> BShift;
1655 pixel = (red & RMask) | (green & GMask) | (blue & BMask);
1656 /* recall that we set ximage->byte_order = MSBFirst above */
1658 *dest++ = (char)((pixel >> 24) & 0xff);
1659 *dest++ = (char)((pixel >> 16) & 0xff);
1660 *dest++ = (char)((pixel >> 8) & 0xff);
1661 *dest++ = (char)( pixel & 0xff);
1664 /* this assumes bpp == 24 & bits are packed low */
1665 /* (probably need to use RShift/RMask/etc. here, too) */
1666 *dest++ = (char)((pixel >> 16) & 0xff);
1667 *dest++ = (char)((pixel >> 8) & 0xff);
1668 *dest++ = (char)( pixel & 0xff);
1673 } else /* if (rpng2_info.channels == 4) */ {
1674 for (i = rpng2_info.width; i > 0; --i) {
1688 } else if (a == 0) {
1693 /* this macro (from png.h) composites the foreground
1694 * and background values and puts the result into the
1696 alpha_composite(red, r, a, bg_red);
1697 alpha_composite(green, g, a, bg_green);
1698 alpha_composite(blue, b, a, bg_blue);
1700 #ifdef NO_24BIT_MASKS
1701 pixel = (red << RShift) |
1704 /* recall that we set ximage->byte_order = MSBFirst above */
1706 *dest++ = (char)((pixel >> 24) & 0xff);
1707 *dest++ = (char)((pixel >> 16) & 0xff);
1708 *dest++ = (char)((pixel >> 8) & 0xff);
1709 *dest++ = (char)( pixel & 0xff);
1711 /* this assumes bpp == 24 & bits are packed low */
1712 /* (probably need to use RShift, RMask, etc.) */
1713 *dest++ = (char)((pixel >> 16) & 0xff);
1714 *dest++ = (char)((pixel >> 8) & 0xff);
1715 *dest++ = (char)( pixel & 0xff);
1718 red = (RShift < 0)? red << (-RShift) : red >> RShift;
1719 green = (GShift < 0)? green << (-GShift) : green >> GShift;
1720 blue = (BShift < 0)? blue << (-BShift) : blue >> BShift;
1721 pixel = (red & RMask) | (green & GMask) | (blue & BMask);
1722 /* recall that we set ximage->byte_order = MSBFirst above */
1724 *dest++ = (char)((pixel >> 24) & 0xff);
1725 *dest++ = (char)((pixel >> 16) & 0xff);
1726 *dest++ = (char)((pixel >> 8) & 0xff);
1727 *dest++ = (char)( pixel & 0xff);
1730 /* this assumes bpp == 24 & bits are packed low */
1731 /* (probably need to use RShift/RMask/etc. here, too) */
1732 *dest++ = (char)((pixel >> 16) & 0xff);
1733 *dest++ = (char)((pixel >> 8) & 0xff);
1734 *dest++ = (char)( pixel & 0xff);
1739 /* display after every 16 lines */
1740 if (((row+1) & 0xf) == 0) {
1741 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1742 (int)lastrow, rpng2_info.width, 16);
1748 } else if (depth == 16) {
1749 ush red, green, blue;
1751 for (lastrow = row = startrow; row < startrow+height; ++row) {
1752 src = rpng2_info.row_pointers[row];
1754 src2 = bg_data + row*bg_rowbytes;
1755 dest = ximage->data + row*ximage_rowbytes;
1756 if (rpng2_info.channels == 3) {
1757 for (i = rpng2_info.width; i > 0; --i) {
1758 red = ((ush)(*src) << 8);
1760 green = ((ush)(*src) << 8);
1762 blue = ((ush)(*src) << 8);
1764 pixel = ((red >> RShift) & RMask) |
1765 ((green >> GShift) & GMask) |
1766 ((blue >> BShift) & BMask);
1767 /* recall that we set ximage->byte_order = MSBFirst above */
1768 *dest++ = (char)((pixel >> 8) & 0xff);
1769 *dest++ = (char)( pixel & 0xff);
1771 } else /* if (rpng2_info.channels == 4) */ {
1772 for (i = rpng2_info.width; i > 0; --i) {
1783 red = ((ush)r << 8);
1784 green = ((ush)g << 8);
1785 blue = ((ush)b << 8);
1786 } else if (a == 0) {
1787 red = ((ush)bg_red << 8);
1788 green = ((ush)bg_green << 8);
1789 blue = ((ush)bg_blue << 8);
1791 /* this macro (from png.h) composites the foreground
1792 * and background values and puts the result back into
1793 * the first argument (== fg byte here: safe) */
1794 alpha_composite(r, r, a, bg_red);
1795 alpha_composite(g, g, a, bg_green);
1796 alpha_composite(b, b, a, bg_blue);
1797 red = ((ush)r << 8);
1798 green = ((ush)g << 8);
1799 blue = ((ush)b << 8);
1801 pixel = ((red >> RShift) & RMask) |
1802 ((green >> GShift) & GMask) |
1803 ((blue >> BShift) & BMask);
1804 /* recall that we set ximage->byte_order = MSBFirst above */
1805 *dest++ = (char)((pixel >> 8) & 0xff);
1806 *dest++ = (char)( pixel & 0xff);
1809 /* display after every 16 lines */
1810 if (((row+1) & 0xf) == 0) {
1811 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1812 (int)lastrow, rpng2_info.width, 16);
1818 } else /* depth == 8 */ {
1820 /* GRR: add 8-bit support */
1824 Trace((stderr, "calling final XPutImage()\n"))
1825 if (lastrow < startrow+height) {
1826 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1827 (int)lastrow, rpng2_info.width, rpng2_info.height-lastrow);
1831 } /* end function rpng2_x_redisplay_image() */
1839 static void rpng2_x_reload_bg_image(void)
1842 uch r1, r2, g1, g2, b1, b2;
1843 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
1845 int xidx, yidx, yidx_max;
1846 int even_odd_vert, even_odd_horiz, even_odd;
1847 int invert_gradient2 = (bg[pat].type & 0x08);
1852 bgscale = (pat == 0)? 8 : bgscale_default;
1853 yidx_max = bgscale - 1;
1855 /*---------------------------------------------------------------------------
1856 Vertical gradients (ramps) in NxN squares, alternating direction and
1857 colors (N == bgscale).
1858 ---------------------------------------------------------------------------*/
1860 if ((bg[pat].type & 0x07) == 0) {
1861 uch r1_min = rgb[bg[pat].rgb1_min].r;
1862 uch g1_min = rgb[bg[pat].rgb1_min].g;
1863 uch b1_min = rgb[bg[pat].rgb1_min].b;
1864 uch r2_min = rgb[bg[pat].rgb2_min].r;
1865 uch g2_min = rgb[bg[pat].rgb2_min].g;
1866 uch b2_min = rgb[bg[pat].rgb2_min].b;
1867 int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
1868 int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
1869 int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
1870 int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
1871 int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
1872 int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
1874 for (row = 0; row < rpng2_info.height; ++row) {
1875 yidx = (int)(row % bgscale);
1876 even_odd_vert = (int)((row / bgscale) & 1);
1878 r1 = r1_min + (r1_diff * yidx) / yidx_max;
1879 g1 = g1_min + (g1_diff * yidx) / yidx_max;
1880 b1 = b1_min + (b1_diff * yidx) / yidx_max;
1881 r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
1882 g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
1883 b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
1885 r2 = r2_min + (r2_diff * yidx) / yidx_max;
1886 g2 = g2_min + (g2_diff * yidx) / yidx_max;
1887 b2 = b2_min + (b2_diff * yidx) / yidx_max;
1888 r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
1889 g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
1890 b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
1892 dest = (char *)bg_data + row*bg_rowbytes;
1893 for (i = 0; i < rpng2_info.width; ++i) {
1894 even_odd_horiz = (int)((i / bgscale) & 1);
1895 even_odd = even_odd_vert ^ even_odd_horiz;
1897 (even_odd_horiz && (bg[pat].type & 0x10));
1898 if (even_odd == 0) { /* gradient #1 */
1899 if (invert_column) {
1908 } else { /* gradient #2 */
1909 if ((invert_column && invert_gradient2) ||
1910 (!invert_column && !invert_gradient2))
1912 *dest++ = r2; /* not inverted or */
1913 *dest++ = g2; /* doubly inverted */
1917 *dest++ = g2_inv; /* singly inverted */
1924 /*---------------------------------------------------------------------------
1925 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
1927 ---------------------------------------------------------------------------*/
1929 } else if ((bg[pat].type & 0x07) == 1) {
1931 hmax = (bgscale-1)/2; /* half the max weight of a color */
1932 max = 2*hmax; /* the max weight of a color */
1934 r1 = rgb[bg[pat].rgb1_max].r;
1935 g1 = rgb[bg[pat].rgb1_max].g;
1936 b1 = rgb[bg[pat].rgb1_max].b;
1937 r2 = rgb[bg[pat].rgb2_max].r;
1938 g2 = rgb[bg[pat].rgb2_max].g;
1939 b2 = rgb[bg[pat].rgb2_max].b;
1941 for (row = 0; row < rpng2_info.height; ++row) {
1942 yidx = (int)(row % bgscale);
1944 yidx = bgscale-1 - yidx;
1945 dest = (char *)bg_data + row*bg_rowbytes;
1946 for (i = 0; i < rpng2_info.width; ++i) {
1947 xidx = (int)(i % bgscale);
1949 xidx = bgscale-1 - xidx;
1951 *dest++ = (k*r1 + (max-k)*r2) / max;
1952 *dest++ = (k*g1 + (max-k)*g2) / max;
1953 *dest++ = (k*b1 + (max-k)*b2) / max;
1957 /*---------------------------------------------------------------------------
1958 Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
1959 soids will equal bgscale?]. This one is slow but very cool. Code con-
1960 tributed by Pieter S. van der Meulen (originally in Smalltalk).
1961 ---------------------------------------------------------------------------*/
1963 } else if ((bg[pat].type & 0x07) == 2) {
1965 int ii, x, y, hw, hh, grayspot;
1966 double freq, rotate, saturate, gray, intensity;
1967 double angle=0.0, aoffset=0.0, maxDist, dist;
1968 double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
1970 hh = (int)(rpng2_info.height / 2);
1971 hw = (int)(rpng2_info.width / 2);
1973 /* variables for radial waves:
1974 * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED]
1975 * freq: number of color beams originating from the center
1976 * grayspot: size of the graying center area (anti-alias)
1977 * rotate: rotation of the beams as a function of radius
1978 * saturate: saturation of beams' shape azimuthally
1980 angle = CLIP(angle, 0.0, 360.0);
1981 grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
1982 freq = MAX((double)bg[pat].bg_freq, 0.0);
1983 saturate = (double)bg[pat].bg_bsat * 0.1;
1984 rotate = (double)bg[pat].bg_brot * 0.1;
1987 maxDist = (double)((hw*hw) + (hh*hh));
1989 for (row = 0; row < rpng2_info.height; ++row) {
1990 y = (int)(row - hh);
1991 dest = (char *)bg_data + row*bg_rowbytes;
1992 for (i = 0; i < rpng2_info.width; ++i) {
1994 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
1995 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
1996 gray = MIN(1.0, gray);
1997 dist = (double)((x*x) + (y*y)) / maxDist;
1998 intensity = cos((angle+(rotate*dist*PI)) * freq) *
2000 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
2001 hue = (angle + PI) * INV_PI_360 + aoffset;
2002 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
2003 s = MIN(MAX(s,0.0), 1.0);
2004 v = MIN(MAX(intensity,0.0), 1.0);
2007 ch = (uch)(v * 255.0);
2012 if ((hue < 0.0) || (hue >= 360.0))
2013 hue -= (((int)(hue / 360.0)) * 360.0);
2016 f = hue - (double)ii;
2018 q = (1.0 - (s * f)) * v;
2019 t = (1.0 - (s * (1.0 - f))) * v;
2020 if (ii == 0) { red = v; green = t; blue = p; }
2021 else if (ii == 1) { red = q; green = v; blue = p; }
2022 else if (ii == 2) { red = p; green = v; blue = t; }
2023 else if (ii == 3) { red = p; green = q; blue = v; }
2024 else if (ii == 4) { red = t; green = p; blue = v; }
2025 else if (ii == 5) { red = v; green = p; blue = q; }
2026 *dest++ = (uch)(red * 255.0);
2027 *dest++ = (uch)(green * 255.0);
2028 *dest++ = (uch)(blue * 255.0);
2034 } /* end function rpng2_x_reload_bg_image() */
2040 static int is_number(char *p)
2050 #endif /* FEATURE_LOOP */
2056 static void rpng2_x_cleanup(void)
2058 if (bg_image && bg_data) {
2063 if (rpng2_info.image_data) {
2064 free(rpng2_info.image_data);
2065 rpng2_info.image_data = NULL;
2068 if (rpng2_info.row_pointers) {
2069 free(rpng2_info.row_pointers);
2070 rpng2_info.row_pointers = NULL;
2075 free(ximage->data); /* we allocated it, so we free it */
2076 ximage->data = (char *)NULL; /* instead of XDestroyImage() */
2078 XDestroyImage(ximage);
2083 XFreeGC(display, gc);
2086 XDestroyWindow(display, window);
2089 XFreeColormap(display, colormap);
2091 if (have_nondefault_visual)
2099 static int rpng2_x_msb(ulg u32val)
2103 for (i = 31; i >= 0; --i) {
2104 if (u32val & 0x80000000L)