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