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