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-
44 ---------------------------------------------------------------------------
46 Copyright (c) 1998-2008 Greg Roelofs. All rights reserved.
48 This software is provided "as is," without warranty of any kind,
49 express or implied. In no event shall the author or contributors
50 be held liable for any damages arising in any way from the use of
53 The contents of this file are DUAL-LICENSED. You may modify and/or
54 redistribute this software according to the terms of one of the
55 following two licenses (at your option):
58 LICENSE 1 ("BSD-like with advertising clause"):
60 Permission is granted to anyone to use this software for any purpose,
61 including commercial applications, and to alter it and redistribute
62 it freely, subject to the following restrictions:
64 1. Redistributions of source code must retain the above copyright
65 notice, disclaimer, and this list of conditions.
66 2. Redistributions in binary form must reproduce the above copyright
67 notice, disclaimer, and this list of conditions in the documenta-
68 tion and/or other materials provided with the distribution.
69 3. All advertising materials mentioning features or use of this
70 software must display the following acknowledgment:
72 This product includes software developed by Greg Roelofs
73 and contributors for the book, "PNG: The Definitive Guide,"
74 published by O'Reilly and Associates.
77 LICENSE 2 (GNU GPL v2 or later):
79 This program is free software; you can redistribute it and/or modify
80 it under the terms of the GNU General Public License as published by
81 the Free Software Foundation; either version 2 of the License, or
82 (at your option) any later version.
84 This program is distributed in the hope that it will be useful,
85 but WITHOUT ANY WARRANTY; without even the implied warranty of
86 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
87 GNU General Public License for more details.
89 You should have received a copy of the GNU General Public License
90 along with this program; if not, write to the Free Software Foundation,
91 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
93 ---------------------------------------------------------------------------*/
95 #define PROGNAME "rpng2-x"
96 #define LONGNAME "Progressive PNG Viewer for X"
97 #define VERSION "2.02 of 16 March 2008"
98 #define RESNAME "rpng2" /* our X resource application name */
99 #define RESCLASS "Rpng" /* our X resource class name */
105 #include <setjmp.h> /* for jmpbuf declaration in readpng2.h */
107 #include <math.h> /* only for PvdM background code */
108 #include <X11/Xlib.h>
109 #include <X11/Xutil.h>
111 #include <X11/keysym.h> /* defines XK_* macros */
117 /* all for PvdM background code: */
119 # define PI 3.141592653589793238
121 #define PI_2 (PI*0.5)
122 #define INV_PI_360 (360.0 / PI)
123 #define MAX(a,b) (a>b?a:b)
124 #define MIN(a,b) (a<b?a:b)
125 #define CLIP(a,min,max) MAX(min,MIN((a),max))
126 #define ABS(a) ((a)<0?-(a):(a))
127 #define CLIP8P(c) MAX(0,(MIN((c),255))) /* 8-bit pos. integer (uch) */
128 #define ROUNDF(f) ((int)(f + 0.5))
130 #define QUIT(e,k) ((e.type == ButtonPress && e.xbutton.button == Button1) || \
131 (e.type == KeyPress && /* v--- or 1 for shifted keys */ \
132 ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape)))
134 #define NO_24BIT_MASKS /* undef case not fully written--only for redisplay() */
136 #define rgb1_max bg_freq
137 #define rgb1_min bg_gray
138 #define rgb2_max bg_bsat
139 #define rgb2_min bg_brot
141 /* #define DEBUG */ /* this enables the Trace() macros */
143 #include "readpng2.h" /* typedefs, common macros, readpng2 prototypes */
146 /* could just include png.h, but this macro is the only thing we need
147 * (name and typedefs changed to local versions); note that side effects
148 * only happen with alpha (which could easily be avoided with
149 * "ush acopy = (alpha);") */
151 #define alpha_composite(composite, fg, alpha, bg) { \
152 ush temp = ((ush)(fg)*(ush)(alpha) + \
153 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \
154 (composite) = (uch)((temp + (temp >> 8)) >> 8); \
158 #define INBUFSIZE 4096 /* with pseudo-timing on (1 sec delay/block), this
159 * block size corresponds roughly to a download
160 * speed 10% faster than theoretical 33.6K maximum
161 * (assuming 8 data bits, 1 stop bit and no other
164 /* local prototypes */
165 static void rpng2_x_init (void);
166 static int rpng2_x_create_window (void);
167 static int rpng2_x_load_bg_image (void);
168 static void rpng2_x_display_row (ulg row);
169 static void rpng2_x_finish_display (void);
170 static void rpng2_x_redisplay_image (ulg startcol, ulg startrow,
171 ulg width, ulg height);
173 static void rpng2_x_reload_bg_image (void);
174 static int is_number (char *p);
176 static void rpng2_x_cleanup (void);
177 static int rpng2_x_msb (ulg u32val);
180 static char titlebar[1024], *window_name = titlebar;
181 static char *appname = LONGNAME;
182 static char *icon_name = PROGNAME;
183 static char *res_name = RESNAME;
184 static char *res_class = RESCLASS;
185 static char *filename;
188 static mainprog_info rpng2_info;
190 static uch inbuf[INBUFSIZE];
193 static int pat = 6; /* must be less than num_bgpat */
194 static int bg_image = 0;
195 static int bgscale, bgscale_default = 16;
196 static ulg bg_rowbytes;
199 int pause_after_pass = FALSE;
200 int demo_timing = FALSE;
201 ulg usleep_duration = 0L;
203 static struct rgb_color {
206 { 0, 0, 0}, /* 0: black */
207 {255, 255, 255}, /* 1: white */
208 {173, 132, 57}, /* 2: tan */
209 { 64, 132, 0}, /* 3: medium green */
210 {189, 117, 1}, /* 4: gold */
211 {253, 249, 1}, /* 5: yellow */
212 { 0, 0, 255}, /* 6: blue */
213 { 0, 0, 120}, /* 7: medium blue */
214 {255, 0, 255}, /* 8: magenta */
215 { 64, 0, 64}, /* 9: dark magenta */
216 {255, 0, 0}, /* 10: red */
217 { 64, 0, 0}, /* 11: dark red */
218 {255, 127, 0}, /* 12: orange */
219 {192, 96, 0}, /* 13: darker orange */
220 { 24, 60, 0}, /* 14: dark green-yellow */
221 { 85, 125, 200}, /* 15: ice blue */
222 {192, 192, 192} /* 16: Netscape/Mosaic gray */
224 /* not used for now, but should be for error-checking:
225 static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
229 This whole struct is a fairly cheesy way to keep the number of
230 command-line options to a minimum. The radial-waves background
231 type is a particularly poor fit to the integer elements of the
232 struct...but a few macros and a little fixed-point math will do
236 F E D C B A 9 8 7 6 5 4 3 2 1 0
238 | | +-+-+-- 0 = sharp-edged checkerboard
239 | | 1 = soft diamonds
242 | +-- gradient #2 inverted?
243 +-- alternating columns inverted?
245 static struct background_pattern {
247 int rgb1_max, rgb1_min; /* or bg_freq, bg_gray */
248 int rgb2_max, rgb2_min; /* or bg_bsat, bg_brot (both scaled by 10)*/
250 {0, 1,1, 16,16}, /* checkered: white vs. light gray (basic) */
251 {0+8, 2,0, 1,15}, /* checkered: tan/black vs. white/ice blue */
252 {0+24, 2,0, 1,0}, /* checkered: tan/black vs. white/black */
253 {0+8, 4,5, 0,2}, /* checkered: gold/yellow vs. black/tan */
254 {0+8, 4,5, 0,6}, /* checkered: gold/yellow vs. black/blue */
255 {0, 7,0, 8,9}, /* checkered: deep blue/black vs. magenta */
256 {0+8, 13,0, 5,14}, /* checkered: orange/black vs. yellow */
257 {0+8, 12,0, 10,11}, /* checkered: orange/black vs. red */
258 {1, 7,0, 8,0}, /* diamonds: deep blue/black vs. magenta */
259 {1, 12,0, 11,0}, /* diamonds: orange vs. dark red */
260 {1, 10,0, 7,0}, /* diamonds: red vs. medium blue */
261 {1, 4,0, 5,0}, /* diamonds: gold vs. yellow */
262 {1, 3,0, 0,0}, /* diamonds: medium green vs. black */
263 {2, 16, 100, 20, 0}, /* radial: ~hard radial color-beams */
264 {2, 18, 100, 10, 2}, /* radial: soft, curved radial color-beams */
265 {2, 16, 256, 100, 250}, /* radial: very tight spiral */
266 {2, 10000, 256, 11, 0} /* radial: dipole-moire' (almost fractal) */
268 static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
271 /* X-specific variables */
272 static char *displayname;
273 static XImage *ximage;
274 static Display *display;
276 static Visual *visual;
277 static XVisualInfo *visual_list;
278 static int RShift, GShift, BShift;
279 static ulg RMask, GMask, BMask;
280 static Window window;
282 static Colormap colormap;
284 static int have_nondefault_visual = FALSE;
285 static int have_colormap = FALSE;
286 static int have_window = FALSE;
287 static int have_gc = FALSE;
292 int main(int argc, char **argv)
297 char *p, *bgstr = NULL;
304 long loop_interval = -1; /* seconds (100,000 max) */
306 double LUT_exponent; /* just the lookup table */
307 double CRT_exponent = 2.2; /* just the monitor */
308 double default_display_exponent; /* whole display system */
313 /* First initialize a few things, just to be sure--memset takes care of
314 * default background color (black), booleans (FALSE), pointers (NULL),
317 displayname = (char *)NULL;
318 filename = (char *)NULL;
319 memset(&rpng2_info, 0, sizeof(mainprog_info));
322 /* Set the default value for our display-system exponent, i.e., the
323 * product of the CRT exponent and the exponent corresponding to
324 * the frame-buffer's lookup table (LUT), if any. This is not an
325 * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
326 * ones), but it should cover 99% of the current possibilities. */
329 /* third-party utilities can modify the default LUT exponent */
330 LUT_exponent = 1.0 / 2.2;
332 if (some_next_function_that_returns_gamma(&next_gamma))
333 LUT_exponent = 1.0 / next_gamma;
336 LUT_exponent = 1.0 / 1.7;
337 /* there doesn't seem to be any documented function to
338 * get the "gamma" value, so we do it the hard way */
339 infile = fopen("/etc/config/system.glGammaVal", "r");
343 fgets(tmpline, 80, infile);
345 sgi_gamma = atof(tmpline);
347 LUT_exponent = 1.0 / sgi_gamma;
349 #elif defined(Macintosh)
350 LUT_exponent = 1.8 / 2.61;
352 if (some_mac_function_that_returns_gamma(&mac_gamma))
353 LUT_exponent = mac_gamma / 2.61;
356 LUT_exponent = 1.0; /* assume no LUT: most PCs */
359 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
360 default_display_exponent = LUT_exponent * CRT_exponent;
363 /* If the user has set the SCREEN_GAMMA environment variable as suggested
364 * (somewhat imprecisely) in the libpng documentation, use that; otherwise
365 * use the default value we just calculated. Either way, the user may
366 * override this via a command-line option. */
368 if ((p = getenv("SCREEN_GAMMA")) != NULL)
369 rpng2_info.display_exponent = atof(p);
371 rpng2_info.display_exponent = default_display_exponent;
374 /* Now parse the command line for options and the PNG filename. */
376 while (*++argv && !error) {
377 if (!strncmp(*argv, "-display", 2)) {
382 } else if (!strncmp(*argv, "-gamma", 2)) {
386 rpng2_info.display_exponent = atof(*argv);
387 if (rpng2_info.display_exponent <= 0.0)
390 } else if (!strncmp(*argv, "-bgcolor", 4)) {
395 if (strlen(bgstr) != 7 || bgstr[0] != '#')
402 } else if (!strncmp(*argv, "-bgpat", 4)) {
407 if (pat >= 0 && pat < num_bgpat) {
413 } else if (!strncmp(*argv, "-usleep", 2)) {
417 usleep_duration = (ulg)atol(*argv);
420 } else if (!strncmp(*argv, "-pause", 2)) {
421 pause_after_pass = TRUE;
422 } else if (!strncmp(*argv, "-timing", 2)) {
425 } else if (!strncmp(*argv, "-loop", 2)) {
427 if (!argv[1] || !is_number(argv[1]))
431 loop_interval = atol(*argv);
432 if (loop_interval < 0)
434 else if (loop_interval > 100000) /* bit more than one day */
435 loop_interval = 100000;
438 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
439 } else if (!strncmp(*argv, "-nommxfilters", 7)) {
440 rpng2_info.nommxfilters = TRUE;
441 } else if (!strncmp(*argv, "-nommxcombine", 7)) {
442 rpng2_info.nommxcombine = TRUE;
443 } else if (!strncmp(*argv, "-nommxinterlace", 7)) {
444 rpng2_info.nommxinterlace = TRUE;
445 } else if (!strcmp(*argv, "-nommx")) {
446 rpng2_info.nommxfilters = TRUE;
447 rpng2_info.nommxcombine = TRUE;
448 rpng2_info.nommxinterlace = TRUE;
453 if (argv[1]) /* shouldn't be any more args after filename */
456 ++error; /* not expecting any other options */
464 /* print usage screen if any errors up to this point */
467 fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname);
468 readpng2_version_info();
470 "Usage: %s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
471 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
472 " %*s [[-nommxfilters] [-nommxcombine] [-nommxinterlace] | -nommx]\n"
475 " %*s [-usleep dur | -timing] [-pause] [-loop [sec]] file.png\n\n"
477 " %*s [-usleep dur | -timing] [-pause] file.png\n\n"
479 " xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
480 " exp \ttransfer-function exponent (``gamma'') of the display\n"
481 "\t\t system in floating-point format (e.g., ``%.1f''); equal\n"
482 "\t\t to the product of the lookup-table exponent (varies)\n"
483 "\t\t and the CRT exponent (usually 2.2); must be positive\n"
484 " bg \tdesired background color in 7-character hex RGB format\n"
485 "\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n"
486 "\t\t used with transparent images; overrides -bgpat\n"
487 " pat \tdesired background pattern number (0-%d); used with\n"
488 "\t\t transparent images; overrides -bgcolor\n"
490 " -loop\tloops through background images after initial display\n"
491 "\t\t is complete (depends on -bgpat)\n"
492 " sec \tseconds to display each background image (default = 2)\n"
494 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
495 " -nommx*\tdisable optimized MMX routines for decoding row filters,\n"
496 "\t\t combining rows, and expanding interlacing, respectively\n"
498 " dur \tduration in microseconds to wait after displaying each\n"
499 "\t\t row (for demo purposes)\n"
500 " -timing\tenables delay for every block read, to simulate modem\n"
501 "\t\t download of image (~36 Kbps)\n"
502 " -pause\tpauses after displaying each pass until mouse clicked\n"
503 "\nPress Q, Esc or mouse button 1 (within image window, after image\n"
504 "is displayed) to quit.\n"
506 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
507 (int)strlen(PROGNAME), " ",
509 (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat-1);
514 if (!(infile = fopen(filename, "rb"))) {
515 fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename);
518 incount = fread(inbuf, 1, INBUFSIZE, infile);
519 if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
520 fprintf(stderr, PROGNAME
521 ": [%s] is not a PNG file: incorrect signature\n",
524 } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
527 fprintf(stderr, PROGNAME
528 ": [%s] has bad IHDR (libpng longjmp)\n", filename);
531 fprintf(stderr, PROGNAME ": insufficient memory\n");
534 fprintf(stderr, PROGNAME
535 ": unknown readpng2_init() error\n");
540 Trace((stderr, "about to call XOpenDisplay()\n"))
541 display = XOpenDisplay(displayname);
543 readpng2_cleanup(&rpng2_info);
544 fprintf(stderr, PROGNAME ": can't open X display [%s]\n",
545 displayname? displayname : "default");
555 fprintf(stderr, PROGNAME ": aborting.\n");
560 /* set the title-bar string, but make sure buffer doesn't overflow */
562 alen = strlen(appname);
563 flen = strlen(filename);
564 if (alen + flen + 3 > 1023)
565 sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023));
567 sprintf(titlebar, "%s: %s", appname, filename);
570 /* set some final rpng2_info variables before entering main data loop */
573 unsigned r, g, b; /* this approach quiets compiler warnings */
575 sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
576 rpng2_info.bg_red = (uch)r;
577 rpng2_info.bg_green = (uch)g;
578 rpng2_info.bg_blue = (uch)b;
580 rpng2_info.need_bgcolor = TRUE;
582 rpng2_info.state = kPreInit;
583 rpng2_info.mainprog_init = rpng2_x_init;
584 rpng2_info.mainprog_display_row = rpng2_x_display_row;
585 rpng2_info.mainprog_finish_display = rpng2_x_finish_display;
588 /* OK, this is the fun part: call readpng2_decode_data() at the start of
589 * the loop to deal with our first buffer of data (read in above to verify
590 * that the file is a PNG image), then loop through the file and continue
591 * calling the same routine to handle each chunk of data. It in turn
592 * passes the data to libpng, which will invoke one or more of our call-
593 * backs as decoded data become available. We optionally call sleep() for
594 * one second per iteration to simulate downloading the image via an analog
598 Trace((stderr, "about to call readpng2_decode_data()\n"))
599 if (readpng2_decode_data(&rpng2_info, inbuf, incount))
601 Trace((stderr, "done with readpng2_decode_data()\n"))
603 if (error || incount != INBUFSIZE || rpng2_info.state == kDone) {
604 if (rpng2_info.state == kDone) {
605 Trace((stderr, "done decoding PNG image\n"))
606 } else if (ferror(infile)) {
607 fprintf(stderr, PROGNAME
608 ": error while reading PNG image file\n");
610 } else if (feof(infile)) {
611 fprintf(stderr, PROGNAME ": end of file reached "
612 "(unexpectedly) while reading PNG image file\n");
614 } else /* if (error) */ {
615 // will print error message below
623 incount = fread(inbuf, 1, INBUFSIZE, infile);
627 /* clean up PNG stuff and report any decoding errors */
630 Trace((stderr, "about to call readpng2_cleanup()\n"))
631 readpng2_cleanup(&rpng2_info);
634 fprintf(stderr, PROGNAME ": libpng error while decoding PNG image\n");
641 if (loop && bg_image) {
642 Trace((stderr, "entering -loop loop (FEATURE_LOOP)\n"))
645 struct timeval now, then;
647 /* get current time and add loop_interval to get target time */
648 if (gettimeofday(&then, NULL) == 0) {
649 then.tv_sec += loop_interval;
654 /* do quick check for a quit event but don't wait for it */
655 /* GRR BUG: should also check for Expose events and redraw... */
656 if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask, &e))
660 /* generate next background image */
661 if (++pat >= num_bgpat)
663 rpng2_x_reload_bg_image();
665 /* wait for timeout, using whatever means are available */
666 if (use_sleep || gettimeofday(&now, NULL) != 0) {
667 for (i = loop_interval; i > 0; --i) {
669 /* GRR BUG: also need to check for Expose (and redraw!) */
670 if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask,
676 if (now.tv_sec < then.tv_sec ||
677 (now.tv_sec == then.tv_sec && now.tv_usec < then.tv_usec))
680 long seconds_to_go = then.tv_sec - now.tv_sec;
683 /* basically chew up most of remaining loop-interval with
684 * calls to sleep(1) interleaved with checks for quit
685 * events, but also recalc time-to-go periodically; when
686 * done, clean up any remaining time with usleep() call
687 * (could also use SIGALRM, but signals are a pain...) */
688 while (seconds_to_go-- > 1) {
689 int seconds_done = 0;
691 for (i = seconds_to_go; i > 0 && !quit; --i) {
693 /* GRR BUG: need to check for Expose and redraw */
694 if (XCheckMaskEvent(display, KeyPressMask |
695 ButtonPressMask, &e) && QUIT(e,k))
697 if (++seconds_done > 1000)
698 break; /* time to redo seconds_to_go meas. */
703 /* OK, more than 1000 seconds since last check:
704 * correct the time-to-go measurement for drift */
705 if (gettimeofday(&now, NULL) == 0) {
706 if (now.tv_sec >= then.tv_sec)
708 seconds_to_go = then.tv_sec - now.tv_sec;
710 ++seconds_to_go; /* restore what we subtracted */
713 break; /* breaks outer do-loop, skips redisplay */
715 /* since difference between "now" and "then" is already
716 * eaten up to within a couple of seconds, don't need to
717 * worry about overflow--but might have overshot (neg.) */
718 if (gettimeofday(&now, NULL) == 0) {
719 usleep_usec = 1000000L*(then.tv_sec - now.tv_sec) +
720 then.tv_usec - now.tv_usec;
722 usleep((ulg)usleep_usec);
727 /* composite image against new background and display (note that
728 * we do not take into account the time spent doing this...) */
729 rpng2_x_redisplay_image (0, 0, rpng2_info.width, rpng2_info.height);
732 } else /* FALL THROUGH and do the normal thing */
734 #endif /* FEATURE_LOOP */
736 /* wait for the user to tell us when to quit */
738 if (rpng2_info.state >= kWindowInit) {
739 Trace((stderr, "entering final wait-for-quit-event loop\n"))
741 XNextEvent(display, &e);
742 if (e.type == Expose) {
743 XExposeEvent *ex = (XExposeEvent *)&e;
744 rpng2_x_redisplay_image (ex->x, ex->y, ex->width, ex->height);
746 } while (!QUIT(e,k));
748 fprintf(stderr, PROGNAME ": init callback never called: probable "
749 "libpng error while decoding PNG metadata\n");
754 /* we're done: clean up all image and X resources and go away */
756 Trace((stderr, "about to call rpng2_x_cleanup()\n"))
766 /* this function is called by readpng2_info_callback() in readpng2.c, which
767 * in turn is called by libpng after all of the pre-IDAT chunks have been
768 * read and processed--i.e., we now have enough info to finish initializing */
770 static void rpng2_x_init(void)
773 ulg rowbytes = rpng2_info.rowbytes;
775 Trace((stderr, "beginning rpng2_x_init()\n"))
776 Trace((stderr, " rowbytes = %d\n", rpng2_info.rowbytes))
777 Trace((stderr, " width = %ld\n", rpng2_info.width))
778 Trace((stderr, " height = %ld\n", rpng2_info.height))
780 rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
781 if (!rpng2_info.image_data) {
782 readpng2_cleanup(&rpng2_info);
786 rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
787 if (!rpng2_info.row_pointers) {
788 free(rpng2_info.image_data);
789 rpng2_info.image_data = NULL;
790 readpng2_cleanup(&rpng2_info);
794 for (i = 0; i < rpng2_info.height; ++i)
795 rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
798 /* do the basic X initialization stuff, make the window, and fill it with
799 * the user-specified, file-specified or default background color or
802 if (rpng2_x_create_window()) {
804 /* GRR TEMPORARY HACK: this is fundamentally no different from cases
805 * above; libpng should longjmp() back to us when png_ptr goes away.
806 * If we/it segfault instead, seems like a libpng bug... */
808 /* we're here via libpng callback, so if window fails, clean and bail */
809 readpng2_cleanup(&rpng2_info);
814 rpng2_info.state = kWindowInit;
821 static int rpng2_x_create_window(void)
823 ulg bg_red = rpng2_info.bg_red;
824 ulg bg_green = rpng2_info.bg_green;
825 ulg bg_blue = rpng2_info.bg_blue;
828 int need_colormap = FALSE;
834 XSetWindowAttributes attr;
835 XTextProperty windowName, *pWindowName = &windowName;
836 XTextProperty iconName, *pIconName = &iconName;
837 XVisualInfo visual_info;
838 XSizeHints *size_hints;
840 XClassHint *class_hints;
843 Trace((stderr, "beginning rpng2_x_create_window()\n"))
845 screen = DefaultScreen(display);
846 depth = DisplayPlanes(display, screen);
847 root = RootWindow(display, screen);
850 XSynchronize(display, True);
853 if (depth != 16 && depth != 24 && depth != 32) {
854 int visuals_matched = 0;
856 Trace((stderr, "default depth is %d: checking other visuals\n",
860 visual_info.screen = screen;
861 visual_info.depth = 24;
862 visual_list = XGetVisualInfo(display,
863 VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
864 if (visuals_matched == 0) {
865 /* GRR: add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
866 fprintf(stderr, "default screen depth %d not supported, and no"
867 " 24-bit visuals found\n", depth);
870 Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
872 visual = visual_list[0].visual;
873 depth = visual_list[0].depth;
875 colormap_size = visual_list[0].colormap_size;
876 visual_class = visual->class;
877 visualID = XVisualIDFromVisual(visual);
879 have_nondefault_visual = TRUE;
880 need_colormap = TRUE;
882 XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
883 visual = visual_info.visual;
886 RMask = visual->red_mask;
887 GMask = visual->green_mask;
888 BMask = visual->blue_mask;
890 /* GRR: add/check 8-bit support */
891 if (depth == 8 || need_colormap) {
892 colormap = XCreateColormap(display, root, visual, AllocNone);
894 fprintf(stderr, "XCreateColormap() failed\n");
897 have_colormap = TRUE;
899 bg_image = FALSE; /* gradient just wastes palette entries */
901 if (depth == 15 || depth == 16) {
902 RShift = 15 - rpng2_x_msb(RMask); /* these are right-shifts */
903 GShift = 15 - rpng2_x_msb(GMask);
904 BShift = 15 - rpng2_x_msb(BMask);
905 } else if (depth > 16) {
906 RShift = rpng2_x_msb(RMask) - 7; /* these are left-shifts */
907 GShift = rpng2_x_msb(GMask) - 7;
908 BShift = rpng2_x_msb(BMask) - 7;
910 if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
911 fprintf(stderr, "rpng2 internal logic error: negative X shift(s)!\n");
915 /*---------------------------------------------------------------------------
916 Finally, create the window.
917 ---------------------------------------------------------------------------*/
919 attr.backing_store = Always;
920 attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
921 attrmask = CWBackingStore | CWEventMask;
922 if (have_nondefault_visual) {
923 attr.colormap = colormap;
924 attr.background_pixel = 0;
925 attr.border_pixel = 1;
926 attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
929 window = XCreateWindow(display, root, 0, 0, rpng2_info.width,
930 rpng2_info.height, 0, depth, InputOutput, visual, attrmask, &attr);
932 if (window == None) {
933 fprintf(stderr, "XCreateWindow() failed\n");
939 XSetWindowColormap(display, window, colormap);
941 if (!XStringListToTextProperty(&window_name, 1, pWindowName))
943 if (!XStringListToTextProperty(&icon_name, 1, pIconName))
946 /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */
948 if ((size_hints = XAllocSizeHints()) != NULL) {
949 /* window will not be resizable */
950 size_hints->flags = PMinSize | PMaxSize;
951 size_hints->min_width = size_hints->max_width = (int)rpng2_info.width;
952 size_hints->min_height = size_hints->max_height =
953 (int)rpng2_info.height;
956 if ((wm_hints = XAllocWMHints()) != NULL) {
957 wm_hints->initial_state = NormalState;
958 wm_hints->input = True;
959 /* wm_hints->icon_pixmap = icon_pixmap; */
960 wm_hints->flags = StateHint | InputHint /* | IconPixmapHint */ ;
963 if ((class_hints = XAllocClassHint()) != NULL) {
964 class_hints->res_name = res_name;
965 class_hints->res_class = res_class;
968 XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
969 size_hints, wm_hints, class_hints);
971 /* various properties and hints no longer needed; free memory */
973 XFree(pWindowName->value);
975 XFree(pIconName->value);
983 XMapWindow(display, window);
985 gc = XCreateGC(display, window, 0, &gcvalues);
988 /*---------------------------------------------------------------------------
989 Allocate memory for the X- and display-specific version of the image.
990 ---------------------------------------------------------------------------*/
992 if (depth == 24 || depth == 32) {
993 xdata = (uch *)malloc(4*rpng2_info.width*rpng2_info.height);
995 } else if (depth == 16) {
996 xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height);
998 } else /* depth == 8 */ {
999 xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height);
1004 fprintf(stderr, PROGNAME ": unable to allocate image memory\n");
1008 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
1009 (char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0);
1012 fprintf(stderr, PROGNAME ": XCreateImage() failed\n");
1017 /* to avoid testing the byte order every pixel (or doubling the size of
1018 * the drawing routine with a giant if-test), we arbitrarily set the byte
1019 * order to MSBFirst and let Xlib worry about inverting things on little-
1020 * endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the
1021 * most efficient approach (the giant if-test would be better), but in
1022 * the interest of clarity, we'll take the easy way out... */
1024 ximage->byte_order = MSBFirst;
1026 /*---------------------------------------------------------------------------
1027 Fill window with the specified background color (default is black) or
1028 faked "background image" (but latter is disabled if 8-bit; gradients
1029 just waste palette entries).
1030 ---------------------------------------------------------------------------*/
1033 rpng2_x_load_bg_image(); /* resets bg_image if fails */
1036 if (depth == 24 || depth == 32) {
1037 bg_pixel = (bg_red << RShift) |
1038 (bg_green << GShift) |
1039 (bg_blue << BShift);
1040 } else if (depth == 16) {
1041 bg_pixel = (((bg_red << 8) >> RShift) & RMask) |
1042 (((bg_green << 8) >> GShift) & GMask) |
1043 (((bg_blue << 8) >> BShift) & BMask);
1044 } else /* depth == 8 */ {
1046 /* GRR: add 8-bit support */
1049 XSetForeground(display, gc, bg_pixel);
1050 XFillRectangle(display, window, gc, 0, 0, rpng2_info.width,
1054 /*---------------------------------------------------------------------------
1055 Wait for first Expose event to do any drawing, then flush and return.
1056 ---------------------------------------------------------------------------*/
1059 XNextEvent(display, &e);
1060 while (e.type != Expose || e.xexpose.count);
1066 } /* end function rpng2_x_create_window() */
1072 static int rpng2_x_load_bg_image(void)
1076 uch r1, r2, g1, g2, b1, b2;
1077 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
1079 int xidx, yidx, yidx_max;
1080 int even_odd_vert, even_odd_horiz, even_odd;
1081 int invert_gradient2 = (bg[pat].type & 0x08);
1083 int ximage_rowbytes = ximage->bytes_per_line;
1087 /*---------------------------------------------------------------------------
1088 Allocate buffer for fake background image to be used with transparent
1089 images; if this fails, revert to plain background color.
1090 ---------------------------------------------------------------------------*/
1092 bg_rowbytes = 3 * rpng2_info.width;
1093 bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
1095 fprintf(stderr, PROGNAME
1096 ": unable to allocate memory for background image\n");
1101 bgscale = (pat == 0)? 8 : bgscale_default;
1102 yidx_max = bgscale - 1;
1104 /*---------------------------------------------------------------------------
1105 Vertical gradients (ramps) in NxN squares, alternating direction and
1106 colors (N == bgscale).
1107 ---------------------------------------------------------------------------*/
1109 if ((bg[pat].type & 0x07) == 0) {
1110 uch r1_min = rgb[bg[pat].rgb1_min].r;
1111 uch g1_min = rgb[bg[pat].rgb1_min].g;
1112 uch b1_min = rgb[bg[pat].rgb1_min].b;
1113 uch r2_min = rgb[bg[pat].rgb2_min].r;
1114 uch g2_min = rgb[bg[pat].rgb2_min].g;
1115 uch b2_min = rgb[bg[pat].rgb2_min].b;
1116 int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
1117 int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
1118 int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
1119 int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
1120 int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
1121 int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
1123 for (row = 0; row < rpng2_info.height; ++row) {
1124 yidx = (int)(row % bgscale);
1125 even_odd_vert = (int)((row / bgscale) & 1);
1127 r1 = r1_min + (r1_diff * yidx) / yidx_max;
1128 g1 = g1_min + (g1_diff * yidx) / yidx_max;
1129 b1 = b1_min + (b1_diff * yidx) / yidx_max;
1130 r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
1131 g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
1132 b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
1134 r2 = r2_min + (r2_diff * yidx) / yidx_max;
1135 g2 = g2_min + (g2_diff * yidx) / yidx_max;
1136 b2 = b2_min + (b2_diff * yidx) / yidx_max;
1137 r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
1138 g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
1139 b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
1141 dest = (char *)bg_data + row*bg_rowbytes;
1142 for (i = 0; i < rpng2_info.width; ++i) {
1143 even_odd_horiz = (int)((i / bgscale) & 1);
1144 even_odd = even_odd_vert ^ even_odd_horiz;
1146 (even_odd_horiz && (bg[pat].type & 0x10));
1147 if (even_odd == 0) { /* gradient #1 */
1148 if (invert_column) {
1157 } else { /* gradient #2 */
1158 if ((invert_column && invert_gradient2) ||
1159 (!invert_column && !invert_gradient2))
1161 *dest++ = r2; /* not inverted or */
1162 *dest++ = g2; /* doubly inverted */
1166 *dest++ = g2_inv; /* singly inverted */
1173 /*---------------------------------------------------------------------------
1174 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
1176 ---------------------------------------------------------------------------*/
1178 } else if ((bg[pat].type & 0x07) == 1) {
1180 hmax = (bgscale-1)/2; /* half the max weight of a color */
1181 max = 2*hmax; /* the max weight of a color */
1183 r1 = rgb[bg[pat].rgb1_max].r;
1184 g1 = rgb[bg[pat].rgb1_max].g;
1185 b1 = rgb[bg[pat].rgb1_max].b;
1186 r2 = rgb[bg[pat].rgb2_max].r;
1187 g2 = rgb[bg[pat].rgb2_max].g;
1188 b2 = rgb[bg[pat].rgb2_max].b;
1190 for (row = 0; row < rpng2_info.height; ++row) {
1191 yidx = (int)(row % bgscale);
1193 yidx = bgscale-1 - yidx;
1194 dest = (char *)bg_data + row*bg_rowbytes;
1195 for (i = 0; i < rpng2_info.width; ++i) {
1196 xidx = (int)(i % bgscale);
1198 xidx = bgscale-1 - xidx;
1200 *dest++ = (k*r1 + (max-k)*r2) / max;
1201 *dest++ = (k*g1 + (max-k)*g2) / max;
1202 *dest++ = (k*b1 + (max-k)*b2) / max;
1206 /*---------------------------------------------------------------------------
1207 Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
1208 soids will equal bgscale?]. This one is slow but very cool. Code con-
1209 tributed by Pieter S. van der Meulen (originally in Smalltalk).
1210 ---------------------------------------------------------------------------*/
1212 } else if ((bg[pat].type & 0x07) == 2) {
1214 int ii, x, y, hw, hh, grayspot;
1215 double freq, rotate, saturate, gray, intensity;
1216 double angle=0.0, aoffset=0.0, maxDist, dist;
1217 double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
1219 fprintf(stderr, "%s: computing radial background...",
1223 hh = (int)(rpng2_info.height / 2);
1224 hw = (int)(rpng2_info.width / 2);
1226 /* variables for radial waves:
1227 * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED]
1228 * freq: number of color beams originating from the center
1229 * grayspot: size of the graying center area (anti-alias)
1230 * rotate: rotation of the beams as a function of radius
1231 * saturate: saturation of beams' shape azimuthally
1233 angle = CLIP(angle, 0.0, 360.0);
1234 grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
1235 freq = MAX((double)bg[pat].bg_freq, 0.0);
1236 saturate = (double)bg[pat].bg_bsat * 0.1;
1237 rotate = (double)bg[pat].bg_brot * 0.1;
1240 maxDist = (double)((hw*hw) + (hh*hh));
1242 for (row = 0; row < rpng2_info.height; ++row) {
1243 y = (int)(row - hh);
1244 dest = (char *)bg_data + row*bg_rowbytes;
1245 for (i = 0; i < rpng2_info.width; ++i) {
1247 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
1248 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
1249 gray = MIN(1.0, gray);
1250 dist = (double)((x*x) + (y*y)) / maxDist;
1251 intensity = cos((angle+(rotate*dist*PI)) * freq) *
1253 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
1254 hue = (angle + PI) * INV_PI_360 + aoffset;
1255 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
1256 s = MIN(MAX(s,0.0), 1.0);
1257 v = MIN(MAX(intensity,0.0), 1.0);
1260 ch = (uch)(v * 255.0);
1265 if ((hue < 0.0) || (hue >= 360.0))
1266 hue -= (((int)(hue / 360.0)) * 360.0);
1269 f = hue - (double)ii;
1271 q = (1.0 - (s * f)) * v;
1272 t = (1.0 - (s * (1.0 - f))) * v;
1273 if (ii == 0) { red = v; green = t; blue = p; }
1274 else if (ii == 1) { red = q; green = v; blue = p; }
1275 else if (ii == 2) { red = p; green = v; blue = t; }
1276 else if (ii == 3) { red = p; green = q; blue = v; }
1277 else if (ii == 4) { red = t; green = p; blue = v; }
1278 else if (ii == 5) { red = v; green = p; blue = q; }
1279 *dest++ = (uch)(red * 255.0);
1280 *dest++ = (uch)(green * 255.0);
1281 *dest++ = (uch)(blue * 255.0);
1285 fprintf(stderr, "done.\n");
1289 /*---------------------------------------------------------------------------
1290 Blast background image to display buffer before beginning PNG decode.
1291 ---------------------------------------------------------------------------*/
1293 if (depth == 24 || depth == 32) {
1294 ulg red, green, blue;
1295 int bpp = ximage->bits_per_pixel;
1297 for (row = 0; row < rpng2_info.height; ++row) {
1298 src = bg_data + row*bg_rowbytes;
1299 dest = ximage->data + row*ximage_rowbytes;
1300 if (bpp == 32) { /* slightly optimized version */
1301 for (i = rpng2_info.width; i > 0; --i) {
1305 pixel = (red << RShift) |
1308 /* recall that we set ximage->byte_order = MSBFirst above */
1309 *dest++ = (char)((pixel >> 24) & 0xff);
1310 *dest++ = (char)((pixel >> 16) & 0xff);
1311 *dest++ = (char)((pixel >> 8) & 0xff);
1312 *dest++ = (char)( pixel & 0xff);
1315 for (i = rpng2_info.width; i > 0; --i) {
1319 pixel = (red << RShift) |
1322 /* recall that we set ximage->byte_order = MSBFirst above */
1323 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1324 /* (probably need to use RShift, RMask, etc.) */
1325 *dest++ = (char)((pixel >> 16) & 0xff);
1326 *dest++ = (char)((pixel >> 8) & 0xff);
1327 *dest++ = (char)( pixel & 0xff);
1332 } else if (depth == 16) {
1333 ush red, green, blue;
1335 for (row = 0; row < rpng2_info.height; ++row) {
1336 src = bg_data + row*bg_rowbytes;
1337 dest = ximage->data + row*ximage_rowbytes;
1338 for (i = rpng2_info.width; i > 0; --i) {
1339 red = ((ush)(*src) << 8); ++src;
1340 green = ((ush)(*src) << 8); ++src;
1341 blue = ((ush)(*src) << 8); ++src;
1342 pixel = ((red >> RShift) & RMask) |
1343 ((green >> GShift) & GMask) |
1344 ((blue >> BShift) & BMask);
1345 /* recall that we set ximage->byte_order = MSBFirst above */
1346 *dest++ = (char)((pixel >> 8) & 0xff);
1347 *dest++ = (char)( pixel & 0xff);
1351 } else /* depth == 8 */ {
1353 /* GRR: add 8-bit support */
1357 XPutImage(display, window, gc, ximage, 0, 0, 0, 0, rpng2_info.width,
1362 } /* end function rpng2_x_load_bg_image() */
1368 static void rpng2_x_display_row(ulg row)
1370 uch bg_red = rpng2_info.bg_red;
1371 uch bg_green = rpng2_info.bg_green;
1372 uch bg_blue = rpng2_info.bg_blue;
1373 uch *src, *src2=NULL;
1376 int ximage_rowbytes = ximage->bytes_per_line;
1378 static int rows=0, prevpass=(-1);
1379 static ulg firstrow;
1381 /*---------------------------------------------------------------------------
1382 rows and firstrow simply track how many rows (and which ones) have not
1383 yet been displayed; alternatively, we could call XPutImage() for every
1384 row and not bother with the records-keeping.
1385 ---------------------------------------------------------------------------*/
1387 Trace((stderr, "beginning rpng2_x_display_row()\n"))
1389 if (rpng2_info.pass != prevpass) {
1390 if (pause_after_pass && rpng2_info.pass > 0) {
1395 "%s: end of pass %d of 7; click in image window to continue\n",
1396 PROGNAME, prevpass + 1);
1398 XNextEvent(display, &e);
1401 fprintf(stderr, "%s: pass %d of 7\r", PROGNAME, rpng2_info.pass + 1);
1403 prevpass = rpng2_info.pass;
1407 firstrow = row; /* first row that is not yet displayed */
1409 ++rows; /* count of rows received but not yet displayed */
1411 /*---------------------------------------------------------------------------
1412 Aside from the use of the rpng2_info struct, the lack of an outer loop
1413 (over rows) and moving the XPutImage() call outside the "if (depth)"
1414 tests, this routine is identical to rpng_x_display_image() in the non-
1415 progressive version of the program.
1416 ---------------------------------------------------------------------------*/
1418 if (depth == 24 || depth == 32) {
1419 ulg red, green, blue;
1420 int bpp = ximage->bits_per_pixel;
1422 src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1424 src2 = bg_data + row*bg_rowbytes;
1425 dest = ximage->data + row*ximage_rowbytes;
1426 if (rpng2_info.channels == 3) {
1427 for (i = rpng2_info.width; i > 0; --i) {
1431 pixel = (red << RShift) |
1434 /* recall that we set ximage->byte_order = MSBFirst above */
1436 *dest++ = (char)((pixel >> 24) & 0xff);
1437 *dest++ = (char)((pixel >> 16) & 0xff);
1438 *dest++ = (char)((pixel >> 8) & 0xff);
1439 *dest++ = (char)( pixel & 0xff);
1441 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1442 /* (probably need to use RShift, RMask, etc.) */
1443 *dest++ = (char)((pixel >> 16) & 0xff);
1444 *dest++ = (char)((pixel >> 8) & 0xff);
1445 *dest++ = (char)( pixel & 0xff);
1448 } else /* if (rpng2_info.channels == 4) */ {
1449 for (i = rpng2_info.width; i > 0; --i) {
1463 } else if (a == 0) {
1468 /* this macro (from png.h) composites the foreground
1469 * and background values and puts the result into the
1471 alpha_composite(red, r, a, bg_red);
1472 alpha_composite(green, g, a, bg_green);
1473 alpha_composite(blue, b, a, bg_blue);
1475 pixel = (red << RShift) |
1478 /* recall that we set ximage->byte_order = MSBFirst above */
1480 *dest++ = (char)((pixel >> 24) & 0xff);
1481 *dest++ = (char)((pixel >> 16) & 0xff);
1482 *dest++ = (char)((pixel >> 8) & 0xff);
1483 *dest++ = (char)( pixel & 0xff);
1485 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1486 /* (probably need to use RShift, RMask, etc.) */
1487 *dest++ = (char)((pixel >> 16) & 0xff);
1488 *dest++ = (char)((pixel >> 8) & 0xff);
1489 *dest++ = (char)( pixel & 0xff);
1494 } else if (depth == 16) {
1495 ush red, green, blue;
1497 src = rpng2_info.row_pointers[row];
1499 src2 = bg_data + row*bg_rowbytes;
1500 dest = ximage->data + row*ximage_rowbytes;
1501 if (rpng2_info.channels == 3) {
1502 for (i = rpng2_info.width; i > 0; --i) {
1503 red = ((ush)(*src) << 8);
1505 green = ((ush)(*src) << 8);
1507 blue = ((ush)(*src) << 8);
1509 pixel = ((red >> RShift) & RMask) |
1510 ((green >> GShift) & GMask) |
1511 ((blue >> BShift) & BMask);
1512 /* recall that we set ximage->byte_order = MSBFirst above */
1513 *dest++ = (char)((pixel >> 8) & 0xff);
1514 *dest++ = (char)( pixel & 0xff);
1516 } else /* if (rpng2_info.channels == 4) */ {
1517 for (i = rpng2_info.width; i > 0; --i) {
1528 red = ((ush)r << 8);
1529 green = ((ush)g << 8);
1530 blue = ((ush)b << 8);
1531 } else if (a == 0) {
1532 red = ((ush)bg_red << 8);
1533 green = ((ush)bg_green << 8);
1534 blue = ((ush)bg_blue << 8);
1536 /* this macro (from png.h) composites the foreground
1537 * and background values and puts the result back into
1538 * the first argument (== fg byte here: safe) */
1539 alpha_composite(r, r, a, bg_red);
1540 alpha_composite(g, g, a, bg_green);
1541 alpha_composite(b, b, a, bg_blue);
1542 red = ((ush)r << 8);
1543 green = ((ush)g << 8);
1544 blue = ((ush)b << 8);
1546 pixel = ((red >> RShift) & RMask) |
1547 ((green >> GShift) & GMask) |
1548 ((blue >> BShift) & BMask);
1549 /* recall that we set ximage->byte_order = MSBFirst above */
1550 *dest++ = (char)((pixel >> 8) & 0xff);
1551 *dest++ = (char)( pixel & 0xff);
1555 } else /* depth == 8 */ {
1557 /* GRR: add 8-bit support */
1562 /*---------------------------------------------------------------------------
1563 Display after every 16 rows or when on one of last two rows. (Region
1564 may include previously displayed lines due to interlacing--i.e., not
1565 contiguous. Also, second-to-last row is final one in interlaced images
1566 with odd number of rows.) For demos, flush (and delay) after every 16th
1567 row so "sparse" passes don't go twice as fast.
1568 ---------------------------------------------------------------------------*/
1570 if (demo_timing && (row - firstrow >= 16 || row >= rpng2_info.height-2)) {
1571 XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1572 (int)firstrow, rpng2_info.width, row - firstrow + 1);
1575 usleep(usleep_duration);
1577 if (!demo_timing && ((rows & 0xf) == 0 || row >= rpng2_info.height-2)) {
1578 XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1579 (int)firstrow, rpng2_info.width, row - firstrow + 1);
1590 static void rpng2_x_finish_display(void)
1592 Trace((stderr, "beginning rpng2_x_finish_display()\n"))
1594 /* last row has already been displayed by rpng2_x_display_row(), so we
1595 * have nothing to do here except set a flag and let the user know that
1596 * the image is done */
1598 rpng2_info.state = kDone;
1600 "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
1608 static void rpng2_x_redisplay_image(ulg startcol, ulg startrow,
1609 ulg width, ulg height)
1611 uch bg_red = rpng2_info.bg_red;
1612 uch bg_green = rpng2_info.bg_green;
1613 uch bg_blue = rpng2_info.bg_blue;
1614 uch *src, *src2=NULL;
1617 ulg i, row, lastrow = 0;
1619 int ximage_rowbytes = ximage->bytes_per_line;
1622 Trace((stderr, "beginning display loop (image_channels == %d)\n",
1623 rpng2_info.channels))
1624 Trace((stderr, " (width = %ld, rowbytes = %d, ximage_rowbytes = %d)\n",
1625 rpng2_info.width, rpng2_info.rowbytes, ximage_rowbytes))
1626 Trace((stderr, " (bpp = %d)\n", ximage->bits_per_pixel))
1627 Trace((stderr, " (byte_order = %s)\n", ximage->byte_order == MSBFirst?
1628 "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown")))
1630 /*---------------------------------------------------------------------------
1631 Aside from the use of the rpng2_info struct and of src2 (for background
1632 image), this routine is identical to rpng_x_display_image() in the non-
1633 progressive version of the program--for the simple reason that redisplay
1634 of the image against a new background happens after the image is fully
1635 decoded and therefore is, by definition, non-progressive.
1636 ---------------------------------------------------------------------------*/
1638 if (depth == 24 || depth == 32) {
1639 ulg red, green, blue;
1640 int bpp = ximage->bits_per_pixel;
1642 for (lastrow = row = startrow; row < startrow+height; ++row) {
1643 src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1645 src2 = bg_data + row*bg_rowbytes;
1646 dest = ximage->data + row*ximage_rowbytes;
1647 if (rpng2_info.channels == 3) {
1648 for (i = rpng2_info.width; i > 0; --i) {
1652 #ifdef NO_24BIT_MASKS
1653 pixel = (red << RShift) |
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);
1663 /* this assumes bpp == 24 & bits are packed low */
1664 /* (probably need to use RShift, RMask, etc.) */
1665 *dest++ = (char)((pixel >> 16) & 0xff);
1666 *dest++ = (char)((pixel >> 8) & 0xff);
1667 *dest++ = (char)( pixel & 0xff);
1670 red = (RShift < 0)? red << (-RShift) : red >> RShift;
1671 green = (GShift < 0)? green << (-GShift) : green >> GShift;
1672 blue = (BShift < 0)? blue << (-BShift) : blue >> BShift;
1673 pixel = (red & RMask) | (green & GMask) | (blue & BMask);
1674 /* recall that we set ximage->byte_order = MSBFirst above */
1676 *dest++ = (char)((pixel >> 24) & 0xff);
1677 *dest++ = (char)((pixel >> 16) & 0xff);
1678 *dest++ = (char)((pixel >> 8) & 0xff);
1679 *dest++ = (char)( pixel & 0xff);
1682 /* this assumes bpp == 24 & bits are packed low */
1683 /* (probably need to use RShift/RMask/etc. here, too) */
1684 *dest++ = (char)((pixel >> 16) & 0xff);
1685 *dest++ = (char)((pixel >> 8) & 0xff);
1686 *dest++ = (char)( pixel & 0xff);
1691 } else /* if (rpng2_info.channels == 4) */ {
1692 for (i = rpng2_info.width; i > 0; --i) {
1706 } else if (a == 0) {
1711 /* this macro (from png.h) composites the foreground
1712 * and background values and puts the result into the
1714 alpha_composite(red, r, a, bg_red);
1715 alpha_composite(green, g, a, bg_green);
1716 alpha_composite(blue, b, a, bg_blue);
1718 #ifdef NO_24BIT_MASKS
1719 pixel = (red << RShift) |
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);
1729 /* this assumes bpp == 24 & bits are packed low */
1730 /* (probably need to use RShift, RMask, etc.) */
1731 *dest++ = (char)((pixel >> 16) & 0xff);
1732 *dest++ = (char)((pixel >> 8) & 0xff);
1733 *dest++ = (char)( pixel & 0xff);
1736 red = (RShift < 0)? red << (-RShift) : red >> RShift;
1737 green = (GShift < 0)? green << (-GShift) : green >> GShift;
1738 blue = (BShift < 0)? blue << (-BShift) : blue >> BShift;
1739 pixel = (red & RMask) | (green & GMask) | (blue & BMask);
1740 /* recall that we set ximage->byte_order = MSBFirst above */
1742 *dest++ = (char)((pixel >> 24) & 0xff);
1743 *dest++ = (char)((pixel >> 16) & 0xff);
1744 *dest++ = (char)((pixel >> 8) & 0xff);
1745 *dest++ = (char)( pixel & 0xff);
1748 /* this assumes bpp == 24 & bits are packed low */
1749 /* (probably need to use RShift/RMask/etc. here, too) */
1750 *dest++ = (char)((pixel >> 16) & 0xff);
1751 *dest++ = (char)((pixel >> 8) & 0xff);
1752 *dest++ = (char)( pixel & 0xff);
1757 /* display after every 16 lines */
1758 if (((row+1) & 0xf) == 0) {
1759 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1760 (int)lastrow, rpng2_info.width, 16);
1766 } else if (depth == 16) {
1767 ush red, green, blue;
1769 for (lastrow = row = startrow; row < startrow+height; ++row) {
1770 src = rpng2_info.row_pointers[row];
1772 src2 = bg_data + row*bg_rowbytes;
1773 dest = ximage->data + row*ximage_rowbytes;
1774 if (rpng2_info.channels == 3) {
1775 for (i = rpng2_info.width; i > 0; --i) {
1776 red = ((ush)(*src) << 8);
1778 green = ((ush)(*src) << 8);
1780 blue = ((ush)(*src) << 8);
1782 pixel = ((red >> RShift) & RMask) |
1783 ((green >> GShift) & GMask) |
1784 ((blue >> BShift) & BMask);
1785 /* recall that we set ximage->byte_order = MSBFirst above */
1786 *dest++ = (char)((pixel >> 8) & 0xff);
1787 *dest++ = (char)( pixel & 0xff);
1789 } else /* if (rpng2_info.channels == 4) */ {
1790 for (i = rpng2_info.width; i > 0; --i) {
1801 red = ((ush)r << 8);
1802 green = ((ush)g << 8);
1803 blue = ((ush)b << 8);
1804 } else if (a == 0) {
1805 red = ((ush)bg_red << 8);
1806 green = ((ush)bg_green << 8);
1807 blue = ((ush)bg_blue << 8);
1809 /* this macro (from png.h) composites the foreground
1810 * and background values and puts the result back into
1811 * the first argument (== fg byte here: safe) */
1812 alpha_composite(r, r, a, bg_red);
1813 alpha_composite(g, g, a, bg_green);
1814 alpha_composite(b, b, a, bg_blue);
1815 red = ((ush)r << 8);
1816 green = ((ush)g << 8);
1817 blue = ((ush)b << 8);
1819 pixel = ((red >> RShift) & RMask) |
1820 ((green >> GShift) & GMask) |
1821 ((blue >> BShift) & BMask);
1822 /* recall that we set ximage->byte_order = MSBFirst above */
1823 *dest++ = (char)((pixel >> 8) & 0xff);
1824 *dest++ = (char)( pixel & 0xff);
1827 /* display after every 16 lines */
1828 if (((row+1) & 0xf) == 0) {
1829 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1830 (int)lastrow, rpng2_info.width, 16);
1836 } else /* depth == 8 */ {
1838 /* GRR: add 8-bit support */
1842 Trace((stderr, "calling final XPutImage()\n"))
1843 if (lastrow < startrow+height) {
1844 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1845 (int)lastrow, rpng2_info.width, rpng2_info.height-lastrow);
1849 } /* end function rpng2_x_redisplay_image() */
1857 static void rpng2_x_reload_bg_image(void)
1860 uch r1, r2, g1, g2, b1, b2;
1861 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
1863 int xidx, yidx, yidx_max;
1864 int even_odd_vert, even_odd_horiz, even_odd;
1865 int invert_gradient2 = (bg[pat].type & 0x08);
1870 bgscale = (pat == 0)? 8 : bgscale_default;
1871 yidx_max = bgscale - 1;
1873 /*---------------------------------------------------------------------------
1874 Vertical gradients (ramps) in NxN squares, alternating direction and
1875 colors (N == bgscale).
1876 ---------------------------------------------------------------------------*/
1878 if ((bg[pat].type & 0x07) == 0) {
1879 uch r1_min = rgb[bg[pat].rgb1_min].r;
1880 uch g1_min = rgb[bg[pat].rgb1_min].g;
1881 uch b1_min = rgb[bg[pat].rgb1_min].b;
1882 uch r2_min = rgb[bg[pat].rgb2_min].r;
1883 uch g2_min = rgb[bg[pat].rgb2_min].g;
1884 uch b2_min = rgb[bg[pat].rgb2_min].b;
1885 int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
1886 int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
1887 int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
1888 int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
1889 int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
1890 int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
1892 for (row = 0; row < rpng2_info.height; ++row) {
1893 yidx = (int)(row % bgscale);
1894 even_odd_vert = (int)((row / bgscale) & 1);
1896 r1 = r1_min + (r1_diff * yidx) / yidx_max;
1897 g1 = g1_min + (g1_diff * yidx) / yidx_max;
1898 b1 = b1_min + (b1_diff * yidx) / yidx_max;
1899 r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
1900 g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
1901 b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
1903 r2 = r2_min + (r2_diff * yidx) / yidx_max;
1904 g2 = g2_min + (g2_diff * yidx) / yidx_max;
1905 b2 = b2_min + (b2_diff * yidx) / yidx_max;
1906 r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
1907 g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
1908 b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
1910 dest = (char *)bg_data + row*bg_rowbytes;
1911 for (i = 0; i < rpng2_info.width; ++i) {
1912 even_odd_horiz = (int)((i / bgscale) & 1);
1913 even_odd = even_odd_vert ^ even_odd_horiz;
1915 (even_odd_horiz && (bg[pat].type & 0x10));
1916 if (even_odd == 0) { /* gradient #1 */
1917 if (invert_column) {
1926 } else { /* gradient #2 */
1927 if ((invert_column && invert_gradient2) ||
1928 (!invert_column && !invert_gradient2))
1930 *dest++ = r2; /* not inverted or */
1931 *dest++ = g2; /* doubly inverted */
1935 *dest++ = g2_inv; /* singly inverted */
1942 /*---------------------------------------------------------------------------
1943 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
1945 ---------------------------------------------------------------------------*/
1947 } else if ((bg[pat].type & 0x07) == 1) {
1949 hmax = (bgscale-1)/2; /* half the max weight of a color */
1950 max = 2*hmax; /* the max weight of a color */
1952 r1 = rgb[bg[pat].rgb1_max].r;
1953 g1 = rgb[bg[pat].rgb1_max].g;
1954 b1 = rgb[bg[pat].rgb1_max].b;
1955 r2 = rgb[bg[pat].rgb2_max].r;
1956 g2 = rgb[bg[pat].rgb2_max].g;
1957 b2 = rgb[bg[pat].rgb2_max].b;
1959 for (row = 0; row < rpng2_info.height; ++row) {
1960 yidx = (int)(row % bgscale);
1962 yidx = bgscale-1 - yidx;
1963 dest = (char *)bg_data + row*bg_rowbytes;
1964 for (i = 0; i < rpng2_info.width; ++i) {
1965 xidx = (int)(i % bgscale);
1967 xidx = bgscale-1 - xidx;
1969 *dest++ = (k*r1 + (max-k)*r2) / max;
1970 *dest++ = (k*g1 + (max-k)*g2) / max;
1971 *dest++ = (k*b1 + (max-k)*b2) / max;
1975 /*---------------------------------------------------------------------------
1976 Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
1977 soids will equal bgscale?]. This one is slow but very cool. Code con-
1978 tributed by Pieter S. van der Meulen (originally in Smalltalk).
1979 ---------------------------------------------------------------------------*/
1981 } else if ((bg[pat].type & 0x07) == 2) {
1983 int ii, x, y, hw, hh, grayspot;
1984 double freq, rotate, saturate, gray, intensity;
1985 double angle=0.0, aoffset=0.0, maxDist, dist;
1986 double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
1988 hh = (int)(rpng2_info.height / 2);
1989 hw = (int)(rpng2_info.width / 2);
1991 /* variables for radial waves:
1992 * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED]
1993 * freq: number of color beams originating from the center
1994 * grayspot: size of the graying center area (anti-alias)
1995 * rotate: rotation of the beams as a function of radius
1996 * saturate: saturation of beams' shape azimuthally
1998 angle = CLIP(angle, 0.0, 360.0);
1999 grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
2000 freq = MAX((double)bg[pat].bg_freq, 0.0);
2001 saturate = (double)bg[pat].bg_bsat * 0.1;
2002 rotate = (double)bg[pat].bg_brot * 0.1;
2005 maxDist = (double)((hw*hw) + (hh*hh));
2007 for (row = 0; row < rpng2_info.height; ++row) {
2008 y = (int)(row - hh);
2009 dest = (char *)bg_data + row*bg_rowbytes;
2010 for (i = 0; i < rpng2_info.width; ++i) {
2012 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
2013 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
2014 gray = MIN(1.0, gray);
2015 dist = (double)((x*x) + (y*y)) / maxDist;
2016 intensity = cos((angle+(rotate*dist*PI)) * freq) *
2018 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
2019 hue = (angle + PI) * INV_PI_360 + aoffset;
2020 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
2021 s = MIN(MAX(s,0.0), 1.0);
2022 v = MIN(MAX(intensity,0.0), 1.0);
2025 ch = (uch)(v * 255.0);
2030 if ((hue < 0.0) || (hue >= 360.0))
2031 hue -= (((int)(hue / 360.0)) * 360.0);
2034 f = hue - (double)ii;
2036 q = (1.0 - (s * f)) * v;
2037 t = (1.0 - (s * (1.0 - f))) * v;
2038 if (ii == 0) { red = v; green = t; blue = p; }
2039 else if (ii == 1) { red = q; green = v; blue = p; }
2040 else if (ii == 2) { red = p; green = v; blue = t; }
2041 else if (ii == 3) { red = p; green = q; blue = v; }
2042 else if (ii == 4) { red = t; green = p; blue = v; }
2043 else if (ii == 5) { red = v; green = p; blue = q; }
2044 *dest++ = (uch)(red * 255.0);
2045 *dest++ = (uch)(green * 255.0);
2046 *dest++ = (uch)(blue * 255.0);
2052 } /* end function rpng2_x_reload_bg_image() */
2058 static int is_number(char *p)
2068 #endif /* FEATURE_LOOP */
2074 static void rpng2_x_cleanup(void)
2076 if (bg_image && bg_data) {
2081 if (rpng2_info.image_data) {
2082 free(rpng2_info.image_data);
2083 rpng2_info.image_data = NULL;
2086 if (rpng2_info.row_pointers) {
2087 free(rpng2_info.row_pointers);
2088 rpng2_info.row_pointers = NULL;
2093 free(ximage->data); /* we allocated it, so we free it */
2094 ximage->data = (char *)NULL; /* instead of XDestroyImage() */
2096 XDestroyImage(ximage);
2101 XFreeGC(display, gc);
2104 XDestroyWindow(display, window);
2107 XFreeColormap(display, colormap);
2109 if (have_nondefault_visual)
2117 static int rpng2_x_msb(ulg u32val)
2121 for (i = 31; i >= 0; --i) {
2122 if (u32val & 0x80000000L)