4  * xtiff - view a TIFF file in an X window 
   9  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. 
  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. 
  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 
  29  * Revision 1.0  90/05/07 
  31  * Revision 2.0  90/12/20 
  32  *      Converted to use the Athena Widgets and the Xt Intrinsics. 
  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. 
  44  * TIFFTAG_COLORMAP is often incorrectly written as ranging from 
  45  * 0 to 255 rather than from 0 to 65535.  CheckAndCorrectColormap() 
  48  * Only ORIENTATION_TOPLEFT is supported correctly.  This is the 
  49  * default TIFF and X orientation.  Other orientations will be 
  50  * displayed incorrectly. 
  52  * There is no support for or use of 3/3/2 DirectColor visuals. 
  53  * TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE are not supported. 
  55  * Only TIFFTAG_BITSPERSAMPLE values that are 1, 2, 4 or 8 are supported. 
  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> 
  71 #include <X11/keysymdef.h> 
  72 #include "xtifficon.h" 
  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 
  90 int main 
PP((int argc
, char **argv
)); 
  91 void OpenTIFFFile 
PP((void)); 
  92 void GetTIFFHeader 
PP((void)); 
  93 void SetNameLabel 
PP((void)); 
  94 void CheckAndCorrectColormap 
PP((void)); 
  95 void SimpleGammaCorrection 
PP((void)); 
  96 void GetVisual 
PP((void)); 
  97 Boolean SearchVisualList 
PP((int image_depth
, 
  98     int visual_class
, Visual 
**visual
)); 
  99 void GetTIFFImage 
PP((void)); 
 100 void CreateXImage 
PP((void)); 
 101 XtCallbackProc SelectProc 
PP((Widget w
, caddr_t unused_1
, caddr_t unused_2
)); 
 102 void QuitProc 
PP((void)); 
 103 void NextProc 
PP((void)); 
 104 void PreviousProc 
PP((void)); 
 105 void PageProc 
PP((int direction
)); 
 106 void EventProc 
PP((Widget widget
, caddr_t unused
, XEvent 
*event
)); 
 107 void ResizeProc 
PP((void)); 
 108 int XTiffErrorHandler 
PP((Display 
*display
, XErrorEvent 
*error_event
)); 
 109 void Usage 
PP((void)); 
 111 int xtVersion 
= XtSpecificationRelease
;     /* xtiff depends on R4 or higher */ 
 116 Widget shellWidget
, formWidget
, listWidget
, labelWidget
, imageWidget
; 
 118 enum { ButtonQuit 
= 0, ButtonPreviousPage 
= 1, ButtonNextPage 
= 2 }; 
 120 String buttonStrings
[] = { "Quit", "Previous", "Next" }; 
 122 static 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 
} 
 136     uint32 viewportWidth
; 
 137     uint32 viewportHeight
; 
 140 } AppData
, *AppDataPtr
; 
 144 XtResource clientResources
[] = { 
 146         "help", XtCBoolean
, XtRBoolean
, sizeof(Boolean
), 
 147         XtOffset(AppDataPtr
, help
), XtRImmediate
, (XtPointer
) False
 
 149         "gamma", "Gamma", XtRFloat
, sizeof(float), 
 150         XtOffset(AppDataPtr
, gamma
), XtRString
, (XtPointer
) TIFF_GAMMA
 
 152         "usePixmap", "UsePixmap", XtRBoolean
, sizeof(Boolean
), 
 153         XtOffset(AppDataPtr
, usePixmap
), XtRImmediate
, (XtPointer
) True
 
 155         "viewportWidth", "ViewportWidth", XtRInt
, sizeof(int), 
 156         XtOffset(AppDataPtr
, viewportWidth
), XtRImmediate
, 
 157         (XtPointer
) VIEWPORT_WIDTH
 
 159         "viewportHeight", "ViewportHeight", XtRInt
, sizeof(int), 
 160         XtOffset(AppDataPtr
, viewportHeight
), XtRImmediate
, 
 161         (XtPointer
) VIEWPORT_HEIGHT
 
 163         "translate", "Translate", XtRInt
, sizeof(int), 
 164         XtOffset(AppDataPtr
, translate
), XtRImmediate
, (XtPointer
) KEY_TRANSLATE
 
 166         "verbose", "Verbose", XtRBoolean
, sizeof(Boolean
), 
 167         XtOffset(AppDataPtr
, verbose
), XtRImmediate
, (XtPointer
) True
 
 172     { XtNresizable
, True 
} 
 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 
} 
 189     { XtNresizable
, False 
}, 
 191     { XtNborderWidth
, 0 }, 
 192     { XtNjustify
, XtJustifyLeft 
}, 
 193     { XtNtop
, XtChainTop 
}, 
 194     { XtNleft
, XtChainLeft 
}, 
 195     { XtNbottom
, XtChainTop 
}, 
 196     { XtNright
, XtChainLeft 
} 
 200     { XtNresizable
, True 
}, 
 201     { XtNborderWidth
, 0 }, 
 202     { XtNtop
, XtChainTop 
}, 
 203     { XtNleft
, XtChainLeft 
}, 
 204     { XtNbottom
, XtChainTop 
}, 
 205     { XtNright
, XtChainLeft 
} 
 208 XtActionsRec actionsTable
