beta-0.89.2
[luatex.git] / source / libs / libpng / libpng-src / contrib / gregbook / rpng2-x.c
blob0c8ddeba223930f379fe21f648c68aa8798f6f06
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 ---------------------------------------------------------------------------
23 Changelog:
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
37 handling
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-
42 paste bugs
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)
46 - 2.05: Use nanosleep() instead of usleep(), which is deprecated (GR-P).
47 ---------------------------------------------------------------------------
49 Copyright (c) 1998-2010, 2014-2015 Greg Roelofs. All rights reserved.
51 This software is provided "as is," without warranty of any kind,
52 express or implied. In no event shall the author or contributors
53 be held liable for any damages arising in any way from the use of
54 this software.
56 The contents of this file are DUAL-LICENSED. You may modify and/or
57 redistribute this software according to the terms of one of the
58 following two licenses (at your option):
61 LICENSE 1 ("BSD-like with advertising clause"):
63 Permission is granted to anyone to use this software for any purpose,
64 including commercial applications, and to alter it and redistribute
65 it freely, subject to the following restrictions:
67 1. Redistributions of source code must retain the above copyright
68 notice, disclaimer, and this list of conditions.
69 2. Redistributions in binary form must reproduce the above copyright
70 notice, disclaimer, and this list of conditions in the documenta-
71 tion and/or other materials provided with the distribution.
72 3. All advertising materials mentioning features or use of this
73 software must display the following acknowledgment:
75 This product includes software developed by Greg Roelofs
76 and contributors for the book, "PNG: The Definitive Guide,"
77 published by O'Reilly and Associates.
80 LICENSE 2 (GNU GPL v2 or later):
82 This program is free software; you can redistribute it and/or modify
83 it under the terms of the GNU General Public License as published by
84 the Free Software Foundation; either version 2 of the License, or
85 (at your option) any later version.
87 This program is distributed in the hope that it will be useful,
88 but WITHOUT ANY WARRANTY; without even the implied warranty of
89 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
90 GNU General Public License for more details.
92 You should have received a copy of the GNU General Public License
93 along with this program; if not, write to the Free Software Foundation,
94 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
96 ---------------------------------------------------------------------------*/
98 #define PROGNAME "rpng2-x"
99 #define LONGNAME "Progressive PNG Viewer for X"
100 #define VERSION "2.04 of 15 June 2014"
101 #define RESNAME "rpng2" /* our X resource application name */
102 #define RESCLASS "Rpng" /* our X resource class name */
104 #include <stdio.h>
105 #include <stdlib.h>
106 #include <ctype.h>
107 #include <string.h>
108 #include <setjmp.h> /* for jmpbuf declaration in readpng2.h */
109 #include <time.h>
110 #include <math.h> /* only for PvdM background code */
111 #include <X11/Xlib.h>
112 #include <X11/Xutil.h>
113 #include <X11/Xos.h>
114 #include <X11/keysym.h> /* defines XK_* macros */
116 #if _POSIX_C_SOURCE >= 199309L /* have nanosleep() */
117 # undef usleep
118 # define usleep(usec) { \
119 struct timespec ts; \
120 ts.tv_sec = 0; \
121 ts.tv_nsec = (usec) * 1000; \
122 nanosleep(&ts, NULL); }
123 # endif
125 #ifndef usleep /* have neither nanosleep() nor usleep() */
126 # define usleep(x) sleep(((x)+499999)/1000000)
127 #endif
129 #ifdef VMS
130 # include <unistd.h>
131 #endif
133 /* all for PvdM background code: */
134 #ifndef PI
135 # define PI 3.141592653589793238
136 #endif
137 #define PI_2 (PI*0.5)
138 #define INV_PI_360 (360.0 / PI)
139 #define MAX(a,b) (a>b?a:b)
140 #define MIN(a,b) (a<b?a:b)
141 #define CLIP(a,min,max) MAX(min,MIN((a),max))
142 #define ABS(a) ((a)<0?-(a):(a))
143 #define CLIP8P(c) MAX(0,(MIN((c),255))) /* 8-bit pos. integer (uch) */
144 #define ROUNDF(f) ((int)(f + 0.5))
146 #define QUIT(e,k) ((e.type == ButtonPress && e.xbutton.button == Button1) || \
147 (e.type == KeyPress && /* v--- or 1 for shifted keys */ \
148 ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape)))
150 #define NO_24BIT_MASKS /* undef case not fully written--only for redisplay() */
152 #define rgb1_max bg_freq
153 #define rgb1_min bg_gray
154 #define rgb2_max bg_bsat
155 #define rgb2_min bg_brot
157 /* #define DEBUG */ /* this enables the Trace() macros */
159 #include "readpng2.h" /* typedefs, common macros, readpng2 prototypes */
162 /* could just include png.h, but this macro is the only thing we need
163 * (name and typedefs changed to local versions); note that side effects
164 * only happen with alpha (which could easily be avoided with
165 * "ush acopy = (alpha);") */
167 #define alpha_composite(composite, fg, alpha, bg) { \
168 ush temp = ((ush)(fg)*(ush)(alpha) + \
169 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \
170 (composite) = (uch)((temp + (temp >> 8)) >> 8); \
174 #define INBUFSIZE 4096 /* with pseudo-timing on (1 sec delay/block), this
175 * block size corresponds roughly to a download
176 * speed 10% faster than theoretical 33.6K maximum
177 * (assuming 8 data bits, 1 stop bit and no other
178 * overhead) */
180 /* local prototypes */
181 static void rpng2_x_init (void);
182 static int rpng2_x_create_window (void);
183 static int rpng2_x_load_bg_image (void);
184 static void rpng2_x_display_row (ulg row);
185 static void rpng2_x_finish_display (void);
186 static void rpng2_x_redisplay_image (ulg startcol, ulg startrow,
187 ulg width, ulg height);
188 #ifdef FEATURE_LOOP
189 static void rpng2_x_reload_bg_image (void);
190 static int is_number (char *p);
191 #endif
192 static void rpng2_x_cleanup (void);
193 static int rpng2_x_msb (ulg u32val);
196 static char titlebar[1024], *window_name = titlebar;
197 static char *appname = LONGNAME;
198 static char *icon_name = PROGNAME;
199 static char *res_name = RESNAME;
200 static char *res_class = RESCLASS;
201 static char *filename;
202 static FILE *infile;
204 static mainprog_info rpng2_info;
206 static uch inbuf[INBUFSIZE];
207 static int incount;
209 static int pat = 6; /* must be less than num_bgpat */
210 static int bg_image = 0;
211 static int bgscale, bgscale_default = 16;
212 static ulg bg_rowbytes;
213 static uch *bg_data;
215 int pause_after_pass = FALSE;
216 int demo_timing = FALSE;
217 ulg usleep_duration = 0L;
219 static struct rgb_color {
220 uch r, g, b;
221 } rgb[] = {
222 { 0, 0, 0}, /* 0: black */
223 {255, 255, 255}, /* 1: white */
224 {173, 132, 57}, /* 2: tan */
225 { 64, 132, 0}, /* 3: medium green */
226 {189, 117, 1}, /* 4: gold */
227 {253, 249, 1}, /* 5: yellow */
228 { 0, 0, 255}, /* 6: blue */
229 { 0, 0, 120}, /* 7: medium blue */
230 {255, 0, 255}, /* 8: magenta */
231 { 64, 0, 64}, /* 9: dark magenta */
232 {255, 0, 0}, /* 10: red */
233 { 64, 0, 0}, /* 11: dark red */
234 {255, 127, 0}, /* 12: orange */
235 {192, 96, 0}, /* 13: darker orange */
236 { 24, 60, 0}, /* 14: dark green-yellow */
237 { 85, 125, 200}, /* 15: ice blue */
238 {192, 192, 192} /* 16: Netscape/Mosaic gray */
240 /* not used for now, but should be for error-checking:
241 static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
245 This whole struct is a fairly cheesy way to keep the number of
246 command-line options to a minimum. The radial-waves background
247 type is a particularly poor fit to the integer elements of the
248 struct...but a few macros and a little fixed-point math will do
249 wonders for ya.
251 type bits:
252 F E D C B A 9 8 7 6 5 4 3 2 1 0
253 | | | | |
254 | | +-+-+-- 0 = sharp-edged checkerboard
255 | | 1 = soft diamonds
256 | | 2 = radial waves
257 | | 3-7 = undefined
258 | +-- gradient #2 inverted?
259 +-- alternating columns inverted?
261 static struct background_pattern {
262 ush type;
263 int rgb1_max, rgb1_min; /* or bg_freq, bg_gray */
264 int rgb2_max, rgb2_min; /* or bg_bsat, bg_brot (both scaled by 10)*/
265 } bg[] = {
266 {0, 1,1, 16,16}, /* checkered: white vs. light gray (basic) */
267 {0+8, 2,0, 1,15}, /* checkered: tan/black vs. white/ice blue */
268 {0+24, 2,0, 1,0}, /* checkered: tan/black vs. white/black */
269 {0+8, 4,5, 0,2}, /* checkered: gold/yellow vs. black/tan */
270 {0+8, 4,5, 0,6}, /* checkered: gold/yellow vs. black/blue */
271 {0, 7,0, 8,9}, /* checkered: deep blue/black vs. magenta */
272 {0+8, 13,0, 5,14}, /* checkered: orange/black vs. yellow */
273 {0+8, 12,0, 10,11}, /* checkered: orange/black vs. red */
274 {1, 7,0, 8,0}, /* diamonds: deep blue/black vs. magenta */
275 {1, 12,0, 11,0}, /* diamonds: orange vs. dark red */
276 {1, 10,0, 7,0}, /* diamonds: red vs. medium blue */
277 {1, 4,0, 5,0}, /* diamonds: gold vs. yellow */
278 {1, 3,0, 0,0}, /* diamonds: medium green vs. black */
279 {2, 16, 100, 20, 0}, /* radial: ~hard radial color-beams */
280 {2, 18, 100, 10, 2}, /* radial: soft, curved radial color-beams */
281 {2, 16, 256, 100, 250}, /* radial: very tight spiral */
282 {2, 10000, 256, 11, 0} /* radial: dipole-moire' (almost fractal) */
284 static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
287 /* X-specific variables */
288 static char *displayname;
289 static XImage *ximage;
290 static Display *display;
291 static int depth;
292 static Visual *visual;
293 static XVisualInfo *visual_list;
294 static int RShift, GShift, BShift;
295 static ulg RMask, GMask, BMask;
296 static Window window;
297 static GC gc;
298 static Colormap colormap;
300 static int have_nondefault_visual = FALSE;
301 static int have_colormap = FALSE;
302 static int have_window = FALSE;
303 static int have_gc = FALSE;
308 int main(int argc, char **argv)
310 #ifdef sgi
311 char tmpline[80];
312 #endif
313 char *p, *bgstr = NULL;
314 int rc, alen, flen;
315 int error = 0;
316 int timing = FALSE;
317 int have_bg = FALSE;
318 #ifdef FEATURE_LOOP
319 int loop = FALSE;
320 long loop_interval = -1; /* seconds (100,000 max) */
321 #endif
322 double LUT_exponent; /* just the lookup table */
323 double CRT_exponent = 2.2; /* just the monitor */
324 double default_display_exponent; /* whole display system */
325 XEvent e;
326 KeySym k;
329 /* First initialize a few things, just to be sure--memset takes care of
330 * default background color (black), booleans (FALSE), pointers (NULL),
331 * etc. */
333 displayname = (char *)NULL;
334 filename = (char *)NULL;
335 memset(&rpng2_info, 0, sizeof(mainprog_info));
338 /* Set the default value for our display-system exponent, i.e., the
339 * product of the CRT exponent and the exponent corresponding to
340 * the frame-buffer's lookup table (LUT), if any. This is not an
341 * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
342 * ones), but it should cover 99% of the current possibilities. */
344 #if defined(NeXT)
345 /* third-party utilities can modify the default LUT exponent */
346 LUT_exponent = 1.0 / 2.2;
348 if (some_next_function_that_returns_gamma(&next_gamma))
349 LUT_exponent = 1.0 / next_gamma;
351 #elif defined(sgi)
352 LUT_exponent = 1.0 / 1.7;
353 /* there doesn't seem to be any documented function to
354 * get the "gamma" value, so we do it the hard way */
355 infile = fopen("/etc/config/system.glGammaVal", "r");
356 if (infile) {
357 double sgi_gamma;
359 fgets(tmpline, 80, infile);
360 fclose(infile);
361 sgi_gamma = atof(tmpline);
362 if (sgi_gamma > 0.0)
363 LUT_exponent = 1.0 / sgi_gamma;
365 #elif defined(Macintosh)
366 LUT_exponent = 1.8 / 2.61;
368 if (some_mac_function_that_returns_gamma(&mac_gamma))
369 LUT_exponent = mac_gamma / 2.61;
371 #else
372 LUT_exponent = 1.0; /* assume no LUT: most PCs */
373 #endif
375 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
376 default_display_exponent = LUT_exponent * CRT_exponent;
379 /* If the user has set the SCREEN_GAMMA environment variable as suggested
380 * (somewhat imprecisely) in the libpng documentation, use that; otherwise
381 * use the default value we just calculated. Either way, the user may
382 * override this via a command-line option. */
384 if ((p = getenv("SCREEN_GAMMA")) != NULL)
385 rpng2_info.display_exponent = atof(p);
386 else
387 rpng2_info.display_exponent = default_display_exponent;
390 /* Now parse the command line for options and the PNG filename. */
392 while (*++argv && !error) {
393 if (!strncmp(*argv, "-display", 2)) {
394 if (!*++argv)
395 ++error;
396 else
397 displayname = *argv;
398 } else if (!strncmp(*argv, "-gamma", 2)) {
399 if (!*++argv)
400 ++error;
401 else {
402 rpng2_info.display_exponent = atof(*argv);
403 if (rpng2_info.display_exponent <= 0.0)
404 ++error;
406 } else if (!strncmp(*argv, "-bgcolor", 4)) {
407 if (!*++argv)
408 ++error;
409 else {
410 bgstr = *argv;
411 if (strlen(bgstr) != 7 || bgstr[0] != '#')
412 ++error;
413 else {
414 have_bg = TRUE;
415 bg_image = FALSE;
418 } else if (!strncmp(*argv, "-bgpat", 4)) {
419 if (!*++argv)
420 ++error;
421 else {
422 pat = atoi(*argv);
423 if (pat >= 0 && pat < num_bgpat) {
424 bg_image = TRUE;
425 have_bg = FALSE;
426 } else
427 ++error;
429 } else if (!strncmp(*argv, "-usleep", 2)) {
430 if (!*++argv)
431 ++error;
432 else {
433 usleep_duration = (ulg)atol(*argv);
434 demo_timing = TRUE;
436 } else if (!strncmp(*argv, "-pause", 2)) {
437 pause_after_pass = TRUE;
438 } else if (!strncmp(*argv, "-timing", 2)) {
439 timing = TRUE;
440 #ifdef FEATURE_LOOP
441 } else if (!strncmp(*argv, "-loop", 2)) {
442 loop = TRUE;
443 if (!argv[1] || !is_number(argv[1]))
444 loop_interval = 2;
445 else {
446 ++argv;
447 loop_interval = atol(*argv);
448 if (loop_interval < 0)
449 loop_interval = 2;
450 else if (loop_interval > 100000) /* bit more than one day */
451 loop_interval = 100000;
453 #endif
454 } else {
455 if (**argv != '-') {
456 filename = *argv;
457 if (argv[1]) /* shouldn't be any more args after filename */
458 ++error;
459 } else
460 ++error; /* not expecting any other options */
464 if (!filename)
465 ++error;
468 /* print usage screen if any errors up to this point */
470 if (error) {
471 fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname);
472 readpng2_version_info();
473 fprintf(stderr, "\n"
474 "Usage: ");
475 fprintf(stderr,
476 "%s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
477 " %*s [-usleep dur | -timing] [-pause]\n",
478 PROGNAME, (int)strlen(PROGNAME), " ");
479 fprintf(stderr,
480 #ifdef FEATURE_LOOP
481 " [-loop [sec]]"
482 #endif
483 " file.png\n\n");
484 fprintf(stderr,
485 " xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
486 " exp \ttransfer-function exponent (``gamma'') of the display\n"
487 "\t\t system in floating-point format (e.g., ``%.1f''); equal\n"
488 "\t\t to the product of the lookup-table exponent (varies)\n",
489 default_display_exponent);
490 fprintf(stderr,
491 "\t\t and the CRT exponent (usually 2.2); must be positive\n"
492 " bg \tdesired background color in 7-character hex RGB format\n"
493 "\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n"
494 "\t\t used with transparent images; overrides -bgpat\n"
495 " pat \tdesired background pattern number (0-%d); used with\n"
496 "\t\t transparent images; overrides -bgcolor\n",
497 num_bgpat-1);
498 #ifdef FEATURE_LOOP
499 fprintf(stderr,
500 " -loop\tloops through background images after initial display\n"
501 "\t\t is complete (depends on -bgpat)\n"
502 " sec \tseconds to display each background image (default = 2)\n");
503 #endif
504 fprintf(stderr,
505 " dur \tduration in microseconds to wait after displaying each\n"
506 "\t\t row (for demo purposes)\n"
507 " -timing\tenables delay for every block read, to simulate modem\n"
508 "\t\t download of image (~36 Kbps)\n"
509 " -pause\tpauses after displaying each pass until mouse clicked\n"
510 "\nPress Q, Esc or mouse button 1 (within image window, after image\n"
511 "is displayed) to quit.\n");
512 exit(1);
515 if (!(infile = fopen(filename, "rb"))) {
516 fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename);
517 ++error;
518 } else {
519 incount = fread(inbuf, 1, INBUFSIZE, infile);
520 if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
521 fprintf(stderr, PROGNAME
522 ": [%s] is not a PNG file: incorrect signature\n",
523 filename);
524 ++error;
525 } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
526 switch (rc) {
527 case 2:
528 fprintf(stderr, PROGNAME
529 ": [%s] has bad IHDR (libpng longjmp)\n", filename);
530 break;
531 case 4:
532 fprintf(stderr, PROGNAME ": insufficient memory\n");
533 break;
534 default:
535 fprintf(stderr, PROGNAME
536 ": unknown readpng2_init() error\n");
537 break;
539 ++error;
540 } else {
541 Trace((stderr, "about to call XOpenDisplay()\n"))
542 display = XOpenDisplay(displayname);
543 if (!display) {
544 readpng2_cleanup(&rpng2_info);
545 fprintf(stderr, PROGNAME ": can't open X display [%s]\n",
546 displayname? displayname : "default");
547 ++error;
550 if (error)
551 fclose(infile);
555 if (error) {
556 fprintf(stderr, PROGNAME ": aborting.\n");
557 exit(2);
561 /* set the title-bar string, but make sure buffer doesn't overflow */
563 alen = strlen(appname);
564 flen = strlen(filename);
565 if (alen + flen + 3 > 1023)
566 sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023));
567 else
568 sprintf(titlebar, "%s: %s", appname, filename);
571 /* set some final rpng2_info variables before entering main data loop */
573 if (have_bg) {
574 unsigned r, g, b; /* this approach quiets compiler warnings */
576 sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
577 rpng2_info.bg_red = (uch)r;
578 rpng2_info.bg_green = (uch)g;
579 rpng2_info.bg_blue = (uch)b;
580 } else
581 rpng2_info.need_bgcolor = TRUE;
583 rpng2_info.state = kPreInit;
584 rpng2_info.mainprog_init = rpng2_x_init;
585 rpng2_info.mainprog_display_row = rpng2_x_display_row;
586 rpng2_info.mainprog_finish_display = rpng2_x_finish_display;
589 /* OK, this is the fun part: call readpng2_decode_data() at the start of
590 * the loop to deal with our first buffer of data (read in above to verify
591 * that the file is a PNG image), then loop through the file and continue
592 * calling the same routine to handle each chunk of data. It in turn
593 * passes the data to libpng, which will invoke one or more of our call-
594 * backs as decoded data become available. We optionally call sleep() for
595 * one second per iteration to simulate downloading the image via an analog
596 * modem. */
598 for (;;) {
599 Trace((stderr, "about to call readpng2_decode_data()\n"))
600 if (readpng2_decode_data(&rpng2_info, inbuf, incount))
601 ++error;
602 Trace((stderr, "done with readpng2_decode_data()\n"))
604 if (error || incount != INBUFSIZE || rpng2_info.state == kDone) {
605 if (rpng2_info.state == kDone) {
606 Trace((stderr, "done decoding PNG image\n"))
607 } else if (ferror(infile)) {
608 fprintf(stderr, PROGNAME
609 ": error while reading PNG image file\n");
610 exit(3);
611 } else if (feof(infile)) {
612 fprintf(stderr, PROGNAME ": end of file reached "
613 "(unexpectedly) while reading PNG image file\n");
614 exit(3);
615 } else /* if (error) */ {
616 /* will print error message below */
618 break;
621 if (timing)
622 sleep(1);
624 incount = fread(inbuf, 1, INBUFSIZE, infile);
628 /* clean up PNG stuff and report any decoding errors */
630 fclose(infile);
631 Trace((stderr, "about to call readpng2_cleanup()\n"))
632 readpng2_cleanup(&rpng2_info);
634 if (error) {
635 fprintf(stderr, PROGNAME ": libpng error while decoding PNG image\n");
636 exit(3);
640 #ifdef FEATURE_LOOP
642 if (loop && bg_image) {
643 Trace((stderr, "entering -loop loop (FEATURE_LOOP)\n"))
644 for (;;) {
645 int i, use_sleep;
646 struct timeval now, then;
648 /* get current time and add loop_interval to get target time */
649 if (gettimeofday(&then, NULL) == 0) {
650 then.tv_sec += loop_interval;
651 use_sleep = FALSE;
652 } else
653 use_sleep = TRUE;
655 /* do quick check for a quit event but don't wait for it */
656 /* GRR BUG: should also check for Expose events and redraw... */
657 if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask, &e))
658 if (QUIT(e,k))
659 break;
661 /* generate next background image */
662 if (++pat >= num_bgpat)
663 pat = 0;
664 rpng2_x_reload_bg_image();
666 /* wait for timeout, using whatever means are available */
667 if (use_sleep || gettimeofday(&now, NULL) != 0) {
668 for (i = loop_interval; i > 0; --i) {
669 sleep(1);
670 /* GRR BUG: also need to check for Expose (and redraw!) */
671 if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask,
672 &e) && QUIT(e,k))
673 break;
675 } else {
676 /* Y2038 BUG! */
677 if (now.tv_sec < then.tv_sec ||
678 (now.tv_sec == then.tv_sec && now.tv_usec < then.tv_usec))
680 int quit = FALSE;
681 long seconds_to_go = then.tv_sec - now.tv_sec;
682 long usleep_usec;
684 /* basically chew up most of remaining loop-interval with
685 * calls to sleep(1) interleaved with checks for quit
686 * events, but also recalc time-to-go periodically; when
687 * done, clean up any remaining time with usleep() call
688 * (could also use SIGALRM, but signals are a pain...) */
689 while (seconds_to_go-- > 1) {
690 int seconds_done = 0;
692 for (i = seconds_to_go; i > 0 && !quit; --i) {
693 sleep(1);
694 /* GRR BUG: need to check for Expose and redraw */
695 if (XCheckMaskEvent(display, KeyPressMask |
696 ButtonPressMask, &e) && QUIT(e,k))
697 quit = TRUE;
698 if (++seconds_done > 1000)
699 break; /* time to redo seconds_to_go meas. */
701 if (quit)
702 break;
704 /* OK, more than 1000 seconds since last check:
705 * correct the time-to-go measurement for drift */
706 if (gettimeofday(&now, NULL) == 0) {
707 if (now.tv_sec >= then.tv_sec)
708 break;
709 seconds_to_go = then.tv_sec - now.tv_sec;
710 } else
711 ++seconds_to_go; /* restore what we subtracted */
713 if (quit)
714 break; /* breaks outer do-loop, skips redisplay */
716 /* since difference between "now" and "then" is already
717 * eaten up to within a couple of seconds, don't need to
718 * worry about overflow--but might have overshot (neg.) */
719 if (gettimeofday(&now, NULL) == 0) {
720 usleep_usec = 1000000L*(then.tv_sec - now.tv_sec) +
721 then.tv_usec - now.tv_usec;
722 if (usleep_usec > 0)
723 usleep((ulg)usleep_usec);
728 /* composite image against new background and display (note that
729 * we do not take into account the time spent doing this...) */
730 rpng2_x_redisplay_image (0, 0, rpng2_info.width, rpng2_info.height);
733 } else /* FALL THROUGH and do the normal thing */
735 #endif /* FEATURE_LOOP */
737 /* wait for the user to tell us when to quit */
739 if (rpng2_info.state >= kWindowInit) {
740 Trace((stderr, "entering final wait-for-quit-event loop\n"))
741 do {
742 XNextEvent(display, &e);
743 if (e.type == Expose) {
744 XExposeEvent *ex = (XExposeEvent *)&e;
745 rpng2_x_redisplay_image (ex->x, ex->y, ex->width, ex->height);
747 } while (!QUIT(e,k));
748 } else {
749 fprintf(stderr, PROGNAME ": init callback never called: probable "
750 "libpng error while decoding PNG metadata\n");
751 exit(4);
755 /* we're done: clean up all image and X resources and go away */
757 Trace((stderr, "about to call rpng2_x_cleanup()\n"))
758 rpng2_x_cleanup();
760 (void)argc; /* Unused */
762 return 0;
769 /* this function is called by readpng2_info_callback() in readpng2.c, which
770 * in turn is called by libpng after all of the pre-IDAT chunks have been
771 * read and processed--i.e., we now have enough info to finish initializing */
773 static void rpng2_x_init(void)
775 ulg i;
776 ulg rowbytes = rpng2_info.rowbytes;
778 Trace((stderr, "beginning rpng2_x_init()\n"))
779 Trace((stderr, " rowbytes = %d\n", rpng2_info.rowbytes))
780 Trace((stderr, " width = %ld\n", rpng2_info.width))
781 Trace((stderr, " height = %ld\n", rpng2_info.height))
783 rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
784 if (!rpng2_info.image_data) {
785 readpng2_cleanup(&rpng2_info);
786 return;
789 rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
790 if (!rpng2_info.row_pointers) {
791 free(rpng2_info.image_data);
792 rpng2_info.image_data = NULL;
793 readpng2_cleanup(&rpng2_info);
794 return;
797 for (i = 0; i < rpng2_info.height; ++i)
798 rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
801 /* do the basic X initialization stuff, make the window, and fill it with
802 * the user-specified, file-specified or default background color or
803 * pattern */
805 if (rpng2_x_create_window()) {
807 /* GRR TEMPORARY HACK: this is fundamentally no different from cases
808 * above; libpng should call our error handler to longjmp() back to us
809 * when png_ptr goes away. If we/it segfault instead, seems like a
810 * libpng bug... */
812 /* we're here via libpng callback, so if window fails, clean and bail */
813 readpng2_cleanup(&rpng2_info);
814 rpng2_x_cleanup();
815 exit(2);
818 rpng2_info.state = kWindowInit;
825 static int rpng2_x_create_window(void)
827 ulg bg_red = rpng2_info.bg_red;
828 ulg bg_green = rpng2_info.bg_green;
829 ulg bg_blue = rpng2_info.bg_blue;
830 ulg bg_pixel = 0L;
831 ulg attrmask;
832 int need_colormap = FALSE;
833 int screen, pad;
834 uch *xdata;
835 Window root;
836 XEvent e;
837 XGCValues gcvalues;
838 XSetWindowAttributes attr;
839 XTextProperty windowName, *pWindowName = &windowName;
840 XTextProperty iconName, *pIconName = &iconName;
841 XVisualInfo visual_info;
842 XSizeHints *size_hints;
843 XWMHints *wm_hints;
844 XClassHint *class_hints;
847 Trace((stderr, "beginning rpng2_x_create_window()\n"))
849 screen = DefaultScreen(display);
850 depth = DisplayPlanes(display, screen);
851 root = RootWindow(display, screen);
853 #ifdef DEBUG
854 XSynchronize(display, True);
855 #endif
857 if (depth != 16 && depth != 24 && depth != 32) {
858 int visuals_matched = 0;
860 Trace((stderr, "default depth is %d: checking other visuals\n",
861 depth))
863 /* 24-bit first */
864 visual_info.screen = screen;
865 visual_info.depth = 24;
866 visual_list = XGetVisualInfo(display,
867 VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
868 if (visuals_matched == 0) {
869 /* GRR: add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
870 fprintf(stderr, "default screen depth %d not supported, and no"
871 " 24-bit visuals found\n", depth);
872 return 2;
874 Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
875 visuals_matched))
876 visual = visual_list[0].visual;
877 depth = visual_list[0].depth;
879 colormap_size = visual_list[0].colormap_size;
880 visual_class = visual->class;
881 visualID = XVisualIDFromVisual(visual);
883 have_nondefault_visual = TRUE;
884 need_colormap = TRUE;
885 } else {
886 XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
887 visual = visual_info.visual;
890 RMask = visual->red_mask;
891 GMask = visual->green_mask;
892 BMask = visual->blue_mask;
894 /* GRR: add/check 8-bit support */
895 if (depth == 8 || need_colormap) {
896 colormap = XCreateColormap(display, root, visual, AllocNone);
897 if (!colormap) {
898 fprintf(stderr, "XCreateColormap() failed\n");
899 return 2;
901 have_colormap = TRUE;
902 if (depth == 8)
903 bg_image = FALSE; /* gradient just wastes palette entries */
905 if (depth == 15 || depth == 16) {
906 RShift = 15 - rpng2_x_msb(RMask); /* these are right-shifts */
907 GShift = 15 - rpng2_x_msb(GMask);
908 BShift = 15 - rpng2_x_msb(BMask);
909 } else if (depth > 16) {
910 RShift = rpng2_x_msb(RMask) - 7; /* these are left-shifts */
911 GShift = rpng2_x_msb(GMask) - 7;
912 BShift = rpng2_x_msb(BMask) - 7;
914 if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
915 fprintf(stderr, "rpng2 internal logic error: negative X shift(s)!\n");
916 return 2;
919 /*---------------------------------------------------------------------------
920 Finally, create the window.
921 ---------------------------------------------------------------------------*/
923 attr.backing_store = Always;
924 attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
925 attrmask = CWBackingStore | CWEventMask;
926 if (have_nondefault_visual) {
927 attr.colormap = colormap;
928 attr.background_pixel = 0;
929 attr.border_pixel = 1;
930 attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
933 window = XCreateWindow(display, root, 0, 0, rpng2_info.width,
934 rpng2_info.height, 0, depth, InputOutput, visual, attrmask, &attr);
936 if (window == None) {
937 fprintf(stderr, "XCreateWindow() failed\n");
938 return 2;
939 } else
940 have_window = TRUE;
942 if (depth == 8)
943 XSetWindowColormap(display, window, colormap);
945 if (!XStringListToTextProperty(&window_name, 1, pWindowName))
946 pWindowName = NULL;
947 if (!XStringListToTextProperty(&icon_name, 1, pIconName))
948 pIconName = NULL;
950 /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */
952 if ((size_hints = XAllocSizeHints()) != NULL) {
953 /* window will not be resizable */
954 size_hints->flags = PMinSize | PMaxSize;
955 size_hints->min_width = size_hints->max_width = (int)rpng2_info.width;
956 size_hints->min_height = size_hints->max_height =
957 (int)rpng2_info.height;
960 if ((wm_hints = XAllocWMHints()) != NULL) {
961 wm_hints->initial_state = NormalState;
962 wm_hints->input = True;
963 /* wm_hints->icon_pixmap = icon_pixmap; */
964 wm_hints->flags = StateHint | InputHint /* | IconPixmapHint */ ;
967 if ((class_hints = XAllocClassHint()) != NULL) {
968 class_hints->res_name = res_name;
969 class_hints->res_class = res_class;
972 XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
973 size_hints, wm_hints, class_hints);
975 /* various properties and hints no longer needed; free memory */
976 if (pWindowName)
977 XFree(pWindowName->value);
978 if (pIconName)
979 XFree(pIconName->value);
980 if (size_hints)
981 XFree(size_hints);
982 if (wm_hints)
983 XFree(wm_hints);
984 if (class_hints)
985 XFree(class_hints);
987 XMapWindow(display, window);
989 gc = XCreateGC(display, window, 0, &gcvalues);
990 have_gc = TRUE;
992 /*---------------------------------------------------------------------------
993 Allocate memory for the X- and display-specific version of the image.
994 ---------------------------------------------------------------------------*/
996 if (depth == 24 || depth == 32) {
997 xdata = (uch *)malloc(4*rpng2_info.width*rpng2_info.height);
998 pad = 32;
999 } else if (depth == 16) {
1000 xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height);
1001 pad = 16;
1002 } else /* depth == 8 */ {
1003 xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height);
1004 pad = 8;
1007 if (!xdata) {
1008 fprintf(stderr, PROGNAME ": unable to allocate image memory\n");
1009 return 4;
1012 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
1013 (char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0);
1015 if (!ximage) {
1016 fprintf(stderr, PROGNAME ": XCreateImage() failed\n");
1017 free(xdata);
1018 return 3;
1021 /* to avoid testing the byte order every pixel (or doubling the size of
1022 * the drawing routine with a giant if-test), we arbitrarily set the byte
1023 * order to MSBFirst and let Xlib worry about inverting things on little-
1024 * endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the
1025 * most efficient approach (the giant if-test would be better), but in
1026 * the interest of clarity, we'll take the easy way out... */
1028 ximage->byte_order = MSBFirst;
1030 /*---------------------------------------------------------------------------
1031 Fill window with the specified background color (default is black) or
1032 faked "background image" (but latter is disabled if 8-bit; gradients
1033 just waste palette entries).
1034 ---------------------------------------------------------------------------*/
1036 if (bg_image)
1037 rpng2_x_load_bg_image(); /* resets bg_image if fails */
1039 if (!bg_image) {
1040 if (depth == 24 || depth == 32) {
1041 bg_pixel = (bg_red << RShift) |
1042 (bg_green << GShift) |
1043 (bg_blue << BShift);
1044 } else if (depth == 16) {
1045 bg_pixel = (((bg_red << 8) >> RShift) & RMask) |
1046 (((bg_green << 8) >> GShift) & GMask) |
1047 (((bg_blue << 8) >> BShift) & BMask);
1048 } else /* depth == 8 */ {
1050 /* GRR: add 8-bit support */
1053 XSetForeground(display, gc, bg_pixel);
1054 XFillRectangle(display, window, gc, 0, 0, rpng2_info.width,
1055 rpng2_info.height);
1058 /*---------------------------------------------------------------------------
1059 Wait for first Expose event to do any drawing, then flush and return.
1060 ---------------------------------------------------------------------------*/
1063 XNextEvent(display, &e);
1064 while (e.type != Expose || e.xexpose.count);
1066 XFlush(display);
1068 return 0;
1070 } /* end function rpng2_x_create_window() */
1076 static int rpng2_x_load_bg_image(void)
1078 uch *src;
1079 char *dest;
1080 uch r1, r2, g1, g2, b1, b2;
1081 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
1082 int k, hmax, max;
1083 int xidx, yidx, yidx_max;
1084 int even_odd_vert, even_odd_horiz, even_odd;
1085 int invert_gradient2 = (bg[pat].type & 0x08);
1086 int invert_column;
1087 int ximage_rowbytes = ximage->bytes_per_line;
1088 ulg i, row;
1089 ulg pixel;
1091 /*---------------------------------------------------------------------------
1092 Allocate buffer for fake background image to be used with transparent
1093 images; if this fails, revert to plain background color.
1094 ---------------------------------------------------------------------------*/
1096 bg_rowbytes = 3 * rpng2_info.width;
1097 bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
1098 if (!bg_data) {
1099 fprintf(stderr, PROGNAME
1100 ": unable to allocate memory for background image\n");
1101 bg_image = 0;
1102 return 1;
1105 bgscale = (pat == 0)? 8 : bgscale_default;
1106 yidx_max = bgscale - 1;
1108 /*---------------------------------------------------------------------------
1109 Vertical gradients (ramps) in NxN squares, alternating direction and
1110 colors (N == bgscale).
1111 ---------------------------------------------------------------------------*/
1113 if ((bg[pat].type & 0x07) == 0) {
1114 uch r1_min = rgb[bg[pat].rgb1_min].r;
1115 uch g1_min = rgb[bg[pat].rgb1_min].g;
1116 uch b1_min = rgb[bg[pat].rgb1_min].b;
1117 uch r2_min = rgb[bg[pat].rgb2_min].r;
1118 uch g2_min = rgb[bg[pat].rgb2_min].g;
1119 uch b2_min = rgb[bg[pat].rgb2_min].b;
1120 int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
1121 int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
1122 int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
1123 int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
1124 int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
1125 int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
1127 for (row = 0; row < rpng2_info.height; ++row) {
1128 yidx = (int)(row % bgscale);
1129 even_odd_vert = (int)((row / bgscale) & 1);
1131 r1 = r1_min + (r1_diff * yidx) / yidx_max;
1132 g1 = g1_min + (g1_diff * yidx) / yidx_max;
1133 b1 = b1_min + (b1_diff * yidx) / yidx_max;
1134 r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
1135 g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
1136 b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
1138 r2 = r2_min + (r2_diff * yidx) / yidx_max;
1139 g2 = g2_min + (g2_diff * yidx) / yidx_max;
1140 b2 = b2_min + (b2_diff * yidx) / yidx_max;
1141 r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
1142 g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
1143 b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
1145 dest = (char *)bg_data + row*bg_rowbytes;
1146 for (i = 0; i < rpng2_info.width; ++i) {
1147 even_odd_horiz = (int)((i / bgscale) & 1);
1148 even_odd = even_odd_vert ^ even_odd_horiz;
1149 invert_column =
1150 (even_odd_horiz && (bg[pat].type & 0x10));
1151 if (even_odd == 0) { /* gradient #1 */
1152 if (invert_column) {
1153 *dest++ = r1_inv;
1154 *dest++ = g1_inv;
1155 *dest++ = b1_inv;
1156 } else {
1157 *dest++ = r1;
1158 *dest++ = g1;
1159 *dest++ = b1;
1161 } else { /* gradient #2 */
1162 if ((invert_column && invert_gradient2) ||
1163 (!invert_column && !invert_gradient2))
1165 *dest++ = r2; /* not inverted or */
1166 *dest++ = g2; /* doubly inverted */
1167 *dest++ = b2;
1168 } else {
1169 *dest++ = r2_inv;
1170 *dest++ = g2_inv; /* singly inverted */
1171 *dest++ = b2_inv;
1177 /*---------------------------------------------------------------------------
1178 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
1179 M. Costello.
1180 ---------------------------------------------------------------------------*/
1182 } else if ((bg[pat].type & 0x07) == 1) {
1184 hmax = (bgscale-1)/2; /* half the max weight of a color */
1185 max = 2*hmax; /* the max weight of a color */
1187 r1 = rgb[bg[pat].rgb1_max].r;
1188 g1 = rgb[bg[pat].rgb1_max].g;
1189 b1 = rgb[bg[pat].rgb1_max].b;
1190 r2 = rgb[bg[pat].rgb2_max].r;
1191 g2 = rgb[bg[pat].rgb2_max].g;
1192 b2 = rgb[bg[pat].rgb2_max].b;
1194 for (row = 0; row < rpng2_info.height; ++row) {
1195 yidx = (int)(row % bgscale);
1196 if (yidx > hmax)
1197 yidx = bgscale-1 - yidx;
1198 dest = (char *)bg_data + row*bg_rowbytes;
1199 for (i = 0; i < rpng2_info.width; ++i) {
1200 xidx = (int)(i % bgscale);
1201 if (xidx > hmax)
1202 xidx = bgscale-1 - xidx;
1203 k = xidx + yidx;
1204 *dest++ = (k*r1 + (max-k)*r2) / max;
1205 *dest++ = (k*g1 + (max-k)*g2) / max;
1206 *dest++ = (k*b1 + (max-k)*b2) / max;
1210 /*---------------------------------------------------------------------------
1211 Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
1212 soids will equal bgscale?]. This one is slow but very cool. Code con-
1213 tributed by Pieter S. van der Meulen (originally in Smalltalk).
1214 ---------------------------------------------------------------------------*/
1216 } else if ((bg[pat].type & 0x07) == 2) {
1217 uch ch;
1218 int ii, x, y, hw, hh, grayspot;
1219 double freq, rotate, saturate, gray, intensity;
1220 double angle=0.0, aoffset=0.0, maxDist, dist;
1221 double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
1223 fprintf(stderr, "%s: computing radial background...",
1224 PROGNAME);
1225 fflush(stderr);
1227 hh = (int)(rpng2_info.height / 2);
1228 hw = (int)(rpng2_info.width / 2);
1230 /* variables for radial waves:
1231 * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED]
1232 * freq: number of color beams originating from the center
1233 * grayspot: size of the graying center area (anti-alias)
1234 * rotate: rotation of the beams as a function of radius
1235 * saturate: saturation of beams' shape azimuthally
1237 angle = CLIP(angle, 0.0, 360.0);
1238 grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
1239 freq = MAX((double)bg[pat].bg_freq, 0.0);
1240 saturate = (double)bg[pat].bg_bsat * 0.1;
1241 rotate = (double)bg[pat].bg_brot * 0.1;
1242 gray = 0.0;
1243 intensity = 0.0;
1244 maxDist = (double)((hw*hw) + (hh*hh));
1246 for (row = 0; row < rpng2_info.height; ++row) {
1247 y = (int)(row - hh);
1248 dest = (char *)bg_data + row*bg_rowbytes;
1249 for (i = 0; i < rpng2_info.width; ++i) {
1250 x = (int)(i - hw);
1251 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
1252 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
1253 gray = MIN(1.0, gray);
1254 dist = (double)((x*x) + (y*y)) / maxDist;
1255 intensity = cos((angle+(rotate*dist*PI)) * freq) *
1256 gray * saturate;
1257 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
1258 hue = (angle + PI) * INV_PI_360 + aoffset;
1259 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
1260 s = MIN(MAX(s,0.0), 1.0);
1261 v = MIN(MAX(intensity,0.0), 1.0);
1263 if (s == 0.0) {
1264 ch = (uch)(v * 255.0);
1265 *dest++ = ch;
1266 *dest++ = ch;
1267 *dest++ = ch;
1268 } else {
1269 if ((hue < 0.0) || (hue >= 360.0))
1270 hue -= (((int)(hue / 360.0)) * 360.0);
1271 hue /= 60.0;
1272 ii = (int)hue;
1273 f = hue - (double)ii;
1274 p = (1.0 - s) * v;
1275 q = (1.0 - (s * f)) * v;
1276 t = (1.0 - (s * (1.0 - f))) * v;
1277 if (ii == 0) { red = v; green = t; blue = p; }
1278 else if (ii == 1) { red = q; green = v; blue = p; }
1279 else if (ii == 2) { red = p; green = v; blue = t; }
1280 else if (ii == 3) { red = p; green = q; blue = v; }
1281 else if (ii == 4) { red = t; green = p; blue = v; }
1282 else if (ii == 5) { red = v; green = p; blue = q; }
1283 *dest++ = (uch)(red * 255.0);
1284 *dest++ = (uch)(green * 255.0);
1285 *dest++ = (uch)(blue * 255.0);
1289 fprintf(stderr, "done.\n");
1290 fflush(stderr);
1293 /*---------------------------------------------------------------------------
1294 Blast background image to display buffer before beginning PNG decode.
1295 ---------------------------------------------------------------------------*/
1297 if (depth == 24 || depth == 32) {
1298 ulg red, green, blue;
1299 int bpp = ximage->bits_per_pixel;
1301 for (row = 0; row < rpng2_info.height; ++row) {
1302 src = bg_data + row*bg_rowbytes;
1303 dest = ximage->data + row*ximage_rowbytes;
1304 if (bpp == 32) { /* slightly optimized version */
1305 for (i = rpng2_info.width; i > 0; --i) {
1306 red = *src++;
1307 green = *src++;
1308 blue = *src++;
1309 pixel = (red << RShift) |
1310 (green << GShift) |
1311 (blue << BShift);
1312 /* recall that we set ximage->byte_order = MSBFirst above */
1313 *dest++ = (char)((pixel >> 24) & 0xff);
1314 *dest++ = (char)((pixel >> 16) & 0xff);
1315 *dest++ = (char)((pixel >> 8) & 0xff);
1316 *dest++ = (char)( pixel & 0xff);
1318 } else {
1319 for (i = rpng2_info.width; i > 0; --i) {
1320 red = *src++;
1321 green = *src++;
1322 blue = *src++;
1323 pixel = (red << RShift) |
1324 (green << GShift) |
1325 (blue << BShift);
1326 /* recall that we set ximage->byte_order = MSBFirst above */
1327 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1328 /* (probably need to use RShift, RMask, etc.) */
1329 *dest++ = (char)((pixel >> 16) & 0xff);
1330 *dest++ = (char)((pixel >> 8) & 0xff);
1331 *dest++ = (char)( pixel & 0xff);
1336 } else if (depth == 16) {
1337 ush red, green, blue;
1339 for (row = 0; row < rpng2_info.height; ++row) {
1340 src = bg_data + row*bg_rowbytes;
1341 dest = ximage->data + row*ximage_rowbytes;
1342 for (i = rpng2_info.width; i > 0; --i) {
1343 red = ((ush)(*src) << 8); ++src;
1344 green = ((ush)(*src) << 8); ++src;
1345 blue = ((ush)(*src) << 8); ++src;
1346 pixel = ((red >> RShift) & RMask) |
1347 ((green >> GShift) & GMask) |
1348 ((blue >> BShift) & BMask);
1349 /* recall that we set ximage->byte_order = MSBFirst above */
1350 *dest++ = (char)((pixel >> 8) & 0xff);
1351 *dest++ = (char)( pixel & 0xff);
1355 } else /* depth == 8 */ {
1357 /* GRR: add 8-bit support */
1361 XPutImage(display, window, gc, ximage, 0, 0, 0, 0, rpng2_info.width,
1362 rpng2_info.height);
1364 return 0;
1366 } /* end function rpng2_x_load_bg_image() */
1372 static void rpng2_x_display_row(ulg row)
1374 uch bg_red = rpng2_info.bg_red;
1375 uch bg_green = rpng2_info.bg_green;
1376 uch bg_blue = rpng2_info.bg_blue;
1377 uch *src, *src2=NULL;
1378 char *dest;
1379 uch r, g, b, a;
1380 int ximage_rowbytes = ximage->bytes_per_line;
1381 ulg i, pixel;
1382 static int rows=0, prevpass=(-1);
1383 static ulg firstrow;
1385 /*---------------------------------------------------------------------------
1386 rows and firstrow simply track how many rows (and which ones) have not
1387 yet been displayed; alternatively, we could call XPutImage() for every
1388 row and not bother with the records-keeping.
1389 ---------------------------------------------------------------------------*/
1391 Trace((stderr, "beginning rpng2_x_display_row()\n"))
1393 if (rpng2_info.pass != prevpass) {
1394 if (pause_after_pass && rpng2_info.pass > 0) {
1395 XEvent e;
1396 KeySym k;
1398 fprintf(stderr,
1399 "%s: end of pass %d of 7; click in image window to continue\n",
1400 PROGNAME, prevpass + 1);
1402 XNextEvent(display, &e);
1403 while (!QUIT(e,k));
1405 fprintf(stderr, "%s: pass %d of 7\r", PROGNAME, rpng2_info.pass + 1);
1406 fflush(stderr);
1407 prevpass = rpng2_info.pass;
1410 if (rows == 0)
1411 firstrow = row; /* first row that is not yet displayed */
1413 ++rows; /* count of rows received but not yet displayed */
1415 /*---------------------------------------------------------------------------
1416 Aside from the use of the rpng2_info struct, the lack of an outer loop
1417 (over rows) and moving the XPutImage() call outside the "if (depth)"
1418 tests, this routine is identical to rpng_x_display_image() in the non-
1419 progressive version of the program.
1420 ---------------------------------------------------------------------------*/
1422 if (depth == 24 || depth == 32) {
1423 ulg red, green, blue;
1424 int bpp = ximage->bits_per_pixel;
1426 src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1427 if (bg_image)
1428 src2 = bg_data + row*bg_rowbytes;
1429 dest = ximage->data + row*ximage_rowbytes;
1430 if (rpng2_info.channels == 3) {
1431 for (i = rpng2_info.width; i > 0; --i) {
1432 red = *src++;
1433 green = *src++;
1434 blue = *src++;
1435 pixel = (red << RShift) |
1436 (green << GShift) |
1437 (blue << BShift);
1438 /* recall that we set ximage->byte_order = MSBFirst above */
1439 if (bpp == 32) {
1440 *dest++ = (char)((pixel >> 24) & 0xff);
1441 *dest++ = (char)((pixel >> 16) & 0xff);
1442 *dest++ = (char)((pixel >> 8) & 0xff);
1443 *dest++ = (char)( pixel & 0xff);
1444 } else {
1445 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1446 /* (probably need to use RShift, RMask, etc.) */
1447 *dest++ = (char)((pixel >> 16) & 0xff);
1448 *dest++ = (char)((pixel >> 8) & 0xff);
1449 *dest++ = (char)( pixel & 0xff);
1452 } else /* if (rpng2_info.channels == 4) */ {
1453 for (i = rpng2_info.width; i > 0; --i) {
1454 r = *src++;
1455 g = *src++;
1456 b = *src++;
1457 a = *src++;
1458 if (bg_image) {
1459 bg_red = *src2++;
1460 bg_green = *src2++;
1461 bg_blue = *src2++;
1463 if (a == 255) {
1464 red = r;
1465 green = g;
1466 blue = b;
1467 } else if (a == 0) {
1468 red = bg_red;
1469 green = bg_green;
1470 blue = bg_blue;
1471 } else {
1472 /* this macro (from png.h) composites the foreground
1473 * and background values and puts the result into the
1474 * first argument */
1475 alpha_composite(red, r, a, bg_red);
1476 alpha_composite(green, g, a, bg_green);
1477 alpha_composite(blue, b, a, bg_blue);
1479 pixel = (red << RShift) |
1480 (green << GShift) |
1481 (blue << BShift);
1482 /* recall that we set ximage->byte_order = MSBFirst above */
1483 if (bpp == 32) {
1484 *dest++ = (char)((pixel >> 24) & 0xff);
1485 *dest++ = (char)((pixel >> 16) & 0xff);
1486 *dest++ = (char)((pixel >> 8) & 0xff);
1487 *dest++ = (char)( pixel & 0xff);
1488 } else {
1489 /* GRR BUG? this assumes bpp == 24 & bits are packed low */
1490 /* (probably need to use RShift, RMask, etc.) */
1491 *dest++ = (char)((pixel >> 16) & 0xff);
1492 *dest++ = (char)((pixel >> 8) & 0xff);
1493 *dest++ = (char)( pixel & 0xff);
1498 } else if (depth == 16) {
1499 ush red, green, blue;
1501 src = rpng2_info.row_pointers[row];
1502 if (bg_image)
1503 src2 = bg_data + row*bg_rowbytes;
1504 dest = ximage->data + row*ximage_rowbytes;
1505 if (rpng2_info.channels == 3) {
1506 for (i = rpng2_info.width; i > 0; --i) {
1507 red = ((ush)(*src) << 8);
1508 ++src;
1509 green = ((ush)(*src) << 8);
1510 ++src;
1511 blue = ((ush)(*src) << 8);
1512 ++src;
1513 pixel = ((red >> RShift) & RMask) |
1514 ((green >> GShift) & GMask) |
1515 ((blue >> BShift) & BMask);
1516 /* recall that we set ximage->byte_order = MSBFirst above */
1517 *dest++ = (char)((pixel >> 8) & 0xff);
1518 *dest++ = (char)( pixel & 0xff);
1520 } else /* if (rpng2_info.channels == 4) */ {
1521 for (i = rpng2_info.width; i > 0; --i) {
1522 r = *src++;
1523 g = *src++;
1524 b = *src++;
1525 a = *src++;
1526 if (bg_image) {
1527 bg_red = *src2++;
1528 bg_green = *src2++;
1529 bg_blue = *src2++;
1531 if (a == 255) {
1532 red = ((ush)r << 8);
1533 green = ((ush)g << 8);
1534 blue = ((ush)b << 8);
1535 } else if (a == 0) {
1536 red = ((ush)bg_red << 8);
1537 green = ((ush)bg_green << 8);
1538 blue = ((ush)bg_blue << 8);
1539 } else {
1540 /* this macro (from png.h) composites the foreground
1541 * and background values and puts the result back into
1542 * the first argument (== fg byte here: safe) */
1543 alpha_composite(r, r, a, bg_red);
1544 alpha_composite(g, g, a, bg_green);
1545 alpha_composite(b, b, a, bg_blue);
1546 red = ((ush)r << 8);
1547 green = ((ush)g << 8);
1548 blue = ((ush)b << 8);
1550 pixel = ((red >> RShift) & RMask) |
1551 ((green >> GShift) & GMask) |
1552 ((blue >> BShift) & BMask);
1553 /* recall that we set ximage->byte_order = MSBFirst above */
1554 *dest++ = (char)((pixel >> 8) & 0xff);
1555 *dest++ = (char)( pixel & 0xff);
1559 } else /* depth == 8 */ {
1561 /* GRR: add 8-bit support */
1566 /*---------------------------------------------------------------------------
1567 Display after every 16 rows or when on one of last two rows. (Region
1568 may include previously displayed lines due to interlacing--i.e., not
1569 contiguous. Also, second-to-last row is final one in interlaced images
1570 with odd number of rows.) For demos, flush (and delay) after every 16th
1571 row so "sparse" passes don't go twice as fast.
1572 ---------------------------------------------------------------------------*/
1574 if (demo_timing && (row - firstrow >= 16 || row >= rpng2_info.height-2)) {
1575 XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1576 (int)firstrow, rpng2_info.width, row - firstrow + 1);
1577 XFlush(display);
1578 rows = 0;
1579 usleep(usleep_duration);
1580 } else
1581 if (!demo_timing && ((rows & 0xf) == 0 || row >= rpng2_info.height-2)) {
1582 XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1583 (int)firstrow, rpng2_info.width, row - firstrow + 1);
1584 XFlush(display);
1585 rows = 0;
1594 static void rpng2_x_finish_display(void)
1596 Trace((stderr, "beginning rpng2_x_finish_display()\n"))
1598 /* last row has already been displayed by rpng2_x_display_row(), so we
1599 * have nothing to do here except set a flag and let the user know that
1600 * the image is done */
1602 rpng2_info.state = kDone;
1603 printf(
1604 "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
1605 fflush(stdout);
1612 static void rpng2_x_redisplay_image(ulg startcol, ulg startrow,
1613 ulg width, ulg height)
1615 uch bg_red = rpng2_info.bg_red;
1616 uch bg_green = rpng2_info.bg_green;
1617 uch bg_blue = rpng2_info.bg_blue;
1618 uch *src, *src2=NULL;
1619 char *dest;
1620 uch r, g, b, a;
1621 ulg i, row, lastrow = 0;
1622 ulg pixel;
1623 int ximage_rowbytes = ximage->bytes_per_line;
1626 Trace((stderr, "beginning display loop (image_channels == %d)\n",
1627 rpng2_info.channels))
1628 Trace((stderr, " (width = %ld, rowbytes = %d, ximage_rowbytes = %d)\n",
1629 rpng2_info.width, rpng2_info.rowbytes, ximage_rowbytes))
1630 Trace((stderr, " (bpp = %d)\n", ximage->bits_per_pixel))
1631 Trace((stderr, " (byte_order = %s)\n", ximage->byte_order == MSBFirst?
1632 "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown")))
1634 /*---------------------------------------------------------------------------
1635 Aside from the use of the rpng2_info struct and of src2 (for background
1636 image), this routine is identical to rpng_x_display_image() in the non-
1637 progressive version of the program--for the simple reason that redisplay
1638 of the image against a new background happens after the image is fully
1639 decoded and therefore is, by definition, non-progressive.
1640 ---------------------------------------------------------------------------*/
1642 if (depth == 24 || depth == 32) {
1643 ulg red, green, blue;
1644 int bpp = ximage->bits_per_pixel;
1646 for (lastrow = row = startrow; row < startrow+height; ++row) {
1647 src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1648 if (bg_image)
1649 src2 = bg_data + row*bg_rowbytes;
1650 dest = ximage->data + row*ximage_rowbytes;
1651 if (rpng2_info.channels == 3) {
1652 for (i = rpng2_info.width; i > 0; --i) {
1653 red = *src++;
1654 green = *src++;
1655 blue = *src++;
1656 #ifdef NO_24BIT_MASKS
1657 pixel = (red << RShift) |
1658 (green << GShift) |
1659 (blue << BShift);
1660 /* recall that we set ximage->byte_order = MSBFirst above */
1661 if (bpp == 32) {
1662 *dest++ = (char)((pixel >> 24) & 0xff);
1663 *dest++ = (char)((pixel >> 16) & 0xff);
1664 *dest++ = (char)((pixel >> 8) & 0xff);
1665 *dest++ = (char)( pixel & 0xff);
1666 } else {
1667 /* this assumes bpp == 24 & bits are packed low */
1668 /* (probably need to use RShift, RMask, etc.) */
1669 *dest++ = (char)((pixel >> 16) & 0xff);
1670 *dest++ = (char)((pixel >> 8) & 0xff);
1671 *dest++ = (char)( pixel & 0xff);
1673 #else
1674 red = (RShift < 0)? red << (-RShift) : red >> RShift;
1675 green = (GShift < 0)? green << (-GShift) : green >> GShift;
1676 blue = (BShift < 0)? blue << (-BShift) : blue >> BShift;
1677 pixel = (red & RMask) | (green & GMask) | (blue & BMask);
1678 /* recall that we set ximage->byte_order = MSBFirst above */
1679 if (bpp == 32) {
1680 *dest++ = (char)((pixel >> 24) & 0xff);
1681 *dest++ = (char)((pixel >> 16) & 0xff);
1682 *dest++ = (char)((pixel >> 8) & 0xff);
1683 *dest++ = (char)( pixel & 0xff);
1684 } else {
1685 /* GRR BUG */
1686 /* this assumes bpp == 24 & bits are packed low */
1687 /* (probably need to use RShift/RMask/etc. here, too) */
1688 *dest++ = (char)((pixel >> 16) & 0xff);
1689 *dest++ = (char)((pixel >> 8) & 0xff);
1690 *dest++ = (char)( pixel & 0xff);
1692 #endif
1695 } else /* if (rpng2_info.channels == 4) */ {
1696 for (i = rpng2_info.width; i > 0; --i) {
1697 r = *src++;
1698 g = *src++;
1699 b = *src++;
1700 a = *src++;
1701 if (bg_image) {
1702 bg_red = *src2++;
1703 bg_green = *src2++;
1704 bg_blue = *src2++;
1706 if (a == 255) {
1707 red = r;
1708 green = g;
1709 blue = b;
1710 } else if (a == 0) {
1711 red = bg_red;
1712 green = bg_green;
1713 blue = bg_blue;
1714 } else {
1715 /* this macro (from png.h) composites the foreground
1716 * and background values and puts the result into the
1717 * first argument */
1718 alpha_composite(red, r, a, bg_red);
1719 alpha_composite(green, g, a, bg_green);
1720 alpha_composite(blue, b, a, bg_blue);
1722 #ifdef NO_24BIT_MASKS
1723 pixel = (red << RShift) |
1724 (green << GShift) |
1725 (blue << BShift);
1726 /* recall that we set ximage->byte_order = MSBFirst above */
1727 if (bpp == 32) {
1728 *dest++ = (char)((pixel >> 24) & 0xff);
1729 *dest++ = (char)((pixel >> 16) & 0xff);
1730 *dest++ = (char)((pixel >> 8) & 0xff);
1731 *dest++ = (char)( pixel & 0xff);
1732 } else {
1733 /* this assumes bpp == 24 & bits are packed low */
1734 /* (probably need to use RShift, RMask, etc.) */
1735 *dest++ = (char)((pixel >> 16) & 0xff);
1736 *dest++ = (char)((pixel >> 8) & 0xff);
1737 *dest++ = (char)( pixel & 0xff);
1739 #else
1740 red = (RShift < 0)? red << (-RShift) : red >> RShift;
1741 green = (GShift < 0)? green << (-GShift) : green >> GShift;
1742 blue = (BShift < 0)? blue << (-BShift) : blue >> BShift;
1743 pixel = (red & RMask) | (green & GMask) | (blue & BMask);
1744 /* recall that we set ximage->byte_order = MSBFirst above */
1745 if (bpp == 32) {
1746 *dest++ = (char)((pixel >> 24) & 0xff);
1747 *dest++ = (char)((pixel >> 16) & 0xff);
1748 *dest++ = (char)((pixel >> 8) & 0xff);
1749 *dest++ = (char)( pixel & 0xff);
1750 } else {
1751 /* GRR BUG */
1752 /* this assumes bpp == 24 & bits are packed low */
1753 /* (probably need to use RShift/RMask/etc. here, too) */
1754 *dest++ = (char)((pixel >> 16) & 0xff);
1755 *dest++ = (char)((pixel >> 8) & 0xff);
1756 *dest++ = (char)( pixel & 0xff);
1758 #endif
1761 /* display after every 16 lines */
1762 if (((row+1) & 0xf) == 0) {
1763 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1764 (int)lastrow, rpng2_info.width, 16);
1765 XFlush(display);
1766 lastrow = row + 1;
1770 } else if (depth == 16) {
1771 ush red, green, blue;
1773 for (lastrow = row = startrow; row < startrow+height; ++row) {
1774 src = rpng2_info.row_pointers[row];
1775 if (bg_image)
1776 src2 = bg_data + row*bg_rowbytes;
1777 dest = ximage->data + row*ximage_rowbytes;
1778 if (rpng2_info.channels == 3) {
1779 for (i = rpng2_info.width; i > 0; --i) {
1780 red = ((ush)(*src) << 8);
1781 ++src;
1782 green = ((ush)(*src) << 8);
1783 ++src;
1784 blue = ((ush)(*src) << 8);
1785 ++src;
1786 pixel = ((red >> RShift) & RMask) |
1787 ((green >> GShift) & GMask) |
1788 ((blue >> BShift) & BMask);
1789 /* recall that we set ximage->byte_order = MSBFirst above */
1790 *dest++ = (char)((pixel >> 8) & 0xff);
1791 *dest++ = (char)( pixel & 0xff);
1793 } else /* if (rpng2_info.channels == 4) */ {
1794 for (i = rpng2_info.width; i > 0; --i) {
1795 r = *src++;
1796 g = *src++;
1797 b = *src++;
1798 a = *src++;
1799 if (bg_image) {
1800 bg_red = *src2++;
1801 bg_green = *src2++;
1802 bg_blue = *src2++;
1804 if (a == 255) {
1805 red = ((ush)r << 8);
1806 green = ((ush)g << 8);
1807 blue = ((ush)b << 8);
1808 } else if (a == 0) {
1809 red = ((ush)bg_red << 8);
1810 green = ((ush)bg_green << 8);
1811 blue = ((ush)bg_blue << 8);
1812 } else {
1813 /* this macro (from png.h) composites the foreground
1814 * and background values and puts the result back into
1815 * the first argument (== fg byte here: safe) */
1816 alpha_composite(r, r, a, bg_red);
1817 alpha_composite(g, g, a, bg_green);
1818 alpha_composite(b, b, a, bg_blue);
1819 red = ((ush)r << 8);
1820 green = ((ush)g << 8);
1821 blue = ((ush)b << 8);
1823 pixel = ((red >> RShift) & RMask) |
1824 ((green >> GShift) & GMask) |
1825 ((blue >> BShift) & BMask);
1826 /* recall that we set ximage->byte_order = MSBFirst above */
1827 *dest++ = (char)((pixel >> 8) & 0xff);
1828 *dest++ = (char)( pixel & 0xff);
1831 /* display after every 16 lines */
1832 if (((row+1) & 0xf) == 0) {
1833 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1834 (int)lastrow, rpng2_info.width, 16);
1835 XFlush(display);
1836 lastrow = row + 1;
1840 } else /* depth == 8 */ {
1842 /* GRR: add 8-bit support */
1846 Trace((stderr, "calling final XPutImage()\n"))
1847 if (lastrow < startrow+height) {
1848 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
1849 (int)lastrow, rpng2_info.width, rpng2_info.height-lastrow);
1850 XFlush(display);
1853 (void)startcol;
1854 (void)width;
1856 } /* end function rpng2_x_redisplay_image() */
1862 #ifdef FEATURE_LOOP
1864 static void rpng2_x_reload_bg_image(void)
1866 char *dest;
1867 uch r1, r2, g1, g2, b1, b2;
1868 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
1869 int k, hmax, max;
1870 int xidx, yidx, yidx_max;
1871 int even_odd_vert, even_odd_horiz, even_odd;
1872 int invert_gradient2 = (bg[pat].type & 0x08);
1873 int invert_column;
1874 ulg i, row;
1877 bgscale = (pat == 0)? 8 : bgscale_default;
1878 yidx_max = bgscale - 1;
1880 /*---------------------------------------------------------------------------
1881 Vertical gradients (ramps) in NxN squares, alternating direction and
1882 colors (N == bgscale).
1883 ---------------------------------------------------------------------------*/
1885 if ((bg[pat].type & 0x07) == 0) {
1886 uch r1_min = rgb[bg[pat].rgb1_min].r;
1887 uch g1_min = rgb[bg[pat].rgb1_min].g;
1888 uch b1_min = rgb[bg[pat].rgb1_min].b;
1889 uch r2_min = rgb[bg[pat].rgb2_min].r;
1890 uch g2_min = rgb[bg[pat].rgb2_min].g;
1891 uch b2_min = rgb[bg[pat].rgb2_min].b;
1892 int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
1893 int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
1894 int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
1895 int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
1896 int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
1897 int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
1899 for (row = 0; row < rpng2_info.height; ++row) {
1900 yidx = (int)(row % bgscale);
1901 even_odd_vert = (int)((row / bgscale) & 1);
1903 r1 = r1_min + (r1_diff * yidx) / yidx_max;
1904 g1 = g1_min + (g1_diff * yidx) / yidx_max;
1905 b1 = b1_min + (b1_diff * yidx) / yidx_max;
1906 r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
1907 g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
1908 b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
1910 r2 = r2_min + (r2_diff * yidx) / yidx_max;
1911 g2 = g2_min + (g2_diff * yidx) / yidx_max;
1912 b2 = b2_min + (b2_diff * yidx) / yidx_max;
1913 r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
1914 g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
1915 b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
1917 dest = (char *)bg_data + row*bg_rowbytes;
1918 for (i = 0; i < rpng2_info.width; ++i) {
1919 even_odd_horiz = (int)((i / bgscale) & 1);
1920 even_odd = even_odd_vert ^ even_odd_horiz;
1921 invert_column =
1922 (even_odd_horiz && (bg[pat].type & 0x10));
1923 if (even_odd == 0) { /* gradient #1 */
1924 if (invert_column) {
1925 *dest++ = r1_inv;
1926 *dest++ = g1_inv;
1927 *dest++ = b1_inv;
1928 } else {
1929 *dest++ = r1;
1930 *dest++ = g1;
1931 *dest++ = b1;
1933 } else { /* gradient #2 */
1934 if ((invert_column && invert_gradient2) ||
1935 (!invert_column && !invert_gradient2))
1937 *dest++ = r2; /* not inverted or */
1938 *dest++ = g2; /* doubly inverted */
1939 *dest++ = b2;
1940 } else {
1941 *dest++ = r2_inv;
1942 *dest++ = g2_inv; /* singly inverted */
1943 *dest++ = b2_inv;
1949 /*---------------------------------------------------------------------------
1950 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
1951 M. Costello.
1952 ---------------------------------------------------------------------------*/
1954 } else if ((bg[pat].type & 0x07) == 1) {
1956 hmax = (bgscale-1)/2; /* half the max weight of a color */
1957 max = 2*hmax; /* the max weight of a color */
1959 r1 = rgb[bg[pat].rgb1_max].r;
1960 g1 = rgb[bg[pat].rgb1_max].g;
1961 b1 = rgb[bg[pat].rgb1_max].b;
1962 r2 = rgb[bg[pat].rgb2_max].r;
1963 g2 = rgb[bg[pat].rgb2_max].g;
1964 b2 = rgb[bg[pat].rgb2_max].b;
1966 for (row = 0; row < rpng2_info.height; ++row) {
1967 yidx = (int)(row % bgscale);
1968 if (yidx > hmax)
1969 yidx = bgscale-1 - yidx;
1970 dest = (char *)bg_data + row*bg_rowbytes;
1971 for (i = 0; i < rpng2_info.width; ++i) {
1972 xidx = (int)(i % bgscale);
1973 if (xidx > hmax)
1974 xidx = bgscale-1 - xidx;
1975 k = xidx + yidx;
1976 *dest++ = (k*r1 + (max-k)*r2) / max;
1977 *dest++ = (k*g1 + (max-k)*g2) / max;
1978 *dest++ = (k*b1 + (max-k)*b2) / max;
1982 /*---------------------------------------------------------------------------
1983 Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
1984 soids will equal bgscale?]. This one is slow but very cool. Code con-
1985 tributed by Pieter S. van der Meulen (originally in Smalltalk).
1986 ---------------------------------------------------------------------------*/
1988 } else if ((bg[pat].type & 0x07) == 2) {
1989 uch ch;
1990 int ii, x, y, hw, hh, grayspot;
1991 double freq, rotate, saturate, gray, intensity;
1992 double angle=0.0, aoffset=0.0, maxDist, dist;
1993 double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
1995 hh = (int)(rpng2_info.height / 2);
1996 hw = (int)(rpng2_info.width / 2);
1998 /* variables for radial waves:
1999 * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED]
2000 * freq: number of color beams originating from the center
2001 * grayspot: size of the graying center area (anti-alias)
2002 * rotate: rotation of the beams as a function of radius
2003 * saturate: saturation of beams' shape azimuthally
2005 angle = CLIP(angle, 0.0, 360.0);
2006 grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
2007 freq = MAX((double)bg[pat].bg_freq, 0.0);
2008 saturate = (double)bg[pat].bg_bsat * 0.1;
2009 rotate = (double)bg[pat].bg_brot * 0.1;
2010 gray = 0.0;
2011 intensity = 0.0;
2012 maxDist = (double)((hw*hw) + (hh*hh));
2014 for (row = 0; row < rpng2_info.height; ++row) {
2015 y = (int)(row - hh);
2016 dest = (char *)bg_data + row*bg_rowbytes;
2017 for (i = 0; i < rpng2_info.width; ++i) {
2018 x = (int)(i - hw);
2019 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
2020 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
2021 gray = MIN(1.0, gray);
2022 dist = (double)((x*x) + (y*y)) / maxDist;
2023 intensity = cos((angle+(rotate*dist*PI)) * freq) *
2024 gray * saturate;
2025 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
2026 hue = (angle + PI) * INV_PI_360 + aoffset;
2027 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
2028 s = MIN(MAX(s,0.0), 1.0);
2029 v = MIN(MAX(intensity,0.0), 1.0);
2031 if (s == 0.0) {
2032 ch = (uch)(v * 255.0);
2033 *dest++ = ch;
2034 *dest++ = ch;
2035 *dest++ = ch;
2036 } else {
2037 if ((hue < 0.0) || (hue >= 360.0))
2038 hue -= (((int)(hue / 360.0)) * 360.0);
2039 hue /= 60.0;
2040 ii = (int)hue;
2041 f = hue - (double)ii;
2042 p = (1.0 - s) * v;
2043 q = (1.0 - (s * f)) * v;
2044 t = (1.0 - (s * (1.0 - f))) * v;
2045 if (ii == 0) { red = v; green = t; blue = p; }
2046 else if (ii == 1) { red = q; green = v; blue = p; }
2047 else if (ii == 2) { red = p; green = v; blue = t; }
2048 else if (ii == 3) { red = p; green = q; blue = v; }
2049 else if (ii == 4) { red = t; green = p; blue = v; }
2050 else if (ii == 5) { red = v; green = p; blue = q; }
2051 *dest++ = (uch)(red * 255.0);
2052 *dest++ = (uch)(green * 255.0);
2053 *dest++ = (uch)(blue * 255.0);
2059 } /* end function rpng2_x_reload_bg_image() */
2065 static int is_number(char *p)
2067 while (*p) {
2068 if (!isdigit(*p))
2069 return FALSE;
2070 ++p;
2072 return TRUE;
2075 #endif /* FEATURE_LOOP */
2081 static void rpng2_x_cleanup(void)
2083 if (bg_image && bg_data) {
2084 free(bg_data);
2085 bg_data = NULL;
2088 if (rpng2_info.image_data) {
2089 free(rpng2_info.image_data);
2090 rpng2_info.image_data = NULL;
2093 if (rpng2_info.row_pointers) {
2094 free(rpng2_info.row_pointers);
2095 rpng2_info.row_pointers = NULL;
2098 if (ximage) {
2099 if (ximage->data) {
2100 free(ximage->data); /* we allocated it, so we free it */
2101 ximage->data = (char *)NULL; /* instead of XDestroyImage() */
2103 XDestroyImage(ximage);
2104 ximage = NULL;
2107 if (have_gc)
2108 XFreeGC(display, gc);
2110 if (have_window)
2111 XDestroyWindow(display, window);
2113 if (have_colormap)
2114 XFreeColormap(display, colormap);
2116 if (have_nondefault_visual)
2117 XFree(visual_list);
2124 static int rpng2_x_msb(ulg u32val)
2126 int i;
2128 for (i = 31; i >= 0; --i) {
2129 if (u32val & 0x80000000L)
2130 break;
2131 u32val <<= 1;
2133 return i;