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