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