1/* 2 * $Id: xtiff.c 276 2010-06-30 12:18:30Z nijtmans $ 3 * 4 * xtiff - view a TIFF file in an X window 5 * 6 * Dan Sears 7 * Chris Sears 8 * 9 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. 10 * 11 * All Rights Reserved 12 * 13 * Permission to use, copy, modify, and distribute this software and its 14 * documentation for any purpose and without fee is hereby granted, 15 * provided that the above copyright notice appear in all copies and that 16 * both that copyright notice and this permission notice appear in 17 * supporting documentation, and that the name of Digital not be 18 * used in advertising or publicity pertaining to distribution of the 19 * software without specific, written prior permission. 20 * 21 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 22 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 23 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 24 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 25 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 26 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 27 * SOFTWARE. 28 * 29 * Revision 1.0 90/05/07 30 * Initial release. 31 * Revision 2.0 90/12/20 32 * Converted to use the Athena Widgets and the Xt Intrinsics. 33 * 34 * Notes: 35 * 36 * According to the TIFF 5.0 Specification, it is possible to have 37 * both a TIFFTAG_COLORMAP and a TIFFTAG_COLORRESPONSECURVE. This 38 * doesn't make sense since a TIFFTAG_COLORMAP is 16 bits wide and 39 * a TIFFTAG_COLORRESPONSECURVE is tfBitsPerSample bits wide for each 40 * channel. This is probably a bug in the specification. 41 * In this case, TIFFTAG_COLORRESPONSECURVE is ignored. 42 * This might make sense if TIFFTAG_COLORMAP was 8 bits wide. 43 * 44 * TIFFTAG_COLORMAP is often incorrectly written as ranging from 45 * 0 to 255 rather than from 0 to 65535. CheckAndCorrectColormap() 46 * takes care of this. 47 * 48 * Only ORIENTATION_TOPLEFT is supported correctly. This is the 49 * default TIFF and X orientation. Other orientations will be 50 * displayed incorrectly. 51 * 52 * There is no support for or use of 3/3/2 DirectColor visuals. 53 * TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE are not supported. 54 * 55 * Only TIFFTAG_BITSPERSAMPLE values that are 1, 2, 4 or 8 are supported. 56 */ 57#include <math.h> 58#include <stdio.h> 59#include <stdlib.h> 60#include <tiffio.h> 61#include <X11/Xatom.h> 62#include <X11/Intrinsic.h> 63#include <X11/StringDefs.h> 64#include <X11/Xproto.h> 65#include <X11/Shell.h> 66#include <X11/Xaw/Form.h> 67#include <X11/Xaw/List.h> 68#include <X11/Xaw/Label.h> 69#include <X11/cursorfont.h> 70#define XK_MISCELLANY 71#include <X11/keysymdef.h> 72#include "xtifficon.h" 73 74#define TIFF_GAMMA "2.2" /* default gamma from the TIFF 5.0 spec */ 75#define ROUND(x) (uint16) ((x) + 0.5) 76#define SCALE(x, s) (((x) * 65535L) / (s)) 77#define MCHECK(m) if (!m) { fprintf(stderr, "malloc failed\n"); exit(0); } 78#define MIN(a, b) (((a) < (b)) ? (a) : (b)) 79#define MAX(a, b) (((a) > (b)) ? (a) : (b)) 80#define VIEWPORT_WIDTH 700 81#define VIEWPORT_HEIGHT 500 82#define KEY_TRANSLATE 20 83 84#ifdef __STDC__ 85#define PP(args) args 86#else 87#define PP(args) () 88#endif 89 90int main PP((int argc, char **argv)); 91void OpenTIFFFile PP((void)); 92void GetTIFFHeader PP((void)); 93void SetNameLabel PP((void)); 94void CheckAndCorrectColormap PP((void)); 95void SimpleGammaCorrection PP((void)); 96void GetVisual PP((void)); 97Boolean SearchVisualList PP((int image_depth, 98 int visual_class, Visual **visual)); 99void GetTIFFImage PP((void)); 100void CreateXImage PP((void)); 101XtCallbackProc SelectProc PP((Widget w, caddr_t unused_1, caddr_t unused_2)); 102void QuitProc PP((void)); 103void NextProc PP((void)); 104void PreviousProc PP((void)); 105void PageProc PP((int direction)); 106void EventProc PP((Widget widget, caddr_t unused, XEvent *event)); 107void ResizeProc PP((void)); 108int XTiffErrorHandler PP((Display *display, XErrorEvent *error_event)); 109void Usage PP((void)); 110 111int xtVersion = XtSpecificationRelease; /* xtiff depends on R4 or higher */ 112 113/* 114 * Xt data structures 115 */ 116Widget shellWidget, formWidget, listWidget, labelWidget, imageWidget; 117 118enum { ButtonQuit = 0, ButtonPreviousPage = 1, ButtonNextPage = 2 }; 119 120String buttonStrings[] = { "Quit", "Previous", "Next" }; 121 122static XrmOptionDescRec shellOptions[] = { 123 { "-help", "*help", XrmoptionNoArg, (caddr_t) "True" }, 124 { "-gamma", "*gamma", XrmoptionSepArg, NULL }, 125 { "-usePixmap", "*usePixmap", XrmoptionSepArg, NULL }, 126 { "-viewportWidth", "*viewportWidth", XrmoptionSepArg, NULL }, 127 { "-viewportHeight", "*viewportHeight", XrmoptionSepArg, NULL }, 128 { "-translate", "*translate", XrmoptionSepArg, NULL }, 129 { "-verbose", "*verbose", XrmoptionSepArg, NULL } 130}; 131 132typedef struct { 133 Boolean help; 134 float gamma; 135 Boolean usePixmap; 136 uint32 viewportWidth; 137 uint32 viewportHeight; 138 int translate; 139 Boolean verbose; 140} AppData, *AppDataPtr; 141 142AppData appData; 143 144XtResource clientResources[] = { 145 { 146 "help", XtCBoolean, XtRBoolean, sizeof(Boolean), 147 XtOffset(AppDataPtr, help), XtRImmediate, (XtPointer) False 148 }, { 149 "gamma", "Gamma", XtRFloat, sizeof(float), 150 XtOffset(AppDataPtr, gamma), XtRString, (XtPointer) TIFF_GAMMA 151 }, { 152 "usePixmap", "UsePixmap", XtRBoolean, sizeof(Boolean), 153 XtOffset(AppDataPtr, usePixmap), XtRImmediate, (XtPointer) True 154 }, { 155 "viewportWidth", "ViewportWidth", XtRInt, sizeof(int), 156 XtOffset(AppDataPtr, viewportWidth), XtRImmediate, 157 (XtPointer) VIEWPORT_WIDTH 158 }, { 159 "viewportHeight", "ViewportHeight", XtRInt, sizeof(int), 160 XtOffset(AppDataPtr, viewportHeight), XtRImmediate, 161 (XtPointer) VIEWPORT_HEIGHT 162 }, { 163 "translate", "Translate", XtRInt, sizeof(int), 164 XtOffset(AppDataPtr, translate), XtRImmediate, (XtPointer) KEY_TRANSLATE 165 }, { 166 "verbose", "Verbose", XtRBoolean, sizeof(Boolean), 167 XtOffset(AppDataPtr, verbose), XtRImmediate, (XtPointer) True 168 } 169}; 170 171Arg formArgs[] = { 172 { XtNresizable, True } 173}; 174 175Arg listArgs[] = { 176 { XtNresizable, False }, 177 { XtNborderWidth, 0 }, 178 { XtNdefaultColumns, 3 }, 179 { XtNforceColumns, True }, 180 { XtNlist, (int) buttonStrings }, 181 { XtNnumberStrings, XtNumber(buttonStrings) }, 182 { XtNtop, XtChainTop }, 183 { XtNleft, XtChainLeft }, 184 { XtNbottom, XtChainTop }, 185 { XtNright, XtChainLeft } 186}; 187 188Arg labelArgs[] = { 189 { XtNresizable, False }, 190 { XtNwidth, 200 }, 191 { XtNborderWidth, 0 }, 192 { XtNjustify, XtJustifyLeft }, 193 { XtNtop, XtChainTop }, 194 { XtNleft, XtChainLeft }, 195 { XtNbottom, XtChainTop }, 196 { XtNright, XtChainLeft } 197}; 198 199Arg imageArgs[] = { 200 { XtNresizable, True }, 201 { XtNborderWidth, 0 }, 202 { XtNtop, XtChainTop }, 203 { XtNleft, XtChainLeft }, 204 { XtNbottom, XtChainTop }, 205 { XtNright, XtChainLeft } 206}; 207 208XtActionsRec actionsTable[] = { 209 { "quit", QuitProc }, 210 { "next", NextProc }, 211 { "previous", PreviousProc }, 212 { "notifyresize", ResizeProc } 213}; 214 215char translationsTable[] = "<Key>q: quit() \n \ 216 <Key>Q: quit() \n \ 217 <Message>WM_PROTOCOLS: quit()\n \ 218 <Key>p: previous() \n \ 219 <Key>P: previous() \n \ 220 <Key>n: next() \n \ 221 <Key>N: next() \n \ 222 <Configure>: notifyresize()"; 223 224/* 225 * X data structures 226 */ 227Colormap xColormap; 228Display * xDisplay; 229Pixmap xImagePixmap; 230Visual * xVisual; 231XImage * xImage; 232GC xWinGc; 233int xImageDepth, xScreen, xRedMask, xGreenMask, xBlueMask, 234 xOffset = 0, yOffset = 0, grabX = -1, grabY = -1; 235unsigned char basePixel = 0; 236 237/* 238 * TIFF data structures 239 */ 240TIFF * tfFile = NULL; 241uint32 tfImageWidth, tfImageHeight; 242uint16 tfBitsPerSample, tfSamplesPerPixel, tfPlanarConfiguration, 243 tfPhotometricInterpretation, tfGrayResponseUnit, 244 tfImageDepth, tfBytesPerRow; 245int tfDirectory = 0, tfMultiPage = False; 246double tfUnitMap, tfGrayResponseUnitMap[] = { 247 -1, -10, -100, -1000, -10000, -100000 248 }; 249 250/* 251 * display data structures 252 */ 253double *dRed, *dGreen, *dBlue; 254 255/* 256 * shared data structures 257 */ 258uint16 * redMap = NULL, *greenMap = NULL, *blueMap = NULL, 259 *grayMap = NULL, colormapSize; 260char * imageMemory; 261char * fileName; 262 263int 264main(int argc, char **argv) 265{ 266 XSetWindowAttributes window_attributes; 267 Widget widget_list[3]; 268 Arg args[5]; 269 270 setbuf(stdout, NULL); setbuf(stderr, NULL); 271 272 shellWidget = XtInitialize(argv[0], "XTiff", shellOptions, 273 XtNumber(shellOptions), &argc, argv); 274 275 XSetErrorHandler(XTiffErrorHandler); 276 277 XtGetApplicationResources(shellWidget, &appData, 278 (XtResourceList) clientResources, (Cardinal) XtNumber(clientResources), 279 (ArgList) NULL, (Cardinal) 0); 280 281 if ((argc <= 1) || (argc > 2) || appData.help) 282 Usage(); 283 284 if (appData.verbose == False) { 285 TIFFSetErrorHandler(0); 286 TIFFSetWarningHandler(0); 287 } 288 289 fileName = argv[1]; 290 291 xDisplay = XtDisplay(shellWidget); 292 xScreen = DefaultScreen(xDisplay); 293 294 OpenTIFFFile(); 295 GetTIFFHeader(); 296 SimpleGammaCorrection(); 297 GetVisual(); 298 GetTIFFImage(); 299 300 /* 301 * Send visual, colormap, depth and iconPixmap to shellWidget. 302 * Sending the visual to the shell is only possible with the advent of R4. 303 */ 304 XtSetArg(args[0], XtNvisual, xVisual); 305 XtSetArg(args[1], XtNcolormap, xColormap); 306 XtSetArg(args[2], XtNdepth, 307 xImageDepth == 1 ? DefaultDepth(xDisplay, xScreen) : xImageDepth); 308 XtSetArg(args[3], XtNiconPixmap, 309 XCreateBitmapFromData(xDisplay, RootWindow(xDisplay, xScreen), 310 xtifficon_bits, xtifficon_width, xtifficon_height)); 311 XtSetArg(args[4], XtNallowShellResize, True); 312 XtSetValues(shellWidget, args, 5); 313 314 /* 315 * widget instance hierarchy 316 */ 317 formWidget = XtCreateManagedWidget("form", formWidgetClass, 318 shellWidget, formArgs, XtNumber(formArgs)); 319 320 widget_list[0] = listWidget = XtCreateWidget("list", 321 listWidgetClass, formWidget, listArgs, XtNumber(listArgs)); 322 323 widget_list[1] = labelWidget = XtCreateWidget("label", 324 labelWidgetClass, formWidget, labelArgs, XtNumber(labelArgs)); 325 326 widget_list[2] = imageWidget = XtCreateWidget("image", 327 widgetClass, formWidget, imageArgs, XtNumber(imageArgs)); 328 329 XtManageChildren(widget_list, XtNumber(widget_list)); 330 331 /* 332 * initial widget sizes - for small images let xtiff size itself 333 */ 334 if (tfImageWidth >= appData.viewportWidth) { 335 XtSetArg(args[0], XtNwidth, appData.viewportWidth); 336 XtSetValues(shellWidget, args, 1); 337 } 338 if (tfImageHeight >= appData.viewportHeight) { 339 XtSetArg(args[0], XtNheight, appData.viewportHeight); 340 XtSetValues(shellWidget, args, 1); 341 } 342 343 XtSetArg(args[0], XtNwidth, tfImageWidth); 344 XtSetArg(args[1], XtNheight, tfImageHeight); 345 XtSetValues(imageWidget, args, 2); 346 347 /* 348 * formWidget uses these constraints but they are stored in the children. 349 */ 350 XtSetArg(args[0], XtNfromVert, listWidget); 351 XtSetValues(imageWidget, args, 1); 352 XtSetArg(args[0], XtNfromHoriz, listWidget); 353 XtSetValues(labelWidget, args, 1); 354 355 SetNameLabel(); 356 357 XtAddCallback(listWidget, XtNcallback, (XtCallbackProc) SelectProc, 358 (XtPointer) NULL); 359 360 XtAddActions(actionsTable, XtNumber(actionsTable)); 361 XtSetArg(args[0], XtNtranslations, 362 XtParseTranslationTable(translationsTable)); 363 XtSetValues(formWidget, &args[0], 1); 364 XtSetValues(imageWidget, &args[0], 1); 365 366 /* 367 * This is intended to be a little faster than going through 368 * the translation manager. 369 */ 370 XtAddEventHandler(imageWidget, ExposureMask | ButtonPressMask 371 | ButtonReleaseMask | Button1MotionMask | KeyPressMask, 372 False, EventProc, NULL); 373 374 XtRealizeWidget(shellWidget); 375 376 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_fleur); 377 XChangeWindowAttributes(xDisplay, XtWindow(imageWidget), 378 CWCursor, &window_attributes); 379 380 CreateXImage(); 381 382 XtMainLoop(); 383 384 return 0; 385} 386 387void 388OpenTIFFFile() 389{ 390 if (tfFile != NULL) 391 TIFFClose(tfFile); 392 393 if ((tfFile = TIFFOpen(fileName, "r")) == NULL) { 394 fprintf(appData.verbose ? stderr : stdout, 395 "xtiff: can't open %s as a TIFF file\n", fileName); 396 exit(0); 397 } 398 399 tfMultiPage = (TIFFLastDirectory(tfFile) ? False : True); 400} 401 402void 403GetTIFFHeader() 404{ 405 register int i; 406 407 if (!TIFFSetDirectory(tfFile, tfDirectory)) { 408 fprintf(stderr, "xtiff: can't seek to directory %d in %s\n", 409 tfDirectory, fileName); 410 exit(0); 411 } 412 413 TIFFGetField(tfFile, TIFFTAG_IMAGEWIDTH, &tfImageWidth); 414 TIFFGetField(tfFile, TIFFTAG_IMAGELENGTH, &tfImageHeight); 415 416 /* 417 * If the following tags aren't present then use the TIFF defaults. 418 */ 419 TIFFGetFieldDefaulted(tfFile, TIFFTAG_BITSPERSAMPLE, &tfBitsPerSample); 420 TIFFGetFieldDefaulted(tfFile, TIFFTAG_SAMPLESPERPIXEL, &tfSamplesPerPixel); 421 TIFFGetFieldDefaulted(tfFile, TIFFTAG_PLANARCONFIG, &tfPlanarConfiguration); 422 TIFFGetFieldDefaulted(tfFile, TIFFTAG_GRAYRESPONSEUNIT, &tfGrayResponseUnit); 423 424 tfUnitMap = tfGrayResponseUnitMap[tfGrayResponseUnit]; 425 colormapSize = 1 << tfBitsPerSample; 426 tfImageDepth = tfBitsPerSample * tfSamplesPerPixel; 427 428 dRed = (double *) malloc(colormapSize * sizeof(double)); 429 dGreen = (double *) malloc(colormapSize * sizeof(double)); 430 dBlue = (double *) malloc(colormapSize * sizeof(double)); 431 MCHECK(dRed); MCHECK(dGreen); MCHECK(dBlue); 432 433 /* 434 * If TIFFTAG_PHOTOMETRIC is not present then assign a reasonable default. 435 * The TIFF 5.0 specification doesn't give a default. 436 */ 437 if (!TIFFGetField(tfFile, TIFFTAG_PHOTOMETRIC, 438 &tfPhotometricInterpretation)) { 439 if (tfSamplesPerPixel != 1) 440 tfPhotometricInterpretation = PHOTOMETRIC_RGB; 441 else if (tfBitsPerSample == 1) 442 tfPhotometricInterpretation = PHOTOMETRIC_MINISBLACK; 443 else if (TIFFGetField(tfFile, TIFFTAG_COLORMAP, 444 &redMap, &greenMap, &blueMap)) { 445 tfPhotometricInterpretation = PHOTOMETRIC_PALETTE; 446 redMap = greenMap = blueMap = NULL; 447 } else 448 tfPhotometricInterpretation = PHOTOMETRIC_MINISBLACK; 449 } 450 451 /* 452 * Given TIFFTAG_PHOTOMETRIC extract or create the response curves. 453 */ 454 switch (tfPhotometricInterpretation) { 455 case PHOTOMETRIC_RGB: 456 redMap = (uint16 *) malloc(colormapSize * sizeof(uint16)); 457 greenMap = (uint16 *) malloc(colormapSize * sizeof(uint16)); 458 blueMap = (uint16 *) malloc(colormapSize * sizeof(uint16)); 459 MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap); 460 for (i = 0; i < colormapSize; i++) 461 dRed[i] = dGreen[i] = dBlue[i] 462 = (double) SCALE(i, colormapSize - 1); 463 break; 464 case PHOTOMETRIC_PALETTE: 465 if (!TIFFGetField(tfFile, TIFFTAG_COLORMAP, 466 &redMap, &greenMap, &blueMap)) { 467 redMap = (uint16 *) malloc(colormapSize * sizeof(uint16)); 468 greenMap = (uint16 *) malloc(colormapSize * sizeof(uint16)); 469 blueMap = (uint16 *) malloc(colormapSize * sizeof(uint16)); 470 MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap); 471 for (i = 0; i < colormapSize; i++) 472 dRed[i] = dGreen[i] = dBlue[i] 473 = (double) SCALE(i, colormapSize - 1); 474 } else { 475 CheckAndCorrectColormap(); 476 for (i = 0; i < colormapSize; i++) { 477 dRed[i] = (double) redMap[i]; 478 dGreen[i] = (double) greenMap[i]; 479 dBlue[i] = (double) blueMap[i]; 480 } 481 } 482 break; 483 case PHOTOMETRIC_MINISWHITE: 484 redMap = (uint16 *) malloc(colormapSize * sizeof(uint16)); 485 greenMap = (uint16 *) malloc(colormapSize * sizeof(uint16)); 486 blueMap = (uint16 *) malloc(colormapSize * sizeof(uint16)); 487 MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap); 488 for (i = 0; i < colormapSize; i++) 489 dRed[i] = dGreen[i] = dBlue[i] = (double) 490 SCALE(colormapSize-1-i, colormapSize-1); 491 break; 492 case PHOTOMETRIC_MINISBLACK: 493 redMap = (uint16 *) malloc(colormapSize * sizeof(uint16)); 494 greenMap = (uint16 *) malloc(colormapSize * sizeof(uint16)); 495 blueMap = (uint16 *) malloc(colormapSize * sizeof(uint16)); 496 MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap); 497 for (i = 0; i < colormapSize; i++) 498 dRed[i] = dGreen[i] = dBlue[i] = (double) SCALE(i, colormapSize-1); 499 break; 500 default: 501 fprintf(stderr, 502 "xtiff: can't display photometric interpretation type %d\n", 503 tfPhotometricInterpretation); 504 exit(0); 505 } 506} 507 508void 509SetNameLabel() 510{ 511 char buffer[BUFSIZ]; 512 Arg args[1]; 513 514 if (tfMultiPage) 515 sprintf(buffer, "%s - page %d", fileName, tfDirectory); 516 else 517 strcpy(buffer, fileName); 518 XtSetArg(args[0], XtNlabel, buffer); 519 XtSetValues(labelWidget, args, 1); 520} 521 522/* 523 * Many programs get TIFF colormaps wrong. They use 8-bit colormaps instead of 524 * 16-bit colormaps. This function is a heuristic to detect and correct this. 525 */ 526void 527CheckAndCorrectColormap() 528{ 529 register int i; 530 531 for (i = 0; i < colormapSize; i++) 532 if ((redMap[i] > 255) || (greenMap[i] > 255) || (blueMap[i] > 255)) 533 return; 534 535 for (i = 0; i < colormapSize; i++) { 536 redMap[i] = SCALE(redMap[i], 255); 537 greenMap[i] = SCALE(greenMap[i], 255); 538 blueMap[i] = SCALE(blueMap[i], 255); 539 } 540 TIFFWarning(fileName, "Assuming 8-bit colormap"); 541} 542 543void 544SimpleGammaCorrection() 545{ 546 register int i; 547 register double i_gamma = 1.0 / appData.gamma; 548 549 for (i = 0; i < colormapSize; i++) { 550 if (((tfPhotometricInterpretation == PHOTOMETRIC_MINISWHITE) 551 && (i == colormapSize - 1)) 552 || ((tfPhotometricInterpretation == PHOTOMETRIC_MINISBLACK) 553 && (i == 0))) 554 redMap[i] = greenMap[i] = blueMap[i] = 0; 555 else { 556 redMap[i] = ROUND((pow(dRed[i] / 65535.0, i_gamma) * 65535.0)); 557 greenMap[i] = ROUND((pow(dGreen[i] / 65535.0, i_gamma) * 65535.0)); 558 blueMap[i] = ROUND((pow(dBlue[i] / 65535.0, i_gamma) * 65535.0)); 559 } 560 } 561 562 free(dRed); free(dGreen); free(dBlue); 563} 564 565static char* classNames[] = { 566 "StaticGray", 567 "GrayScale", 568 "StaticColor", 569 "PseudoColor", 570 "TrueColor", 571 "DirectColor" 572}; 573 574/* 575 * Current limitation: the visual is set initially by the first file. 576 * It cannot be changed. 577 */ 578void 579GetVisual() 580{ 581 XColor *colors = NULL; 582 unsigned long *pixels = NULL; 583 unsigned long i; 584 585 switch (tfImageDepth) { 586 /* 587 * X really wants a 32-bit image with the fourth channel unused, 588 * but the visual structure thinks it's 24-bit. bitmap_unit is 32. 589 */ 590 case 32: 591 case 24: 592 if (SearchVisualList(24, DirectColor, &xVisual) == False) { 593 fprintf(stderr, "xtiff: 24-bit DirectColor visual not available\n"); 594 exit(0); 595 } 596 597 colors = (XColor *) malloc(3 * colormapSize * sizeof(XColor)); 598 MCHECK(colors); 599 600 for (i = 0; i < colormapSize; i++) { 601 colors[i].pixel = (i << 16) + (i << 8) + i; 602 colors[i].red = redMap[i]; 603 colors[i].green = greenMap[i]; 604 colors[i].blue = blueMap[i]; 605 colors[i].flags = DoRed | DoGreen | DoBlue; 606 } 607 608 xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen), 609 xVisual, AllocAll); 610 XStoreColors(xDisplay, xColormap, colors, colormapSize); 611 break; 612 case 8: 613 case 4: 614 case 2: 615 /* 616 * We assume that systems with 24-bit visuals also have 8-bit visuals. 617 * We don't promote from 8-bit PseudoColor to 24/32 bit DirectColor. 618 */ 619 switch (tfPhotometricInterpretation) { 620 case PHOTOMETRIC_MINISWHITE: 621 case PHOTOMETRIC_MINISBLACK: 622 if (SearchVisualList((int) tfImageDepth, GrayScale, &xVisual) == True) 623 break; 624 case PHOTOMETRIC_PALETTE: 625 if (SearchVisualList((int) tfImageDepth, PseudoColor, &xVisual) == True) 626 break; 627 default: 628 fprintf(stderr, "xtiff: Unsupported TIFF/X configuration\n"); 629 exit(0); 630 } 631 632 colors = (XColor *) malloc(colormapSize * sizeof(XColor)); 633 MCHECK(colors); 634 635 for (i = 0; i < colormapSize; i++) { 636 colors[i].pixel = i; 637 colors[i].red = redMap[i]; 638 colors[i].green = greenMap[i]; 639 colors[i].blue = blueMap[i]; 640 colors[i].flags = DoRed | DoGreen | DoBlue; 641 } 642 643 /* 644 * xtiff's colormap allocation is private. It does not attempt 645 * to detect whether any existing colormap entries are suitable 646 * for its use. This will cause colormap flashing. Furthermore, 647 * background and foreground are taken from the environment. 648 * For example, the foreground color may be red when the visual 649 * is GrayScale. If the colormap is completely populated, 650 * Xt will not be able to allocate fg and bg. 651 */ 652 if (tfImageDepth == 8) 653 xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen), 654 xVisual, AllocAll); 655 else { 656 xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen), 657 xVisual, AllocNone); 658 pixels = (unsigned long *) 659 malloc(colormapSize * sizeof(unsigned long)); 660 MCHECK(pixels); 661 (void) XAllocColorCells(xDisplay, xColormap, True, 662 NULL, 0, pixels, colormapSize); 663 basePixel = (unsigned char) pixels[0]; 664 free(pixels); 665 } 666 XStoreColors(xDisplay, xColormap, colors, colormapSize); 667 break; 668 case 1: 669 xImageDepth = 1; 670 xVisual = DefaultVisual(xDisplay, xScreen); 671 xColormap = DefaultColormap(xDisplay, xScreen); 672 break; 673 default: 674 fprintf(stderr, "xtiff: unsupported image depth %d\n", tfImageDepth); 675 exit(0); 676 } 677 678 if (appData.verbose == True) 679 fprintf(stderr, "%s: Using %d-bit %s visual.\n", 680 fileName, xImageDepth, classNames[xVisual->class]); 681 682 if (colors != NULL) 683 free(colors); 684 if (grayMap != NULL) 685 free(grayMap); 686 if (redMap != NULL) 687 free(redMap); 688 if (greenMap != NULL) 689 free(greenMap); 690 if (blueMap != NULL) 691 free(blueMap); 692 693 colors = NULL; grayMap = redMap = greenMap = blueMap = NULL; 694} 695 696/* 697 * Search for an appropriate visual. Promote where necessary. 698 * Check to make sure that ENOUGH colormap entries are writeable. 699 * basePixel was determined when XAllocColorCells() contiguously 700 * allocated enough entries. basePixel is used below in GetTIFFImage. 701 */ 702Boolean 703SearchVisualList(image_depth, visual_class, visual) 704 int image_depth, visual_class; 705 Visual **visual; 706{ 707 XVisualInfo template_visual, *visual_list, *vl; 708 int i, n_visuals; 709 710 template_visual.screen = xScreen; 711 vl = visual_list = XGetVisualInfo(xDisplay, VisualScreenMask, 712 &template_visual, &n_visuals); 713 714 if (n_visuals == 0) { 715 fprintf(stderr, "xtiff: visual list not available\n"); 716 exit(0); 717 } 718 719 for (i = 0; i < n_visuals; vl++, i++) { 720 if ((vl->class == visual_class) && (vl->depth >= image_depth) 721 && (vl->visual->map_entries >= (1 << vl->depth))) { 722 *visual = vl->visual; 723 xImageDepth = vl->depth; 724 xRedMask = vl->red_mask; 725 xGreenMask = vl->green_mask; 726 xBlueMask = vl->blue_mask; 727 XFree((char *) visual_list); 728 return True; 729 } 730 } 731 732 XFree((char *) visual_list); 733 return False; 734} 735 736void 737GetTIFFImage() 738{ 739 int pixel_map[3], red_shift, green_shift, blue_shift; 740 char *scan_line, *output_p, *input_p; 741 uint32 i, j; 742 uint16 s; 743 744 scan_line = (char *) malloc(tfBytesPerRow = TIFFScanlineSize(tfFile)); 745 MCHECK(scan_line); 746 747 if ((tfImageDepth == 32) || (tfImageDepth == 24)) { 748 output_p = imageMemory = (char *) 749 malloc(tfImageWidth * tfImageHeight * 4); 750 MCHECK(imageMemory); 751 752 /* 753 * Handle different color masks for different frame buffers. 754 */ 755 if (ImageByteOrder(xDisplay) == LSBFirst) { /* DECstation 5000 */ 756 red_shift = pixel_map[0] = xRedMask == 0xFF000000 ? 3 757 : (xRedMask == 0xFF0000 ? 2 : (xRedMask == 0xFF00 ? 1 : 0)); 758 green_shift = pixel_map[1] = xGreenMask == 0xFF000000 ? 3 759 : (xGreenMask == 0xFF0000 ? 2 : (xGreenMask == 0xFF00 ? 1 : 0)); 760 blue_shift = pixel_map[2] = xBlueMask == 0xFF000000 ? 3 761 : (xBlueMask == 0xFF0000 ? 2 : (xBlueMask == 0xFF00 ? 1 : 0)); 762 } else { /* Ardent */ 763 red_shift = pixel_map[0] = xRedMask == 0xFF000000 ? 0 764 : (xRedMask == 0xFF0000 ? 1 : (xRedMask == 0xFF00 ? 2 : 3)); 765 green_shift = pixel_map[0] = xGreenMask == 0xFF000000 ? 0 766 : (xGreenMask == 0xFF0000 ? 1 : (xGreenMask == 0xFF00 ? 2 : 3)); 767 blue_shift = pixel_map[0] = xBlueMask == 0xFF000000 ? 0 768 : (xBlueMask == 0xFF0000 ? 1 : (xBlueMask == 0xFF00 ? 2 : 3)); 769 } 770 771 if (tfPlanarConfiguration == PLANARCONFIG_CONTIG) { 772 for (i = 0; i < tfImageHeight; i++) { 773 if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0) 774 break; 775 for (input_p = scan_line, j = 0; j < tfImageWidth; j++) { 776 *(output_p + red_shift) = *input_p++; 777 *(output_p + green_shift) = *input_p++; 778 *(output_p + blue_shift) = *input_p++; 779 output_p += 4; 780 if (tfSamplesPerPixel == 4) /* skip the fourth channel */ 781 input_p++; 782 } 783 } 784 } else { 785 for (s = 0; s < tfSamplesPerPixel; s++) { 786 if (s == 3) /* skip the fourth channel */ 787 continue; 788 for (i = 0; i < tfImageHeight; i++) { 789 if (TIFFReadScanline(tfFile, scan_line, i, s) < 0) 790 break; 791 input_p = scan_line; 792 output_p = imageMemory + (i*tfImageWidth*4) + pixel_map[s]; 793 for (j = 0; j < tfImageWidth; j++, output_p += 4) 794 *output_p = *input_p++; 795 } 796 } 797 } 798 } else { 799 if (xImageDepth == tfImageDepth) { 800 output_p = imageMemory = (char *) 801 malloc(tfBytesPerRow * tfImageHeight); 802 MCHECK(imageMemory); 803 804 for (i = 0; i < tfImageHeight; i++, output_p += tfBytesPerRow) 805 if (TIFFReadScanline(tfFile, output_p, i, 0) < 0) 806 break; 807 } else if ((xImageDepth == 8) && (tfImageDepth == 4)) { 808 output_p = imageMemory = (char *) 809 malloc(tfBytesPerRow * 2 * tfImageHeight + 2); 810 MCHECK(imageMemory); 811 812 /* 813 * If a scanline is of odd size the inner loop below will overshoot. 814 * This is handled very simply by recalculating the start point at 815 * each scanline and padding imageMemory a little at the end. 816 */ 817 for (i = 0; i < tfImageHeight; i++) { 818 if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0) 819 break; 820 output_p = &imageMemory[i * tfImageWidth]; 821 input_p = scan_line; 822 for (j = 0; j < tfImageWidth; j += 2, input_p++) { 823 *output_p++ = (*input_p >> 4) + basePixel; 824 *output_p++ = (*input_p & 0xf) + basePixel; 825 } 826 } 827 } else if ((xImageDepth == 8) && (tfImageDepth == 2)) { 828 output_p = imageMemory = (char *) 829 malloc(tfBytesPerRow * 4 * tfImageHeight + 4); 830 MCHECK(imageMemory); 831 832 for (i = 0; i < tfImageHeight; i++) { 833 if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0) 834 break; 835 output_p = &imageMemory[i * tfImageWidth]; 836 input_p = scan_line; 837 for (j = 0; j < tfImageWidth; j += 4, input_p++) { 838 *output_p++ = (*input_p >> 6) + basePixel; 839 *output_p++ = ((*input_p >> 4) & 3) + basePixel; 840 *output_p++ = ((*input_p >> 2) & 3) + basePixel; 841 *output_p++ = (*input_p & 3) + basePixel; 842 } 843 } 844 } else if ((xImageDepth == 4) && (tfImageDepth == 2)) { 845 output_p = imageMemory = (char *) 846 malloc(tfBytesPerRow * 2 * tfImageHeight + 2); 847 MCHECK(imageMemory); 848 849 for (i = 0; i < tfImageHeight; i++) { 850 if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0) 851 break; 852 output_p = &imageMemory[i * tfBytesPerRow * 2]; 853 input_p = scan_line; 854 for (j = 0; j < tfImageWidth; j += 4, input_p++) { 855 *output_p++ = (((*input_p>>6) << 4) 856 | ((*input_p >> 4) & 3)) + basePixel; 857 *output_p++ = ((((*input_p>>2) & 3) << 4) 858 | (*input_p & 3)) + basePixel; 859 } 860 } 861 } else { 862 fprintf(stderr, 863 "xtiff: can't handle %d-bit TIFF file on an %d-bit display\n", 864 tfImageDepth, xImageDepth); 865 exit(0); 866 } 867 } 868 869 free(scan_line); 870} 871 872void 873CreateXImage() 874{ 875 XGCValues gc_values; 876 GC bitmap_gc; 877 878 xOffset = yOffset = 0; 879 grabX = grabY = -1; 880 881 xImage = XCreateImage(xDisplay, xVisual, xImageDepth, 882 xImageDepth == 1 ? XYBitmap : ZPixmap, /* offset */ 0, 883 (char *) imageMemory, tfImageWidth, tfImageHeight, 884 /* bitmap_pad */ 8, /* bytes_per_line */ 0); 885 886 /* 887 * libtiff converts LSB data into MSB but doesn't change the FillOrder tag. 888 */ 889 if (xImageDepth == 1) 890 xImage->bitmap_bit_order = MSBFirst; 891 if (xImageDepth <= 8) 892 xImage->byte_order = MSBFirst; 893 894 /* 895 * create an appropriate GC 896 */ 897 gc_values.function = GXcopy; 898 gc_values.plane_mask = AllPlanes; 899 if (tfPhotometricInterpretation == PHOTOMETRIC_MINISBLACK) { 900 gc_values.foreground = XWhitePixel(xDisplay, xScreen); 901 gc_values.background = XBlackPixel(xDisplay, xScreen); 902 } else { 903 gc_values.foreground = XBlackPixel(xDisplay, xScreen); 904 gc_values.background = XWhitePixel(xDisplay, xScreen); 905 } 906 xWinGc = XCreateGC(xDisplay, XtWindow(shellWidget), 907 GCFunction | GCPlaneMask | GCForeground | GCBackground, &gc_values); 908 909 /* 910 * create the pixmap and load the image 911 */ 912 if (appData.usePixmap == True) { 913 xImagePixmap = XCreatePixmap(xDisplay, RootWindow(xDisplay, xScreen), 914 xImage->width, xImage->height, xImageDepth); 915 916 /* 917 * According to the O'Reilly X Protocol Reference Manual, page 53, 918 * "A pixmap depth of one is always supported and listed, but windows 919 * of depth one might not be supported." Therefore we create a pixmap 920 * of depth one and use XCopyPlane(). This is idiomatic. 921 */ 922 if (xImageDepth == 1) { /* just pass the bits through */ 923 gc_values.foreground = 1; /* foreground describes set bits */ 924 gc_values.background = 0; /* background describes clear bits */ 925 bitmap_gc = XCreateGC(xDisplay, xImagePixmap, 926 GCForeground | GCBackground, &gc_values); 927 XPutImage(xDisplay, xImagePixmap, bitmap_gc, xImage, 928 0, 0, 0, 0, xImage->width, xImage->height); 929 } else 930 XPutImage(xDisplay, xImagePixmap, xWinGc, xImage, 931 0, 0, 0, 0, xImage->width, xImage->height); 932 XDestroyImage(xImage); 933 free(imageMemory); 934 } 935} 936 937XtCallbackProc 938SelectProc(w, unused_1, unused_2) 939 Widget w; 940 caddr_t unused_1; 941 caddr_t unused_2; 942{ 943 XawListReturnStruct *list_return; 944 945 list_return = XawListShowCurrent(w); 946 947 switch (list_return->list_index) { 948 case ButtonQuit: 949 QuitProc(); 950 break; 951 case ButtonPreviousPage: 952 PreviousProc(); 953 break; 954 case ButtonNextPage: 955 NextProc(); 956 break; 957 default: 958 fprintf(stderr, "error in SelectProc\n"); 959 exit(0); 960 } 961 XawListUnhighlight(w); 962} 963 964void 965QuitProc(void) 966{ 967 exit(0); 968} 969 970void 971NextProc() 972{ 973 PageProc(ButtonNextPage); 974} 975 976void 977PreviousProc() 978{ 979 PageProc(ButtonPreviousPage); 980} 981 982void 983PageProc(direction) 984 int direction; 985{ 986 XEvent fake_event; 987 Arg args[4]; 988 989 switch (direction) { 990 case ButtonPreviousPage: 991 if (tfDirectory > 0) 992 TIFFSetDirectory(tfFile, --tfDirectory); 993 else 994 return; 995 break; 996 case ButtonNextPage: 997 if (TIFFReadDirectory(tfFile) == True) 998 tfDirectory++; 999 else 1000 return; 1001 break; 1002 default: 1003 fprintf(stderr, "error in PageProc\n"); 1004 exit(0); 1005 } 1006 1007 xOffset = yOffset = 0; 1008 grabX = grabY = -1; 1009 1010 GetTIFFHeader(); 1011 SetNameLabel(); 1012 GetTIFFImage(); 1013 1014 if (appData.usePixmap == True) 1015 XFreePixmap(xDisplay, xImagePixmap); 1016 else 1017 XDestroyImage(xImage); 1018 1019 CreateXImage(); 1020 1021 /* 1022 * Using XtSetValues() to set the widget size causes a resize. 1023 * This resize gets propagated up to the parent shell. 1024 * In order to disable this visually disconcerting effect, 1025 * shell resizing is temporarily disabled. 1026 */ 1027 XtSetArg(args[0], XtNallowShellResize, False); 1028 XtSetValues(shellWidget, args, 1); 1029 1030 XtSetArg(args[0], XtNwidth, tfImageWidth); 1031 XtSetArg(args[1], XtNheight, tfImageHeight); 1032 XtSetValues(imageWidget, args, 2); 1033 1034 XtSetArg(args[0], XtNallowShellResize, True); 1035 XtSetValues(shellWidget, args, 1); 1036 1037 XClearWindow(xDisplay, XtWindow(imageWidget)); 1038 1039 fake_event.type = Expose; 1040 fake_event.xexpose.x = fake_event.xexpose.y = 0; 1041 fake_event.xexpose.width = tfImageWidth; /* the window will clip */ 1042 fake_event.xexpose.height = tfImageHeight; 1043 EventProc(imageWidget, NULL, &fake_event); 1044} 1045 1046void 1047EventProc(widget, unused, event) 1048 Widget widget; 1049 caddr_t unused; 1050 XEvent *event; 1051{ 1052 int ih, iw, ww, wh, sx, sy, w, h, dx, dy; 1053 Dimension w_width, w_height; 1054 XEvent next_event; 1055 Arg args[2]; 1056 1057 if (event->type == MappingNotify) { 1058 XRefreshKeyboardMapping((XMappingEvent *) event); 1059 return; 1060 } 1061 1062 if (!XtIsRealized(widget)) 1063 return; 1064 1065 if ((event->type == ButtonPress) || (event->type == ButtonRelease)) 1066 if (event->xbutton.button != Button1) 1067 return; 1068 1069 iw = tfImageWidth; /* avoid sign problems */ 1070 ih = tfImageHeight; 1071 1072 /* 1073 * The grabX and grabY variables record where the user grabbed the image. 1074 * They also record whether the mouse button is down or not. 1075 */ 1076 if (event->type == ButtonPress) { 1077 grabX = event->xbutton.x; 1078 grabY = event->xbutton.y; 1079 return; 1080 } 1081 1082 /* 1083 * imageWidget is a Core widget and doesn't get resized. 1084 * So we calculate the size of its viewport here. 1085 */ 1086 XtSetArg(args[0], XtNwidth, &w_width); 1087 XtSetArg(args[1], XtNheight, &w_height); 1088 XtGetValues(shellWidget, args, 2); 1089 ww = w_width; 1090 wh = w_height; 1091 XtGetValues(listWidget, args, 2); 1092 wh -= w_height; 1093 1094 switch (event->type) { 1095 case Expose: 1096 dx = event->xexpose.x; 1097 dy = event->xexpose.y; 1098 sx = dx + xOffset; 1099 sy = dy + yOffset; 1100 w = MIN(event->xexpose.width, iw); 1101 h = MIN(event->xexpose.height, ih); 1102 break; 1103 case KeyPress: 1104 if ((grabX >= 0) || (grabY >= 0)) /* Mouse button is still down */ 1105 return; 1106 switch (XLookupKeysym((XKeyEvent *) event, /* KeySyms index */ 0)) { 1107 case XK_Up: 1108 if (ih < wh) /* Don't scroll if the window fits the image. */ 1109 return; 1110 sy = yOffset + appData.translate; 1111 sy = MIN(ih - wh, sy); 1112 if (sy == yOffset) /* Filter redundant stationary refreshes. */ 1113 return; 1114 yOffset = sy; 1115 sx = xOffset; 1116 dx = dy = 0; 1117 w = ww; h = wh; 1118 break; 1119 case XK_Down: 1120 if (ih < wh) 1121 return; 1122 sy = yOffset - appData.translate; 1123 sy = MAX(sy, 0); 1124 if (sy == yOffset) 1125 return; 1126 yOffset = sy; 1127 sx = xOffset; 1128 dx = dy = 0; 1129 w = ww; h = wh; 1130 break; 1131 case XK_Left: 1132 if (iw < ww) 1133 return; 1134 sx = xOffset + appData.translate; 1135 sx = MIN(iw - ww, sx); 1136 if (sx == xOffset) 1137 return; 1138 xOffset = sx; 1139 sy = yOffset; 1140 dx = dy = 0; 1141 w = ww; h = wh; 1142 break; 1143 case XK_Right: 1144 if (iw < ww) 1145 return; 1146 sx = xOffset - appData.translate; 1147 sx = MAX(sx, 0); 1148 if (sx == xOffset) 1149 return; 1150 xOffset = sx; 1151 sy = yOffset; 1152 dx = dy = 0; 1153 w = ww; h = wh; 1154 break; 1155 default: 1156 return; 1157 } 1158 break; 1159 case MotionNotify: 1160 /* 1161 * MotionEvent compression. Ignore multiple motion events. 1162 * Ignore motion events if the mouse button is up. 1163 */ 1164 if (XPending(xDisplay)) /* Xlib doesn't flush the output buffer */ 1165 if (XtPeekEvent(&next_event)) 1166 if (next_event.type == MotionNotify) 1167 return; 1168 if ((grabX < 0) || (grabY < 0)) 1169 return; 1170 sx = xOffset + grabX - (int) event->xmotion.x; 1171 if (sx >= (iw - ww)) /* clamp x motion but allow y motion */ 1172 sx = iw - ww; 1173 sx = MAX(sx, 0); 1174 sy = yOffset + grabY - (int) event->xmotion.y; 1175 if (sy >= (ih - wh)) /* clamp y motion but allow x motion */ 1176 sy = ih - wh; 1177 sy = MAX(sy, 0); 1178 if ((sx == xOffset) && (sy == yOffset)) 1179 return; 1180 dx = dy = 0; 1181 w = ww; h = wh; 1182 break; 1183 case ButtonRelease: 1184 xOffset = xOffset + grabX - (int) event->xbutton.x; 1185 xOffset = MIN(iw - ww, xOffset); 1186 xOffset = MAX(xOffset, 0); 1187 yOffset = yOffset + grabY - (int) event->xbutton.y; 1188 yOffset = MIN(ih - wh, yOffset); 1189 yOffset = MAX(yOffset, 0); 1190 grabX = grabY = -1; 1191 default: 1192 return; 1193 } 1194 1195 if (appData.usePixmap == True) { 1196 if (xImageDepth == 1) 1197 XCopyPlane(xDisplay, xImagePixmap, XtWindow(widget), 1198 xWinGc, sx, sy, w, h, dx, dy, 1); 1199 else 1200 XCopyArea(xDisplay, xImagePixmap, XtWindow(widget), 1201 xWinGc, sx, sy, w, h, dx, dy); 1202 } else 1203 XPutImage(xDisplay, XtWindow(widget), xWinGc, xImage, 1204 sx, sy, dx, dy, w, h); 1205} 1206 1207void 1208ResizeProc() 1209{ 1210 Dimension w_width, w_height; 1211 int xo, yo, ww, wh; 1212 XEvent fake_event; 1213 Arg args[2]; 1214 1215 if ((xOffset == 0) && (yOffset == 0)) 1216 return; 1217 1218 XtSetArg(args[0], XtNwidth, &w_width); 1219 XtSetArg(args[1], XtNheight, &w_height); 1220 XtGetValues(shellWidget, args, 2); 1221 ww = w_width; 1222 wh = w_height; 1223 XtGetValues(listWidget, args, 2); 1224 wh -= w_height; 1225 1226 xo = xOffset; yo = yOffset; 1227 1228 if ((xOffset + ww) >= tfImageWidth) 1229 xOffset = MAX((int) tfImageWidth - ww, 0); 1230 if ((yOffset + wh) >= tfImageHeight) 1231 yOffset = MAX((int) tfImageHeight - wh, 0); 1232 1233 /* 1234 * Send an ExposeEvent if the origin changed. 1235 * We have to do this because of the use and semantics of bit gravity. 1236 */ 1237 if ((xo != xOffset) || (yo != yOffset)) { 1238 fake_event.type = Expose; 1239 fake_event.xexpose.x = fake_event.xexpose.y = 0; 1240 fake_event.xexpose.width = tfImageWidth; 1241 fake_event.xexpose.height = tfImageHeight; 1242 EventProc(imageWidget, NULL, &fake_event); 1243 } 1244} 1245 1246int 1247XTiffErrorHandler(display, error_event) 1248 Display *display; 1249 XErrorEvent *error_event; 1250{ 1251 char message[80]; 1252 1253 /* 1254 * Some X servers limit the size of pixmaps. 1255 */ 1256 if ((error_event->error_code == BadAlloc) 1257 && (error_event->request_code == X_CreatePixmap)) 1258 fprintf(stderr, "xtiff: requested pixmap too big for display\n"); 1259 else { 1260 XGetErrorText(display, error_event->error_code, message, 80); 1261 fprintf(stderr, "xtiff: error code %s\n", message); 1262 } 1263 1264 exit(0); 1265} 1266 1267void 1268Usage() 1269{ 1270 fprintf(stderr, "Usage xtiff: [options] tiff-file\n"); 1271 fprintf(stderr, "\tstandard Xt options\n"); 1272 fprintf(stderr, "\t[-help]\n"); 1273 fprintf(stderr, "\t[-gamma gamma]\n"); 1274 fprintf(stderr, "\t[-usePixmap (True | False)]\n"); 1275 fprintf(stderr, "\t[-viewportWidth pixels]\n"); 1276 fprintf(stderr, "\t[-viewportHeight pixels]\n"); 1277 fprintf(stderr, "\t[-translate pixels]\n"); 1278 fprintf(stderr, "\t[-verbose (True | False)]\n"); 1279 exit(0); 1280} 1281 1282/* vim: set ts=8 sts=8 sw=8 noet: */ 1283 1284/* 1285 * Local Variables: 1286 * mode: c 1287 * c-basic-offset: 8 1288 * fill-column: 78 1289 * End: 1290 */ 1291