1 /*--------------------------------------------------------------------------- 2 3 rpng - simple PNG display program rpng-x.c 4 5 This program decodes and displays PNG images, with gamma correction and 6 optionally with a user-specified background color (in case the image has 7 transparency). It is very nearly the most basic PNG viewer possible. 8 This version is for the X Window System (tested by author under Unix and 9 by Martin Zinser under OpenVMS; may work under OS/2 with some tweaking). 10 11 to do: 12 - 8-bit (colormapped) X support 13 - use %.1023s to simplify truncation of title-bar string? 14 15 --------------------------------------------------------------------------- 16 17 Changelog: 18 - 1.01: initial public release 19 - 1.02: modified to allow abbreviated options; fixed long/ulong mis- 20 match; switched to png_jmpbuf() macro 21 - 1.10: added support for non-default visuals; fixed X pixel-conversion 22 - 1.11: added extra set of parentheses to png_jmpbuf() macro; fixed 23 command-line parsing bug 24 - 1.12: fixed some small X memory leaks (thanks to Franois Petitjean) 25 - 1.13: fixed XFreeGC() crash bug (thanks to Patrick Welche) 26 - 1.14: added support for X resources (thanks to Gerhard Niklasch) 27 - 2.00: dual-licensed (added GNU GPL) 28 - 2.01: fixed improper display of usage screen on PNG error(s) 29 30 --------------------------------------------------------------------------- 31 32 Copyright (c) 1998-2008 Greg Roelofs. All rights reserved. 33 34 This software is provided "as is," without warranty of any kind, 35 express or implied. In no event shall the author or contributors 36 be held liable for any damages arising in any way from the use of 37 this software. 38 39 The contents of this file are DUAL-LICENSED. You may modify and/or 40 redistribute this software according to the terms of one of the 41 following two licenses (at your option): 42 43 44 LICENSE 1 ("BSD-like with advertising clause"): 45 46 Permission is granted to anyone to use this software for any purpose, 47 including commercial applications, and to alter it and redistribute 48 it freely, subject to the following restrictions: 49 50 1. Redistributions of source code must retain the above copyright 51 notice, disclaimer, and this list of conditions. 52 2. Redistributions in binary form must reproduce the above copyright 53 notice, disclaimer, and this list of conditions in the documenta- 54 tion and/or other materials provided with the distribution. 55 3. All advertising materials mentioning features or use of this 56 software must display the following acknowledgment: 57 58 This product includes software developed by Greg Roelofs 59 and contributors for the book, "PNG: The Definitive Guide," 60 published by O'Reilly and Associates. 61 62 63 LICENSE 2 (GNU GPL v2 or later): 64 65 This program is free software; you can redistribute it and/or modify 66 it under the terms of the GNU General Public License as published by 67 the Free Software Foundation; either version 2 of the License, or 68 (at your option) any later version. 69 70 This program is distributed in the hope that it will be useful, 71 but WITHOUT ANY WARRANTY; without even the implied warranty of 72 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 73 GNU General Public License for more details. 74 75 You should have received a copy of the GNU General Public License 76 along with this program; if not, write to the Free Software Foundation, 77 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 78 79 ---------------------------------------------------------------------------*/ 80 81 #define PROGNAME "rpng-x" 82 #define LONGNAME "Simple PNG Viewer for X" 83 #define VERSION "2.01 of 16 March 2008" 84 #define RESNAME "rpng" /* our X resource application name */ 85 #define RESCLASS "Rpng" /* our X resource class name */ 86 87 #include <stdio.h> 88 #include <stdlib.h> 89 #include <string.h> 90 #include <time.h> 91 #include <X11/Xlib.h> 92 #include <X11/Xutil.h> 93 #include <X11/Xos.h> 94 #include <X11/keysym.h> 95 96 /* #define DEBUG : this enables the Trace() macros */ 97 98 #include "readpng.h" /* typedefs, common macros, readpng prototypes */ 99 100 101 /* could just include png.h, but this macro is the only thing we need 102 * (name and typedefs changed to local versions); note that side effects 103 * only happen with alpha (which could easily be avoided with 104 * "ush acopy = (alpha);") */ 105 106 #define alpha_composite(composite, fg, alpha, bg) { \ 107 ush temp = ((ush)(fg)*(ush)(alpha) + \ 108 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \ 109 (composite) = (uch)((temp + (temp >> 8)) >> 8); \ 110 } 111 112 113 /* local prototypes */ 114 static int rpng_x_create_window(void); 115 static int rpng_x_display_image(void); 116 static void rpng_x_cleanup(void); 117 static int rpng_x_msb(ulg u32val); 118 119 120 static char titlebar[1024], *window_name = titlebar; 121 static char *appname = LONGNAME; 122 static char *icon_name = PROGNAME; 123 static char *res_name = RESNAME; 124 static char *res_class = RESCLASS; 125 static char *filename; 126 static FILE *infile; 127 128 static char *bgstr; 129 static uch bg_red=0, bg_green=0, bg_blue=0; 130 131 static double display_exponent; 132 133 static ulg image_width, image_height, image_rowbytes; 134 static int image_channels; 135 static uch *image_data; 136 137 /* X-specific variables */ 138 static char *displayname; 139 static XImage *ximage; 140 static Display *display; 141 static int depth; 142 static Visual *visual; 143 static XVisualInfo *visual_list; 144 static int RShift, GShift, BShift; 145 static ulg RMask, GMask, BMask; 146 static Window window; 147 static GC gc; 148 static Colormap colormap; 149 150 static int have_nondefault_visual = FALSE; 151 static int have_colormap = FALSE; 152 static int have_window = FALSE; 153 static int have_gc = FALSE; 154 /* 155 ulg numcolors=0, pixels[256]; 156 ush reds[256], greens[256], blues[256]; 157 */ 158 159 160 161 162 int main(int argc, char **argv) 163 { 164 #ifdef sgi 165 char tmpline[80]; 166 #endif 167 char *p; 168 int rc, alen, flen; 169 int error = 0; 170 int have_bg = FALSE; 171 double LUT_exponent; /* just the lookup table */ 172 double CRT_exponent = 2.2; /* just the monitor */ 173 double default_display_exponent; /* whole display system */ 174 XEvent e; 175 KeySym k; 176 177 178 displayname = (char *)NULL; 179 filename = (char *)NULL; 180 181 182 /* First set the default value for our display-system exponent, i.e., 183 * the product of the CRT exponent and the exponent corresponding to 184 * the frame-buffer's lookup table (LUT), if any. This is not an 185 * exhaustive list of LUT values (e.g., OpenStep has a lot of weird 186 * ones), but it should cover 99% of the current possibilities. */ 187 188 #if defined(NeXT) 189 LUT_exponent = 1.0 / 2.2; 190 /* 191 if (some_next_function_that_returns_gamma(&next_gamma)) 192 LUT_exponent = 1.0 / next_gamma; 193 */ 194 #elif defined(sgi) 195 LUT_exponent = 1.0 / 1.7; 196 /* there doesn't seem to be any documented function to get the 197 * "gamma" value, so we do it the hard way */ 198 infile = fopen("/etc/config/system.glGammaVal", "r"); 199 if (infile) { 200 double sgi_gamma; 201 202 fgets(tmpline, 80, infile); 203 fclose(infile); 204 sgi_gamma = atof(tmpline); 205 if (sgi_gamma > 0.0) 206 LUT_exponent = 1.0 / sgi_gamma; 207 } 208 #elif defined(Macintosh) 209 LUT_exponent = 1.8 / 2.61; 210 /* 211 if (some_mac_function_that_returns_gamma(&mac_gamma)) 212 LUT_exponent = mac_gamma / 2.61; 213 */ 214 #else 215 LUT_exponent = 1.0; /* assume no LUT: most PCs */ 216 #endif 217 218 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */ 219 default_display_exponent = LUT_exponent * CRT_exponent; 220 221 222 /* If the user has set the SCREEN_GAMMA environment variable as suggested 223 * (somewhat imprecisely) in the libpng documentation, use that; otherwise 224 * use the default value we just calculated. Either way, the user may 225 * override this via a command-line option. */ 226 227 if ((p = getenv("SCREEN_GAMMA")) != NULL) 228 display_exponent = atof(p); 229 else 230 display_exponent = default_display_exponent; 231 232 233 /* Now parse the command line for options and the PNG filename. */ 234 235 while (*++argv && !error) { 236 if (!strncmp(*argv, "-display", 2)) { 237 if (!*++argv) 238 ++error; 239 else 240 displayname = *argv; 241 } else if (!strncmp(*argv, "-gamma", 2)) { 242 if (!*++argv) 243 ++error; 244 else { 245 display_exponent = atof(*argv); 246 if (display_exponent <= 0.0) 247 ++error; 248 } 249 } else if (!strncmp(*argv, "-bgcolor", 2)) { 250 if (!*++argv) 251 ++error; 252 else { 253 bgstr = *argv; 254 if (strlen(bgstr) != 7 || bgstr[0] != '#') 255 ++error; 256 else 257 have_bg = TRUE; 258 } 259 } else { 260 if (**argv != '-') { 261 filename = *argv; 262 if (argv[1]) /* shouldn't be any more args after filename */ 263 ++error; 264 } else 265 ++error; /* not expecting any other options */ 266 } 267 } 268 269 if (!filename) 270 ++error; 271 272 273 /* print usage screen if any errors up to this point */ 274 275 if (error) { 276 fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, appname); 277 readpng_version_info(); 278 fprintf(stderr, "\n" 279 "Usage: %s [-display xdpy] [-gamma exp] [-bgcolor bg] file.png\n" 280 " xdpy\tname of the target X display (e.g., ``hostname:0'')\n" 281 " exp \ttransfer-function exponent (``gamma'') of the display\n" 282 "\t\t system in floating-point format (e.g., ``%.1f''); equal\n" 283 "\t\t to the product of the lookup-table exponent (varies)\n" 284 "\t\t and the CRT exponent (usually 2.2); must be positive\n" 285 " bg \tdesired background color in 7-character hex RGB format\n" 286 "\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n" 287 "\t\t used with transparent images\n" 288 "\nPress Q, Esc or mouse button 1 (within image window, after image\n" 289 "is displayed) to quit.\n" 290 "\n", PROGNAME, default_display_exponent); 291 exit(1); 292 } 293 294 295 if (!(infile = fopen(filename, "rb"))) { 296 fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename); 297 ++error; 298 } else { 299 if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) { 300 switch (rc) { 301 case 1: 302 fprintf(stderr, PROGNAME 303 ": [%s] is not a PNG file: incorrect signature\n", 304 filename); 305 break; 306 case 2: 307 fprintf(stderr, PROGNAME 308 ": [%s] has bad IHDR (libpng longjmp)\n", filename); 309 break; 310 case 4: 311 fprintf(stderr, PROGNAME ": insufficient memory\n"); 312 break; 313 default: 314 fprintf(stderr, PROGNAME 315 ": unknown readpng_init() error\n"); 316 break; 317 } 318 ++error; 319 } else { 320 display = XOpenDisplay(displayname); 321 if (!display) { 322 readpng_cleanup(TRUE); 323 fprintf(stderr, PROGNAME ": can't open X display [%s]\n", 324 displayname? displayname : "default"); 325 ++error; 326 } 327 } 328 if (error) 329 fclose(infile); 330 } 331 332 333 if (error) { 334 fprintf(stderr, PROGNAME ": aborting.\n"); 335 exit(2); 336 } 337 338 339 /* set the title-bar string, but make sure buffer doesn't overflow */ 340 341 alen = strlen(appname); 342 flen = strlen(filename); 343 if (alen + flen + 3 > 1023) 344 sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023)); 345 else 346 sprintf(titlebar, "%s: %s", appname, filename); 347 348 349 /* if the user didn't specify a background color on the command line, 350 * check for one in the PNG file--if not, the initialized values of 0 351 * (black) will be used */ 352 353 if (have_bg) { 354 unsigned r, g, b; /* this approach quiets compiler warnings */ 355 356 sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b); 357 bg_red = (uch)r; 358 bg_green = (uch)g; 359 bg_blue = (uch)b; 360 } else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) { 361 readpng_cleanup(TRUE); 362 fprintf(stderr, PROGNAME 363 ": libpng error while checking for background color\n"); 364 exit(2); 365 } 366 367 368 /* do the basic X initialization stuff, make the window and fill it 369 * with the background color */ 370 371 if (rpng_x_create_window()) 372 exit(2); 373 374 375 /* decode the image, all at once */ 376 377 Trace((stderr, "calling readpng_get_image()\n")) 378 image_data = readpng_get_image(display_exponent, &image_channels, 379 &image_rowbytes); 380 Trace((stderr, "done with readpng_get_image()\n")) 381 382 383 /* done with PNG file, so clean up to minimize memory usage (but do NOT 384 * nuke image_data!) */ 385 386 readpng_cleanup(FALSE); 387 fclose(infile); 388 389 if (!image_data) { 390 fprintf(stderr, PROGNAME ": unable to decode PNG image\n"); 391 exit(3); 392 } 393 394 395 /* display image (composite with background if requested) */ 396 397 Trace((stderr, "calling rpng_x_display_image()\n")) 398 if (rpng_x_display_image()) { 399 free(image_data); 400 exit(4); 401 } 402 Trace((stderr, "done with rpng_x_display_image()\n")) 403 404 405 /* wait for the user to tell us when to quit */ 406 407 printf( 408 "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n"); 409 fflush(stdout); 410 411 do 412 XNextEvent(display, &e); 413 while (!(e.type == ButtonPress && e.xbutton.button == Button1) && 414 !(e.type == KeyPress && /* v--- or 1 for shifted keys */ 415 ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape) )); 416 417 418 /* OK, we're done: clean up all image and X resources and go away */ 419 420 rpng_x_cleanup(); 421 422 return 0; 423 } 424 425 426 427 428 429 static int rpng_x_create_window(void) 430 { 431 uch *xdata; 432 int need_colormap = FALSE; 433 int screen, pad; 434 ulg bg_pixel = 0L; 435 ulg attrmask; 436 Window root; 437 XEvent e; 438 XGCValues gcvalues; 439 XSetWindowAttributes attr; 440 XTextProperty windowName, *pWindowName = &windowName; 441 XTextProperty iconName, *pIconName = &iconName; 442 XVisualInfo visual_info; 443 XSizeHints *size_hints; 444 XWMHints *wm_hints; 445 XClassHint *class_hints; 446 447 448 screen = DefaultScreen(display); 449 depth = DisplayPlanes(display, screen); 450 root = RootWindow(display, screen); 451 452 #ifdef DEBUG 453 XSynchronize(display, True); 454 #endif 455 456 #if 0 457 /* GRR: add 8-bit support */ 458 if (/* depth != 8 && */ depth != 16 && depth != 24 && depth != 32) { 459 fprintf(stderr, 460 "screen depth %d not supported (only 16-, 24- or 32-bit TrueColor)\n", 461 depth); 462 return 2; 463 } 464 465 XMatchVisualInfo(display, screen, depth, 466 (depth == 8)? PseudoColor : TrueColor, &visual_info); 467 visual = visual_info.visual; 468 #else 469 if (depth != 16 && depth != 24 && depth != 32) { 470 int visuals_matched = 0; 471 472 Trace((stderr, "default depth is %d: checking other visuals\n", 473 depth)) 474 475 /* 24-bit first */ 476 visual_info.screen = screen; 477 visual_info.depth = 24; 478 visual_list = XGetVisualInfo(display, 479 VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched); 480 if (visuals_matched == 0) { 481 /* GRR: add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */ 482 fprintf(stderr, "default screen depth %d not supported, and no" 483 " 24-bit visuals found\n", depth); 484 return 2; 485 } 486 Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n", 487 visuals_matched)) 488 visual = visual_list[0].visual; 489 depth = visual_list[0].depth; 490 /* 491 colormap_size = visual_list[0].colormap_size; 492 visual_class = visual->class; 493 visualID = XVisualIDFromVisual(visual); 494 */ 495 have_nondefault_visual = TRUE; 496 need_colormap = TRUE; 497 } else { 498 XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info); 499 visual = visual_info.visual; 500 } 501 #endif 502 503 RMask = visual->red_mask; 504 GMask = visual->green_mask; 505 BMask = visual->blue_mask; 506 507 /* GRR: add/check 8-bit support */ 508 if (depth == 8 || need_colormap) { 509 colormap = XCreateColormap(display, root, visual, AllocNone); 510 if (!colormap) { 511 fprintf(stderr, "XCreateColormap() failed\n"); 512 return 2; 513 } 514 have_colormap = TRUE; 515 } 516 if (depth == 15 || depth == 16) { 517 RShift = 15 - rpng_x_msb(RMask); /* these are right-shifts */ 518 GShift = 15 - rpng_x_msb(GMask); 519 BShift = 15 - rpng_x_msb(BMask); 520 } else if (depth > 16) { 521 #define NO_24BIT_MASKS 522 #ifdef NO_24BIT_MASKS 523 RShift = rpng_x_msb(RMask) - 7; /* these are left-shifts */ 524 GShift = rpng_x_msb(GMask) - 7; 525 BShift = rpng_x_msb(BMask) - 7; 526 #else 527 RShift = 7 - rpng_x_msb(RMask); /* these are right-shifts, too */ 528 GShift = 7 - rpng_x_msb(GMask); 529 BShift = 7 - rpng_x_msb(BMask); 530 #endif 531 } 532 if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) { 533 fprintf(stderr, "rpng internal logic error: negative X shift(s)!\n"); 534 return 2; 535 } 536 537 /*--------------------------------------------------------------------------- 538 Finally, create the window. 539 ---------------------------------------------------------------------------*/ 540 541 attr.backing_store = Always; 542 attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask; 543 attrmask = CWBackingStore | CWEventMask; 544 if (have_nondefault_visual) { 545 attr.colormap = colormap; 546 attr.background_pixel = 0; 547 attr.border_pixel = 1; 548 attrmask |= CWColormap | CWBackPixel | CWBorderPixel; 549 } 550 551 window = XCreateWindow(display, root, 0, 0, image_width, image_height, 0, 552 depth, InputOutput, visual, attrmask, &attr); 553 554 if (window == None) { 555 fprintf(stderr, "XCreateWindow() failed\n"); 556 return 2; 557 } else 558 have_window = TRUE; 559 560 if (depth == 8) 561 XSetWindowColormap(display, window, colormap); 562 563 if (!XStringListToTextProperty(&window_name, 1, pWindowName)) 564 pWindowName = NULL; 565 if (!XStringListToTextProperty(&icon_name, 1, pIconName)) 566 pIconName = NULL; 567 568 /* OK if any hints allocation fails; XSetWMProperties() allows NULLs */ 569 570 if ((size_hints = XAllocSizeHints()) != NULL) { 571 /* window will not be resizable */ 572 size_hints->flags = PMinSize | PMaxSize; 573 size_hints->min_width = size_hints->max_width = (int)image_width; 574 size_hints->min_height = size_hints->max_height = (int)image_height; 575 } 576 577 if ((wm_hints = XAllocWMHints()) != NULL) { 578 wm_hints->initial_state = NormalState; 579 wm_hints->input = True; 580 /* wm_hints->icon_pixmap = icon_pixmap; */ 581 wm_hints->flags = StateHint | InputHint /* | IconPixmapHint */ ; 582 } 583 584 if ((class_hints = XAllocClassHint()) != NULL) { 585 class_hints->res_name = res_name; 586 class_hints->res_class = res_class; 587 } 588 589 XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0, 590 size_hints, wm_hints, class_hints); 591 592 /* various properties and hints no longer needed; free memory */ 593 if (pWindowName) 594 XFree(pWindowName->value); 595 if (pIconName) 596 XFree(pIconName->value); 597 if (size_hints) 598 XFree(size_hints); 599 if (wm_hints) 600 XFree(wm_hints); 601 if (class_hints) 602 XFree(class_hints); 603 604 XMapWindow(display, window); 605 606 gc = XCreateGC(display, window, 0, &gcvalues); 607 have_gc = TRUE; 608 609 /*--------------------------------------------------------------------------- 610 Fill window with the specified background color. 611 ---------------------------------------------------------------------------*/ 612 613 if (depth == 24 || depth == 32) { 614 bg_pixel = ((ulg)bg_red << RShift) | 615 ((ulg)bg_green << GShift) | 616 ((ulg)bg_blue << BShift); 617 } else if (depth == 16) { 618 bg_pixel = ((((ulg)bg_red << 8) >> RShift) & RMask) | 619 ((((ulg)bg_green << 8) >> GShift) & GMask) | 620 ((((ulg)bg_blue << 8) >> BShift) & BMask); 621 } else /* depth == 8 */ { 622 623 /* GRR: add 8-bit support */ 624 625 } 626 627 XSetForeground(display, gc, bg_pixel); 628 XFillRectangle(display, window, gc, 0, 0, image_width, image_height); 629 630 /*--------------------------------------------------------------------------- 631 Wait for first Expose event to do any drawing, then flush. 632 ---------------------------------------------------------------------------*/ 633 634 do 635 XNextEvent(display, &e); 636 while (e.type != Expose || e.xexpose.count); 637 638 XFlush(display); 639 640 /*--------------------------------------------------------------------------- 641 Allocate memory for the X- and display-specific version of the image. 642 ---------------------------------------------------------------------------*/ 643 644 if (depth == 24 || depth == 32) { 645 xdata = (uch *)malloc(4*image_width*image_height); 646 pad = 32; 647 } else if (depth == 16) { 648 xdata = (uch *)malloc(2*image_width*image_height); 649 pad = 16; 650 } else /* depth == 8 */ { 651 xdata = (uch *)malloc(image_width*image_height); 652 pad = 8; 653 } 654 655 if (!xdata) { 656 fprintf(stderr, PROGNAME ": unable to allocate image memory\n"); 657 return 4; 658 } 659 660 ximage = XCreateImage(display, visual, depth, ZPixmap, 0, 661 (char *)xdata, image_width, image_height, pad, 0); 662 663 if (!ximage) { 664 fprintf(stderr, PROGNAME ": XCreateImage() failed\n"); 665 free(xdata); 666 return 3; 667 } 668 669 /* to avoid testing the byte order every pixel (or doubling the size of 670 * the drawing routine with a giant if-test), we arbitrarily set the byte 671 * order to MSBFirst and let Xlib worry about inverting things on little- 672 * endian machines (like Linux/x86, old VAXen, etc.)--this is not the most 673 * efficient approach (the giant if-test would be better), but in the 674 * interest of clarity, we take the easy way out... */ 675 676 ximage->byte_order = MSBFirst; 677 678 return 0; 679 680 } /* end function rpng_x_create_window() */ 681 682 683 684 685 686 static int rpng_x_display_image(void) 687 { 688 uch *src; 689 char *dest; 690 uch r, g, b, a; 691 ulg i, row, lastrow = 0; 692 ulg pixel; 693 int ximage_rowbytes = ximage->bytes_per_line; 694 /* int bpp = ximage->bits_per_pixel; */ 695 696 697 Trace((stderr, "beginning display loop (image_channels == %d)\n", 698 image_channels)) 699 Trace((stderr, " (width = %ld, rowbytes = %ld, ximage_rowbytes = %d)\n", 700 image_width, image_rowbytes, ximage_rowbytes)) 701 Trace((stderr, " (bpp = %d)\n", ximage->bits_per_pixel)) 702 Trace((stderr, " (byte_order = %s)\n", ximage->byte_order == MSBFirst? 703 "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown"))) 704 705 if (depth == 24 || depth == 32) { 706 ulg red, green, blue; 707 708 for (lastrow = row = 0; row < image_height; ++row) { 709 src = image_data + row*image_rowbytes; 710 dest = ximage->data + row*ximage_rowbytes; 711 if (image_channels == 3) { 712 for (i = image_width; i > 0; --i) { 713 red = *src++; 714 green = *src++; 715 blue = *src++; 716 #ifdef NO_24BIT_MASKS 717 pixel = (red << RShift) | 718 (green << GShift) | 719 (blue << BShift); 720 /* recall that we set ximage->byte_order = MSBFirst above */ 721 /* GRR BUG: this assumes bpp == 32, but may be 24: */ 722 *dest++ = (char)((pixel >> 24) & 0xff); 723 *dest++ = (char)((pixel >> 16) & 0xff); 724 *dest++ = (char)((pixel >> 8) & 0xff); 725 *dest++ = (char)( pixel & 0xff); 726 #else 727 red = (RShift < 0)? red << (-RShift) : red >> RShift; 728 green = (GShift < 0)? green << (-GShift) : green >> GShift; 729 blue = (BShift < 0)? blue << (-BShift) : blue >> BShift; 730 pixel = (red & RMask) | (green & GMask) | (blue & BMask); 731 /* recall that we set ximage->byte_order = MSBFirst above */ 732 *dest++ = (char)((pixel >> 24) & 0xff); 733 *dest++ = (char)((pixel >> 16) & 0xff); 734 *dest++ = (char)((pixel >> 8) & 0xff); 735 *dest++ = (char)( pixel & 0xff); 736 #endif 737 } 738 } else /* if (image_channels == 4) */ { 739 for (i = image_width; i > 0; --i) { 740 r = *src++; 741 g = *src++; 742 b = *src++; 743 a = *src++; 744 if (a == 255) { 745 red = r; 746 green = g; 747 blue = b; 748 } else if (a == 0) { 749 red = bg_red; 750 green = bg_green; 751 blue = bg_blue; 752 } else { 753 /* this macro (from png.h) composites the foreground 754 * and background values and puts the result into the 755 * first argument */ 756 alpha_composite(red, r, a, bg_red); 757 alpha_composite(green, g, a, bg_green); 758 alpha_composite(blue, b, a, bg_blue); 759 } 760 pixel = (red << RShift) | 761 (green << GShift) | 762 (blue << BShift); 763 /* recall that we set ximage->byte_order = MSBFirst above */ 764 *dest++ = (char)((pixel >> 24) & 0xff); 765 *dest++ = (char)((pixel >> 16) & 0xff); 766 *dest++ = (char)((pixel >> 8) & 0xff); 767 *dest++ = (char)( pixel & 0xff); 768 } 769 } 770 /* display after every 16 lines */ 771 if (((row+1) & 0xf) == 0) { 772 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0, 773 (int)lastrow, image_width, 16); 774 XFlush(display); 775 lastrow = row + 1; 776 } 777 } 778 779 } else if (depth == 16) { 780 ush red, green, blue; 781 782 for (lastrow = row = 0; row < image_height; ++row) { 783 src = image_data + row*image_rowbytes; 784 dest = ximage->data + row*ximage_rowbytes; 785 if (image_channels == 3) { 786 for (i = image_width; i > 0; --i) { 787 red = ((ush)(*src) << 8); 788 ++src; 789 green = ((ush)(*src) << 8); 790 ++src; 791 blue = ((ush)(*src) << 8); 792 ++src; 793 pixel = ((red >> RShift) & RMask) | 794 ((green >> GShift) & GMask) | 795 ((blue >> BShift) & BMask); 796 /* recall that we set ximage->byte_order = MSBFirst above */ 797 *dest++ = (char)((pixel >> 8) & 0xff); 798 *dest++ = (char)( pixel & 0xff); 799 } 800 } else /* if (image_channels == 4) */ { 801 for (i = image_width; i > 0; --i) { 802 r = *src++; 803 g = *src++; 804 b = *src++; 805 a = *src++; 806 if (a == 255) { 807 red = ((ush)r << 8); 808 green = ((ush)g << 8); 809 blue = ((ush)b << 8); 810 } else if (a == 0) { 811 red = ((ush)bg_red << 8); 812 green = ((ush)bg_green << 8); 813 blue = ((ush)bg_blue << 8); 814 } else { 815 /* this macro (from png.h) composites the foreground 816 * and background values and puts the result back into 817 * the first argument (== fg byte here: safe) */ 818 alpha_composite(r, r, a, bg_red); 819 alpha_composite(g, g, a, bg_green); 820 alpha_composite(b, b, a, bg_blue); 821 red = ((ush)r << 8); 822 green = ((ush)g << 8); 823 blue = ((ush)b << 8); 824 } 825 pixel = ((red >> RShift) & RMask) | 826 ((green >> GShift) & GMask) | 827 ((blue >> BShift) & BMask); 828 /* recall that we set ximage->byte_order = MSBFirst above */ 829 *dest++ = (char)((pixel >> 8) & 0xff); 830 *dest++ = (char)( pixel & 0xff); 831 } 832 } 833 /* display after every 16 lines */ 834 if (((row+1) & 0xf) == 0) { 835 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0, 836 (int)lastrow, image_width, 16); 837 XFlush(display); 838 lastrow = row + 1; 839 } 840 } 841 842 } else /* depth == 8 */ { 843 844 /* GRR: add 8-bit support */ 845 846 } 847 848 Trace((stderr, "calling final XPutImage()\n")) 849 if (lastrow < image_height) { 850 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0, 851 (int)lastrow, image_width, image_height-lastrow); 852 XFlush(display); 853 } 854 855 return 0; 856 } 857 858 859 860 861 static void rpng_x_cleanup(void) 862 { 863 if (image_data) { 864 free(image_data); 865 image_data = NULL; 866 } 867 868 if (ximage) { 869 if (ximage->data) { 870 free(ximage->data); /* we allocated it, so we free it */ 871 ximage->data = (char *)NULL; /* instead of XDestroyImage() */ 872 } 873 XDestroyImage(ximage); 874 ximage = NULL; 875 } 876 877 if (have_gc) 878 XFreeGC(display, gc); 879 880 if (have_window) 881 XDestroyWindow(display, window); 882 883 if (have_colormap) 884 XFreeColormap(display, colormap); 885 886 if (have_nondefault_visual) 887 XFree(visual_list); 888 } 889 890 891 892 893 894 static int rpng_x_msb(ulg u32val) 895 { 896 int i; 897 898 for (i = 31; i >= 0; --i) { 899 if (u32val & 0x80000000L) 900 break; 901 u32val <<= 1; 902 } 903 return i; 904 } 905