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