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