[] = { 
 209     { "quit", QuitProc 
}, 
 210     { "next", NextProc 
}, 
 211     { "previous", PreviousProc 
}, 
 212     { "notifyresize", ResizeProc 
} 
 215 char translationsTable
[] = "<Key>q:      quit() \n \ 
 217                             <Message>WM_PROTOCOLS: quit()\n \ 
 218                             <Key>p:      previous() \n \ 
 219                             <Key>P:      previous() \n \ 
 222                             <Configure>: notifyresize()"; 
 233 int                 xImageDepth
, xScreen
, xRedMask
, xGreenMask
, xBlueMask
, 
 234                     xOffset 
= 0, yOffset 
= 0, grabX 
= -1, grabY 
= -1; 
 235 unsigned char       basePixel 
= 0; 
 238  * TIFF data structures 
 240 TIFF 
*              tfFile 
= NULL
; 
 241 uint32              tfImageWidth
, tfImageHeight
; 
 242 uint16              tfBitsPerSample
, tfSamplesPerPixel
, tfPlanarConfiguration
, 
 243                     tfPhotometricInterpretation
, tfGrayResponseUnit
, 
 244                     tfImageDepth
, tfBytesPerRow
; 
 245 int                 tfDirectory 
= 0, tfMultiPage 
= False
; 
 246 double              tfUnitMap
, tfGrayResponseUnitMap
[] = { 
 247                         -1, -10, -100, -1000, -10000, -100000 
 251  * display data structures 
 253 double              *dRed
, *dGreen
, *dBlue
; 
 256  * shared data structures 
 258 uint16 
*            redMap 
= NULL
, *greenMap 
= NULL
, *blueMap 
= NULL
, 
 259                     *grayMap 
= NULL
, colormapSize
; 
 264 main(int argc
, char **argv
) 
 266     XSetWindowAttributes window_attributes
; 
 267     Widget widget_list
[3]; 
 270     setbuf(stdout
, NULL
); setbuf(stderr
, NULL
); 
 272     shellWidget 
= XtInitialize(argv
[0], "XTiff", shellOptions
, 
 273         XtNumber(shellOptions
), &argc
, argv
); 
 275     XSetErrorHandler(XTiffErrorHandler
); 
 277     XtGetApplicationResources(shellWidget
, &appData
, 
 278         (XtResourceList
) clientResources
, (Cardinal
) XtNumber(clientResources
), 
 279         (ArgList
) NULL
, (Cardinal
) 0); 
 281     if ((argc 
<= 1) || (argc 
> 2) || appData
.help
) 
 284     if (appData
.verbose 
== False
) { 
 285         TIFFSetErrorHandler(0); 
 286         TIFFSetWarningHandler(0); 
 291     xDisplay 
= XtDisplay(shellWidget
); 
 292     xScreen 
= DefaultScreen(xDisplay
); 
 296     SimpleGammaCorrection(); 
 301      * Send visual, colormap, depth and iconPixmap to shellWidget. 
 302      * Sending the visual to the shell is only possible with the advent of R4. 
 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); 
 315      * widget instance hierarchy 
 317     formWidget 
= XtCreateManagedWidget("form", formWidgetClass
, 
 318         shellWidget
, formArgs
, XtNumber(formArgs
)); 
 320         widget_list
[0] = listWidget 
= XtCreateWidget("list", 
 321             listWidgetClass
, formWidget
, listArgs
, XtNumber(listArgs
)); 
 323         widget_list
[1] = labelWidget 
= XtCreateWidget("label", 
 324             labelWidgetClass
, formWidget
, labelArgs
, XtNumber(labelArgs
)); 
 326         widget_list
[2] = imageWidget 
= XtCreateWidget("image", 
 327             widgetClass
, formWidget
, imageArgs
, XtNumber(imageArgs
)); 
 329     XtManageChildren(widget_list
, XtNumber(widget_list
)); 
 332      * initial widget sizes - for small images let xtiff size itself 
 334     if (tfImageWidth 
>= appData
.viewportWidth
) { 
 335         XtSetArg(args
[0], XtNwidth
, appData
.viewportWidth
); 
 336         XtSetValues(shellWidget
, args
, 1); 
 338     if (tfImageHeight 
>= appData
.viewportHeight
) { 
 339         XtSetArg(args
[0], XtNheight
, appData
.viewportHeight
); 
 340         XtSetValues(shellWidget
, args
, 1); 
 343     XtSetArg(args
[0], XtNwidth
, tfImageWidth
); 
 344     XtSetArg(args
[1], XtNheight
, tfImageHeight
); 
 345     XtSetValues(imageWidget
, args
, 2); 
 348      * formWidget uses these constraints but they are stored in the children. 
 350     XtSetArg(args
[0], XtNfromVert
, listWidget
); 
 351     XtSetValues(imageWidget
, args
, 1); 
 352     XtSetArg(args
[0], XtNfromHoriz
, listWidget
); 
 353     XtSetValues(labelWidget
, args
, 1); 
 357     XtAddCallback(listWidget
, XtNcallback
, (XtCallbackProc
) SelectProc
, 
 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); 
 367      * This is intended to be a little faster than going through 
 368      * the translation manager. 
 370     XtAddEventHandler(imageWidget
, ExposureMask 
| ButtonPressMask
 
 371         | ButtonReleaseMask 
| Button1MotionMask 
| KeyPressMask
, 
 372         False
, EventProc
, NULL
); 
 374     XtRealizeWidget(shellWidget
); 
 376     window_attributes
.cursor 
= XCreateFontCursor(xDisplay
, XC_fleur
); 
 377     XChangeWindowAttributes(xDisplay
, XtWindow(imageWidget
), 
 378         CWCursor
, &window_attributes
); 
 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
); 
 399     tfMultiPage 
= (TIFFLastDirectory(tfFile
) ? False 
: True
); 
 407     if (!TIFFSetDirectory(tfFile
, tfDirectory
)) { 
 408         fprintf(stderr
, "xtiff: can't seek to directory %d in %s\n", 
 409             tfDirectory
, fileName
); 
 413     TIFFGetField(tfFile
, TIFFTAG_IMAGEWIDTH
, &tfImageWidth
); 
 414     TIFFGetField(tfFile
, TIFFTAG_IMAGELENGTH
, &tfImageHeight
); 
 417      * If the following tags aren't present then use the TIFF defaults. 
 419     TIFFGetFieldDefaulted(tfFile
, TIFFTAG_BITSPERSAMPLE
, &tfBitsPerSample
); 
 420     TIFFGetFieldDefaulted(tfFile
, TIFFTAG_SAMPLESPERPIXEL
, &tfSamplesPerPixel
); 
 421     TIFFGetFieldDefaulted(tfFile
, TIFFTAG_PLANARCONFIG
, &tfPlanarConfiguration
); 
 422     TIFFGetFieldDefaulted(tfFile
, TIFFTAG_GRAYRESPONSEUNIT
, &tfGrayResponseUnit
); 
 424     tfUnitMap 
= tfGrayResponseUnitMap
[tfGrayResponseUnit
]; 
 425     colormapSize 
= 1 << tfBitsPerSample
; 
 426     tfImageDepth 
= tfBitsPerSample 
* tfSamplesPerPixel
; 
 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
); 
 434      * If TIFFTAG_PHOTOMETRIC is not present then assign a reasonable default. 
 435      * The TIFF 5.0 specification doesn't give a default. 
 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
; 
 448             tfPhotometricInterpretation 
= PHOTOMETRIC_MINISBLACK
; 
 452      * Given TIFFTAG_PHOTOMETRIC extract or create the response curves. 
 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); 
 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); 
 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
]; 
 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); 
 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); 
 502             "xtiff: can't display photometric interpretation type %d\n", 
 503             tfPhotometricInterpretation
); 
 515         sprintf(buffer
, "%s - page %d", fileName
, tfDirectory
); 
 517         strcpy(buffer
, fileName
); 
 518     XtSetArg(args
[0], XtNlabel
, buffer
); 
 519     XtSetValues(labelWidget
, args
, 1); 
 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. 
 527 CheckAndCorrectColormap() 
 531     for (i 
= 0; i 
< colormapSize
; i
++) 
 532         if ((redMap
[i
] > 255) || (greenMap
[i
] > 255) || (blueMap
[i
] > 255)) 
 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); 
 540     TIFFWarning(fileName
, "Assuming 8-bit colormap"); 
 544 SimpleGammaCorrection() 
 547     register double i_gamma 
