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
44 - 2.04: Added "void(foo);" statements to quiet pedantic compiler warnings
45 about unused variables (GR-P)
48 use nanosleep() instead of usleep(), which is obsolete/deprecated.
49 ---------------------------------------------------------------------------
51 Copyright (c) 1998-2008 Greg Roelofs. All rights reserved.
53 This software is provided "as is," without warranty of any kind,
54 express or implied. In no event shall the author or contributors
55 be held liable for any damages arising in any way from the use of
58 The contents of this file are DUAL-LICENSED. You may modify and/or
59 redistribute this software according to the terms of one of the
60 following two licenses (at your option):
63 LICENSE 1 ("BSD-like with advertising clause"):
65 Permission is granted to anyone to use this software for any purpose,
66 including commercial applications, and to alter it and redistribute
67 it freely, subject to the following restrictions:
69 1. Redistributions of source code must retain the above copyright
70 notice, disclaimer, and this list of conditions.
71 2. Redistributions in binary form must reproduce the above copyright
72 notice, disclaimer, and this list of conditions in the documenta-
73 tion and/or other materials provided with the distribution.
74 3. All advertising materials mentioning features or use of this
75 software must display the following acknowledgment:
77 This product includes software developed by Greg Roelofs
78 and contributors for the book, "PNG: The Definitive Guide,"
79 published by O'Reilly and Associates.
82 LICENSE 2 (GNU GPL v2 or later):
84 This program is free software; you can redistribute it and/or modify
85 it under the terms of the GNU General Public License as published by
86 the Free Software Foundation; either version 2 of the License, or
87 (at your option) any later version.
89 This program is distributed in the hope that it will be useful,
90 but WITHOUT ANY WARRANTY; without even the implied warranty of
91 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
92 GNU General Public License for more details.
94 You should have received a copy of the GNU General Public License
95 along with this program; if not, write to the Free Software Foundation,
96 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
98 ---------------------------------------------------------------------------*/
100 #define PROGNAME "rpng2-x"
101 #define LONGNAME "Progressive PNG Viewer for X"
102 #define VERSION "2.04 of 15 June 2014"
103 #define RESNAME "rpng2" /* our X resource application name */
104 #define RESCLASS "Rpng" /* our X resource class name */
106 /* This is temporary until the code is rewritten to use nanosleep(). */
107 #define usleep(x) sleep(((x)+499999)/1000000)
113 #include <setjmp.h> /* for jmpbuf declaration in readpng2.h */
115 #include <math.h> /* only for PvdM background code */
116 #include <X11/Xlib.h>
117 #include <X11/Xutil.h>
119 #include <X11/keysym.h> /* defines XK_* macros */
125 /* all for PvdM background code: */
127 # define PI 3.141592653589793238
129 #define PI_2 (PI*0.5)
130 #define INV_PI_360 (360.0 / PI)
131 #define MAX(a,b) (a>b?a:b)
132 #define MIN(a,b) (a<b?a:b)
133 #define CLIP(a,min,max) MAX(min,MIN((a),max))
134 #define ABS(a) ((a)<0?-(a):(a))
135 #define CLIP8P(c) MAX(0,(MIN((c),255))) /* 8-bit pos. integer (uch) */
136 #define ROUNDF(f) ((int)(f + 0.5))
138 #define QUIT(e,k) ((e.type == ButtonPress && e.xbutton.button == Button1) || \
139 (e.type == KeyPress && /* v--- or 1 for shifted keys */ \
140 ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape)))
142 #define NO_24BIT_MASKS /* undef case not fully written--only for redisplay() */
144 #define rgb1_max bg_freq
145 #define rgb1_min bg_gray
146 #define rgb2_max bg_bsat
147 #define rgb2_min bg_brot
149 /* #define DEBUG */ /* this enables the Trace() macros */
151 #include "readpng2.h" /* typedefs, common macros, readpng2 prototypes */
154 /* could just include png.h, but this macro is the only thing we need
155 * (name and typedefs changed to local versions); note that side effects
156 * only happen with alpha (which could easily be avoided with
157 * "ush acopy = (alpha);") */
159 #define alpha_composite(composite, fg, alpha, bg) { \
160 ush temp = ((ush)(fg)*(ush)(alpha) + \
161 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \
162 (composite) = (uch)((temp + (temp >> 8)) >> 8); \
166 #define INBUFSIZE 4096 /* with pseudo-timing on (1 sec delay/block), this
167 * block size corresponds roughly to a download
168 * speed 10% faster than theoretical 33.6K maximum
169 * (assuming 8 data bits, 1 stop bit and no other
172 /* local prototypes */
173 static void rpng2_x_init (void);
174 static int rpng2_x_create_window (void);
175 static int rpng2_x_load_bg_image (void);
176 static void rpng2_x_display_row (ulg row);
177 static void rpng2_x_finish_display (void);
178 static void rpng2_x_redisplay_image (ulg startcol, ulg startrow,
179 ulg width, ulg height);
181 static void rpng2_x_reload_bg_image (void);
182 static int is_number (char *p);
184 static void rpng2_x_cleanup (void);
185 static int rpng2_x_msb (ulg u32val);
188 static char titlebar[1024], *window_name = titlebar;
189 static char *appname = LONGNAME;
190 static char *icon_name = PROGNAME;
191 static char *res_name = RESNAME;
192 static char *res_class = RESCLASS;
193 static char *filename;
196 static mainprog_info rpng2_info;
198 static uch inbuf[INBUFSIZE];
201 static int pat = 6; /* must be less than num_bgpat */
202 static int bg_image = 0;
203 static int bgscale, bgscale_default = 16;
204 static ulg bg_rowbytes;
207 int pause_after_pass = FALSE;
208 int demo_timing = FALSE;
209 ulg usleep_duration = 0L;
211 static struct rgb_color {
214 { 0, 0, 0}, /* 0: black */
215 {255, 255, 255}, /* 1: white */
216 {173, 132, 57}, /* 2: tan */
217 { 64, 132, 0}, /* 3: medium green */
218 {189, 117, 1}, /* 4: gold */
219 {253, 249, 1}, /* 5: yellow */
220 { 0, 0, 255}, /* 6: blue */
221 { 0, 0, 120}, /* 7: medium blue */
222 {255, 0, 255}, /* 8: magenta */
223 { 64, 0, 64}, /* 9: dark magenta */
224 {255, 0, 0}, /* 10: red */
225 { 64, 0, 0}, /* 11: dark red */
226 {255, 127, 0}, /* 12: orange */
227 {192, 96, 0}, /* 13: darker orange */
228 { 24, 60, 0}, /* 14: dark green-yellow */
229 { 85, 125, 200}, /* 15: ice blue */
230 {192, 192, 192} /* 16: Netscape/Mosaic gray */
232 /* not used for now, but should be for error-checking:
233 static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
237 This whole struct is a fairly cheesy way to keep the number of
238 command-line options to a minimum. The radial-waves background
239 type is a particularly poor fit to the integer elements of the
240 struct...but a few macros and a little fixed-point math will do
244 F E D C B A 9 8 7 6 5 4 3 2 1 0
246 | | +-+-+-- 0 = sharp-edged checkerboard
247 | | 1 = soft diamonds
250 | +-- gradient #2 inverted?
251 +-- alternating columns inverted?
253 static struct background_pattern {
255 int rgb1_max, rgb1_min; /* or bg_freq, bg_gray */
256 int rgb2_max, rgb2_min; /* or bg_bsat, bg_brot (both scaled by 10)*/
258 {0, 1,1, 16,16}, /* checkered: white vs. light gray (basic) */
259 {0+8, 2,0, 1,15}, /* checkered: tan/black vs. white/ice blue */
260 {0+24, 2,0, 1,0}, /* checkered: tan/black vs. white/black */
261 {0+8, 4,5, 0,2}, /* checkered: gold/yellow vs. black/tan */
262 {0+8, 4,5, 0,6}, /* checkered: gold/yellow vs. black/blue */
263 {0, 7,0, 8,9}, /* checkered: deep blue/black vs. magenta */
264 {0+8, 13,0, 5,14}, /* checkered: orange/black vs. yellow */
265 {0+8, 12,0, 10,11}, /* checkered: orange/black vs. red */
266 {1, 7,0, 8,0}, /* diamonds: deep blue/black vs. magenta */
267 {1, 12,0, 11,0}, /* diamonds: orange vs. dark red */
268 {1, 10,0, 7,0}, /* diamonds: red vs. medium blue */
269 {1, 4,0, 5,0}, /* diamonds: gold vs. yellow */
270 {1, 3,0, 0,0}, /* diamonds: medium green vs. black */
271 {2, 16, 100, 20, 0}, /* radial: ~hard radial color-beams */
272 {2, 18, 100, 10, 2}, /* radial: soft, curved radial color-beams */
273 {2, 16, 256, 100, 250}, /* radial: very tight spiral */
274 {2, 10000, 256, 11, 0} /* radial: dipole-moire' (almost fractal) */
276 static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
279 /* X-specific variables */
280 static char *displayname;
281 static XImage *ximage;
282 static Display *display;
284 static Visual *visual;
285 static XVisualInfo *visual_list;
286 static int RShift, GShift, BShift;
287 static ulg RMask, GMask, BMask;
288 static Window window;
290 static Colormap colormap;
292 static int have_nondefault_visual = FALSE;
293 static int have_colormap = FALSE;
294 static int have_window = FALSE;
295 static int have_gc = FALSE;
300 int main(int argc, char **argv)
305 char *p, *bgstr = NULL;
312 long loop_interval = -1; /* seconds (100,000 max) */
314 double LUT_exponent; /* just the lookup table */
315 double CRT_exponent = 2.2; /* just the monitor */
316 double default_display_exponent; /* whole display system */
321 /* First initialize a few things, just to be sure--memset takes care of
322 * default background color (black), booleans (FALSE), pointers (NULL),
325 displayname = (char *)NULL;
326 filename = (char *)NULL;
327 memset(&rpng2_info, 0, sizeof(mainprog_info));
330 /* Set the default value for our display-system exponent, i.e., the
331 * product of the CRT exponent and the exponent corresponding to
332 * the frame-buffer's lookup table (LUT), if any. This is not an
333 * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
334 * ones), but it should cover 99% of the current possibilities. */
337 /* third-party utilities can modify the default LUT exponent */
338 LUT_exponent = 1.0 / 2.2;
340 if (some_next_function_that_returns_gamma(&next_gamma))
341 LUT_exponent = 1.0 / next_gamma;
344 LUT_exponent = 1.0 / 1.7;
345 /* there doesn't seem to be any documented function to
346 * get the "gamma" value, so we do it the hard way */
347 infile = fopen("/etc/config/system.glGammaVal", "r");
351 fgets(tmpline, 80, infile);
353 sgi_gamma = atof(tmpline);
355 LUT_exponent = 1.0 / sgi_gamma;
357 #elif defined(Macintosh)
358 LUT_exponent = 1.8 / 2.61;
360 if (some_mac_function_that_returns_gamma(&mac_gamma))
361 LUT_exponent = mac_gamma / 2.61;
364 LUT_exponent = 1.0; /* assume no LUT: most PCs */
367 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
368 default_display_exponent = LUT_exponent * CRT_exponent;
371 /* If the user has set the SCREEN_GAMMA environment variable as suggested
372 * (somewhat imprecisely) in the libpng documentation, use that; otherwise
373 * use the default value we just calculated. Either way, the user may
374 * override this via a command-line option. */
376 if ((p = getenv("SCREEN_GAMMA")) != NULL)
377 rpng2_info.display_exponent = atof(p);
379 rpng2_info.display_exponent = default_display_exponent;
382 /* Now parse the command line for options and the PNG filename. */
384 while (*++argv && !error) {
385 if (!strncmp(*argv, "-display", 2)) {
390 } else if (!strncmp(*argv, "-gamma", 2)) {
394 rpng2_info.display_exponent = atof(*argv);
395 if (rpng2_info.display_exponent <= 0.0)
398 } else if (!strncmp(*argv, "-bgcolor", 4)) {
403 if (strlen(bgstr) != 7 || bgstr[0] != '#')
410 } else if (!strncmp(*argv, "-bgpat", 4)) {
415 if (pat >= 0 && pat < num_bgpat) {
421 } else if (!strncmp(*argv, "-usleep", 2)) {
425 usleep_duration = (ulg)atol(*argv);
428 } else if (!strncmp(*argv, "-pause", 2)) {
429 pause_after_pass = TRUE;
430 } else if (!strncmp(*argv, "-timing", 2)) {
433 } else if (!strncmp(*argv, "-loop", 2)) {
435 if (!argv[1] || !is_number(argv[1]))
439 loop_interval = atol(*argv);
440 if (loop_interval < 0)
442 else if (loop_interval > 100000) /* bit more than one day */
443 loop_interval = 100000;
449 if (argv[1]) /* shouldn't be any more args after filename */
452 ++error; /* not expecting any other options */
460 /* print usage screen if any errors up to this point */
463 fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname);
464 readpng2_version_info();
468 "%s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
469 " %*s [-usleep dur | -timing] [-pause]\n",
470 PROGNAME, (int)strlen(PROGNAME), " ");
477 " xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
478 " exp \ttransfer-function exponent (``gamma'') of the display\n"
479 "\t\t system in floating-point format (e.g., ``%.1f''); equal\n"
480 "\t\t to the product of the lookup-table exponent (varies)\n",
481 default_display_exponent);
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",
492 " -loop\tloops through background images after initial display\n"
493 "\t\t is complete (depends on -bgpat)\n"
494 " sec \tseconds to display each background image (default = 2)\n");
497 " dur \tduration in microseconds to wait after displaying each\n"
498 "\t\t row (for demo purposes)\n"
499 " -timing\tenables delay for every block read, to simulate modem\n"
500 "\t\t download of image (~36 Kbps)\n"
501 " -pause\tpauses after displaying each pass until mouse clicked\n"
502 "\nPress Q, Esc or mouse button 1 (within image window, after image\n"
503 "is displayed) to quit.\n");
507 if (!(infile = fopen(filename, "rb"))) {
508 fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename);
511 incount = fread(inbuf, 1, INBUFSIZE, infile);
512 if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
513 fprintf(stderr, PROGNAME
514 ": [%s] is not a PNG file: incorrect signature\n",
517 } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
520 fprintf(stderr, PROGNAME
521 ": [%s] has bad IHDR (libpng longjmp)\n", filename);
524 fprintf(stderr, PROGNAME ": insufficient memory\n");
527 fprintf(stderr, PROGNAME
528 ": unknown readpng2_init() error\n");
533 Trace((stderr, "about to call XOpenDisplay()\n"))
534 display = XOpenDisplay(displayname);
536 readpng2_cleanup(&rpng2_info);
537 fprintf(stderr, PROGNAME ": can't open X display [%s]\n",
538 displayname? displayname : "default");
548 fprintf(stderr, PROGNAME ": aborting.\n");
553 /* set the title-bar string, but make sure buffer doesn't overflow */
555 alen = strlen(appname);
556 flen = strlen(filename);
557 if (alen + flen + 3 > 1023)
558 sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023));
560 sprintf(titlebar, "%s: %s", appname, filename);
563 /* set some final rpng2_info variables before entering main data loop */
566 unsigned r, g, b; /* this approach quiets compiler warnings */
568 sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
569 rpng2_info.bg_red = (uch)r;
570 rpng2_info.bg_green = (uch)g;
571 rpng2_info.bg_blue = (uch)b;
573 rpng2_info.need_bgcolor = TRUE;
575 rpng2_info.state = kPreInit;
576 rpng2_info.mainprog_init = rpng2_x_init;
577 rpng2_info.mainprog_display_row = rpng2_x_display_row;
578 rpng2_info.mainprog_finish_display = rpng2_x_finish_display;
581 /* OK, this is the fun part: call readpng2_decode_data() at the start of
582 * the loop to deal with our first buffer of data (read in above to verify
583 * that the file is a PNG image), then loop through the file and continue
584 * calling the same routine to handle each chunk of data. It in turn
585 * passes the data to libpng, which will invoke one or more of our call-
586 * backs as decoded data become available. We optionally call sleep() for
587 * one second per iteration to simulate downloading the image via an analog
591 Trace((stderr, "about to call readpng2_decode_data()\n"))
592 if (readpng2_decode_data(&rpng2_info, inbuf, incount))
594 Trace((stderr, "done with readpng2_decode_data()\n"))
596 if (error || incount != INBUFSIZE || rpng2_info.state == kDone) {
597 if (rpng2_info.state == kDone) {
598 Trace((stderr, "done decoding PNG image\n"))
599 } else if (ferror(infile)) {
600 fprintf(stderr, PROGNAME
601 ": error while reading PNG image file\n");
603 } else if (feof(infile)) {
604 fprintf(stderr, PROGNAME ": end of file reached "
605 "(unexpectedly) while reading PNG image file\n");
607 } else /* if (error) */ {
608 /* will print error message below */
616 incount = fread(inbuf, 1, INBUFSIZE, infile);
620 /* clean up PNG stuff and report any decoding errors */
623 Trace((stderr, "about to call readpng2_cleanup()\n"))
624 readpng2_cleanup(&rpng2_info);
627 fprintf(stderr, PROGNAME ": libpng error while decoding PNG image\n");
634 if (loop && bg_image) {
635 Trace((stderr, "entering -loop loop (FEATURE_LOOP)\n"))
638 struct timeval now, then;
640 /* get current time and add loop_interval to get target time */
641 if (gettimeofday(&then, NULL) == 0) {
642 then.tv_sec += loop_interval;
647 /* do quick check for a quit event but don't wait for it */
648 /* GRR BUG: should also check for Expose events and redraw... */
649 if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask, &e))
653 /* generate next background image */
654 if (++pat >= num_bgpat)
656 rpng2_x_reload_bg_image();
658 /* wait for timeout, using whatever means are available */
659 if (use_sleep || gettimeofday(&now, NULL) != 0) {
660 for (i = loop_interval; i > 0; --i) {
662 /* GRR BUG: also need to check for Expose (and redraw!) */
663 if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask,
669 if (now.tv_sec < then.tv_sec ||
670 (now.tv_sec == then.tv_sec && now.tv_usec < then.tv_usec))
673 long seconds_to_go = then.tv_sec - now.tv_sec;
676 /* basically chew up most of remaining loop-interval with
677 * calls to sleep(1) interleaved with checks for quit
678 * events, but also recalc time-to-go periodically; when
679 * done, clean up any remaining time with usleep() call
680 * (could also use SIGALRM, but signals are a pain...) */
681 while (seconds_to_go-- > 1) {
682 int seconds_done = 0;
684 for (i = seconds_to_go; i > 0 && !quit; --i) {
686 /* GRR BUG: need to check for Expose and redraw */
687 if (XCheckMaskEvent(display, KeyPressMask |
688 ButtonPressMask, &e) && QUIT(e,k))
690 if (++seconds_done > 1000)
691 break; /* time to redo seconds_to_go meas. */
696 /* OK, more than 1000 seconds since last check:
697 * correct the time-to-go measurement for drift */
698 if (gettimeofday(&now, NULL) == 0) {
699 if (now.tv_sec >= then.tv_sec)
701 seconds_to_go = then.tv_sec - now.tv_sec;
703 ++seconds_to_go; /* restore what we subtracted */
706 break; /* breaks outer do-loop, skips redisplay */
708 /* since difference between "now" and "then" is already
709 * eaten up to within a couple of seconds, don't need to
710 * worry about overflow--but might have overshot (neg.) */
711 if (gettimeofday(&now, NULL) == 0) {
712 usleep_usec = 1000000L*(then.tv_sec - now.tv_sec) +
713 then.tv_usec - now.tv_usec;
715 usleep((ulg)usleep_usec);
720 /* composite image against new background and display (note that
721 * we do not take into account the time spent doing this...) */
722 rpng2_x_redisplay_image (0, 0, rpng2_info.width, rpng2_info.height);
725 } else /* FALL THROUGH and do the normal thing */
727 #endif /* FEATURE_LOOP */
729 /* wait for the user to tell us when to quit */
731 if (rpng2_info.state >= kWindowInit) {
732 Trace((stderr, "entering final wait-for-quit-event loop\n"))
734 XNextEvent(display, &e);
735 if (e.type == Expose) {
736 XExposeEvent *ex = (XExposeEvent *)&e;
737 rpng2_x_redisplay_image (ex->x, ex->y, ex->width, ex->height);
739 } while (!QUIT(e,k));
741 fprintf(stderr, PROGNAME ": init callback never called: probable "
742 "libpng error while decoding PNG metadata\n");
747 /* we're done: clean up all image and X resources and go away */
749 Trace((stderr, "about to call rpng2_x_cleanup()\n"))
752 (void)argc; /* Unused */
761 /* this function is called by readpng2_info_callback() in readpng2.c, which
762 * in turn is called by libpng after all of the pre-IDAT chunks have been
763 * read and processed--i.e., we now have enough info to finish initializing */
765 static void rpng2_x_init(void)
768 ulg rowbytes = rpng2_info.rowbytes;
770 Trace((stderr, "beginning rpng2_x_init()\n"))
771 Trace((stderr, " rowbytes = %d\n", rpng2_info.rowbytes))
772 Trace((stderr, " width = %ld\n", rpng2_info.width))
773 Trace((stderr, " height = %ld\n", rpng2_info.height))
775 rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
776 if (!rpng2_info.image_data) {
777 readpng2_cleanup(&rpng2_info);
781 rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
782 if (!rpng2_info.row_pointers) {
783 free(rpng2_info.image_data);
784 rpng2_info.image_data = NULL;
785 readpng2_cleanup(&rpng2_info);
789 for (i = 0; i < rpng2_info.height; ++i)
790 rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
793 /* do the basic X initialization stuff, make the window, and fill it with
794 * the user-specified, file-specified or default background color or
797 if (rpng2_x_create_window()) {
799 /* GRR TEMPORARY HACK: this is fundamentally no different from cases
800 * above; libpng should call our error handler to longjmp() back to us
801 * when png_ptr goes away. If we/it segfault instead, seems like a
804 /* we're here via libpng callback, so if window fails, clean and bail */
805 readpng2_cleanup(&rpng2_info);
810 rpng2_info.state = kWindowInit;
817 static int rpng2_x_create_window(void)
819 ulg bg_red = rpng2_info.bg_red;
820 ulg bg_green = rpng2_info.bg_green;
821 ulg bg_blue = rpng2_info.bg_blue;
824 int need_colormap = FALSE;
830 XSetWindowAttributes attr;
831 XTextProperty windowName, *pWindowName = &windowName;
832 XTextProperty iconName, *pIconName = &iconName;
833 XVisualInfo visual_info;
834 XSizeHints *size_hints;
836 XClassHint *class_hints;
839 Trace((stderr, "beginning rpng2_x_create_window()\n"))
841 screen = DefaultScreen(display);
842 depth = DisplayPlanes(display, screen);
843 root = RootWindow(display, screen);
846 XSynchronize(display, True);
849 if (depth != 16 && depth != 24 && depth != 32) {
850 int visuals_matched = 0;
852 Trace((stderr, "default depth is %d: checking other visuals\n",
856 visual_info.screen = screen;
857 visual_info.depth = 24;
858 visual_list = XGetVisualInfo(display,
859 VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
860 if (visuals_matched == 0) {
861 /* GRR: add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
862 fprintf(stderr, "default screen depth %d not supported, and no"
863 " 24-bit visuals found\n", depth);
866 Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
868 visual = visual_list[0].visual;
869 depth = visual_list[0].depth;
871 colormap_size = visual_list[0].colormap_size;
872 visual_class = visual->class;
873 visualID = XVisualIDFromVisual(visual);
875 have_nondefault_visual = TRUE;
876 need_colormap = TRUE;
878 XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
879 visual = visual_info.visual;
882 RMask = visual->red_mask;
883 GMask = visual->green_mask;
884 BMask = visual->blue_mask;
886 /* GRR: add/check 8-bit support */
887 if (depth == 8 || need_colormap) {
888 colormap = XCreateColormap(display, root, visual, AllocNone);
890 fprintf(stderr, "XCreateColormap() failed\n");
893 have_colormap = TRUE;
895 bg_image = FALSE; /* gradient just wastes palette entries */
897 if (depth == 15 || depth == 16) {
898 RShift = 15 - rpng2_x_msb(RMask); /* these are right-shifts */
899 GShift = 15 - rpng2_x_msb(GMask);
900 BShift = 15 - rpng2_x_msb(BMask);
901 } else if (depth > 16) {
902 RShift = rpng2_x_msb(RMask) - 7; /* these are left-shifts */
903 GShift = rpng2_x_msb(GMask) - 7;
904 BShift = rpng2_x_msb(BMask) - 7;
906 if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
907 fprintf(stderr, "rpng2 internal logic error: negative X shift(s)!\n");
911 /*---------------------------------------------------------------------------
912 Finally, create the window.
913 ---------------------------------------------------------------------------*/
915 attr.backing_store = Always;
916 attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
917 attrmask = CWBackingStore | CWEventMask;
918 if (have_nondefault_visual) {
919 attr.colormap = colormap;
920 attr.background_pixel = 0;
921 attr.border_pixel = 1;
922 attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
925 window = XCreateWindow(display, root, 0, 0, rpng2_info.width,
926 rpng2_info.height, 0, depth, InputOutput, visual, attrmask, &attr);
928 if (window == None) {
929 fprintf(stderr, "XCreateWindow() failed\n");
935 XSetWindowColormap(display, window, colormap);
937 if (!XStringListToTextProperty(&window_name, 1, pWindowName))
939 if (!XStringListToTextProperty(&icon_name, 1, pIconName))
942 /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */
944 if ((size_hints = XAllocSizeHints()) != NULL) {
945 /* window will not be resizable */
946 size_hints->flags = PMinSize | PMaxSize;
947 size_hints->min_width = size_hints->max_width = (int)rpng2_info.width;
948 size_hints->min_height = size_hints->max_height =
949 (int)rpng2_info.height;
952 if ((wm_hints = XAllocWMHints()) != NULL) {
953 wm_hints->initial_state = NormalState;
954 wm_hints->input = True;
955 /* wm_hints->icon_pixmap = icon_pixmap; */
956 wm_hints->flags = StateHint | InputHint /* | IconPixmapHint */ ;
959 if ((class_hints = XAllocClassHint()) != NULL) {
960 class_hints->res_name = res_name;
961 class_hints->res_class = res_class;
964 XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
965 size_hints, wm_hints, class_hints);
967 /* various properties and hints no longer needed; free memory */
969 XFree(pWindowName->value);
971 XFree(pIconName->value);
979 XMapWindow(display, window);
981 gc = XCreateGC(display, window, 0, &gcvalues);
984 /*---------------------------------------------------------------------------
985 Allocate memory for the X- and display-specific version of the image.
986 ---------------------------------------------------------------------------*/
988 if (depth == 24 || depth == 32) {
989 xdata = (uch *)malloc(4*rpng2_info.width*rpng2_info.height);
991 } else if (depth == 16) {
992 xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height);
994 } else /* depth == 8 */ {
995 xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height);
1000 fprintf(stderr, PROGNAME ": unable to allocate image memory\n");
1004 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
1005 (char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0);
1008 fprintf(stderr, PROGNAME ": XCreateImage() failed\n");
1013 /* to avoid testing the byte order every pixel (or doubling the size of
1014 * the drawing routine with a giant if-test), we arbitrarily set the byte
1015 * order to MSBFirst and let Xlib worry about inverting things on little-
1016 * endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the
1017 * most efficient approach (the giant if-test would be better), but in
1018 * the interest of clarity, we'll take the easy way out... */
1020 ximage->byte_order = MSBFirst;
1022 /*---------------------------------------------------------------------------
1023 Fill window with the specified background color (default is black) or
1024 faked "background image" (but latter is disabled if 8-bit; gradients
1025 just waste palette entries).
1026 ---------------------------------------------------------------------------*/
1029 rpng2_x_load_bg_image(); /* resets bg_image if fails */
1032 if (depth == 24 || depth == 32) {
1033 bg_pixel = (bg_red << RShift) |
1034 (bg_green << GShift) |
1035 (bg_blue << BShift);
1036 } else if (depth == 16) {
1037 bg_pixel = (((bg_red << 8) >> RShift) & RMask) |
1038 (((bg_green << 8) >> GShift) & GMask) |
1039 (((bg_blue << 8) >> BShift) & BMask);
1040 } else /* depth == 8 */ {
1042 /* GRR: add 8-bit support */
1045 XSetForeground(display, gc, bg_pixel);
1046 XFillRectangle(display, window, gc, 0, 0, rpng2_info.width,
1050 /*---------------------------------------------------------------------------
1051 Wait for first Expose event to do any drawing, then flush and return.
1052 ---------------------------------------------------------------------------*/
1055 XNextEvent(display, &e);
1056 while (e.type != Expose || e.xexpose.count);
1062 } /* end function rpng2_x_create_window() */
1068 static int rpng2_x_load_bg_image(void)
1072 uch r1, r2, g1, g2, b1, b2;
1073 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
1075 int xidx, yidx, yidx_max;
1076 int even_odd_vert, even_odd_horiz, even_odd;
1077 int invert_gradient2 = (bg[pat].type & 0x08);
1079 int ximage_rowbytes = ximage->bytes_per_line;
1083 /*---------------------------------------------------------------------------
1084 Allocate buffer for fake background image to be used with transparent
1085 images; if this fails, revert to plain background color.
1086 ---------------------------------------------------------------------------*/
1088 bg_rowbytes = 3 * rpng2_info.width;
1089 bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
1091 fprintf(stderr, PROGNAME
1092 ": unable to allocate memory for background image\n");
1097 bgscale = (pat == 0)? 8 : bgscale_default;
1098 yidx_max = bgscale - 1;
1100 /*---------------------------------------------------------------------------
1101 Vertical gradients (ramps) in NxN squares, alternating direction and
1102 colors (N == bgscale).
1103 ---------------------------------------------------------------------------*/
1105 if ((bg[pat].type & 0x07) == 0) {
1106 uch r1_min = rgb[bg[pat].rgb1_min].r;
1107 uch g1_min = rgb[bg[pat].rgb1_min].g;
1108 uch b1_min = rgb[bg[pat].rgb1_min].b;
1109 uch r2_min = rgb[bg[pat].rgb2_min].r;
1110 uch g2_min = rgb[bg[pat].rgb2_min].g;
1111 uch b2_min = rgb[bg[pat].rgb2_min].b;
1112 int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
1113 int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
1114 int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
1115 int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
1116 int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
1117 int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
1119 for (row = 0; row < rpng2_info.height; ++row) {
1120 yidx = (int)(row % bgscale);
1121 even_odd_vert = (int)((row / bgscale) & 1);
1123 r1 = r1_min + (r1_diff * yidx) / yidx_max;
1124 g1 = g1_min + (g1_diff * yidx) / yidx_max;
1125 b1 = b1_min + (b1_diff * yidx) / yidx_max;
1126 r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
1127 g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
1128 b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
1130 r2 = r2_min + (r2_diff * yidx) / yidx_max;
1131 g2 = g2_min + (g2_diff * yidx) / yidx_max;
1132 b2 = b2_min + (b2_diff * yidx) / yidx_max;
1133 r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
1134 g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
1135 b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
1137 dest = (char *)bg_data + row*bg_rowbytes;
1138 for (i = 0; i < rpng2_info.width; ++i) {
1139 even_odd_horiz = (int)((i / bgscale) & 1);
1140 even_odd = even_odd_vert ^ even_odd_horiz;
1142 (even_odd_horiz && (bg[pat].type & 0x10));
1143 if (even_odd == 0) { /* gradient #1 */
1144 if (invert_column) {
1153 } else { /* gradient #2 */
1154 if ((invert_column && invert_gradient2) ||
1155 (!invert_column && !invert_gradient2))
1157 *dest++ = r2; /* not inverted or */
1158 *dest++ = g2; /* doubly inverted */
1162 *dest++ = g2_inv; /* singly inverted */
1169 /*---------------------------------------------------------------------------
1170 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
1172 ---------------------------------------------------------------------------*/
1174 } else if ((bg[pat].type & 0x07) == 1) {
1176 hmax = (bgscale-1)/2; /* half the max weight of a color */
1177 max = 2*hmax; /* the max weight of a color */
1179 r1 = rgb[bg[pat].rgb1_max].r;
1180 g1 = rgb[bg[pat].rgb1_max].g;
1181 b1 = rgb[bg[pat].rgb1_max].b;
1182 r2 = rgb[bg[pat].rgb2_max].r;
1183 g2 = rgb[bg[pat].rgb2_max].g;
1184 b2 = rgb[bg[pat].rgb2_max].b;
1186 for (row = 0; row < rpng2_info.height; ++row) {
1187 yidx = (int)(row % bgscale);
1189 yidx = bgscale-1 - yidx;
1190 dest = (char *)bg_data + row*bg_rowbytes;
1191 for (i = 0; i < rpng2_info.width; ++i) {
1192 xidx = (int)(i % bgscale);
1194 xidx = bgscale-1 - xidx;
1196 *dest++ = (k*r1 + (max-k)*r2) / max;
1197 *dest++ = (k*g1 + (max-k)*g2) / max;
1198 *dest++ = (k*b1 + (max-k)*b2) / max;
1202 /*---------------------------------------------------------------------------
1203 Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
1204 soids will equal bgscale?]. This one is slow but very cool. Code con-
1205 tributed by Pieter S. van der Meulen (originally in Smalltalk).
1206 ---------------------------------------------------------------------------*/
1208 } else if ((bg[pat].type & 0x07) == 2) {
1210 int ii, x, y, hw, hh, grayspot;
1211 double freq, rotate, saturate, gray, intensity;
1212 double angle=0.0, aoffset=0.0, maxDist, dist;
1213 double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
1215 fprintf(stderr, "%s: computing radial background...",
1219 hh = (int)(rpng2_info.height / 2);
1220 hw = (int)(rpng2_info.width / 2);
1222 /* variables for radial waves:
1223 * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED]
1224 * freq: number of color beams originating from the center
1225 * grayspot: size of the graying center area (anti-alias)
1226 * rotate: rotation of the beams as a function of radius
1227 * saturate: saturation of beams' shape azimuthally
1229 angle = CLIP(angle, 0.0, 360.0);
1230 grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
1231 freq = MAX((double)bg[pat].bg_freq, 0.0);
1232 saturate = (double)bg[pat].bg_bsat * 0.1;
1233 rotate = (double)bg[pat].bg_brot * 0.1;
1236 maxDist = (double)((hw*hw) + (hh*hh));
1238 for (row = 0; row < rpng2_info.height; ++row) {
1239 y = (int)(row - hh);
1240 dest = (char *)bg_data + row*bg_rowbytes;
1241 for (i = 0; i < rpng2_info.width; ++i) {
1243 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
1244 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
1245 gray = MIN(1.0, gray);
1246 dist = (double)((x*x) + (y*y)) / maxDist;
1247 intensity = cos((angle+(rotate*dist*PI)) * freq) *
1249 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
1250 hue = (angle + PI) * INV_PI_360 + aoffset;
1251 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
1252 s = MIN(MAX(s,0.0), 1.0);
1253 v = MIN(MAX(intensity,0.0), 1.0);
1256 ch = (uch)(v * 255.0);
1261 if ((hue < 0.0) || (hue >= 360.0))
1262 hue -= (((int)(hue / 360.0)) * 360.0);
1265 f = hue - (double)ii;
1267 q = (1.0 - (s * f)) * v;
1268 t = (1.0 - (s * (1.0 - f))) * v;
1269 if (ii == 0) { red = v; green = t; blue = p; }
1270 else if (ii == 1) { red = q; green = v; blue = p; }
1271 else if (ii == 2) { red = p; green = v; blue = t; }
1272 else if (ii == 3) { red = p; green = q; blue = v; }
1273 else if (ii == 4) { red = t; green = p; blue = v; }
1274 else if (ii == 5) { red = v; green = p; blue = q; }
1275 *dest++ = (uch)(red * 255.0);
1276 *dest++ = (uch)(green * 255.0);
1277 *dest++ = (uch)(blue * 255.0);
1281 fprintf(stderr, "done.\n");
1285 /*---------------------------------------------------------------------------
1286 Blast background image to display buffer before beginning PNG decode.
1287 ---------------------------------------------------------------------------*/
1289 if (depth == 24 || depth == 32) {
1290 ulg red, green, blue;
1291 int bpp = ximage->bits_per_pixel;
1293 for (row = 0; row < rpng2_info.height; ++row) {
1294 src = bg_data + row*bg_rowbytes;
1295 dest = ximage->data + row*ximage_rowbytes;
1296 if (bpp == 32) { /* slightly optimized version */
1297 for (i = rpng2_info.width; i > 0; --i) {
1301 pixel = (red << RShift) |
1304 /* recall that we set ximage->byte_order = MSBFirst above */
1305 *dest++ = (char)((pixel >> 24) & 0xff);
1306 *dest++ = (char)((pixel >> 16) & 0xff);
1307 *dest++ = (char)((pixel >> 8) & 0xff);
1308 *dest++ = (char)( pixel & 0xff);
1311 for (i = rpng2_info.width; i > 0; --i) {
1315 pixel = (red << RShift) |
1318 /* recall that we set ximage->byte_order = MSBFirst above */
1319 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1320 /* (probably need to use RShift, RMask, etc.) */
1321 *dest++ = (char)((pixel >> 16) & 0xff);
1322 *dest++ = (char)((pixel >> 8) & 0xff);
1323 *dest++ = (char)( pixel & 0xff);
1328 } else if (depth == 16) {
1329 ush red, green, blue;
1331 for (row = 0; row < rpng2_info.height; ++row) {
1332 src = bg_data + row*bg_rowbytes;
1333 dest = ximage->data + row*ximage_rowbytes;
1334 for (i = rpng2_info.width; i > 0; --i) {
1335 red = ((ush)(*src) << 8); ++src;
1336 green = ((ush)(*src) << 8); ++src;
1337 blue = ((ush)(*src) << 8); ++src;
1338 pixel = ((red >> RShift) & RMask) |
1339 ((green >> GShift) & GMask) |
1340 ((blue >> BShift) & BMask);
1341 /* recall that we set ximage->byte_order = MSBFirst above */
1342 *dest++ = (char)((pixel >> 8) & 0xff);
1343 *dest++ = (char)( pixel & 0xff);
1347 } else /* depth == 8 */ {
1349 /* GRR: add 8-bit support */
1353 XPutImage(display, window, gc, ximage, 0, 0, 0, 0, rpng2_info.width,
1358 } /* end function rpng2_x_load_bg_image() */
1364 static void rpng2_x_display_row(ulg row)
1366 uch bg_red = rpng2_info.bg_red;
1367 uch bg_green = rpng2_info.bg_green;
1368 uch bg_blue = rpng2_info.bg_blue;
1369 uch *src, *src2=NULL;
1372 int ximage_rowbytes = ximage->bytes_per_line;
1374 static int rows=0, prevpass=(-1);
1375 static ulg firstrow;
1377 /*---------------------------------------------------------------------------
1378 rows and firstrow simply track how many rows (and which ones) have not
1379 yet been displayed; alternatively, we could call XPutImage() for every
1380 row and not bother with the records-keeping.
1381 ---------------------------------------------------------------------------*/
1383 Trace((stderr, "beginning rpng2_x_display_row()\n"))
1385 if (rpng2_info.pass != prevpass) {
1386 if (pause_after_pass && rpng2_info.pass > 0) {
1391 "%s: end of pass %d of 7; click in image window to continue\n",
1392 PROGNAME, prevpass + 1);
1394 XNextEvent(display, &e);
1397 fprintf(stderr, "%s: pass %d of 7\r", PROGNAME, rpng2_info.pass + 1);
1399 prevpass = rpng2_info.pass;
1403 firstrow = row; /* first row that is not yet displayed */
1405 ++rows; /* count of rows received but not yet displayed */
1407 /*---------------------------------------------------------------------------
1408 Aside from the use of the rpng2_info struct, the lack of an outer loop
1409 (over rows) and moving the XPutImage() call outside the "if (depth)"
1410 tests, this routine is identical to rpng_x_display_image() in the non-
1411 progressive version of the program.
1412 ---------------------------------------------------------------------------*/
1414 if (depth == 24 || depth == 32) {
1415 ulg red, green, blue;
1416 int bpp = ximage->bits_per_pixel;
1418 src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1420 src2 = bg_data + row*bg_rowbytes;
1421 dest = ximage->data + row*ximage_rowbytes;
1422 if (rpng2_info.channels == 3) {
1423 for (i = rpng2_info.width; i > 0; --i) {
1427 pixel = (red << RShift) |
1430 /* recall that we set ximage->byte_order = MSBFirst above */
1432 *dest++ = (char)((pixel >> 24) & 0xff);
1433 *dest++ = (char)((pixel >> 16) & 0xff);
1434 *dest++ = (char)((pixel >> 8) & 0xff);
1435 *dest++ = (char)( pixel & 0xff);
1437 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1438 /* (probably need to use RShift, RMask, etc.) */
1439 *dest++ = (char)((pixel >> 16) & 0xff);
1440 *dest++ = (char)((pixel >> 8) & 0xff);
1441 *dest++ = (char)( pixel & 0xff);
1444 } else /* if (rpng2_info.channels == 4) */ {
1445 for (i = rpng2_info.width; i > 0; --i) {
1459 } else if (a == 0) {
1464 /* this macro (from png.h) composites the foreground
1465 * and background values and puts the result into the
1467 alpha_composite(red, r, a, bg_red);
1468 alpha_composite(green, g, a, bg_green);
1469 alpha_composite(blue, b, a, bg_blue);
1471 pixel = (red << RShift) |
1474 /* recall that we set ximage->byte_order = MSBFirst above */
1476 *dest++ = (char)((pixel >> 24) & 0xff);
1477 *dest++ = (char)((pixel >> 16) & 0xff);
1478 *dest++ = (char)((pixel >> 8) & 0xff);
1479 *dest++ = (char)( pixel & 0xff);
1481 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1482 /* (probably need to use RShift, RMask, etc.) */
1483 *dest++ = (char)((pixel >> 16) & 0xff);
1484 *dest++ = (char)((pixel >> 8) & 0xff);
1485 *dest++ = (char)( pixel & 0xff);
1490 } else if (depth == 16) {
1491 ush red, green, blue;
1493 src = rpng2_info.row_pointers[row];
1495 src2 = bg_data + row*bg_rowbytes;
1496 dest = ximage->data + row*ximage_rowbytes;
1497 if (rpng2_info.channels == 3) {
1498 for (i = rpng2_info.width; i > 0; --i) {
1499 red = ((ush)(*src) << 8);
1501 green = ((ush)(*src) << 8);
1503 blue = ((ush)(*src) << 8);
1505 pixel = ((red >> RShift) & RMask) |
1506 ((green >> GShift) & GMask) |
1507 ((blue >> BShift) & BMask);
1508 /* recall that we set ximage->byte_order = MSBFirst above */
1509 *dest++ = (char)((pixel >> 8) & 0xff);
1510 *dest++ = (char)( pixel & 0xff);
1512 } else /* if (rpng2_info.channels == 4) */ {
1513 for (i = rpng2_info.width; i > 0; --i) {
1524 red = ((ush)r << 8);
1525 green = ((ush)g << 8);
1526 blue = ((ush)b << 8);
1527 } else if (a == 0) {
1528 red = ((ush)bg_red << 8);
1529 green = ((ush)bg_green << 8);
1530 blue = ((ush)bg_blue << 8);
1532 /* this macro (from png.h) composites the foreground
1533 * and background values and puts the result back into
1534 * the first argument (== fg byte here: safe) */
1535 alpha_composite(r, r, a, bg_red);
1536 alpha_composite(g, g, a, bg_green);
1537 alpha_composite(b, b, a, bg_blue);
1538 red = ((ush)r << 8);
1539 green = ((ush)g << 8);
1540 blue = ((ush)b << 8);
1542 pixel = ((red >> RShift) & RMask) |
1543 ((green >> GShift) & GMask) |
1544 ((blue >> BShift) & BMask);
1545 /* recall that we set ximage->byte_order = MSBFirst above */
1546 *dest++ = (char)((pixel >> 8) & 0xff);
1547 *dest++ = (char)( pixel & 0xff);
1551 } else /* depth == 8 */ {
1553 /* GRR: add 8-bit support */
1558 /*---------------------------------------------------------------------------
1559 Display after every 16 rows or when on one of last two rows. (Region
1560 may include previously displayed lines due to interlacing--i.e., not
1561 contiguous. Also, second-to-last row is final one in interlaced images
1562 with odd number of rows.) For demos, flush (and delay) after every 16th
1563 row so "sparse" passes don't go twice as fast.
1564 ---------------------------------------------------------------------------*/
1566 if (demo_timing && (row - firstrow >= 16 || row >= rpng2_info.height-2)) {
1567 XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1568 (int)firstrow, rpng2_info.width, row - firstrow + 1);
1571 usleep(usleep_duration);
1573 if (!demo_timing && ((rows & 0xf) == 0 || row >= rpng2_info.height-2)) {
1574 XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1575 (int)firstrow, rpng2_info.width, row - firstrow + 1);
1586 static void rpng2_x_finish_display(void)
1588 Trace((stderr, "beginning rpng2_x_finish_display()\n"))
1590 /* last row has already been displayed by rpng2_x_display_row(), so we
1591 * have nothing to do here except set a flag and let the user know that
1592 * the image is done */
1594 rpng2_info.state = kDone;
1596 "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
1604 static void rpng2_x_redisplay_image(ulg startcol, ulg startrow,
1605 ulg width, ulg height)
1607 uch bg_red = rpng2_info.bg_red;
1608 uch bg_green = rpng2_info.bg_green;
1609 uch bg_blue = rpng2_info.bg_blue;
1610 uch *src, *src2=NULL;
1613 ulg i, row, lastrow = 0;
1615 int ximage_rowbytes = ximage->bytes_per_line;
1618 Trace((stderr, "beginning display loop (image_channels == %d)\n",
1619 rpng2_info.channels))
1620 Trace((stderr, " (width = %ld, rowbytes = %d, ximage_rowbytes = %d)\n",
1621 rpng2_info.width, rpng2_info.rowbytes, ximage_rowbytes))
1622 Trace((stderr, " (bpp = %d)\n", ximage->bits_per_pixel))
1623 Trace((stderr, " (byte_order = %s)\n", ximage->byte_order == MSBFirst?
1624 "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown")))
1626 /*---------------------------------------------------------------------------
1627 Aside from the use of the rpng2_info struct and of src2 (for background
1628 image), this routine is identical to rpng_x_display_image() in the non-
1629 progressive version of the program--for the simple reason that redisplay
1630 of the image against a new background happens after the image is fully
1631 decoded and therefore is, by definition, non-progressive.
1632 ---------------------------------------------------------------------------*/
1634 if (depth == 24 || depth == 32) {
1635 ulg red, green, blue;
1636 int bpp = ximage->bits_per_pixel;
1638 for (lastrow = row = startrow; row < startrow+height; ++row) {
1639 src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1641 src2 = bg_data + row*bg_rowbytes;
1642 dest = ximage->data + row*ximage_rowbytes;
1643 if (rpng2_info.channels == 3) {
1644 for (i = rpng2_info.width; i > 0; --i) {
1648 #ifdef NO_24BIT_MASKS
1649 pixel = (red << RShift) |
1652 /* recall that we set ximage->byte_order = MSBFirst above */
1654 *dest++ = (char)((pixel >> 24) & 0xff);
1655 *dest++ = (char)((pixel >> 16) & 0xff);
1656 *dest++ = (char)((pixel >> 8) & 0xff);
1657 *dest++ = (char)( pixel & 0xff);
1659 /* this assumes bpp == 24 & bits are packed low */
1660 /* (probably need to use RShift, RMask, etc.) */
1661 *dest++ = (char)((pixel >> 16) & 0xff);
1662 *dest++ = (char)((pixel >> 8) & 0xff);
1663 *dest++ = (char)( pixel & 0xff);
1666 red = (RShift < 0)? red << (-RShift) : red >> RShift;
1667 green = (GShift < 0)? green << (-GShift) : green >> GShift;
1668 blue = (BShift < 0)? blue << (-BShift) : blue >> BShift;
1669 pixel = (red & RMask) | (green & GMask) | (blue & BMask);
1670 /* recall that we set ximage->byte_order = MSBFirst above */
1672 *dest++ = (char)((pixel >> 24) & 0xff);
1673 *dest++ = (char)((pixel >> 16) & 0xff);
1674 *dest++ = (char)((pixel >> 8) & 0xff);
1675 *dest++ = (char)( pixel & 0xff);
1678 /* this assumes bpp == 24 & bits are packed low */
1679 /* (probably need to use RShift/RMask/etc. here, too) */
1680 *dest++ = (char)((pixel >> 16) & 0xff);
1681 *dest++ = (char)((pixel >> 8) & 0xff);
1682 *dest++ = (char)( pixel & 0xff);
1687 } else /* if (rpng2_info.channels == 4) */ {
1688 for (i = rpng2_info.width; i > 0; --i) {
1702 } else if (a == 0) {
1707 /* this macro (from png.h) composites the foreground
1708 * and background values and puts the result into the
1710 alpha_composite(red, r, a, bg_red);
1711 alpha_composite(green, g, a, bg_green);
1712 alpha_composite(blue, b, a, bg_blue);
1714 #ifdef NO_24BIT_MASKS
1715 pixel = (red << RShift) |
1718 /* recall that we set ximage->byte_order = MSBFirst above */
1720 *dest++ = (char)((pixel >> 24) & 0xff);
1721 *dest++ = (char)((pixel >> 16) & 0xff);
1722 *dest++ = (char)((pixel >> 8) & 0xff);
1723 *dest++ = (char)( pixel & 0xff);
1725 /* this assumes bpp == 24 & bits are packed low */
1726 /* (probably need to use RShift, RMask, etc.) */
1727 *dest++ = (char)((pixel >> 16) & 0xff);
1728 *dest++ = (char)((pixel >> 8) & 0xff);
1729 *dest++ = (char)( pixel & 0xff);
1732 red = (RShift < 0)? red << (-RShift) : red >> RShift;
1733 green = (GShift < 0)? green << (-GShift) : green >> GShift;
1734 blue = (BShift < 0)? blue << (-BShift) : blue >> BShift;
1735 pixel = (red & RMask) | (green & GMask) | (blue & BMask);
1736 /* recall that we set ximage->byte_order = MSBFirst above */
1738 *dest++ = (char)((pixel >> 24) & 0xff);
1739 *dest++ = (char)((pixel >> 16) & 0xff);
1740 *dest++ = (char)((pixel >> 8) & 0xff);
1741 *dest++ = (char)( pixel & 0xff);
1744 /* this assumes bpp == 24 & bits are packed low */
1745 /* (probably need to use RShift/RMask/etc. here, too) */
1746 *dest++ = (char)((pixel >> 16) & 0xff);
1747 *dest++ = (char)((pixel >> 8) & 0xff);
1748 *dest++ = (char)( pixel & 0xff);
1753 /* display after every 16 lines */
1754 if (((row+1) & 0xf) == 0) {
1755 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1756 (int)lastrow, rpng2_info.width, 16);
1762 } else if (depth == 16) {
1763 ush red, green, blue;
1765 for (lastrow = row = startrow; row < startrow+height; ++row) {
1766 src = rpng2_info.row_pointers[row];
1768 src2 = bg_data + row*bg_rowbytes;
1769 dest = ximage->data + row*ximage_rowbytes;
1770 if (rpng2_info.channels == 3) {
1771 for (i = rpng2_info.width; i > 0; --i) {
1772 red = ((ush)(*src) << 8);
1774 green = ((ush)(*src) << 8);
1776 blue = ((ush)(*src) << 8);
1778 pixel = ((red >> RShift) & RMask) |
1779 ((green >> GShift) & GMask) |
1780 ((blue >> BShift) & BMask);
1781 /* recall that we set ximage->byte_order = MSBFirst above */
1782 *dest++ = (char)((pixel >> 8) & 0xff);
1783 *dest++ = (char)( pixel & 0xff);
1785 } else /* if (rpng2_info.channels == 4) */ {
1786 for (i = rpng2_info.width; i > 0; --i) {
1797 red = ((ush)r << 8);
1798 green = ((ush)g << 8);
1799 blue = ((ush)b << 8);
1800 } else if (a == 0) {
1801 red = ((ush)bg_red << 8);
1802 green = ((ush)bg_green << 8);
1803 blue = ((ush)bg_blue << 8);
1805 /* this macro (from png.h) composites the foreground
1806 * and background values and puts the result back into
1807 * the first argument (== fg byte here: safe) */
1808 alpha_composite(r, r, a, bg_red);
1809 alpha_composite(g, g, a, bg_green);
1810 alpha_composite(b, b, a, bg_blue);
1811 red = ((ush)r << 8);
1812 green = ((ush)g << 8);
1813 blue = ((ush)b << 8);
1815 pixel = ((red >> RShift) & RMask) |
1816 ((green >> GShift) & GMask) |
1817 ((blue >> BShift) & BMask);
1818 /* recall that we set ximage->byte_order = MSBFirst above */
1819 *dest++ = (char)((pixel >> 8) & 0xff);
1820 *dest++ = (char)( pixel & 0xff);
1823 /* display after every 16 lines */
1824 if (((row+1) & 0xf) == 0) {
1825 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1826 (int)lastrow, rpng2_info.width, 16);
1832 } else /* depth == 8 */ {
1834 /* GRR: add 8-bit support */
1838 Trace((stderr, "calling final XPutImage()\n"))
1839 if (lastrow < startrow+height) {
1840 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1841 (int)lastrow, rpng2_info.width, rpng2_info.height-lastrow);
1848 } /* end function rpng2_x_redisplay_image() */
1856 static void rpng2_x_reload_bg_image(void)
1859 uch r1, r2, g1, g2, b1, b2;
1860 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
1862 int xidx, yidx, yidx_max;
1863 int even_odd_vert, even_odd_horiz, even_odd;
1864 int invert_gradient2 = (bg[pat].type & 0x08);
1869 bgscale = (pat == 0)? 8 : bgscale_default;
1870 yidx_max = bgscale - 1;
1872 /*---------------------------------------------------------------------------
1873 Vertical gradients (ramps) in NxN squares, alternating direction and
1874 colors (N == bgscale).
1875 ---------------------------------------------------------------------------*/
1877 if ((bg[pat].type & 0x07) == 0) {
1878 uch r1_min = rgb[bg[pat].rgb1_min].r;
1879 uch g1_min = rgb[bg[pat].rgb1_min].g;
1880 uch b1_min = rgb[bg[pat].rgb1_min].b;
1881 uch r2_min = rgb[bg[pat].rgb2_min].r;
1882 uch g2_min = rgb[bg[pat].rgb2_min].g;
1883 uch b2_min = rgb[bg[pat].rgb2_min].b;
1884 int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
1885 int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
1886 int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
1887 int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
1888 int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
1889 int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
1891 for (row = 0; row < rpng2_info.height; ++row) {
1892 yidx = (int)(row % bgscale);
1893 even_odd_vert = (int)((row / bgscale) & 1);
1895 r1 = r1_min + (r1_diff * yidx) / yidx_max;
1896 g1 = g1_min + (g1_diff * yidx) / yidx_max;
1897 b1 = b1_min + (b1_diff * yidx) / yidx_max;
1898 r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
1899 g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
1900 b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
1902 r2 = r2_min + (r2_diff * yidx) / yidx_max;
1903 g2 = g2_min + (g2_diff * yidx) / yidx_max;
1904 b2 = b2_min + (b2_diff * yidx) / yidx_max;
1905 r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
1906 g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
1907 b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
1909 dest = (char *)bg_data + row*bg_rowbytes;
1910 for (i = 0; i < rpng2_info.width; ++i) {
1911 even_odd_horiz = (int)((i / bgscale) & 1);
1912 even_odd = even_odd_vert ^ even_odd_horiz;
1914 (even_odd_horiz && (bg[pat].type & 0x10));
1915 if (even_odd == 0) { /* gradient #1 */
1916 if (invert_column) {
1925 } else { /* gradient #2 */
1926 if ((invert_column && invert_gradient2) ||
1927 (!invert_column && !invert_gradient2))
1929 *dest++ = r2; /* not inverted or */
1930 *dest++ = g2; /* doubly inverted */
1934 *dest++ = g2_inv; /* singly inverted */
1941 /*---------------------------------------------------------------------------
1942 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
1944 ---------------------------------------------------------------------------*/
1946 } else if ((bg[pat].type & 0x07) == 1) {
1948 hmax = (bgscale-1)/2; /* half the max weight of a color */
1949 max = 2*hmax; /* the max weight of a color */
1951 r1 = rgb[bg[pat].rgb1_max].r;
1952 g1 = rgb[bg[pat].rgb1_max].g;
1953 b1 = rgb[bg[pat].rgb1_max].b;
1954 r2 = rgb[bg[pat].rgb2_max].r;
1955 g2 = rgb[bg[pat].rgb2_max].g;
1956 b2 = rgb[bg[pat].rgb2_max].b;
1958 for (row = 0; row < rpng2_info.height; ++row) {
1959 yidx = (int)(row % bgscale);
1961 yidx = bgscale-1 - yidx;
1962 dest = (char *)bg_data + row*bg_rowbytes;
1963 for (i = 0; i < rpng2_info.width; ++i) {
1964 xidx = (int)(i % bgscale);
1966 xidx = bgscale-1 - xidx;
1968 *dest++ = (k*r1 + (max-k)*r2) / max;
1969 *dest++ = (k*g1 + (max-k)*g2) / max;
1970 *dest++ = (k*b1 + (max-k)*b2) / max;
1974 /*---------------------------------------------------------------------------
1975 Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
1976 soids will equal bgscale?]. This one is slow but very cool. Code con-
1977 tributed by Pieter S. van der Meulen (originally in Smalltalk).
1978 ---------------------------------------------------------------------------*/
1980 } else if ((bg[pat].type & 0x07) == 2) {
1982 int ii, x, y, hw, hh, grayspot;
1983 double freq, rotate, saturate, gray, intensity;
1984 double angle=0.0, aoffset=0.0, maxDist, dist;
1985 double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
1987 hh = (int)(rpng2_info.height / 2);
1988 hw = (int)(rpng2_info.width / 2);
1990 /* variables for radial waves:
1991 * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED]
1992 * freq: number of color beams originating from the center
1993 * grayspot: size of the graying center area (anti-alias)
1994 * rotate: rotation of the beams as a function of radius
1995 * saturate: saturation of beams' shape azimuthally
1997 angle = CLIP(angle, 0.0, 360.0);
1998 grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
1999 freq = MAX((double)bg[pat].bg_freq, 0.0);
2000 saturate = (double)bg[pat].bg_bsat * 0.1;
2001 rotate = (double)bg[pat].bg_brot * 0.1;
2004 maxDist = (double)((hw*hw) + (hh*hh));
2006 for (row = 0; row < rpng2_info.height; ++row) {
2007 y = (int)(row - hh);
2008 dest = (char *)bg_data + row*bg_rowbytes;
2009 for (i = 0; i < rpng2_info.width; ++i) {
2011 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
2012 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
2013 gray = MIN(1.0, gray);
2014 dist = (double)((x*x) + (y*y)) / maxDist;
2015 intensity = cos((angle+(rotate*dist*PI)) * freq) *
2017 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
2018 hue = (angle + PI) * INV_PI_360 + aoffset;
2019 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
2020 s = MIN(MAX(s,0.0), 1.0);
2021 v = MIN(MAX(intensity,0.0), 1.0);
2024 ch = (uch)(v * 255.0);
2029 if ((hue < 0.0) || (hue >= 360.0))
2030 hue -= (((int)(hue / 360.0)) * 360.0);
2033 f = hue - (double)ii;
2035 q = (1.0 - (s * f)) * v;
2036 t = (1.0 - (s * (1.0 - f))) * v;
2037 if (ii == 0) { red = v; green = t; blue = p; }
2038 else if (ii == 1) { red = q; green = v; blue = p; }
2039 else if (ii == 2) { red = p; green = v; blue = t; }
2040 else if (ii == 3) { red = p; green = q; blue = v; }
2041 else if (ii == 4) { red = t; green = p; blue = v; }
2042 else if (ii == 5) { red = v; green = p; blue = q; }
2043 *dest++ = (uch)(red * 255.0);
2044 *dest++ = (uch)(green * 255.0);
2045 *dest++ = (uch)(blue * 255.0);
2051 } /* end function rpng2_x_reload_bg_image() */
2057 static int is_number(char *p)
2067 #endif /* FEATURE_LOOP */
2073 static void rpng2_x_cleanup(void)
2075 if (bg_image && bg_data) {
2080 if (rpng2_info.image_data) {
2081 free(rpng2_info.image_data);
2082 rpng2_info.image_data = NULL;
2085 if (rpng2_info.row_pointers) {
2086 free(rpng2_info.row_pointers);
2087 rpng2_info.row_pointers = NULL;
2092 free(ximage->data); /* we allocated it, so we free it */
2093 ximage->data = (char *)NULL; /* instead of XDestroyImage() */
2095 XDestroyImage(ximage);
2100 XFreeGC(display, gc);
2103 XDestroyWindow(display, window);
2106 XFreeColormap(display, colormap);
2108 if (have_nondefault_visual)
2116 static int rpng2_x_msb(ulg u32val)
2120 for (i = 31; i >= 0; --i) {
2121 if (u32val & 0x80000000L)