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 Franois 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 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 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 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 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 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 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 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 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 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 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 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