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