= 1.0 / appData
.gamma
; 
 549     for (i 
= 0; i 
< colormapSize
; i
++) { 
 550         if (((tfPhotometricInterpretation 
== PHOTOMETRIC_MINISWHITE
) 
 551             && (i 
== colormapSize 
- 1)) 
 552             || ((tfPhotometricInterpretation 
== PHOTOMETRIC_MINISBLACK
) 
 554             redMap
[i
] = greenMap
[i
] = blueMap
[i
] = 0; 
 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)); 
 562     free(dRed
); free(dGreen
); free(dBlue
); 
 565 static char* classNames
[] = { 
 575  * Current limitation: the visual is set initially by the first file. 
 576  * It cannot be changed. 
 581     XColor 
*colors 
= NULL
; 
 582     unsigned long *pixels 
= NULL
; 
 585     switch (tfImageDepth
) { 
 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. 
 592         if (SearchVisualList(24, DirectColor
, &xVisual
) == False
) { 
 593             fprintf(stderr
, "xtiff: 24-bit DirectColor visual not available\n"); 
 597         colors 
= (XColor 
*) malloc(3 * colormapSize 
* sizeof(XColor
)); 
 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
; 
 608         xColormap 
= XCreateColormap(xDisplay
, RootWindow(xDisplay
, xScreen
), 
 610         XStoreColors(xDisplay
, xColormap
, colors
, colormapSize
); 
 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. 
 619         switch (tfPhotometricInterpretation
) { 
 620         case PHOTOMETRIC_MINISWHITE
: 
 621         case PHOTOMETRIC_MINISBLACK
: 
 622             if (SearchVisualList((int) tfImageDepth
, GrayScale
, &xVisual
) == True
) 
 624         case PHOTOMETRIC_PALETTE
: 
 625             if (SearchVisualList((int) tfImageDepth
, PseudoColor
, &xVisual
) == True
) 
 628             fprintf(stderr
, "xtiff: Unsupported TIFF/X configuration\n"); 
 632         colors 
= (XColor 
*) malloc(colormapSize 
* sizeof(XColor
)); 
 635         for (i 
= 0; i 
< colormapSize
; 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
; 
 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. 
 652         if (tfImageDepth 
== 8) 
 653             xColormap 
= XCreateColormap(xDisplay
, RootWindow(xDisplay
, xScreen
), 
 656             xColormap 
= XCreateColormap(xDisplay
, RootWindow(xDisplay
, xScreen
), 
 658             pixels 
= (unsigned long *) 
 659                 malloc(colormapSize 
* sizeof(unsigned long)); 
 661             (void) XAllocColorCells(xDisplay
, xColormap
, True
, 
 662                 NULL
, 0, pixels
, colormapSize
); 
 663             basePixel 
= (unsigned char) pixels
[0]; 
 666         XStoreColors(xDisplay
, xColormap
, colors
, colormapSize
); 
 670         xVisual 
= DefaultVisual(xDisplay
, xScreen
); 
 671         xColormap 
= DefaultColormap(xDisplay
, xScreen
); 
 674         fprintf(stderr
, "xtiff: unsupported image depth %d\n", tfImageDepth
); 
 678     if (appData
.verbose 
== True
) 
 679         fprintf(stderr
, "%s: Using %d-bit %s visual.\n", 
 680             fileName
, xImageDepth
, classNames
[xVisual
->class]); 
 688     if (greenMap 
!= NULL
) 
 693     colors 
= NULL
; grayMap 
= redMap 
= greenMap 
= blueMap 
= NULL
; 
 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. 
 703 SearchVisualList(image_depth
, visual_class
, visual
) 
 704     int image_depth
, visual_class
; 
 707     XVisualInfo template_visual
, *visual_list
, *vl
; 
 710     template_visual
.screen 
= xScreen
; 
 711     vl 
= visual_list 
= XGetVisualInfo(xDisplay
, VisualScreenMask
, 
 712         &template_visual
, &n_visuals
); 
 714     if (n_visuals 
== 0) { 
 715         fprintf(stderr
, "xtiff: visual list not available\n"); 
 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
); 
 732     XFree((char *) visual_list
); 
 739     int pixel_map
[3], red_shift
, green_shift
, blue_shift
; 
 740     char *scan_line
, *output_p
, *input_p
; 
 744     scan_line 
= (char *) malloc(tfBytesPerRow 
= TIFFScanlineSize(tfFile
)); 
 747     if ((tfImageDepth 
== 32) || (tfImageDepth 
== 24)) { 
 748         output_p 
= imageMemory 
= (char *) 
 749             malloc(tfImageWidth 
* tfImageHeight 
* 4); 
 753          * Handle different color masks for different frame buffers. 
 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)); 
 771         if (tfPlanarConfiguration 
== PLANARCONFIG_CONTIG
) { 
 772             for (i 
= 0; i 
< tfImageHeight
; i
++) { 
 773                 if (TIFFReadScanline(tfFile
, scan_line
, i
, 0) < 0) 
 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
++; 
 780                     if (tfSamplesPerPixel 
== 4) /* skip the fourth channel */ 
 785             for (s 
= 0; s 
< tfSamplesPerPixel
; s
++) { 
 786                 if (s 
== 3)             /* skip the fourth channel */ 
 788                 for (i 
= 0; i 
< tfImageHeight
; i
++) { 
 789                     if (TIFFReadScanline(tfFile
, scan_line
, i
, s
) < 0) 
 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
++; 
 799         if (xImageDepth 
== tfImageDepth
) { 
 800             output_p 
= imageMemory 
= (char *) 
 801                 malloc(tfBytesPerRow 
* tfImageHeight
); 
 804             for (i 
= 0; i 
< tfImageHeight
; i
++, output_p 
+= tfBytesPerRow
) 
 805                 if (TIFFReadScanline(tfFile
, output_p
, i
, 0) < 0) 
 807         } else if ((xImageDepth 
== 8) && (tfImageDepth 
== 4)) { 
 808             output_p 
= imageMemory 
= (char *) 
 809                 malloc(tfBytesPerRow 
* 2 * tfImageHeight 
+ 2); 
 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. 
 817             for (i 
= 0; i 
< tfImageHeight
; i
++) { 
 818                 if (TIFFReadScanline(tfFile
, scan_line
, i
, 0) < 0) 
 820                 output_p 
= &imageMemory
[i 
* tfImageWidth
]; 
 822                 for (j 
= 0; j 
< tfImageWidth
; j 
+= 2, input_p
++) { 
 823                     *output_p
++ = (*input_p 
>> 4) + basePixel
; 
 824                     *output_p
++ = (*input_p 
& 0xf) + basePixel
; 
 827         } else if ((xImageDepth 
== 8) && (tfImageDepth 
== 2)) { 
 828             output_p 
= imageMemory 
= (char *) 
 829                 malloc(tfBytesPerRow 
* 4 * tfImageHeight 
+ 4); 
 832             for (i 
= 0; i 
< tfImageHeight
; i
++) { 
 833                 if (TIFFReadScanline(tfFile
, scan_line
, i
, 0) < 0) 
 835                 output_p 
= &imageMemory
[i 
* tfImageWidth
]; 
 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
; 
 844         } else if ((xImageDepth 
== 4) && (tfImageDepth 
== 2)) { 
 845             output_p 
= imageMemory 
= (char *) 
 846                 malloc(tfBytesPerRow 
* 2 * tfImageHeight 
+ 2); 
 849             for (i 
= 0; i 
< tfImageHeight
; i
++) { 
 850                 if (TIFFReadScanline(tfFile
, scan_line
, i
, 0) < 0) 
 852                 output_p 
= &imageMemory
[i 
* tfBytesPerRow 
* 2]; 
 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
; 
 863                 "xtiff: can't handle %d-bit TIFF file on an %d-bit display\n", 
 864                 tfImageDepth
, xImageDepth
); 
 878     xOffset 
= yOffset 
= 0; 
 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); 
 887      * libtiff converts LSB data into MSB but doesn't change the FillOrder tag. 
 889     if (xImageDepth 
== 1) 
 890         xImage
->bitmap_bit_order 
= MSBFirst
; 
 891     if (xImageDepth 
<= 8) 
 892         xImage
->byte_order 
= MSBFirst
; 
 895      * create an appropriate GC 
 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
); 
 903         gc_values
.foreground 
= XBlackPixel(xDisplay
, xScreen
); 
 904         gc_values
.background 
= XWhitePixel(xDisplay
, xScreen
); 
 906     xWinGc 
= XCreateGC(xDisplay
, XtWindow(shellWidget
), 
 907         GCFunction 
| GCPlaneMask 
| GCForeground 
| GCBackground
, &gc_values
); 
 910      * create the pixmap and load the image 
 912     if (appData
.usePixmap 
== True
) { 
 913         xImagePixmap 
= XCreatePixmap(xDisplay
, RootWindow(xDisplay
, xScreen
), 
 914             xImage
->width
, xImage
->height
, xImageDepth
); 
 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. 
 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
); 
 930             XPutImage(xDisplay
, xImagePixmap
, xWinGc
, xImage
, 
 931                 0, 0, 0, 0, xImage
->width
, xImage
->height
); 
 932         XDestroyImage(xImage
); 
 938 SelectProc(w
, unused_1
, unused_2
) 
 943     XawListReturnStruct 
*list_return
; 
 945     list_return 
= XawListShowCurrent(w
); 
 947     switch (list_return
->list_index
) { 
 951     case ButtonPreviousPage
: 
 958         fprintf(stderr
, "error in SelectProc\n"); 
 961     XawListUnhighlight(w
); 
 973     PageProc(ButtonNextPage
); 
 979     PageProc(ButtonPreviousPage
); 
 990     case ButtonPreviousPage
: 
 992             TIFFSetDirectory(tfFile
, --tfDirectory
); 
 997         if (TIFFReadDirectory(tfFile
) == True
) 
1003         fprintf(stderr
, "error in PageProc\n"); 
1007     xOffset 
= yOffset 
= 0; 
1014     if (appData
.usePixmap 
== True
) 
1015         XFreePixmap(xDisplay
, xImagePixmap
); 
1017         XDestroyImage(xImage
); 
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. 
1027     XtSetArg(args
[0], XtNallowShellResize
, False
); 
1028     XtSetValues(shellWidget
, args
, 1); 
1030     XtSetArg(args
[0], XtNwidth
, tfImageWidth
); 
1031     XtSetArg(args
[1], XtNheight
, tfImageHeight
); 
1032     XtSetValues(imageWidget
, args
, 2); 
1034     XtSetArg(args
[0], XtNallowShellResize
, True
); 
1035     XtSetValues(shellWidget
, args
, 1); 
1037     XClearWindow(xDisplay
, XtWindow(imageWidget
)); 
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
); 
1047 EventProc(widget
, unused
, event
) 
1052     int ih
, iw
, ww
, wh
, sx
, sy
, w
, h
, dx
, dy
; 
1053     Dimension w_width
, w_height
; 
1057     if (event
->type 
== MappingNotify
) { 
1058         XRefreshKeyboardMapping((XMappingEvent 
*) event
); 
1062     if (!XtIsRealized(widget
)) 
1065     if ((event
->type 
== ButtonPress
) || (event
->type 
== ButtonRelease
)) 
1066         if (event
->xbutton
.button 
!= Button1
) 
1069     iw 
= tfImageWidth
;  /* avoid sign problems */ 
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. 
1076     if (event
->type 
== ButtonPress
) { 
1077         grabX 
= event
->xbutton
.x
; 
1078         grabY 
= event
->xbutton
.y
; 
1083      * imageWidget is a Core widget and doesn't get resized. 
1084      * So we calculate the size of its viewport here. 
1086     XtSetArg(args
[0], XtNwidth
, &w_width
); 
1087     XtSetArg(args
[1], XtNheight
, &w_height
); 
1088     XtGetValues(shellWidget
, args
, 2); 
1091     XtGetValues(listWidget
, args
, 2); 
1094     switch (event
->type
) { 
1096         dx 
= event
->xexpose
.x
; 
1097         dy 
= event
->xexpose
.y
; 
1100         w 
= MIN(event
->xexpose
.width
, iw
); 
1101         h 
= MIN(event
->xexpose
.height
, ih
); 
1104         if ((grabX 
>= 0) || (grabY 
>= 0)) /* Mouse button is still down */ 
1106         switch (XLookupKeysym((XKeyEvent 
*) event
, /* KeySyms index */ 0)) { 
1108             if (ih 
< wh
)    /* Don't scroll if the window fits the image. */ 
1110             sy 
= yOffset 
+ appData
.translate
; 
1111             sy 
= MIN(ih 
- wh
, sy
); 
1112             if (sy 
== yOffset
)  /* Filter redundant stationary refreshes. */ 
1122             sy 
= yOffset 
- appData
.translate
; 
1134             sx 
= xOffset 
+ appData
.translate
; 
1135             sx 
= MIN(iw 
- ww
, sx
); 
1146             sx 
= xOffset 
- appData
.translate
; 
1161          * MotionEvent compression.  Ignore multiple motion events. 
1162          * Ignore motion events if the mouse button is up. 
1164         if (XPending(xDisplay
)) /* Xlib doesn't flush the output buffer */ 
1165             if (XtPeekEvent(&next_event
)) 
1166                 if (next_event
.type 
== MotionNotify
) 
1168         if ((grabX 
< 0) || (grabY 
< 0)) 
1170         sx 
= xOffset 
+ grabX 
- (int) event
->xmotion
.x
; 
1171         if (sx 
>= (iw 
- ww
))    /* clamp x motion but allow y motion */ 
1174         sy 
= yOffset 
+ grabY 
- (int) event
->xmotion
.y
; 
1175         if (sy 
>= (ih 
- wh
)) /* clamp y motion but allow x motion */ 
1178         if ((sx 
== xOffset
) && (sy 
== yOffset
)) 
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); 
1195     if (appData
.usePixmap 
== True
) { 
1196         if (xImageDepth 
== 1) 
1197             XCopyPlane(xDisplay
, xImagePixmap
, XtWindow(widget
), 
1198                 xWinGc
, sx
, sy
, w
, h
, dx
, dy
, 1); 
1200             XCopyArea(xDisplay
, xImagePixmap
, XtWindow(widget
), 
1201                 xWinGc
, sx
, sy
, w
, h
, dx
, dy
); 
1203         XPutImage(xDisplay
, XtWindow(widget
), xWinGc
, xImage
, 
1204             sx
, sy
, dx
, dy
, w
, h
); 
1210     Dimension w_width
, w_height
; 
1215     if ((xOffset 
== 0) && (yOffset 
== 0)) 
1218     XtSetArg(args
[0], XtNwidth
, &w_width
); 
1219     XtSetArg(args
[1], XtNheight
, &w_height
); 
1220     XtGetValues(shellWidget
, args
, 2); 
1223     XtGetValues(listWidget
, args
, 2); 
1226     xo 
= xOffset
; yo 
= yOffset
; 
1228     if ((xOffset 
+ ww
) >= tfImageWidth
) 
1229         xOffset 
= MAX((int) tfImageWidth 
- ww
, 0); 
1230     if ((yOffset 
+ wh
) >= tfImageHeight
) 
1231         yOffset 
= MAX((int) tfImageHeight 
- wh
, 0); 
1234      * Send an ExposeEvent if the origin changed. 
1235      * We have to do this because of the use and semantics of bit gravity. 
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
); 
1247 XTiffErrorHandler(display
, error_event
) 
1249     XErrorEvent 
*error_event
; 
1254      * Some X servers limit the size of pixmaps. 
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"); 
1260         XGetErrorText(display
, error_event
->error_code
, message
, 80); 
1261         fprintf(stderr
, "xtiff: error code %s\n", message
); 
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"); 
1282 /* vim: set ts=8 sts=8 sw=8 noet: */