1 /*---------------------------------------------------------------------------
3 rpng2 - progressive-model PNG display program rpng2-x.c
5 This program decodes and displays PNG files progressively, as if it were
6 a web browser (though the front end is only set up to read from files).
7 It supports gamma correction, user-specified background colors, and user-
8 specified background patterns (for transparent images). This version is
9 for the X Window System (tested by the author under Unix and by Martin
10 Zinser under OpenVMS; may work under OS/2 with a little tweaking).
12 Thanks to Adam Costello and Pieter S. van der Meulen for the "diamond"
13 and "radial waves" patterns, respectively.
15 to do (someday, maybe):
16 - fix expose/redraw code: don't draw entire row if only part exposed
17 - 8-bit (colormapped) X support
18 - finish resizable checkerboard-gradient (sizes 4-128?)
19 - use %.1023s to simplify truncation of title-bar string?
21 ---------------------------------------------------------------------------
24 - 1.01: initial public release
25 - 1.02: modified to allow abbreviated options; fixed char/uchar mismatch
26 - 1.10: added support for non-default visuals; fixed X pixel-conversion
27 - 1.11: added -usleep option for demos; fixed command-line parsing bug
28 - 1.12: added -pause option for demos and testing
29 - 1.20: added runtime MMX-enabling/disabling and new -mmx* options
30 - 1.21: fixed some small X memory leaks (thanks to François Petitjean)
31 - 1.22: fixed XFreeGC() crash bug (thanks to Patrick Welche)
32 - 1.23: added -bgpat 0 mode (std white/gray checkerboard, 8x8 squares)
33 - 1.30: added -loop option for -bgpat (ifdef FEATURE_LOOP); fixed bpp =
34 24; added support for X resources (thanks to Gerhard Niklasch)
35 - 1.31: added code to skip unused chunks (thanks to Glenn Randers-Pehrson)
36 - 1.32: added AMD64/EM64T support (__x86_64__); added basic expose/redraw
38 - 2.00: dual-licensed (added GNU GPL)
39 - 2.01: fixed 64-bit typo in readpng2.c; fixed -pause usage description
40 - 2.02: fixed improper display of usage screen on PNG error(s); fixed
41 unexpected-EOF and file-read-error cases; fixed Trace() cut-and-
43 - 2.03: deleted runtime MMX-enabling/disabling and obsolete -mmx* options
45 ---------------------------------------------------------------------------
47 Copyright (c) 1998-2008 Greg Roelofs. All rights reserved.
49 This software is provided "as is," without warranty of any kind,
50 express or implied. In no event shall the author or contributors
51 be held liable for any damages arising in any way from the use of
54 The contents of this file are DUAL-LICENSED. You may modify and/or
55 redistribute this software according to the terms of one of the
56 following two licenses (at your option):
59 LICENSE 1 ("BSD-like with advertising clause"):
61 Permission is granted to anyone to use this software for any purpose,
62 including commercial applications, and to alter it and redistribute
63 it freely, subject to the following restrictions:
65 1. Redistributions of source code must retain the above copyright
66 notice, disclaimer, and this list of conditions.
67 2. Redistributions in binary form must reproduce the above copyright
68 notice, disclaimer, and this list of conditions in the documenta-
69 tion and/or other materials provided with the distribution.
70 3. All advertising materials mentioning features or use of this
71 software must display the following acknowledgment:
73 This product includes software developed by Greg Roelofs
74 and contributors for the book, "PNG: The Definitive Guide,"
75 published by O'Reilly and Associates.
78 LICENSE 2 (GNU GPL v2 or later):
80 This program is free software; you can redistribute it and/or modify
81 it under the terms of the GNU General Public License as published by
82 the Free Software Foundation; either version 2 of the License, or
83 (at your option) any later version.
85 This program is distributed in the hope that it will be useful,
86 but WITHOUT ANY WARRANTY; without even the implied warranty of
87 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
88 GNU General Public License for more details.
90 You should have received a copy of the GNU General Public License
91 along with this program; if not, write to the Free Software Foundation,
92 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
94 ---------------------------------------------------------------------------*/
96 #define PROGNAME "rpng2-x"
97 #define LONGNAME "Progressive PNG Viewer for X"
98 #define VERSION "2.03 of 25 February 2010"
99 #define RESNAME "rpng2" /* our X resource application name */
100 #define RESCLASS "Rpng" /* our X resource class name */
106 #include <setjmp.h> /* for jmpbuf declaration in readpng2.h */
108 #include <math.h> /* only for PvdM background code */
109 #include <X11/Xlib.h>
110 #include <X11/Xutil.h>
112 #include <X11/keysym.h> /* defines XK_* macros */
118 /* all for PvdM background code: */
120 # define PI 3.141592653589793238
122 #define PI_2 (PI*0.5)
123 #define INV_PI_360 (360.0 / PI)
124 #define MAX(a,b) (a>b?a:b)
125 #define MIN(a,b) (a<b?a:b)
126 #define CLIP(a,min,max) MAX(min,MIN((a),max))
127 #define ABS(a) ((a)<0?-(a):(a))
128 #define CLIP8P(c) MAX(0,(MIN((c),255))) /* 8-bit pos. integer (uch) */
129 #define ROUNDF(f) ((int)(f + 0.5))
131 #define QUIT(e,k) ((e.type == ButtonPress && e.xbutton.button == Button1) || \
132 (e.type == KeyPress && /* v--- or 1 for shifted keys */ \
133 ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape)))
135 #define NO_24BIT_MASKS /* undef case not fully written--only for redisplay() */
137 #define rgb1_max bg_freq
138 #define rgb1_min bg_gray
139 #define rgb2_max bg_bsat
140 #define rgb2_min bg_brot
142 /* #define DEBUG */ /* this enables the Trace() macros */
144 #include "readpng2.h" /* typedefs, common macros, readpng2 prototypes */
147 /* could just include png.h, but this macro is the only thing we need
148 * (name and typedefs changed to local versions); note that side effects
149 * only happen with alpha (which could easily be avoided with
150 * "ush acopy = (alpha);") */
152 #define alpha_composite(composite, fg, alpha, bg) { \
153 ush temp = ((ush)(fg)*(ush)(alpha) + \
154 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \
155 (composite) = (uch)((temp + (temp >> 8)) >> 8); \
159 #define INBUFSIZE 4096 /* with pseudo-timing on (1 sec delay/block), this
160 * block size corresponds roughly to a download
161 * speed 10% faster than theoretical 33.6K maximum
162 * (assuming 8 data bits, 1 stop bit and no other
165 /* local prototypes */
166 static void rpng2_x_init (void);
167 static int rpng2_x_create_window (void);
168 static int rpng2_x_load_bg_image (void);
169 static void rpng2_x_display_row (ulg row
);
170 static void rpng2_x_finish_display (void);
171 static void rpng2_x_redisplay_image (ulg startcol
, ulg startrow
,
172 ulg width
, ulg height
);
174 static void rpng2_x_reload_bg_image (void);
175 static int is_number (char *p
);
177 static void rpng2_x_cleanup (void);
178 static int rpng2_x_msb (ulg u32val
);
181 static char titlebar
[1024], *window_name
= titlebar
;
182 static char *appname
= LONGNAME
;
183 static char *icon_name
= PROGNAME
;
184 static char *res_name
= RESNAME
;
185 static char *res_class
= RESCLASS
;
186 static char *filename
;
189 static mainprog_info rpng2_info
;
191 static uch inbuf
[INBUFSIZE
];
194 static int pat
= 6; /* must be less than num_bgpat */
195 static int bg_image
= 0;
196 static int bgscale
, bgscale_default
= 16;
197 static ulg bg_rowbytes
;
200 int pause_after_pass
= FALSE
;
201 int demo_timing
= FALSE
;
202 ulg usleep_duration
= 0L;
204 static struct rgb_color
{
207 { 0, 0, 0}, /* 0: black */
208 {255, 255, 255}, /* 1: white */
209 {173, 132, 57}, /* 2: tan */
210 { 64, 132, 0}, /* 3: medium green */
211 {189, 117, 1}, /* 4: gold */
212 {253, 249, 1}, /* 5: yellow */
213 { 0, 0, 255}, /* 6: blue */
214 { 0, 0, 120}, /* 7: medium blue */
215 {255, 0, 255}, /* 8: magenta */
216 { 64, 0, 64}, /* 9: dark magenta */
217 {255, 0, 0}, /* 10: red */
218 { 64, 0, 0}, /* 11: dark red */
219 {255, 127, 0}, /* 12: orange */
220 {192, 96, 0}, /* 13: darker orange */
221 { 24, 60, 0}, /* 14: dark green-yellow */
222 { 85, 125, 200}, /* 15: ice blue */
223 {192, 192, 192} /* 16: Netscape/Mosaic gray */
225 /* not used for now, but should be for error-checking:
226 static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
230 This whole struct is a fairly cheesy way to keep the number of
231 command-line options to a minimum. The radial-waves background
232 type is a particularly poor fit to the integer elements of the
233 struct...but a few macros and a little fixed-point math will do
237 F E D C B A 9 8 7 6 5 4 3 2 1 0
239 | | +-+-+-- 0 = sharp-edged checkerboard
240 | | 1 = soft diamonds
243 | +-- gradient #2 inverted?
244 +-- alternating columns inverted?
246 static struct background_pattern
{
248 int rgb1_max
, rgb1_min
; /* or bg_freq, bg_gray */
249 int rgb2_max
, rgb2_min
; /* or bg_bsat, bg_brot (both scaled by 10)*/
251 {0, 1,1, 16,16}, /* checkered: white vs. light gray (basic) */
252 {0+8, 2,0, 1,15}, /* checkered: tan/black vs. white/ice blue */
253 {0+24, 2,0, 1,0}, /* checkered: tan/black vs. white/black */
254 {0+8, 4,5, 0,2}, /* checkered: gold/yellow vs. black/tan */
255 {0+8, 4,5, 0,6}, /* checkered: gold/yellow vs. black/blue */
256 {0, 7,0, 8,9}, /* checkered: deep blue/black vs. magenta */
257 {0+8, 13,0, 5,14}, /* checkered: orange/black vs. yellow */
258 {0+8, 12,0, 10,11}, /* checkered: orange/black vs. red */
259 {1, 7,0, 8,0}, /* diamonds: deep blue/black vs. magenta */
260 {1, 12,0, 11,0}, /* diamonds: orange vs. dark red */
261 {1, 10,0, 7,0}, /* diamonds: red vs. medium blue */
262 {1, 4,0, 5,0}, /* diamonds: gold vs. yellow */
263 {1, 3,0, 0,0}, /* diamonds: medium green vs. black */
264 {2, 16, 100, 20, 0}, /* radial: ~hard radial color-beams */
265 {2, 18, 100, 10, 2}, /* radial: soft, curved radial color-beams */
266 {2, 16, 256, 100, 250}, /* radial: very tight spiral */
267 {2, 10000, 256, 11, 0} /* radial: dipole-moire' (almost fractal) */
269 static int num_bgpat
= sizeof(bg
) / sizeof(struct background_pattern
);
272 /* X-specific variables */
273 static char *displayname
;
274 static XImage
*ximage
;
275 static Display
*display
;
277 static Visual
*visual
;
278 static XVisualInfo
*visual_list
;
279 static int RShift
, GShift
, BShift
;
280 static ulg RMask
, GMask
, BMask
;
281 static Window window
;
283 static Colormap colormap
;
285 static int have_nondefault_visual
= FALSE
;
286 static int have_colormap
= FALSE
;
287 static int have_window
= FALSE
;
288 static int have_gc
= FALSE
;
293 int main(int argc
, char **argv
)
298 char *p
, *bgstr
= NULL
;
305 long loop_interval
= -1; /* seconds (100,000 max) */
307 double LUT_exponent
; /* just the lookup table */
308 double CRT_exponent
= 2.2; /* just the monitor */
309 double default_display_exponent
; /* whole display system */
314 /* First initialize a few things, just to be sure--memset takes care of
315 * default background color (black), booleans (FALSE), pointers (NULL),
318 displayname
= (char *)NULL
;
319 filename
= (char *)NULL
;
320 memset(&rpng2_info
, 0, sizeof(mainprog_info
));
323 /* Set the default value for our display-system exponent, i.e., the
324 * product of the CRT exponent and the exponent corresponding to
325 * the frame-buffer's lookup table (LUT), if any. This is not an
326 * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
327 * ones), but it should cover 99% of the current possibilities. */
330 /* third-party utilities can modify the default LUT exponent */
331 LUT_exponent
= 1.0 / 2.2;
333 if (some_next_function_that_returns_gamma(&next_gamma))
334 LUT_exponent = 1.0 / next_gamma;
337 LUT_exponent
= 1.0 / 1.7;
338 /* there doesn't seem to be any documented function to
339 * get the "gamma" value, so we do it the hard way */
340 infile
= fopen("/etc/config/system.glGammaVal", "r");
344 fgets(tmpline
, 80, infile
);
346 sgi_gamma
= atof(tmpline
);
348 LUT_exponent
= 1.0 / sgi_gamma
;
350 #elif defined(Macintosh)
351 LUT_exponent
= 1.8 / 2.61;
353 if (some_mac_function_that_returns_gamma(&mac_gamma))
354 LUT_exponent = mac_gamma / 2.61;
357 LUT_exponent
= 1.0; /* assume no LUT: most PCs */
360 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
361 default_display_exponent
= LUT_exponent
* CRT_exponent
;
364 /* If the user has set the SCREEN_GAMMA environment variable as suggested
365 * (somewhat imprecisely) in the libpng documentation, use that; otherwise
366 * use the default value we just calculated. Either way, the user may
367 * override this via a command-line option. */
369 if ((p
= getenv("SCREEN_GAMMA")) != NULL
)
370 rpng2_info
.display_exponent
= atof(p
);
372 rpng2_info
.display_exponent
= default_display_exponent
;
375 /* Now parse the command line for options and the PNG filename. */
377 while (*++argv
&& !error
) {
378 if (!strncmp(*argv
, "-display", 2)) {
383 } else if (!strncmp(*argv
, "-gamma", 2)) {
387 rpng2_info
.display_exponent
= atof(*argv
);
388 if (rpng2_info
.display_exponent
<= 0.0)
391 } else if (!strncmp(*argv
, "-bgcolor", 4)) {
396 if (strlen(bgstr
) != 7 || bgstr
[0] != '#')
403 } else if (!strncmp(*argv
, "-bgpat", 4)) {
408 if (pat
>= 0 && pat
< num_bgpat
) {
414 } else if (!strncmp(*argv
, "-usleep", 2)) {
418 usleep_duration
= (ulg
)atol(*argv
);
421 } else if (!strncmp(*argv
, "-pause", 2)) {
422 pause_after_pass
= TRUE
;
423 } else if (!strncmp(*argv
, "-timing", 2)) {
426 } else if (!strncmp(*argv
, "-loop", 2)) {
428 if (!argv
[1] || !is_number(argv
[1]))
432 loop_interval
= atol(*argv
);
433 if (loop_interval
< 0)
435 else if (loop_interval
> 100000) /* bit more than one day */
436 loop_interval
= 100000;
442 if (argv
[1]) /* shouldn't be any more args after filename */
445 ++error
; /* not expecting any other options */
453 /* print usage screen if any errors up to this point */
456 fprintf(stderr
, "\n%s %s: %s\n\n", PROGNAME
, VERSION
, appname
);
457 readpng2_version_info();
459 "Usage: %s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
461 " %*s [-usleep dur | -timing] [-pause] [-loop [sec]] file.png\n\n"
463 " %*s [-usleep dur | -timing] [-pause] file.png\n\n"
465 " xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
466 " exp \ttransfer-function exponent (``gamma'') of the display\n"
467 "\t\t system in floating-point format (e.g., ``%.1f''); equal\n"
468 "\t\t to the product of the lookup-table exponent (varies)\n"
469 "\t\t and the CRT exponent (usually 2.2); must be positive\n"
470 " bg \tdesired background color in 7-character hex RGB format\n"
471 "\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n"
472 "\t\t used with transparent images; overrides -bgpat\n"
473 " pat \tdesired background pattern number (0-%d); used with\n"
474 "\t\t transparent images; overrides -bgcolor\n"
476 " -loop\tloops through background images after initial display\n"
477 "\t\t is complete (depends on -bgpat)\n"
478 " sec \tseconds to display each background image (default = 2)\n"
480 " dur \tduration in microseconds to wait after displaying each\n"
481 "\t\t row (for demo purposes)\n"
482 " -timing\tenables delay for every block read, to simulate modem\n"
483 "\t\t download of image (~36 Kbps)\n"
484 " -pause\tpauses after displaying each pass until mouse clicked\n"
485 "\nPress Q, Esc or mouse button 1 (within image window, after image\n"
486 "is displayed) to quit.\n"
488 (int)strlen(PROGNAME
), " ", default_display_exponent
, num_bgpat
-1);
493 if (!(infile
= fopen(filename
, "rb"))) {
494 fprintf(stderr
, PROGNAME
": can't open PNG file [%s]\n", filename
);
497 incount
= fread(inbuf
, 1, INBUFSIZE
, infile
);
498 if (incount
< 8 || !readpng2_check_sig(inbuf
, 8)) {
499 fprintf(stderr
, PROGNAME
500 ": [%s] is not a PNG file: incorrect signature\n",
503 } else if ((rc
= readpng2_init(&rpng2_info
)) != 0) {
506 fprintf(stderr
, PROGNAME
507 ": [%s] has bad IHDR (libpng longjmp)\n", filename
);
510 fprintf(stderr
, PROGNAME
": insufficient memory\n");
513 fprintf(stderr
, PROGNAME
514 ": unknown readpng2_init() error\n");
519 Trace((stderr
, "about to call XOpenDisplay()\n"))
520 display
= XOpenDisplay(displayname
);
522 readpng2_cleanup(&rpng2_info
);
523 fprintf(stderr
, PROGNAME
": can't open X display [%s]\n",
524 displayname
? displayname
: "default");
534 fprintf(stderr
, PROGNAME
": aborting.\n");
539 /* set the title-bar string, but make sure buffer doesn't overflow */
541 alen
= strlen(appname
);
542 flen
= strlen(filename
);
543 if (alen
+ flen
+ 3 > 1023)
544 sprintf(titlebar
, "%s: ...%s", appname
, filename
+(alen
+flen
+6-1023));
546 sprintf(titlebar
, "%s: %s", appname
, filename
);
549 /* set some final rpng2_info variables before entering main data loop */
552 unsigned r
, g
, b
; /* this approach quiets compiler warnings */
554 sscanf(bgstr
+1, "%2x%2x%2x", &r
, &g
, &b
);
555 rpng2_info
.bg_red
= (uch
)r
;
556 rpng2_info
.bg_green
= (uch
)g
;
557 rpng2_info
.bg_blue
= (uch
)b
;
559 rpng2_info
.need_bgcolor
= TRUE
;
561 rpng2_info
.state
= kPreInit
;
562 rpng2_info
.mainprog_init
= rpng2_x_init
;
563 rpng2_info
.mainprog_display_row
= rpng2_x_display_row
;
564 rpng2_info
.mainprog_finish_display
= rpng2_x_finish_display
;
567 /* OK, this is the fun part: call readpng2_decode_data() at the start of
568 * the loop to deal with our first buffer of data (read in above to verify
569 * that the file is a PNG image), then loop through the file and continue
570 * calling the same routine to handle each chunk of data. It in turn
571 * passes the data to libpng, which will invoke one or more of our call-
572 * backs as decoded data become available. We optionally call sleep() for
573 * one second per iteration to simulate downloading the image via an analog
577 Trace((stderr
, "about to call readpng2_decode_data()\n"))
578 if (readpng2_decode_data(&rpng2_info
, inbuf
, incount
))
580 Trace((stderr
, "done with readpng2_decode_data()\n"))
582 if (error
|| incount
!= INBUFSIZE
|| rpng2_info
.state
== kDone
) {
583 if (rpng2_info
.state
== kDone
) {
584 Trace((stderr
, "done decoding PNG image\n"))
585 } else if (ferror(infile
)) {
586 fprintf(stderr
, PROGNAME
587 ": error while reading PNG image file\n");
589 } else if (feof(infile
)) {
590 fprintf(stderr
, PROGNAME
": end of file reached "
591 "(unexpectedly) while reading PNG image file\n");
593 } else /* if (error) */ {
594 /* will print error message below */
602 incount
= fread(inbuf
, 1, INBUFSIZE
, infile
);
606 /* clean up PNG stuff and report any decoding errors */
609 Trace((stderr
, "about to call readpng2_cleanup()\n"))
610 readpng2_cleanup(&rpng2_info
);
613 fprintf(stderr
, PROGNAME
": libpng error while decoding PNG image\n");
620 if (loop
&& bg_image
) {
621 Trace((stderr
, "entering -loop loop (FEATURE_LOOP)\n"))
624 struct timeval now
, then
;
626 /* get current time and add loop_interval to get target time */
627 if (gettimeofday(&then
, NULL
) == 0) {
628 then
.tv_sec
+= loop_interval
;
633 /* do quick check for a quit event but don't wait for it */
634 /* GRR BUG: should also check for Expose events and redraw... */
635 if (XCheckMaskEvent(display
, KeyPressMask
| ButtonPressMask
, &e
))
639 /* generate next background image */
640 if (++pat
>= num_bgpat
)
642 rpng2_x_reload_bg_image();
644 /* wait for timeout, using whatever means are available */
645 if (use_sleep
|| gettimeofday(&now
, NULL
) != 0) {
646 for (i
= loop_interval
; i
> 0; --i
) {
648 /* GRR BUG: also need to check for Expose (and redraw!) */
649 if (XCheckMaskEvent(display
, KeyPressMask
| ButtonPressMask
,
655 if (now
.tv_sec
< then
.tv_sec
||
656 (now
.tv_sec
== then
.tv_sec
&& now
.tv_usec
< then
.tv_usec
))
659 long seconds_to_go
= then
.tv_sec
- now
.tv_sec
;
662 /* basically chew up most of remaining loop-interval with
663 * calls to sleep(1) interleaved with checks for quit
664 * events, but also recalc time-to-go periodically; when
665 * done, clean up any remaining time with usleep() call
666 * (could also use SIGALRM, but signals are a pain...) */
667 while (seconds_to_go
-- > 1) {
668 int seconds_done
= 0;
670 for (i
= seconds_to_go
; i
> 0 && !quit
; --i
) {
672 /* GRR BUG: need to check for Expose and redraw */
673 if (XCheckMaskEvent(display
, KeyPressMask
|
674 ButtonPressMask
, &e
) && QUIT(e
,k
))
676 if (++seconds_done
> 1000)
677 break; /* time to redo seconds_to_go meas. */
682 /* OK, more than 1000 seconds since last check:
683 * correct the time-to-go measurement for drift */
684 if (gettimeofday(&now
, NULL
) == 0) {
685 if (now
.tv_sec
>= then
.tv_sec
)
687 seconds_to_go
= then
.tv_sec
- now
.tv_sec
;
689 ++seconds_to_go
; /* restore what we subtracted */
692 break; /* breaks outer do-loop, skips redisplay */
694 /* since difference between "now" and "then" is already
695 * eaten up to within a couple of seconds, don't need to
696 * worry about overflow--but might have overshot (neg.) */
697 if (gettimeofday(&now
, NULL
) == 0) {
698 usleep_usec
= 1000000L*(then
.tv_sec
- now
.tv_sec
) +
699 then
.tv_usec
- now
.tv_usec
;
701 usleep((ulg
)usleep_usec
);
706 /* composite image against new background and display (note that
707 * we do not take into account the time spent doing this...) */
708 rpng2_x_redisplay_image (0, 0, rpng2_info
.width
, rpng2_info
.height
);
711 } else /* FALL THROUGH and do the normal thing */
713 #endif /* FEATURE_LOOP */
715 /* wait for the user to tell us when to quit */
717 if (rpng2_info
.state
>= kWindowInit
) {
718 Trace((stderr
, "entering final wait-for-quit-event loop\n"))
720 XNextEvent(display
, &e
);
721 if (e
.type
== Expose
) {
722 XExposeEvent
*ex
= (XExposeEvent
*)&e
;
723 rpng2_x_redisplay_image (ex
->x
, ex
->y
, ex
->width
, ex
->height
);
725 } while (!QUIT(e
,k
));
727 fprintf(stderr
, PROGNAME
": init callback never called: probable "
728 "libpng error while decoding PNG metadata\n");
733 /* we're done: clean up all image and X resources and go away */
735 Trace((stderr
, "about to call rpng2_x_cleanup()\n"))
745 /* this function is called by readpng2_info_callback() in readpng2.c, which
746 * in turn is called by libpng after all of the pre-IDAT chunks have been
747 * read and processed--i.e., we now have enough info to finish initializing */
749 static void rpng2_x_init(void)
752 ulg rowbytes
= rpng2_info
.rowbytes
;
754 Trace((stderr
, "beginning rpng2_x_init()\n"))
755 Trace((stderr
, " rowbytes = %d\n", rpng2_info
.rowbytes
))
756 Trace((stderr
, " width = %ld\n", rpng2_info
.width
))
757 Trace((stderr
, " height = %ld\n", rpng2_info
.height
))
759 rpng2_info
.image_data
= (uch
*)malloc(rowbytes
* rpng2_info
.height
);
760 if (!rpng2_info
.image_data
) {
761 readpng2_cleanup(&rpng2_info
);
765 rpng2_info
.row_pointers
= (uch
**)malloc(rpng2_info
.height
* sizeof(uch
*));
766 if (!rpng2_info
.row_pointers
) {
767 free(rpng2_info
.image_data
);
768 rpng2_info
.image_data
= NULL
;
769 readpng2_cleanup(&rpng2_info
);
773 for (i
= 0; i
< rpng2_info
.height
; ++i
)
774 rpng2_info
.row_pointers
[i
] = rpng2_info
.image_data
+ i
*rowbytes
;
777 /* do the basic X initialization stuff, make the window, and fill it with
778 * the user-specified, file-specified or default background color or
781 if (rpng2_x_create_window()) {
783 /* GRR TEMPORARY HACK: this is fundamentally no different from cases
784 * above; libpng should call our error handler to longjmp() back to us
785 * when png_ptr goes away. If we/it segfault instead, seems like a
788 /* we're here via libpng callback, so if window fails, clean and bail */
789 readpng2_cleanup(&rpng2_info
);
794 rpng2_info
.state
= kWindowInit
;
801 static int rpng2_x_create_window(void)
803 ulg bg_red
= rpng2_info
.bg_red
;
804 ulg bg_green
= rpng2_info
.bg_green
;
805 ulg bg_blue
= rpng2_info
.bg_blue
;
808 int need_colormap
= FALSE
;
814 XSetWindowAttributes attr
;
815 XTextProperty windowName
, *pWindowName
= &windowName
;
816 XTextProperty iconName
, *pIconName
= &iconName
;
817 XVisualInfo visual_info
;
818 XSizeHints
*size_hints
;
820 XClassHint
*class_hints
;
823 Trace((stderr
, "beginning rpng2_x_create_window()\n"))
825 screen
= DefaultScreen(display
);
826 depth
= DisplayPlanes(display
, screen
);
827 root
= RootWindow(display
, screen
);
830 XSynchronize(display
, True
);
833 if (depth
!= 16 && depth
!= 24 && depth
!= 32) {
834 int visuals_matched
= 0;
836 Trace((stderr
, "default depth is %d: checking other visuals\n",
840 visual_info
.screen
= screen
;
841 visual_info
.depth
= 24;
842 visual_list
= XGetVisualInfo(display
,
843 VisualScreenMask
| VisualDepthMask
, &visual_info
, &visuals_matched
);
844 if (visuals_matched
== 0) {
845 /* GRR: add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
846 fprintf(stderr
, "default screen depth %d not supported, and no"
847 " 24-bit visuals found\n", depth
);
850 Trace((stderr
, "XGetVisualInfo() returned %d 24-bit visuals\n",
852 visual
= visual_list
[0].visual
;
853 depth
= visual_list
[0].depth
;
855 colormap_size = visual_list[0].colormap_size;
856 visual_class = visual->class;
857 visualID = XVisualIDFromVisual(visual);
859 have_nondefault_visual
= TRUE
;
860 need_colormap
= TRUE
;
862 XMatchVisualInfo(display
, screen
, depth
, TrueColor
, &visual_info
);
863 visual
= visual_info
.visual
;
866 RMask
= visual
->red_mask
;
867 GMask
= visual
->green_mask
;
868 BMask
= visual
->blue_mask
;
870 /* GRR: add/check 8-bit support */
871 if (depth
== 8 || need_colormap
) {
872 colormap
= XCreateColormap(display
, root
, visual
, AllocNone
);
874 fprintf(stderr
, "XCreateColormap() failed\n");
877 have_colormap
= TRUE
;
879 bg_image
= FALSE
; /* gradient just wastes palette entries */
881 if (depth
== 15 || depth
== 16) {
882 RShift
= 15 - rpng2_x_msb(RMask
); /* these are right-shifts */
883 GShift
= 15 - rpng2_x_msb(GMask
);
884 BShift
= 15 - rpng2_x_msb(BMask
);
885 } else if (depth
> 16) {
886 RShift
= rpng2_x_msb(RMask
) - 7; /* these are left-shifts */
887 GShift
= rpng2_x_msb(GMask
) - 7;
888 BShift
= rpng2_x_msb(BMask
) - 7;
890 if (depth
>= 15 && (RShift
< 0 || GShift
< 0 || BShift
< 0)) {
891 fprintf(stderr
, "rpng2 internal logic error: negative X shift(s)!\n");
895 /*---------------------------------------------------------------------------
896 Finally, create the window.
897 ---------------------------------------------------------------------------*/
899 attr
.backing_store
= Always
;
900 attr
.event_mask
= ExposureMask
| KeyPressMask
| ButtonPressMask
;
901 attrmask
= CWBackingStore
| CWEventMask
;
902 if (have_nondefault_visual
) {
903 attr
.colormap
= colormap
;
904 attr
.background_pixel
= 0;
905 attr
.border_pixel
= 1;
906 attrmask
|= CWColormap
| CWBackPixel
| CWBorderPixel
;
909 window
= XCreateWindow(display
, root
, 0, 0, rpng2_info
.width
,
910 rpng2_info
.height
, 0, depth
, InputOutput
, visual
, attrmask
, &attr
);
912 if (window
== None
) {
913 fprintf(stderr
, "XCreateWindow() failed\n");
919 XSetWindowColormap(display
, window
, colormap
);
921 if (!XStringListToTextProperty(&window_name
, 1, pWindowName
))
923 if (!XStringListToTextProperty(&icon_name
, 1, pIconName
))
926 /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */
928 if ((size_hints
= XAllocSizeHints()) != NULL
) {
929 /* window will not be resizable */
930 size_hints
->flags
= PMinSize
| PMaxSize
;
931 size_hints
->min_width
= size_hints
->max_width
= (int)rpng2_info
.width
;
932 size_hints
->min_height
= size_hints
->max_height
=
933 (int)rpng2_info
.height
;
936 if ((wm_hints
= XAllocWMHints()) != NULL
) {
937 wm_hints
->initial_state
= NormalState
;
938 wm_hints
->input
= True
;
939 /* wm_hints->icon_pixmap = icon_pixmap; */
940 wm_hints
->flags
= StateHint
| InputHint
/* | IconPixmapHint */ ;
943 if ((class_hints
= XAllocClassHint()) != NULL
) {
944 class_hints
->res_name
= res_name
;
945 class_hints
->res_class
= res_class
;
948 XSetWMProperties(display
, window
, pWindowName
, pIconName
, NULL
, 0,
949 size_hints
, wm_hints
, class_hints
);
951 /* various properties and hints no longer needed; free memory */
953 XFree(pWindowName
->value
);
955 XFree(pIconName
->value
);
963 XMapWindow(display
, window
);
965 gc
= XCreateGC(display
, window
, 0, &gcvalues
);
968 /*---------------------------------------------------------------------------
969 Allocate memory for the X- and display-specific version of the image.
970 ---------------------------------------------------------------------------*/
972 if (depth
== 24 || depth
== 32) {
973 xdata
= (uch
*)malloc(4*rpng2_info
.width
*rpng2_info
.height
);
975 } else if (depth
== 16) {
976 xdata
= (uch
*)malloc(2*rpng2_info
.width
*rpng2_info
.height
);
978 } else /* depth == 8 */ {
979 xdata
= (uch
*)malloc(rpng2_info
.width
*rpng2_info
.height
);
984 fprintf(stderr
, PROGNAME
": unable to allocate image memory\n");
988 ximage
= XCreateImage(display
, visual
, depth
, ZPixmap
, 0,
989 (char *)xdata
, rpng2_info
.width
, rpng2_info
.height
, pad
, 0);
992 fprintf(stderr
, PROGNAME
": XCreateImage() failed\n");
997 /* to avoid testing the byte order every pixel (or doubling the size of
998 * the drawing routine with a giant if-test), we arbitrarily set the byte
999 * order to MSBFirst and let Xlib worry about inverting things on little-
1000 * endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the
1001 * most efficient approach (the giant if-test would be better), but in
1002 * the interest of clarity, we'll take the easy way out... */
1004 ximage
->byte_order
= MSBFirst
;
1006 /*---------------------------------------------------------------------------
1007 Fill window with the specified background color (default is black) or
1008 faked "background image" (but latter is disabled if 8-bit; gradients
1009 just waste palette entries).
1010 ---------------------------------------------------------------------------*/
1013 rpng2_x_load_bg_image(); /* resets bg_image if fails */
1016 if (depth
== 24 || depth
== 32) {
1017 bg_pixel
= (bg_red
<< RShift
) |
1018 (bg_green
<< GShift
) |
1019 (bg_blue
<< BShift
);
1020 } else if (depth
== 16) {
1021 bg_pixel
= (((bg_red
<< 8) >> RShift
) & RMask
) |
1022 (((bg_green
<< 8) >> GShift
) & GMask
) |
1023 (((bg_blue
<< 8) >> BShift
) & BMask
);
1024 } else /* depth == 8 */ {
1026 /* GRR: add 8-bit support */
1029 XSetForeground(display
, gc
, bg_pixel
);
1030 XFillRectangle(display
, window
, gc
, 0, 0, rpng2_info
.width
,
1034 /*---------------------------------------------------------------------------
1035 Wait for first Expose event to do any drawing, then flush and return.
1036 ---------------------------------------------------------------------------*/
1039 XNextEvent(display
, &e
);
1040 while (e
.type
!= Expose
|| e
.xexpose
.count
);
1046 } /* end function rpng2_x_create_window() */
1052 static int rpng2_x_load_bg_image(void)
1056 uch r1
, r2
, g1
, g2
, b1
, b2
;
1057 uch r1_inv
, r2_inv
, g1_inv
, g2_inv
, b1_inv
, b2_inv
;
1059 int xidx
, yidx
, yidx_max
;
1060 int even_odd_vert
, even_odd_horiz
, even_odd
;
1061 int invert_gradient2
= (bg
[pat
].type
& 0x08);
1063 int ximage_rowbytes
= ximage
->bytes_per_line
;
1067 /*---------------------------------------------------------------------------
1068 Allocate buffer for fake background image to be used with transparent
1069 images; if this fails, revert to plain background color.
1070 ---------------------------------------------------------------------------*/
1072 bg_rowbytes
= 3 * rpng2_info
.width
;
1073 bg_data
= (uch
*)malloc(bg_rowbytes
* rpng2_info
.height
);
1075 fprintf(stderr
, PROGNAME
1076 ": unable to allocate memory for background image\n");
1081 bgscale
= (pat
== 0)? 8 : bgscale_default
;
1082 yidx_max
= bgscale
- 1;
1084 /*---------------------------------------------------------------------------
1085 Vertical gradients (ramps) in NxN squares, alternating direction and
1086 colors (N == bgscale).
1087 ---------------------------------------------------------------------------*/
1089 if ((bg
[pat
].type
& 0x07) == 0) {
1090 uch r1_min
= rgb
[bg
[pat
].rgb1_min
].r
;
1091 uch g1_min
= rgb
[bg
[pat
].rgb1_min
].g
;
1092 uch b1_min
= rgb
[bg
[pat
].rgb1_min
].b
;
1093 uch r2_min
= rgb
[bg
[pat
].rgb2_min
].r
;
1094 uch g2_min
= rgb
[bg
[pat
].rgb2_min
].g
;
1095 uch b2_min
= rgb
[bg
[pat
].rgb2_min
].b
;
1096 int r1_diff
= rgb
[bg
[pat
].rgb1_max
].r
- r1_min
;
1097 int g1_diff
= rgb
[bg
[pat
].rgb1_max
].g
- g1_min
;
1098 int b1_diff
= rgb
[bg
[pat
].rgb1_max
].b
- b1_min
;
1099 int r2_diff
= rgb
[bg
[pat
].rgb2_max
].r
- r2_min
;
1100 int g2_diff
= rgb
[bg
[pat
].rgb2_max
].g
- g2_min
;
1101 int b2_diff
= rgb
[bg
[pat
].rgb2_max
].b
- b2_min
;
1103 for (row
= 0; row
< rpng2_info
.height
; ++row
) {
1104 yidx
= (int)(row
% bgscale
);
1105 even_odd_vert
= (int)((row
/ bgscale
) & 1);
1107 r1
= r1_min
+ (r1_diff
* yidx
) / yidx_max
;
1108 g1
= g1_min
+ (g1_diff
* yidx
) / yidx_max
;
1109 b1
= b1_min
+ (b1_diff
* yidx
) / yidx_max
;
1110 r1_inv
= r1_min
+ (r1_diff
* (yidx_max
-yidx
)) / yidx_max
;
1111 g1_inv
= g1_min
+ (g1_diff
* (yidx_max
-yidx
)) / yidx_max
;
1112 b1_inv
= b1_min
+ (b1_diff
* (yidx_max
-yidx
)) / yidx_max
;
1114 r2
= r2_min
+ (r2_diff
* yidx
) / yidx_max
;
1115 g2
= g2_min
+ (g2_diff
* yidx
) / yidx_max
;
1116 b2
= b2_min
+ (b2_diff
* yidx
) / yidx_max
;
1117 r2_inv
= r2_min
+ (r2_diff
* (yidx_max
-yidx
)) / yidx_max
;
1118 g2_inv
= g2_min
+ (g2_diff
* (yidx_max
-yidx
)) / yidx_max
;
1119 b2_inv
= b2_min
+ (b2_diff
* (yidx_max
-yidx
)) / yidx_max
;
1121 dest
= (char *)bg_data
+ row
*bg_rowbytes
;
1122 for (i
= 0; i
< rpng2_info
.width
; ++i
) {
1123 even_odd_horiz
= (int)((i
/ bgscale
) & 1);
1124 even_odd
= even_odd_vert
^ even_odd_horiz
;
1126 (even_odd_horiz
&& (bg
[pat
].type
& 0x10));
1127 if (even_odd
== 0) { /* gradient #1 */
1128 if (invert_column
) {
1137 } else { /* gradient #2 */
1138 if ((invert_column
&& invert_gradient2
) ||
1139 (!invert_column
&& !invert_gradient2
))
1141 *dest
++ = r2
; /* not inverted or */
1142 *dest
++ = g2
; /* doubly inverted */
1146 *dest
++ = g2_inv
; /* singly inverted */
1153 /*---------------------------------------------------------------------------
1154 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
1156 ---------------------------------------------------------------------------*/
1158 } else if ((bg
[pat
].type
& 0x07) == 1) {
1160 hmax
= (bgscale
-1)/2; /* half the max weight of a color */
1161 max
= 2*hmax
; /* the max weight of a color */
1163 r1
= rgb
[bg
[pat
].rgb1_max
].r
;
1164 g1
= rgb
[bg
[pat
].rgb1_max
].g
;
1165 b1
= rgb
[bg
[pat
].rgb1_max
].b
;
1166 r2
= rgb
[bg
[pat
].rgb2_max
].r
;
1167 g2
= rgb
[bg
[pat
].rgb2_max
].g
;
1168 b2
= rgb
[bg
[pat
].rgb2_max
].b
;
1170 for (row
= 0; row
< rpng2_info
.height
; ++row
) {
1171 yidx
= (int)(row
% bgscale
);
1173 yidx
= bgscale
-1 - yidx
;
1174 dest
= (char *)bg_data
+ row
*bg_rowbytes
;
1175 for (i
= 0; i
< rpng2_info
.width
; ++i
) {
1176 xidx
= (int)(i
% bgscale
);
1178 xidx
= bgscale
-1 - xidx
;
1180 *dest
++ = (k
*r1
+ (max
-k
)*r2
) / max
;
1181 *dest
++ = (k
*g1
+ (max
-k
)*g2
) / max
;
1182 *dest
++ = (k
*b1
+ (max
-k
)*b2
) / max
;
1186 /*---------------------------------------------------------------------------
1187 Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
1188 soids will equal bgscale?]. This one is slow but very cool. Code con-
1189 tributed by Pieter S. van der Meulen (originally in Smalltalk).
1190 ---------------------------------------------------------------------------*/
1192 } else if ((bg
[pat
].type
& 0x07) == 2) {
1194 int ii
, x
, y
, hw
, hh
, grayspot
;
1195 double freq
, rotate
, saturate
, gray
, intensity
;
1196 double angle
=0.0, aoffset
=0.0, maxDist
, dist
;
1197 double red
=0.0, green
=0.0, blue
=0.0, hue
, s
, v
, f
, p
, q
, t
;
1199 fprintf(stderr
, "%s: computing radial background...",
1203 hh
= (int)(rpng2_info
.height
/ 2);
1204 hw
= (int)(rpng2_info
.width
/ 2);
1206 /* variables for radial waves:
1207 * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED]
1208 * freq: number of color beams originating from the center
1209 * grayspot: size of the graying center area (anti-alias)
1210 * rotate: rotation of the beams as a function of radius
1211 * saturate: saturation of beams' shape azimuthally
1213 angle
= CLIP(angle
, 0.0, 360.0);
1214 grayspot
= CLIP(bg
[pat
].bg_gray
, 1, (hh
+ hw
));
1215 freq
= MAX((double)bg
[pat
].bg_freq
, 0.0);
1216 saturate
= (double)bg
[pat
].bg_bsat
* 0.1;
1217 rotate
= (double)bg
[pat
].bg_brot
* 0.1;
1220 maxDist
= (double)((hw
*hw
) + (hh
*hh
));
1222 for (row
= 0; row
< rpng2_info
.height
; ++row
) {
1223 y
= (int)(row
- hh
);
1224 dest
= (char *)bg_data
+ row
*bg_rowbytes
;
1225 for (i
= 0; i
< rpng2_info
.width
; ++i
) {
1227 angle
= (x
== 0)? PI_2
: atan((double)y
/ (double)x
);
1228 gray
= (double)MAX(ABS(y
), ABS(x
)) / grayspot
;
1229 gray
= MIN(1.0, gray
);
1230 dist
= (double)((x
*x
) + (y
*y
)) / maxDist
;
1231 intensity
= cos((angle
+(rotate
*dist
*PI
)) * freq
) *
1233 intensity
= (MAX(MIN(intensity
,1.0),-1.0) + 1.0) * 0.5;
1234 hue
= (angle
+ PI
) * INV_PI_360
+ aoffset
;
1235 s
= gray
* ((double)(ABS(x
)+ABS(y
)) / (double)(hw
+ hh
));
1236 s
= MIN(MAX(s
,0.0), 1.0);
1237 v
= MIN(MAX(intensity
,0.0), 1.0);
1240 ch
= (uch
)(v
* 255.0);
1245 if ((hue
< 0.0) || (hue
>= 360.0))
1246 hue
-= (((int)(hue
/ 360.0)) * 360.0);
1249 f
= hue
- (double)ii
;
1251 q
= (1.0 - (s
* f
)) * v
;
1252 t
= (1.0 - (s
* (1.0 - f
))) * v
;
1253 if (ii
== 0) { red
= v
; green
= t
; blue
= p
; }
1254 else if (ii
== 1) { red
= q
; green
= v
; blue
= p
; }
1255 else if (ii
== 2) { red
= p
; green
= v
; blue
= t
; }
1256 else if (ii
== 3) { red
= p
; green
= q
; blue
= v
; }
1257 else if (ii
== 4) { red
= t
; green
= p
; blue
= v
; }
1258 else if (ii
== 5) { red
= v
; green
= p
; blue
= q
; }
1259 *dest
++ = (uch
)(red
* 255.0);
1260 *dest
++ = (uch
)(green
* 255.0);
1261 *dest
++ = (uch
)(blue
* 255.0);
1265 fprintf(stderr
, "done.\n");
1269 /*---------------------------------------------------------------------------
1270 Blast background image to display buffer before beginning PNG decode.
1271 ---------------------------------------------------------------------------*/
1273 if (depth
== 24 || depth
== 32) {
1274 ulg red
, green
, blue
;
1275 int bpp
= ximage
->bits_per_pixel
;
1277 for (row
= 0; row
< rpng2_info
.height
; ++row
) {
1278 src
= bg_data
+ row
*bg_rowbytes
;
1279 dest
= ximage
->data
+ row
*ximage_rowbytes
;
1280 if (bpp
== 32) { /* slightly optimized version */
1281 for (i
= rpng2_info
.width
; i
> 0; --i
) {
1285 pixel
= (red
<< RShift
) |
1288 /* recall that we set ximage->byte_order = MSBFirst above */
1289 *dest
++ = (char)((pixel
>> 24) & 0xff);
1290 *dest
++ = (char)((pixel
>> 16) & 0xff);
1291 *dest
++ = (char)((pixel
>> 8) & 0xff);
1292 *dest
++ = (char)( pixel
& 0xff);
1295 for (i
= rpng2_info
.width
; i
> 0; --i
) {
1299 pixel
= (red
<< RShift
) |
1302 /* recall that we set ximage->byte_order = MSBFirst above */
1303 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1304 /* (probably need to use RShift, RMask, etc.) */
1305 *dest
++ = (char)((pixel
>> 16) & 0xff);
1306 *dest
++ = (char)((pixel
>> 8) & 0xff);
1307 *dest
++ = (char)( pixel
& 0xff);
1312 } else if (depth
== 16) {
1313 ush red
, green
, blue
;
1315 for (row
= 0; row
< rpng2_info
.height
; ++row
) {
1316 src
= bg_data
+ row
*bg_rowbytes
;
1317 dest
= ximage
->data
+ row
*ximage_rowbytes
;
1318 for (i
= rpng2_info
.width
; i
> 0; --i
) {
1319 red
= ((ush
)(*src
) << 8); ++src
;
1320 green
= ((ush
)(*src
) << 8); ++src
;
1321 blue
= ((ush
)(*src
) << 8); ++src
;
1322 pixel
= ((red
>> RShift
) & RMask
) |
1323 ((green
>> GShift
) & GMask
) |
1324 ((blue
>> BShift
) & BMask
);
1325 /* recall that we set ximage->byte_order = MSBFirst above */
1326 *dest
++ = (char)((pixel
>> 8) & 0xff);
1327 *dest
++ = (char)( pixel
& 0xff);
1331 } else /* depth == 8 */ {
1333 /* GRR: add 8-bit support */
1337 XPutImage(display
, window
, gc
, ximage
, 0, 0, 0, 0, rpng2_info
.width
,
1342 } /* end function rpng2_x_load_bg_image() */
1348 static void rpng2_x_display_row(ulg row
)
1350 uch bg_red
= rpng2_info
.bg_red
;
1351 uch bg_green
= rpng2_info
.bg_green
;
1352 uch bg_blue
= rpng2_info
.bg_blue
;
1353 uch
*src
, *src2
=NULL
;
1356 int ximage_rowbytes
= ximage
->bytes_per_line
;
1358 static int rows
=0, prevpass
=(-1);
1359 static ulg firstrow
;
1361 /*---------------------------------------------------------------------------
1362 rows and firstrow simply track how many rows (and which ones) have not
1363 yet been displayed; alternatively, we could call XPutImage() for every
1364 row and not bother with the records-keeping.
1365 ---------------------------------------------------------------------------*/
1367 Trace((stderr
, "beginning rpng2_x_display_row()\n"))
1369 if (rpng2_info
.pass
!= prevpass
) {
1370 if (pause_after_pass
&& rpng2_info
.pass
> 0) {
1375 "%s: end of pass %d of 7; click in image window to continue\n",
1376 PROGNAME
, prevpass
+ 1);
1378 XNextEvent(display
, &e
);
1381 fprintf(stderr
, "%s: pass %d of 7\r", PROGNAME
, rpng2_info
.pass
+ 1);
1383 prevpass
= rpng2_info
.pass
;
1387 firstrow
= row
; /* first row that is not yet displayed */
1389 ++rows
; /* count of rows received but not yet displayed */
1391 /*---------------------------------------------------------------------------
1392 Aside from the use of the rpng2_info struct, the lack of an outer loop
1393 (over rows) and moving the XPutImage() call outside the "if (depth)"
1394 tests, this routine is identical to rpng_x_display_image() in the non-
1395 progressive version of the program.
1396 ---------------------------------------------------------------------------*/
1398 if (depth
== 24 || depth
== 32) {
1399 ulg red
, green
, blue
;
1400 int bpp
= ximage
->bits_per_pixel
;
1402 src
= rpng2_info
.image_data
+ row
*rpng2_info
.rowbytes
;
1404 src2
= bg_data
+ row
*bg_rowbytes
;
1405 dest
= ximage
->data
+ row
*ximage_rowbytes
;
1406 if (rpng2_info
.channels
== 3) {
1407 for (i
= rpng2_info
.width
; i
> 0; --i
) {
1411 pixel
= (red
<< RShift
) |
1414 /* recall that we set ximage->byte_order = MSBFirst above */
1416 *dest
++ = (char)((pixel
>> 24) & 0xff);
1417 *dest
++ = (char)((pixel
>> 16) & 0xff);
1418 *dest
++ = (char)((pixel
>> 8) & 0xff);
1419 *dest
++ = (char)( pixel
& 0xff);
1421 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1422 /* (probably need to use RShift, RMask, etc.) */
1423 *dest
++ = (char)((pixel
>> 16) & 0xff);
1424 *dest
++ = (char)((pixel
>> 8) & 0xff);
1425 *dest
++ = (char)( pixel
& 0xff);
1428 } else /* if (rpng2_info.channels == 4) */ {
1429 for (i
= rpng2_info
.width
; i
> 0; --i
) {
1443 } else if (a
== 0) {
1448 /* this macro (from png.h) composites the foreground
1449 * and background values and puts the result into the
1451 alpha_composite(red
, r
, a
, bg_red
);
1452 alpha_composite(green
, g
, a
, bg_green
);
1453 alpha_composite(blue
, b
, a
, bg_blue
);
1455 pixel
= (red
<< RShift
) |
1458 /* recall that we set ximage->byte_order = MSBFirst above */
1460 *dest
++ = (char)((pixel
>> 24) & 0xff);
1461 *dest
++ = (char)((pixel
>> 16) & 0xff);
1462 *dest
++ = (char)((pixel
>> 8) & 0xff);
1463 *dest
++ = (char)( pixel
& 0xff);
1465 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1466 /* (probably need to use RShift, RMask, etc.) */
1467 *dest
++ = (char)((pixel
>> 16) & 0xff);
1468 *dest
++ = (char)((pixel
>> 8) & 0xff);
1469 *dest
++ = (char)( pixel
& 0xff);
1474 } else if (depth
== 16) {
1475 ush red
, green
, blue
;
1477 src
= rpng2_info
.row_pointers
[row
];
1479 src2
= bg_data
+ row
*bg_rowbytes
;
1480 dest
= ximage
->data
+ row
*ximage_rowbytes
;
1481 if (rpng2_info
.channels
== 3) {
1482 for (i
= rpng2_info
.width
; i
> 0; --i
) {
1483 red
= ((ush
)(*src
) << 8);
1485 green
= ((ush
)(*src
) << 8);
1487 blue
= ((ush
)(*src
) << 8);
1489 pixel
= ((red
>> RShift
) & RMask
) |
1490 ((green
>> GShift
) & GMask
) |
1491 ((blue
>> BShift
) & BMask
);
1492 /* recall that we set ximage->byte_order = MSBFirst above */
1493 *dest
++ = (char)((pixel
>> 8) & 0xff);
1494 *dest
++ = (char)( pixel
& 0xff);
1496 } else /* if (rpng2_info.channels == 4) */ {
1497 for (i
= rpng2_info
.width
; i
> 0; --i
) {
1508 red
= ((ush
)r
<< 8);
1509 green
= ((ush
)g
<< 8);
1510 blue
= ((ush
)b
<< 8);
1511 } else if (a
== 0) {
1512 red
= ((ush
)bg_red
<< 8);
1513 green
= ((ush
)bg_green
<< 8);
1514 blue
= ((ush
)bg_blue
<< 8);
1516 /* this macro (from png.h) composites the foreground
1517 * and background values and puts the result back into
1518 * the first argument (== fg byte here: safe) */
1519 alpha_composite(r
, r
, a
, bg_red
);
1520 alpha_composite(g
, g
, a
, bg_green
);
1521 alpha_composite(b
, b
, a
, bg_blue
);
1522 red
= ((ush
)r
<< 8);
1523 green
= ((ush
)g
<< 8);
1524 blue
= ((ush
)b
<< 8);
1526 pixel
= ((red
>> RShift
) & RMask
) |
1527 ((green
>> GShift
) & GMask
) |
1528 ((blue
>> BShift
) & BMask
);
1529 /* recall that we set ximage->byte_order = MSBFirst above */
1530 *dest
++ = (char)((pixel
>> 8) & 0xff);
1531 *dest
++ = (char)( pixel
& 0xff);
1535 } else /* depth == 8 */ {
1537 /* GRR: add 8-bit support */
1542 /*---------------------------------------------------------------------------
1543 Display after every 16 rows or when on one of last two rows. (Region
1544 may include previously displayed lines due to interlacing--i.e., not
1545 contiguous. Also, second-to-last row is final one in interlaced images
1546 with odd number of rows.) For demos, flush (and delay) after every 16th
1547 row so "sparse" passes don't go twice as fast.
1548 ---------------------------------------------------------------------------*/
1550 if (demo_timing
&& (row
- firstrow
>= 16 || row
>= rpng2_info
.height
-2)) {
1551 XPutImage(display
, window
, gc
, ximage
, 0, (int)firstrow
, 0,
1552 (int)firstrow
, rpng2_info
.width
, row
- firstrow
+ 1);
1555 usleep(usleep_duration
);
1557 if (!demo_timing
&& ((rows
& 0xf) == 0 || row
>= rpng2_info
.height
-2)) {
1558 XPutImage(display
, window
, gc
, ximage
, 0, (int)firstrow
, 0,
1559 (int)firstrow
, rpng2_info
.width
, row
- firstrow
+ 1);
1570 static void rpng2_x_finish_display(void)
1572 Trace((stderr
, "beginning rpng2_x_finish_display()\n"))
1574 /* last row has already been displayed by rpng2_x_display_row(), so we
1575 * have nothing to do here except set a flag and let the user know that
1576 * the image is done */
1578 rpng2_info
.state
= kDone
;
1580 "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
1588 static void rpng2_x_redisplay_image(ulg startcol
, ulg startrow
,
1589 ulg width
, ulg height
)
1591 uch bg_red
= rpng2_info
.bg_red
;
1592 uch bg_green
= rpng2_info
.bg_green
;
1593 uch bg_blue
= rpng2_info
.bg_blue
;
1594 uch
*src
, *src2
=NULL
;
1597 ulg i
, row
, lastrow
= 0;
1599 int ximage_rowbytes
= ximage
->bytes_per_line
;
1602 Trace((stderr
, "beginning display loop (image_channels == %d)\n",
1603 rpng2_info
.channels
))
1604 Trace((stderr
, " (width = %ld, rowbytes = %d, ximage_rowbytes = %d)\n",
1605 rpng2_info
.width
, rpng2_info
.rowbytes
, ximage_rowbytes
))
1606 Trace((stderr
, " (bpp = %d)\n", ximage
->bits_per_pixel
))
1607 Trace((stderr
, " (byte_order = %s)\n", ximage
->byte_order
== MSBFirst
?
1608 "MSBFirst" : (ximage
->byte_order
== LSBFirst
? "LSBFirst" : "unknown")))
1610 /*---------------------------------------------------------------------------
1611 Aside from the use of the rpng2_info struct and of src2 (for background
1612 image), this routine is identical to rpng_x_display_image() in the non-
1613 progressive version of the program--for the simple reason that redisplay
1614 of the image against a new background happens after the image is fully
1615 decoded and therefore is, by definition, non-progressive.
1616 ---------------------------------------------------------------------------*/
1618 if (depth
== 24 || depth
== 32) {
1619 ulg red
, green
, blue
;
1620 int bpp
= ximage
->bits_per_pixel
;
1622 for (lastrow
= row
= startrow
; row
< startrow
+height
; ++row
) {
1623 src
= rpng2_info
.image_data
+ row
*rpng2_info
.rowbytes
;
1625 src2
= bg_data
+ row
*bg_rowbytes
;
1626 dest
= ximage
->data
+ row
*ximage_rowbytes
;
1627 if (rpng2_info
.channels
== 3) {
1628 for (i
= rpng2_info
.width
; i
> 0; --i
) {
1632 #ifdef NO_24BIT_MASKS
1633 pixel
= (red
<< RShift
) |
1636 /* recall that we set ximage->byte_order = MSBFirst above */
1638 *dest
++ = (char)((pixel
>> 24) & 0xff);
1639 *dest
++ = (char)((pixel
>> 16) & 0xff);
1640 *dest
++ = (char)((pixel
>> 8) & 0xff);
1641 *dest
++ = (char)( pixel
& 0xff);
1643 /* this assumes bpp == 24 & bits are packed low */
1644 /* (probably need to use RShift, RMask, etc.) */
1645 *dest
++ = (char)((pixel
>> 16) & 0xff);
1646 *dest
++ = (char)((pixel
>> 8) & 0xff);
1647 *dest
++ = (char)( pixel
& 0xff);
1650 red
= (RShift
< 0)? red
<< (-RShift
) : red
>> RShift
;
1651 green
= (GShift
< 0)? green
<< (-GShift
) : green
>> GShift
;
1652 blue
= (BShift
< 0)? blue
<< (-BShift
) : blue
>> BShift
;
1653 pixel
= (red
& RMask
) | (green
& GMask
) | (blue
& BMask
);
1654 /* recall that we set ximage->byte_order = MSBFirst above */
1656 *dest
++ = (char)((pixel
>> 24) & 0xff);
1657 *dest
++ = (char)((pixel
>> 16) & 0xff);
1658 *dest
++ = (char)((pixel
>> 8) & 0xff);
1659 *dest
++ = (char)( pixel
& 0xff);
1662 /* this assumes bpp == 24 & bits are packed low */
1663 /* (probably need to use RShift/RMask/etc. here, too) */
1664 *dest
++ = (char)((pixel
>> 16) & 0xff);
1665 *dest
++ = (char)((pixel
>> 8) & 0xff);
1666 *dest
++ = (char)( pixel
& 0xff);
1671 } else /* if (rpng2_info.channels == 4) */ {
1672 for (i
= rpng2_info
.width
; i
> 0; --i
) {
1686 } else if (a
== 0) {
1691 /* this macro (from png.h) composites the foreground
1692 * and background values and puts the result into the
1694 alpha_composite(red
, r
, a
, bg_red
);
1695 alpha_composite(green
, g
, a
, bg_green
);
1696 alpha_composite(blue
, b
, a
, bg_blue
);
1698 #ifdef NO_24BIT_MASKS
1699 pixel
= (red
<< RShift
) |
1702 /* recall that we set ximage->byte_order = MSBFirst above */
1704 *dest
++ = (char)((pixel
>> 24) & 0xff);
1705 *dest
++ = (char)((pixel
>> 16) & 0xff);
1706 *dest
++ = (char)((pixel
>> 8) & 0xff);
1707 *dest
++ = (char)( pixel
& 0xff);
1709 /* this assumes bpp == 24 & bits are packed low */
1710 /* (probably need to use RShift, RMask, etc.) */
1711 *dest
++ = (char)((pixel
>> 16) & 0xff);
1712 *dest
++ = (char)((pixel
>> 8) & 0xff);
1713 *dest
++ = (char)( pixel
& 0xff);
1716 red
= (RShift
< 0)? red
<< (-RShift
) : red
>> RShift
;
1717 green
= (GShift
< 0)? green
<< (-GShift
) : green
>> GShift
;
1718 blue
= (BShift
< 0)? blue
<< (-BShift
) : blue
>> BShift
;
1719 pixel
= (red
& RMask
) | (green
& GMask
) | (blue
& BMask
);
1720 /* recall that we set ximage->byte_order = MSBFirst above */
1722 *dest
++ = (char)((pixel
>> 24) & 0xff);
1723 *dest
++ = (char)((pixel
>> 16) & 0xff);
1724 *dest
++ = (char)((pixel
>> 8) & 0xff);
1725 *dest
++ = (char)( pixel
& 0xff);
1728 /* this assumes bpp == 24 & bits are packed low */
1729 /* (probably need to use RShift/RMask/etc. here, too) */
1730 *dest
++ = (char)((pixel
>> 16) & 0xff);
1731 *dest
++ = (char)((pixel
>> 8) & 0xff);
1732 *dest
++ = (char)( pixel
& 0xff);
1737 /* display after every 16 lines */
1738 if (((row
+1) & 0xf) == 0) {
1739 XPutImage(display
, window
, gc
, ximage
, 0, (int)lastrow
, 0,
1740 (int)lastrow
, rpng2_info
.width
, 16);
1746 } else if (depth
== 16) {
1747 ush red
, green
, blue
;
1749 for (lastrow
= row
= startrow
; row
< startrow
+height
; ++row
) {
1750 src
= rpng2_info
.row_pointers
[row
];
1752 src2
= bg_data
+ row
*bg_rowbytes
;
1753 dest
= ximage
->data
+ row
*ximage_rowbytes
;
1754 if (rpng2_info
.channels
== 3) {
1755 for (i
= rpng2_info
.width
; i
> 0; --i
) {
1756 red
= ((ush
)(*src
) << 8);
1758 green
= ((ush
)(*src
) << 8);
1760 blue
= ((ush
)(*src
) << 8);
1762 pixel
= ((red
>> RShift
) & RMask
) |
1763 ((green
>> GShift
) & GMask
) |
1764 ((blue
>> BShift
) & BMask
);
1765 /* recall that we set ximage->byte_order = MSBFirst above */
1766 *dest
++ = (char)((pixel
>> 8) & 0xff);
1767 *dest
++ = (char)( pixel
& 0xff);
1769 } else /* if (rpng2_info.channels == 4) */ {
1770 for (i
= rpng2_info
.width
; i
> 0; --i
) {
1781 red
= ((ush
)r
<< 8);
1782 green
= ((ush
)g
<< 8);
1783 blue
= ((ush
)b
<< 8);
1784 } else if (a
== 0) {
1785 red
= ((ush
)bg_red
<< 8);
1786 green
= ((ush
)bg_green
<< 8);
1787 blue
= ((ush
)bg_blue
<< 8);
1789 /* this macro (from png.h) composites the foreground
1790 * and background values and puts the result back into
1791 * the first argument (== fg byte here: safe) */
1792 alpha_composite(r
, r
, a
, bg_red
);
1793 alpha_composite(g
, g
, a
, bg_green
);
1794 alpha_composite(b
, b
, a
, bg_blue
);
1795 red
= ((ush
)r
<< 8);
1796 green
= ((ush
)g
<< 8);
1797 blue
= ((ush
)b
<< 8);
1799 pixel
= ((red
>> RShift
) & RMask
) |
1800 ((green
>> GShift
) & GMask
) |
1801 ((blue
>> BShift
) & BMask
);
1802 /* recall that we set ximage->byte_order = MSBFirst above */
1803 *dest
++ = (char)((pixel
>> 8) & 0xff);
1804 *dest
++ = (char)( pixel
& 0xff);
1807 /* display after every 16 lines */
1808 if (((row
+1) & 0xf) == 0) {
1809 XPutImage(display
, window
, gc
, ximage
, 0, (int)lastrow
, 0,
1810 (int)lastrow
, rpng2_info
.width
, 16);
1816 } else /* depth == 8 */ {
1818 /* GRR: add 8-bit support */
1822 Trace((stderr
, "calling final XPutImage()\n"))
1823 if (lastrow
< startrow
+height
) {
1824 XPutImage(display
, window
, gc
, ximage
, 0, (int)lastrow
, 0,
1825 (int)lastrow
, rpng2_info
.width
, rpng2_info
.height
-lastrow
);
1829 } /* end function rpng2_x_redisplay_image() */
1837 static void rpng2_x_reload_bg_image(void)
1840 uch r1
, r2
, g1
, g2
, b1
, b2
;
1841 uch r1_inv
, r2_inv
, g1_inv
, g2_inv
, b1_inv
, b2_inv
;
1843 int xidx
, yidx
, yidx_max
;
1844 int even_odd_vert
, even_odd_horiz
, even_odd
;
1845 int invert_gradient2
= (bg
[pat
].type
& 0x08);
1850 bgscale
= (pat
== 0)? 8 : bgscale_default
;
1851 yidx_max
= bgscale
- 1;
1853 /*---------------------------------------------------------------------------
1854 Vertical gradients (ramps) in NxN squares, alternating direction and
1855 colors (N == bgscale).
1856 ---------------------------------------------------------------------------*/
1858 if ((bg
[pat
].type
& 0x07) == 0) {
1859 uch r1_min
= rgb
[bg
[pat
].rgb1_min
].r
;
1860 uch g1_min
= rgb
[bg
[pat
].rgb1_min
].g
;
1861 uch b1_min
= rgb
[bg
[pat
].rgb1_min
].b
;
1862 uch r2_min
= rgb
[bg
[pat
].rgb2_min
].r
;
1863 uch g2_min
= rgb
[bg
[pat
].rgb2_min
].g
;
1864 uch b2_min
= rgb
[bg
[pat
].rgb2_min
].b
;
1865 int r1_diff
= rgb
[bg
[pat
].rgb1_max
].r
- r1_min
;
1866 int g1_diff
= rgb
[bg
[pat
].rgb1_max
].g
- g1_min
;
1867 int b1_diff
= rgb
[bg
[pat
].rgb1_max
].b
- b1_min
;
1868 int r2_diff
= rgb
[bg
[pat
].rgb2_max
].r
- r2_min
;
1869 int g2_diff
= rgb
[bg
[pat
].rgb2_max
].g
- g2_min
;
1870 int b2_diff
= rgb
[bg
[pat
].rgb2_max
].b
- b2_min
;
1872 for (row
= 0; row
< rpng2_info
.height
; ++row
) {
1873 yidx
= (int)(row
% bgscale
);
1874 even_odd_vert
= (int)((row
/ bgscale
) & 1);
1876 r1
= r1_min
+ (r1_diff
* yidx
) / yidx_max
;
1877 g1
= g1_min
+ (g1_diff
* yidx
) / yidx_max
;
1878 b1
= b1_min
+ (b1_diff
* yidx
) / yidx_max
;
1879 r1_inv
= r1_min
+ (r1_diff
* (yidx_max
-yidx
)) / yidx_max
;
1880 g1_inv
= g1_min
+ (g1_diff
* (yidx_max
-yidx
)) / yidx_max
;
1881 b1_inv
= b1_min
+ (b1_diff
* (yidx_max
-yidx
)) / yidx_max
;
1883 r2
= r2_min
+ (r2_diff
* yidx
) / yidx_max
;
1884 g2
= g2_min
+ (g2_diff
* yidx
) / yidx_max
;
1885 b2
= b2_min
+ (b2_diff
* yidx
) / yidx_max
;
1886 r2_inv
= r2_min
+ (r2_diff
* (yidx_max
-yidx
)) / yidx_max
;
1887 g2_inv
= g2_min
+ (g2_diff
* (yidx_max
-yidx
)) / yidx_max
;
1888 b2_inv
= b2_min
+ (b2_diff
* (yidx_max
-yidx
)) / yidx_max
;
1890 dest
= (char *)bg_data
+ row
*bg_rowbytes
;
1891 for (i
= 0; i
< rpng2_info
.width
; ++i
) {
1892 even_odd_horiz
= (int)((i
/ bgscale
) & 1);
1893 even_odd
= even_odd_vert
^ even_odd_horiz
;
1895 (even_odd_horiz
&& (bg
[pat
].type
& 0x10));
1896 if (even_odd
== 0) { /* gradient #1 */
1897 if (invert_column
) {
1906 } else { /* gradient #2 */
1907 if ((invert_column
&& invert_gradient2
) ||
1908 (!invert_column
&& !invert_gradient2
))
1910 *dest
++ = r2
; /* not inverted or */
1911 *dest
++ = g2
; /* doubly inverted */
1915 *dest
++ = g2_inv
; /* singly inverted */
1922 /*---------------------------------------------------------------------------
1923 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
1925 ---------------------------------------------------------------------------*/
1927 } else if ((bg
[pat
].type
& 0x07) == 1) {
1929 hmax
= (bgscale
-1)/2; /* half the max weight of a color */
1930 max
= 2*hmax
; /* the max weight of a color */
1932 r1
= rgb
[bg
[pat
].rgb1_max
].r
;
1933 g1
= rgb
[bg
[pat
].rgb1_max
].g
;
1934 b1
= rgb
[bg
[pat
].rgb1_max
].b
;
1935 r2
= rgb
[bg
[pat
].rgb2_max
].r
;
1936 g2
= rgb
[bg
[pat
].rgb2_max
].g
;
1937 b2
= rgb
[bg
[pat
].rgb2_max
].b
;
1939 for (row
= 0; row
< rpng2_info
.height
; ++row
) {
1940 yidx
= (int)(row
% bgscale
);
1942 yidx
= bgscale
-1 - yidx
;
1943 dest
= (char *)bg_data
+ row
*bg_rowbytes
;
1944 for (i
= 0; i
< rpng2_info
.width
; ++i
) {
1945 xidx
= (int)(i
% bgscale
);
1947 xidx
= bgscale
-1 - xidx
;
1949 *dest
++ = (k
*r1
+ (max
-k
)*r2
) / max
;
1950 *dest
++ = (k
*g1
+ (max
-k
)*g2
) / max
;
1951 *dest
++ = (k
*b1
+ (max
-k
)*b2
) / max
;
1955 /*---------------------------------------------------------------------------
1956 Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
1957 soids will equal bgscale?]. This one is slow but very cool. Code con-
1958 tributed by Pieter S. van der Meulen (originally in Smalltalk).
1959 ---------------------------------------------------------------------------*/
1961 } else if ((bg
[pat
].type
& 0x07) == 2) {
1963 int ii
, x
, y
, hw
, hh
, grayspot
;
1964 double freq
, rotate
, saturate
, gray
, intensity
;
1965 double angle
=0.0, aoffset
=0.0, maxDist
, dist
;
1966 double red
=0.0, green
=0.0, blue
=0.0, hue
, s
, v
, f
, p
, q
, t
;
1968 hh
= (int)(rpng2_info
.height
/ 2);
1969 hw
= (int)(rpng2_info
.width
/ 2);
1971 /* variables for radial waves:
1972 * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED]
1973 * freq: number of color beams originating from the center
1974 * grayspot: size of the graying center area (anti-alias)
1975 * rotate: rotation of the beams as a function of radius
1976 * saturate: saturation of beams' shape azimuthally
1978 angle
= CLIP(angle
, 0.0, 360.0);
1979 grayspot
= CLIP(bg
[pat
].bg_gray
, 1, (hh
+ hw
));
1980 freq
= MAX((double)bg
[pat
].bg_freq
, 0.0);
1981 saturate
= (double)bg
[pat
].bg_bsat
* 0.1;
1982 rotate
= (double)bg
[pat
].bg_brot
* 0.1;
1985 maxDist
= (double)((hw
*hw
) + (hh
*hh
));
1987 for (row
= 0; row
< rpng2_info
.height
; ++row
) {
1988 y
= (int)(row
- hh
);
1989 dest
= (char *)bg_data
+ row
*bg_rowbytes
;
1990 for (i
= 0; i
< rpng2_info
.width
; ++i
) {
1992 angle
= (x
== 0)? PI_2
: atan((double)y
/ (double)x
);
1993 gray
= (double)MAX(ABS(y
), ABS(x
)) / grayspot
;
1994 gray
= MIN(1.0, gray
);
1995 dist
= (double)((x
*x
) + (y
*y
)) / maxDist
;
1996 intensity
= cos((angle
+(rotate
*dist
*PI
)) * freq
) *
1998 intensity
= (MAX(MIN(intensity
,1.0),-1.0) + 1.0) * 0.5;
1999 hue
= (angle
+ PI
) * INV_PI_360
+ aoffset
;
2000 s
= gray
* ((double)(ABS(x
)+ABS(y
)) / (double)(hw
+ hh
));
2001 s
= MIN(MAX(s
,0.0), 1.0);
2002 v
= MIN(MAX(intensity
,0.0), 1.0);
2005 ch
= (uch
)(v
* 255.0);
2010 if ((hue
< 0.0) || (hue
>= 360.0))
2011 hue
-= (((int)(hue
/ 360.0)) * 360.0);
2014 f
= hue
- (double)ii
;
2016 q
= (1.0 - (s
* f
)) * v
;
2017 t
= (1.0 - (s
* (1.0 - f
))) * v
;
2018 if (ii
== 0) { red
= v
; green
= t
; blue
= p
; }
2019 else if (ii
== 1) { red
= q
; green
= v
; blue
= p
; }
2020 else if (ii
== 2) { red
= p
; green
= v
; blue
= t
; }
2021 else if (ii
== 3) { red
= p
; green
= q
; blue
= v
; }
2022 else if (ii
== 4) { red
= t
; green
= p
; blue
= v
; }
2023 else if (ii
== 5) { red
= v
; green
= p
; blue
= q
; }
2024 *dest
++ = (uch
)(red
* 255.0);
2025 *dest
++ = (uch
)(green
* 255.0);
2026 *dest
++ = (uch
)(blue
* 255.0);
2032 } /* end function rpng2_x_reload_bg_image() */
2038 static int is_number(char *p
)
2048 #endif /* FEATURE_LOOP */
2054 static void rpng2_x_cleanup(void)
2056 if (bg_image
&& bg_data
) {
2061 if (rpng2_info
.image_data
) {
2062 free(rpng2_info
.image_data
);
2063 rpng2_info
.image_data
= NULL
;
2066 if (rpng2_info
.row_pointers
) {
2067 free(rpng2_info
.row_pointers
);
2068 rpng2_info
.row_pointers
= NULL
;
2073 free(ximage
->data
); /* we allocated it, so we free it */
2074 ximage
->data
= (char *)NULL
; /* instead of XDestroyImage() */
2076 XDestroyImage(ximage
);
2081 XFreeGC(display
, gc
);
2084 XDestroyWindow(display
, window
);
2087 XFreeColormap(display
, colormap
);
2089 if (have_nondefault_visual
)
2097 static int rpng2_x_msb(ulg u32val
)
2101 for (i
= 31; i
>= 0; --i
) {
2102 if (u32val
& 0x80000000L
)