Bringing libpng-1.5.10 into the main branch
[AROS.git] / workbench / libs / png / contrib / gregbook / rpng2-x.c
blob7f24b632be04953d77437b55b67f3d4c9eb163c4
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
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
52 this software.
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 */
102 #include <stdio.h>
103 #include <stdlib.h>
104 #include <ctype.h>
105 #include <string.h>
106 #include <setjmp.h> /* for jmpbuf declaration in readpng2.h */
107 #include <time.h>
108 #include <math.h> /* only for PvdM background code */
109 #include <X11/Xlib.h>
110 #include <X11/Xutil.h>
111 #include <X11/Xos.h>
112 #include <X11/keysym.h> /* defines XK_* macros */
114 #ifdef VMS
115 # include <unistd.h>
116 #endif
118 /* all for PvdM background code: */
119 #ifndef PI
120 # define PI 3.141592653589793238
121 #endif
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
163 * overhead) */
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);
173 #ifdef FEATURE_LOOP
174 static void rpng2_x_reload_bg_image (void);
175 static int is_number (char *p);
176 #endif
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;
187 static FILE *infile;
189 static mainprog_info rpng2_info;
191 static uch inbuf[INBUFSIZE];
192 static int incount;
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;
198 static uch *bg_data;
200 int pause_after_pass = FALSE;
201 int demo_timing = FALSE;
202 ulg usleep_duration = 0L;
204 static struct rgb_color {
205 uch r, g, b;
206 } rgb[] = {
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
234 wonders for ya.
236 type bits:
237 F E D C B A 9 8 7 6 5 4 3 2 1 0
238 | | | | |
239 | | +-+-+-- 0 = sharp-edged checkerboard
240 | | 1 = soft diamonds
241 | | 2 = radial waves
242 | | 3-7 = undefined
243 | +-- gradient #2 inverted?
244 +-- alternating columns inverted?
246 static struct background_pattern {
247 ush type;
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)*/
250 } bg[] = {
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;
276 static int depth;
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;
282 static GC gc;
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)
295 #ifdef sgi
296 char tmpline[80];
297 #endif
298 char *p, *bgstr = NULL;
299 int rc, alen, flen;
300 int error = 0;
301 int timing = FALSE;
302 int have_bg = FALSE;
303 #ifdef FEATURE_LOOP
304 int loop = FALSE;
305 long loop_interval = -1; /* seconds (100,000 max) */
306 #endif
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 */
310 XEvent e;
311 KeySym k;
314 /* First initialize a few things, just to be sure--memset takes care of
315 * default background color (black), booleans (FALSE), pointers (NULL),
316 * etc. */
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. */
329 #if defined(NeXT)
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;
336 #elif defined(sgi)
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");
341 if (infile) {
342 double sgi_gamma;
344 fgets(tmpline, 80, infile);
345 fclose(infile);
346 sgi_gamma = atof(tmpline);
347 if (sgi_gamma > 0.0)
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;
356 #else
357 LUT_exponent = 1.0; /* assume no LUT: most PCs */
358 #endif
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);
371 else
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)) {
379 if (!*++argv)
380 ++error;
381 else
382 displayname = *argv;
383 } else if (!strncmp(*argv, "-gamma", 2)) {
384 if (!*++argv)
385 ++error;
386 else {
387 rpng2_info.display_exponent = atof(*argv);
388 if (rpng2_info.display_exponent <= 0.0)
389 ++error;
391 } else if (!strncmp(*argv, "-bgcolor", 4)) {
392 if (!*++argv)
393 ++error;
394 else {
395 bgstr = *argv;
396 if (strlen(bgstr) != 7 || bgstr[0] != '#')
397 ++error;
398 else {
399 have_bg = TRUE;
400 bg_image = FALSE;
403 } else if (!strncmp(*argv, "-bgpat", 4)) {
404 if (!*++argv)
405 ++error;
406 else {
407 pat = atoi(*argv);
408 if (pat >= 0 && pat < num_bgpat) {
409 bg_image = TRUE;
410 have_bg = FALSE;
411 } else
412 ++error;
414 } else if (!strncmp(*argv, "-usleep", 2)) {
415 if (!*++argv)
416 ++error;
417 else {
418 usleep_duration = (ulg)atol(*argv);
419 demo_timing = TRUE;
421 } else if (!strncmp(*argv, "-pause", 2)) {
422 pause_after_pass = TRUE;
423 } else if (!strncmp(*argv, "-timing", 2)) {
424 timing = TRUE;
425 #ifdef FEATURE_LOOP
426 } else if (!strncmp(*argv, "-loop", 2)) {
427 loop = TRUE;
428 if (!argv[1] || !is_number(argv[1]))
429 loop_interval = 2;
430 else {
431 ++argv;
432 loop_interval = atol(*argv);
433 if (loop_interval < 0)
434 loop_interval = 2;
435 else if (loop_interval > 100000) /* bit more than one day */
436 loop_interval = 100000;
438 #endif
439 } else {
440 if (**argv != '-') {
441 filename = *argv;
442 if (argv[1]) /* shouldn't be any more args after filename */
443 ++error;
444 } else
445 ++error; /* not expecting any other options */
449 if (!filename)
450 ++error;
453 /* print usage screen if any errors up to this point */
455 if (error) {
456 fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname);
457 readpng2_version_info();
458 fprintf(stderr, "\n"
459 "Usage: %s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
460 #ifdef FEATURE_LOOP
461 " %*s [-usleep dur | -timing] [-pause] [-loop [sec]] file.png\n\n"
462 #else
463 " %*s [-usleep dur | -timing] [-pause] file.png\n\n"
464 #endif
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"
475 #ifdef FEATURE_LOOP
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"
479 #endif
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"
487 "\n", PROGNAME,
488 (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat-1);
489 exit(1);
493 if (!(infile = fopen(filename, "rb"))) {
494 fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename);
495 ++error;
496 } else {
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",
501 filename);
502 ++error;
503 } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
504 switch (rc) {
505 case 2:
506 fprintf(stderr, PROGNAME
507 ": [%s] has bad IHDR (libpng longjmp)\n", filename);
508 break;
509 case 4:
510 fprintf(stderr, PROGNAME ": insufficient memory\n");
511 break;
512 default:
513 fprintf(stderr, PROGNAME
514 ": unknown readpng2_init() error\n");
515 break;
517 ++error;
518 } else {
519 Trace((stderr, "about to call XOpenDisplay()\n"))
520 display = XOpenDisplay(displayname);
521 if (!display) {
522 readpng2_cleanup(&rpng2_info);
523 fprintf(stderr, PROGNAME ": can't open X display [%s]\n",
524 displayname? displayname : "default");
525 ++error;
528 if (error)
529 fclose(infile);
533 if (error) {
534 fprintf(stderr, PROGNAME ": aborting.\n");
535 exit(2);
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));
545 else
546 sprintf(titlebar, "%s: %s", appname, filename);
549 /* set some final rpng2_info variables before entering main data loop */
551 if (have_bg) {
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;
558 } else
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
574 * modem. */
576 for (;;) {
577 Trace((stderr, "about to call readpng2_decode_data()\n"))
578 if (readpng2_decode_data(&rpng2_info, inbuf, incount))
579 ++error;
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");
588 exit(3);
589 } else if (feof(infile)) {
590 fprintf(stderr, PROGNAME ": end of file reached "
591 "(unexpectedly) while reading PNG image file\n");
592 exit(3);
593 } else /* if (error) */ {
594 /* will print error message below */
596 break;
599 if (timing)
600 sleep(1);
602 incount = fread(inbuf, 1, INBUFSIZE, infile);
606 /* clean up PNG stuff and report any decoding errors */
608 fclose(infile);
609 Trace((stderr, "about to call readpng2_cleanup()\n"))
610 readpng2_cleanup(&rpng2_info);
612 if (error) {
613 fprintf(stderr, PROGNAME ": libpng error while decoding PNG image\n");
614 exit(3);
618 #ifdef FEATURE_LOOP
620 if (loop && bg_image) {
621 Trace((stderr, "entering -loop loop (FEATURE_LOOP)\n"))
622 for (;;) {
623 int i, use_sleep;
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;
629 use_sleep = FALSE;
630 } else
631 use_sleep = TRUE;
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))
636 if (QUIT(e,k))
637 break;
639 /* generate next background image */
640 if (++pat >= num_bgpat)
641 pat = 0;
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) {
647 sleep(1);
648 /* GRR BUG: also need to check for Expose (and redraw!) */
649 if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask,
650 &e) && QUIT(e,k))
651 break;
653 } else {
654 /* Y2038 BUG! */
655 if (now.tv_sec < then.tv_sec ||
656 (now.tv_sec == then.tv_sec && now.tv_usec < then.tv_usec))
658 int quit = FALSE;
659 long seconds_to_go = then.tv_sec - now.tv_sec;
660 long usleep_usec;
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) {
671 sleep(1);
672 /* GRR BUG: need to check for Expose and redraw */
673 if (XCheckMaskEvent(display, KeyPressMask |
674 ButtonPressMask, &e) && QUIT(e,k))
675 quit = TRUE;
676 if (++seconds_done > 1000)
677 break; /* time to redo seconds_to_go meas. */
679 if (quit)
680 break;
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)
686 break;
687 seconds_to_go = then.tv_sec - now.tv_sec;
688 } else
689 ++seconds_to_go; /* restore what we subtracted */
691 if (quit)
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;
700 if (usleep_usec > 0)
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"))
719 do {
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));
726 } else {
727 fprintf(stderr, PROGNAME ": init callback never called: probable "
728 "libpng error while decoding PNG metadata\n");
729 exit(4);
733 /* we're done: clean up all image and X resources and go away */
735 Trace((stderr, "about to call rpng2_x_cleanup()\n"))
736 rpng2_x_cleanup();
738 return 0;
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)
751 ulg i;
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);
762 return;
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);
770 return;
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
779 * pattern */
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
786 * libpng bug... */
788 /* we're here via libpng callback, so if window fails, clean and bail */
789 readpng2_cleanup(&rpng2_info);
790 rpng2_x_cleanup();
791 exit(2);
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;
806 ulg bg_pixel = 0L;
807 ulg attrmask;
808 int need_colormap = FALSE;
809 int screen, pad;
810 uch *xdata;
811 Window root;
812 XEvent e;
813 XGCValues gcvalues;
814 XSetWindowAttributes attr;
815 XTextProperty windowName, *pWindowName = &windowName;
816 XTextProperty iconName, *pIconName = &iconName;
817 XVisualInfo visual_info;
818 XSizeHints *size_hints;
819 XWMHints *wm_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);
829 #ifdef DEBUG
830 XSynchronize(display, True);
831 #endif
833 if (depth != 16 && depth != 24 && depth != 32) {
834 int visuals_matched = 0;
836 Trace((stderr, "default depth is %d: checking other visuals\n",
837 depth))
839 /* 24-bit first */
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);
848 return 2;
850 Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
851 visuals_matched))
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;
861 } else {
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);
873 if (!colormap) {
874 fprintf(stderr, "XCreateColormap() failed\n");
875 return 2;
877 have_colormap = TRUE;
878 if (depth == 8)
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");
892 return 2;
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");
914 return 2;
915 } else
916 have_window = TRUE;
918 if (depth == 8)
919 XSetWindowColormap(display, window, colormap);
921 if (!XStringListToTextProperty(&window_name, 1, pWindowName))
922 pWindowName = NULL;
923 if (!XStringListToTextProperty(&icon_name, 1, pIconName))
924 pIconName = NULL;
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 */
952 if (pWindowName)
953 XFree(pWindowName->value);
954 if (pIconName)
955 XFree(pIconName->value);
956 if (size_hints)
957 XFree(size_hints);
958 if (wm_hints)
959 XFree(wm_hints);
960 if (class_hints)
961 XFree(class_hints);
963 XMapWindow(display, window);
965 gc = XCreateGC(display, window, 0, &gcvalues);
966 have_gc = TRUE;
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);
974 pad = 32;
975 } else if (depth == 16) {
976 xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height);
977 pad = 16;
978 } else /* depth == 8 */ {
979 xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height);
980 pad = 8;
983 if (!xdata) {
984 fprintf(stderr, PROGNAME ": unable to allocate image memory\n");
985 return 4;
988 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
989 (char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0);
991 if (!ximage) {
992 fprintf(stderr, PROGNAME ": XCreateImage() failed\n");
993 free(xdata);
994 return 3;
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 ---------------------------------------------------------------------------*/
1012 if (bg_image)
1013 rpng2_x_load_bg_image(); /* resets bg_image if fails */
1015 if (!bg_image) {
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,
1031 rpng2_info.height);
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);
1042 XFlush(display);
1044 return 0;
1046 } /* end function rpng2_x_create_window() */
1052 static int rpng2_x_load_bg_image(void)
1054 uch *src;
1055 char *dest;
1056 uch r1, r2, g1, g2, b1, b2;
1057 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
1058 int k, hmax, max;
1059 int xidx, yidx, yidx_max;
1060 int even_odd_vert, even_odd_horiz, even_odd;
1061 int invert_gradient2 = (bg[pat].type & 0x08);
1062 int invert_column;
1063 int ximage_rowbytes = ximage->bytes_per_line;
1064 ulg i, row;
1065 ulg pixel;
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);
1074 if (!bg_data) {
1075 fprintf(stderr, PROGNAME
1076 ": unable to allocate memory for background image\n");
1077 bg_image = 0;
1078 return 1;
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;
1125 invert_column =
1126 (even_odd_horiz && (bg[pat].type & 0x10));
1127 if (even_odd == 0) { /* gradient #1 */
1128 if (invert_column) {
1129 *dest++ = r1_inv;
1130 *dest++ = g1_inv;
1131 *dest++ = b1_inv;
1132 } else {
1133 *dest++ = r1;
1134 *dest++ = g1;
1135 *dest++ = b1;
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 */
1143 *dest++ = b2;
1144 } else {
1145 *dest++ = r2_inv;
1146 *dest++ = g2_inv; /* singly inverted */
1147 *dest++ = b2_inv;
1153 /*---------------------------------------------------------------------------
1154 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
1155 M. Costello.
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);
1172 if (yidx > hmax)
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);
1177 if (xidx > hmax)
1178 xidx = bgscale-1 - xidx;
1179 k = xidx + yidx;
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) {
1193 uch ch;
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...",
1200 PROGNAME);
1201 fflush(stderr);
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;
1218 gray = 0.0;
1219 intensity = 0.0;
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) {
1226 x = (int)(i - hw);
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) *
1232 gray * saturate;
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);
1239 if (s == 0.0) {
1240 ch = (uch)(v * 255.0);
1241 *dest++ = ch;
1242 *dest++ = ch;
1243 *dest++ = ch;
1244 } else {
1245 if ((hue < 0.0) || (hue >= 360.0))
1246 hue -= (((int)(hue / 360.0)) * 360.0);
1247 hue /= 60.0;
1248 ii = (int)hue;
1249 f = hue - (double)ii;
1250 p = (1.0 - s) * v;
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");
1266 fflush(stderr);
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) {
1282 red = *src++;
1283 green = *src++;
1284 blue = *src++;
1285 pixel = (red << RShift) |
1286 (green << GShift) |
1287 (blue << BShift);
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);
1294 } else {
1295 for (i = rpng2_info.width; i > 0; --i) {
1296 red = *src++;
1297 green = *src++;
1298 blue = *src++;
1299 pixel = (red << RShift) |
1300 (green << GShift) |
1301 (blue << BShift);
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,
1338 rpng2_info.height);
1340 return 0;
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;
1354 char *dest;
1355 uch r, g, b, a;
1356 int ximage_rowbytes = ximage->bytes_per_line;
1357 ulg i, pixel;
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) {
1371 XEvent e;
1372 KeySym k;
1374 fprintf(stderr,
1375 "%s: end of pass %d of 7; click in image window to continue\n",
1376 PROGNAME, prevpass + 1);
1378 XNextEvent(display, &e);
1379 while (!QUIT(e,k));
1381 fprintf(stderr, "%s: pass %d of 7\r", PROGNAME, rpng2_info.pass + 1);
1382 fflush(stderr);
1383 prevpass = rpng2_info.pass;
1386 if (rows == 0)
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;
1403 if (bg_image)
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) {
1408 red = *src++;
1409 green = *src++;
1410 blue = *src++;
1411 pixel = (red << RShift) |
1412 (green << GShift) |
1413 (blue << BShift);
1414 /* recall that we set ximage->byte_order = MSBFirst above */
1415 if (bpp == 32) {
1416 *dest++ = (char)((pixel >> 24) & 0xff);
1417 *dest++ = (char)((pixel >> 16) & 0xff);
1418 *dest++ = (char)((pixel >> 8) & 0xff);
1419 *dest++ = (char)( pixel & 0xff);
1420 } else {
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) {
1430 r = *src++;
1431 g = *src++;
1432 b = *src++;
1433 a = *src++;
1434 if (bg_image) {
1435 bg_red = *src2++;
1436 bg_green = *src2++;
1437 bg_blue = *src2++;
1439 if (a == 255) {
1440 red = r;
1441 green = g;
1442 blue = b;
1443 } else if (a == 0) {
1444 red = bg_red;
1445 green = bg_green;
1446 blue = bg_blue;
1447 } else {
1448 /* this macro (from png.h) composites the foreground
1449 * and background values and puts the result into the
1450 * first argument */
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) |
1456 (green << GShift) |
1457 (blue << BShift);
1458 /* recall that we set ximage->byte_order = MSBFirst above */
1459 if (bpp == 32) {
1460 *dest++ = (char)((pixel >> 24) & 0xff);
1461 *dest++ = (char)((pixel >> 16) & 0xff);
1462 *dest++ = (char)((pixel >> 8) & 0xff);
1463 *dest++ = (char)( pixel & 0xff);
1464 } else {
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];
1478 if (bg_image)
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);
1484 ++src;
1485 green = ((ush)(*src) << 8);
1486 ++src;
1487 blue = ((ush)(*src) << 8);
1488 ++src;
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) {
1498 r = *src++;
1499 g = *src++;
1500 b = *src++;
1501 a = *src++;
1502 if (bg_image) {
1503 bg_red = *src2++;
1504 bg_green = *src2++;
1505 bg_blue = *src2++;
1507 if (a == 255) {
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);
1515 } else {
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);
1553 XFlush(display);
1554 rows = 0;
1555 usleep(usleep_duration);
1556 } else
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);
1560 XFlush(display);
1561 rows = 0;
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;
1579 printf(
1580 "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
1581 fflush(stdout);
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;
1595 char *dest;
1596 uch r, g, b, a;
1597 ulg i, row, lastrow = 0;
1598 ulg pixel;
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;
1624 if (bg_image)
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) {
1629 red = *src++;
1630 green = *src++;
1631 blue = *src++;
1632 #ifdef NO_24BIT_MASKS
1633 pixel = (red << RShift) |
1634 (green << GShift) |
1635 (blue << BShift);
1636 /* recall that we set ximage->byte_order = MSBFirst above */
1637 if (bpp == 32) {
1638 *dest++ = (char)((pixel >> 24) & 0xff);
1639 *dest++ = (char)((pixel >> 16) & 0xff);
1640 *dest++ = (char)((pixel >> 8) & 0xff);
1641 *dest++ = (char)( pixel & 0xff);
1642 } else {
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);
1649 #else
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 */
1655 if (bpp == 32) {
1656 *dest++ = (char)((pixel >> 24) & 0xff);
1657 *dest++ = (char)((pixel >> 16) & 0xff);
1658 *dest++ = (char)((pixel >> 8) & 0xff);
1659 *dest++ = (char)( pixel & 0xff);
1660 } else {
1661 /* GRR BUG */
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);
1668 #endif
1671 } else /* if (rpng2_info.channels == 4) */ {
1672 for (i = rpng2_info.width; i > 0; --i) {
1673 r = *src++;
1674 g = *src++;
1675 b = *src++;
1676 a = *src++;
1677 if (bg_image) {
1678 bg_red = *src2++;
1679 bg_green = *src2++;
1680 bg_blue = *src2++;
1682 if (a == 255) {
1683 red = r;
1684 green = g;
1685 blue = b;
1686 } else if (a == 0) {
1687 red = bg_red;
1688 green = bg_green;
1689 blue = bg_blue;
1690 } else {
1691 /* this macro (from png.h) composites the foreground
1692 * and background values and puts the result into the
1693 * first argument */
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) |
1700 (green << GShift) |
1701 (blue << BShift);
1702 /* recall that we set ximage->byte_order = MSBFirst above */
1703 if (bpp == 32) {
1704 *dest++ = (char)((pixel >> 24) & 0xff);
1705 *dest++ = (char)((pixel >> 16) & 0xff);
1706 *dest++ = (char)((pixel >> 8) & 0xff);
1707 *dest++ = (char)( pixel & 0xff);
1708 } else {
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);
1715 #else
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 */
1721 if (bpp == 32) {
1722 *dest++ = (char)((pixel >> 24) & 0xff);
1723 *dest++ = (char)((pixel >> 16) & 0xff);
1724 *dest++ = (char)((pixel >> 8) & 0xff);
1725 *dest++ = (char)( pixel & 0xff);
1726 } else {
1727 /* GRR BUG */
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);
1734 #endif
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);
1741 XFlush(display);
1742 lastrow = row + 1;
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];
1751 if (bg_image)
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);
1757 ++src;
1758 green = ((ush)(*src) << 8);
1759 ++src;
1760 blue = ((ush)(*src) << 8);
1761 ++src;
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) {
1771 r = *src++;
1772 g = *src++;
1773 b = *src++;
1774 a = *src++;
1775 if (bg_image) {
1776 bg_red = *src2++;
1777 bg_green = *src2++;
1778 bg_blue = *src2++;
1780 if (a == 255) {
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);
1788 } else {
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);
1811 XFlush(display);
1812 lastrow = row + 1;
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);
1826 XFlush(display);
1829 } /* end function rpng2_x_redisplay_image() */
1835 #ifdef FEATURE_LOOP
1837 static void rpng2_x_reload_bg_image(void)
1839 char *dest;
1840 uch r1, r2, g1, g2, b1, b2;
1841 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
1842 int k, hmax, max;
1843 int xidx, yidx, yidx_max;
1844 int even_odd_vert, even_odd_horiz, even_odd;
1845 int invert_gradient2 = (bg[pat].type & 0x08);
1846 int invert_column;
1847 ulg i, row;
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;
1894 invert_column =
1895 (even_odd_horiz && (bg[pat].type & 0x10));
1896 if (even_odd == 0) { /* gradient #1 */
1897 if (invert_column) {
1898 *dest++ = r1_inv;
1899 *dest++ = g1_inv;
1900 *dest++ = b1_inv;
1901 } else {
1902 *dest++ = r1;
1903 *dest++ = g1;
1904 *dest++ = b1;
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 */
1912 *dest++ = b2;
1913 } else {
1914 *dest++ = r2_inv;
1915 *dest++ = g2_inv; /* singly inverted */
1916 *dest++ = b2_inv;
1922 /*---------------------------------------------------------------------------
1923 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
1924 M. Costello.
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);
1941 if (yidx > hmax)
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);
1946 if (xidx > hmax)
1947 xidx = bgscale-1 - xidx;
1948 k = xidx + yidx;
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) {
1962 uch ch;
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;
1983 gray = 0.0;
1984 intensity = 0.0;
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) {
1991 x = (int)(i - hw);
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) *
1997 gray * saturate;
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);
2004 if (s == 0.0) {
2005 ch = (uch)(v * 255.0);
2006 *dest++ = ch;
2007 *dest++ = ch;
2008 *dest++ = ch;
2009 } else {
2010 if ((hue < 0.0) || (hue >= 360.0))
2011 hue -= (((int)(hue / 360.0)) * 360.0);
2012 hue /= 60.0;
2013 ii = (int)hue;
2014 f = hue - (double)ii;
2015 p = (1.0 - s) * v;
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)
2040 while (*p) {
2041 if (!isdigit(*p))
2042 return FALSE;
2043 ++p;
2045 return TRUE;
2048 #endif /* FEATURE_LOOP */
2054 static void rpng2_x_cleanup(void)
2056 if (bg_image && bg_data) {
2057 free(bg_data);
2058 bg_data = NULL;
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;
2071 if (ximage) {
2072 if (ximage->data) {
2073 free(ximage->data); /* we allocated it, so we free it */
2074 ximage->data = (char *)NULL; /* instead of XDestroyImage() */
2076 XDestroyImage(ximage);
2077 ximage = NULL;
2080 if (have_gc)
2081 XFreeGC(display, gc);
2083 if (have_window)
2084 XDestroyWindow(display, window);
2086 if (have_colormap)
2087 XFreeColormap(display, colormap);
2089 if (have_nondefault_visual)
2090 XFree(visual_list);
2097 static int rpng2_x_msb(ulg u32val)
2099 int i;
2101 for (i = 31; i >= 0; --i) {
2102 if (u32val & 0x80000000L)
2103 break;
2104 u32val <<= 1;
2106 return i;