2  * xtiff - view a TIFF file in an X window 
   7  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. 
  11  * Permission to use, copy, modify, and distribute this software and its 
  12  * documentation for any purpose and without fee is hereby granted, 
  13  * provided that the above copyright notice appear in all copies and that 
  14  * both that copyright notice and this permission notice appear in 
  15  * supporting documentation, and that the name of Digital not be 
  16  * used in advertising or publicity pertaining to distribution of the 
  17  * software without specific, written prior permission. 
  19  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 
  20  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 
  21  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 
  22  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
  23  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
  24  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
  27  * Revision 1.0  90/05/07 
  29  * Revision 2.0  90/12/20 
  30  *      Converted to use the Athena Widgets and the Xt Intrinsics. 
  34  * According to the TIFF 5.0 Specification, it is possible to have 
  35  * both a TIFFTAG_COLORMAP and a TIFFTAG_COLORRESPONSECURVE.  This 
  36  * doesn't make sense since a TIFFTAG_COLORMAP is 16 bits wide and 
  37  * a TIFFTAG_COLORRESPONSECURVE is tfBitsPerSample bits wide for each 
  38  * channel.  This is probably a bug in the specification. 
  39  * In this case, TIFFTAG_COLORRESPONSECURVE is ignored. 
  40  * This might make sense if TIFFTAG_COLORMAP was 8 bits wide. 
  42  * TIFFTAG_COLORMAP is often incorrectly written as ranging from 
  43  * 0 to 255 rather than from 0 to 65535.  CheckAndCorrectColormap() 
  46  * Only ORIENTATION_TOPLEFT is supported correctly.  This is the 
  47  * default TIFF and X orientation.  Other orientations will be 
  48  * displayed incorrectly. 
  50  * There is no support for or use of 3/3/2 DirectColor visuals. 
  51  * TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE are not supported. 
  53  * Only TIFFTAG_BITSPERSAMPLE values that are 1, 2, 4 or 8 are supported. 
  58 #include <X11/Xatom.h> 
  59 #include <X11/Intrinsic.h> 
  60 #include <X11/StringDefs.h> 
  61 #include <X11/Xproto.h> 
  62 #include <X11/Shell.h> 
  63 #include <X11/Xaw/Form.h> 
  64 #include <X11/Xaw/List.h> 
  65 #include <X11/Xaw/Label.h> 
  66 #include <X11/cursorfont.h> 
  68 #include <X11/keysymdef.h> 
  69 #include "xtifficon.h" 
  71 #define TIFF_GAMMA      "2.2"     /* default gamma from the TIFF 5.0 spec */ 
  72 #define ROUND(x)        (u_short) ((x) + 0.5) 
  73 #define SCALE(x, s)     (((x) * 65535L) / (s)) 
  74 #define MCHECK(m)       if (!m) { fprintf(stderr, "malloc failed\n"); exit(0); } 
  75 #define MIN(a, b)       (((a) < (b)) ? (a) : (b)) 
  76 #define MAX(a, b)       (((a) > (b)) ? (a) : (b)) 
  77 #define VIEWPORT_WIDTH  700 
  78 #define VIEWPORT_HEIGHT 500 
  79 #define KEY_TRANSLATE   20 
  87 void main 
PP((int argc
, char **argv
)); 
  88 void OpenTIFFFile 
PP((void)); 
  89 void GetTIFFHeader 
PP((void)); 
  90 void SetNameLabel 
PP((void)); 
  91 void CheckAndCorrectColormap 
PP((void)); 
  92 void SimpleGammaCorrection 
PP((void)); 
  93 void GetVisual 
PP((void)); 
  94 Boolean SearchVisualList 
PP((int image_depth
, 
  95     int visual_class
, Visual 
**visual
)); 
  96 void GetTIFFImage 
PP((void)); 
  97 void CreateXImage 
PP((void)); 
  98 XtCallbackProc SelectProc 
PP((Widget w
, caddr_t unused_1
, caddr_t unused_2
)); 
  99 void QuitProc 
PP((void)); 
 100 void NextProc 
PP((void)); 
 101 void PreviousProc 
PP((void)); 
 102 void PageProc 
PP((int direction
)); 
 103 void EventProc 
PP((Widget widget
, caddr_t unused
, XEvent 
*event
)); 
 104 void ResizeProc 
PP((void)); 
 105 int XTiffErrorHandler 
PP((Display 
*display
, XErrorEvent 
*error_event
)); 
 106 void Usage 
PP((void)); 
 108 int xtVersion 
= XtSpecificationRelease
;     /* xtiff depends on R4 or higher */ 
 113 Widget shellWidget
, formWidget
, listWidget
, labelWidget
, imageWidget
; 
 115 enum { ButtonQuit 
= 0, ButtonPreviousPage 
= 1, ButtonNextPage 
= 2 }; 
 117 String buttonStrings
[] = { "Quit", "Previous", "Next" }; 
 119 static XrmOptionDescRec shellOptions
[] = { 
 120     { "-help", "*help", XrmoptionNoArg
, (caddr_t
) "True" }, 
 121     { "-gamma", "*gamma", XrmoptionSepArg
, NULL 
}, 
 122     { "-usePixmap", "*usePixmap", XrmoptionSepArg
, NULL 
}, 
 123     { "-viewportWidth", "*viewportWidth", XrmoptionSepArg
, NULL 
}, 
 124     { "-viewportHeight", "*viewportHeight", XrmoptionSepArg
, NULL 
}, 
 125     { "-translate", "*translate", XrmoptionSepArg
, NULL 
}, 
 126     { "-verbose", "*verbose", XrmoptionSepArg
, NULL 
} 
 137 } AppData
, *AppDataPtr
; 
 141 XtResource clientResources
[] = { 
 143         "help", XtCBoolean
, XtRBoolean
, sizeof(Boolean
), 
 144         XtOffset(AppDataPtr
, help
), XtRImmediate
, (XtPointer
) False
 
 146         "gamma", "Gamma", XtRFloat
, sizeof(float), 
 147         XtOffset(AppDataPtr
, gamma
), XtRString
, (XtPointer
) TIFF_GAMMA
 
 149         "usePixmap", "UsePixmap", XtRBoolean
, sizeof(Boolean
), 
 150         XtOffset(AppDataPtr
, usePixmap
), XtRImmediate
, (XtPointer
) True
 
 152         "viewportWidth", "ViewportWidth", XtRInt
, sizeof(int), 
 153         XtOffset(AppDataPtr
, viewportWidth
), XtRImmediate
, 
 154         (XtPointer
) VIEWPORT_WIDTH
 
 156         "viewportHeight", "ViewportHeight", XtRInt
, sizeof(int), 
 157         XtOffset(AppDataPtr
, viewportHeight
), XtRImmediate
, 
 158         (XtPointer
) VIEWPORT_HEIGHT
 
 160         "translate", "Translate", XtRInt
, sizeof(int), 
 161         XtOffset(AppDataPtr
, translate
), XtRImmediate
, (XtPointer
) KEY_TRANSLATE
 
 163         "verbose", "Verbose", XtRBoolean
, sizeof(Boolean
), 
 164         XtOffset(AppDataPtr
, verbose
), XtRImmediate
, (XtPointer
) True
 
 169     { XtNresizable
, True 
} 
 173     { XtNresizable
, False 
}, 
 174     { XtNborderWidth
, 0 }, 
 175     { XtNdefaultColumns
, 3 }, 
 176     { XtNforceColumns
, True 
}, 
 177     { XtNlist
, (int) buttonStrings 
}, 
 178     { XtNnumberStrings
, XtNumber(buttonStrings
) }, 
 179     { XtNtop
, XtChainTop 
}, 
 180     { XtNleft
, XtChainLeft 
}, 
 181     { XtNbottom
, XtChainTop 
}, 
 182     { XtNright
, XtChainLeft 
} 
 186     { XtNresizable
, False 
}, 
 188     { XtNborderWidth
, 0 }, 
 189     { XtNjustify
, XtJustifyLeft 
}, 
 190     { XtNtop
, XtChainTop 
}, 
 191     { XtNleft
, XtChainLeft 
}, 
 192     { XtNbottom
, XtChainTop 
}, 
 193     { XtNright
, XtChainLeft 
} 
 197     { XtNresizable
, True 
}, 
 198     { XtNborderWidth
, 0 }, 
 199     { XtNtop
, XtChainTop 
}, 
 200     { XtNleft
, XtChainLeft 
}, 
 201     { XtNbottom
, XtChainTop 
}, 
 202     { XtNright
, XtChainLeft 
} 
 205 XtActionsRec actionsTable
[] = { 
 206     { "quit", QuitProc 
}, 
 207     { "next", NextProc 
}, 
 208     { "previous", PreviousProc 
}, 
 209     { "notifyresize", ResizeProc 
} 
 212 char translationsTable
[] = "<Key>q:      quit() \n \ 
 214                             <Message>WM_PROTOCOLS: quit()\n \ 
 215                             <Key>p:      previous() \n \ 
 216                             <Key>P:      previous() \n \ 
 219                             <Configure>: notifyresize()"; 
 230 int                 xImageDepth
, xScreen
, xRedMask
, xGreenMask
, xBlueMask
, 
 231                     xOffset 
= 0, yOffset 
= 0, grabX 
= -1, grabY 
= -1; 
 232 u_char              basePixel 
= 0; 
 235  * TIFF data structures 
 237 TIFF 
*              tfFile 
= NULL
; 
 238 u_long              tfImageWidth
, tfImageHeight
; 
 239 u_short             tfBitsPerSample
, tfSamplesPerPixel
, tfPlanarConfiguration
, 
 240                     tfPhotometricInterpretation
, tfGrayResponseUnit
, 
 241                     tfImageDepth
, tfBytesPerRow
; 
 242 int                 tfDirectory 
= 0, tfMultiPage 
= False
; 
 243 double              tfUnitMap
, tfGrayResponseUnitMap
[] = { 
 244                         -1, -10, -100, -1000, -10000, -100000 
 248  * display data structures 
 250 double              *dRed
, *dGreen
, *dBlue
; 
 253  * shared data structures 
 255 u_short 
*           redMap 
= NULL
, *greenMap 
= NULL
, *blueMap 
= NULL
, 
 256                     *grayMap 
= NULL
, colormapSize
; 
 257 u_char 
*            imageMemory
; 
 265     XSetWindowAttributes window_attributes
; 
 266     Widget widget_list
[3]; 
 269     setbuf(stdout
, NULL
); setbuf(stderr
, NULL
); 
 271     shellWidget 
= XtInitialize(argv
[0], "XTiff", shellOptions
, 
 272         XtNumber(shellOptions
), &argc
, argv
); 
 274     XSetErrorHandler(XTiffErrorHandler
); 
 276     XtGetApplicationResources(shellWidget
, &appData
, 
 277         (XtResourceList
) clientResources
, (Cardinal
) XtNumber(clientResources
), 
 278         (ArgList
) NULL
, (Cardinal
) 0); 
 280     if ((argc 
<= 1) || (argc 
> 2) || appData
.help
) 
 283     if (appData
.verbose 
== False
) { 
 284         TIFFSetErrorHandler(0); 
 285         TIFFSetWarningHandler(0); 
 290     xDisplay 
= XtDisplay(shellWidget
); 
 291     xScreen 
= DefaultScreen(xDisplay
); 
 295     SimpleGammaCorrection(); 
 300      * Send visual, colormap, depth and iconPixmap to shellWidget. 
 301      * Sending the visual to the shell is only possible with the advent of R4. 
 303     XtSetArg(args
[0], XtNvisual
, xVisual
); 
 304     XtSetArg(args
[1], XtNcolormap
, xColormap
); 
 305     XtSetArg(args
[2], XtNdepth
, 
 306         xImageDepth 
== 1 ? DefaultDepth(xDisplay
, xScreen
) : xImageDepth
); 
 307     XtSetArg(args
[3], XtNiconPixmap
, 
 308         XCreateBitmapFromData(xDisplay
, RootWindow(xDisplay
, xScreen
), 
 309             xtifficon_bits
, xtifficon_width
, xtifficon_height
)); 
 310     XtSetArg(args
[4], XtNallowShellResize
, True
); 
 311     XtSetValues(shellWidget
, args
, 5); 
 314      * widget instance hierarchy 
 316     formWidget 
= XtCreateManagedWidget("form", formWidgetClass
, 
 317         shellWidget
, formArgs
, XtNumber(formArgs
)); 
 319         widget_list
[0] = listWidget 
= XtCreateWidget("list", 
 320             listWidgetClass
, formWidget
, listArgs
, XtNumber(listArgs
)); 
 322         widget_list
[1] = labelWidget 
= XtCreateWidget("label", 
 323             labelWidgetClass
, formWidget
, labelArgs
, XtNumber(labelArgs
)); 
 325         widget_list
[2] = imageWidget 
= XtCreateWidget("image", 
 326             widgetClass
, formWidget
, imageArgs
, XtNumber(imageArgs
)); 
 328     XtManageChildren(widget_list
, XtNumber(widget_list
)); 
 331      * initial widget sizes - for small images let xtiff size itself 
 333     if (tfImageWidth 
>= appData
.viewportWidth
) { 
 334         XtSetArg(args
[0], XtNwidth
, appData
.viewportWidth
); 
 335         XtSetValues(shellWidget
, args
, 1); 
 337     if (tfImageHeight 
>= appData
.viewportHeight
) { 
 338         XtSetArg(args
[0], XtNheight
, appData
.viewportHeight
); 
 339         XtSetValues(shellWidget
, args
, 1); 
 342     XtSetArg(args
[0], XtNwidth
, tfImageWidth
); 
 343     XtSetArg(args
[1], XtNheight
, tfImageHeight
); 
 344     XtSetValues(imageWidget
, args
, 2); 
 347      * formWidget uses these constraints but they are stored in the children. 
 349     XtSetArg(args
[0], XtNfromVert
, listWidget
); 
 350     XtSetValues(imageWidget
, args
, 1); 
 351     XtSetArg(args
[0], XtNfromHoriz
, listWidget
); 
 352     XtSetValues(labelWidget
, args
, 1); 
 356     XtAddCallback(listWidget
, XtNcallback
, (XtCallbackProc
) SelectProc
, 
 359     XtAddActions(actionsTable
, XtNumber(actionsTable
)); 
 360     XtSetArg(args
[0], XtNtranslations
, 
 361         XtParseTranslationTable(translationsTable
)); 
 362     XtSetValues(formWidget
, &args
[0], 1); 
 363     XtSetValues(imageWidget
, &args
[0], 1); 
 366      * This is intended to be a little faster than going through 
 367      * the translation manager. 
 369     XtAddEventHandler(imageWidget
, ExposureMask 
| ButtonPressMask
 
 370         | ButtonReleaseMask 
| Button1MotionMask 
| KeyPressMask
, 
 371         False
, EventProc
, NULL
); 
 373     XtRealizeWidget(shellWidget
); 
 375     window_attributes
.cursor 
= XCreateFontCursor(xDisplay
, XC_fleur
); 
 376     XChangeWindowAttributes(xDisplay
, XtWindow(imageWidget
), 
 377         CWCursor
, &window_attributes
); 
 390     if ((tfFile 
= TIFFOpen(fileName
, "r")) == NULL
) { 
 391         fprintf(appData
.verbose 
? stderr 
: stdout
, 
 392             "xtiff: can't open %s as a TIFF file\n", fileName
); 
 396     tfMultiPage 
= (TIFFLastDirectory(tfFile
) ? False 
: True
); 
 404     if (!TIFFSetDirectory(tfFile
, tfDirectory
)) { 
 405         fprintf(stderr
, "xtiff: can't seek to directory %d in %s\n", 
 406             tfDirectory
, fileName
); 
 410     TIFFGetField(tfFile
, TIFFTAG_IMAGEWIDTH
, &tfImageWidth
); 
 411     TIFFGetField(tfFile
, TIFFTAG_IMAGELENGTH
, &tfImageHeight
); 
 414      * If the following tags aren't present then use the TIFF defaults. 
 416     TIFFGetFieldDefaulted(tfFile
, TIFFTAG_BITSPERSAMPLE
, &tfBitsPerSample
); 
 417     TIFFGetFieldDefaulted(tfFile
, TIFFTAG_SAMPLESPERPIXEL
, &tfSamplesPerPixel
); 
 418     TIFFGetFieldDefaulted(tfFile
, TIFFTAG_PLANARCONFIG
, &tfPlanarConfiguration
); 
 419     TIFFGetFieldDefaulted(tfFile
, TIFFTAG_GRAYRESPONSEUNIT
, &tfGrayResponseUnit
); 
 421     tfUnitMap 
= tfGrayResponseUnitMap
[tfGrayResponseUnit
]; 
 422     colormapSize 
= 1 << tfBitsPerSample
; 
 423     tfImageDepth 
= tfBitsPerSample 
* tfSamplesPerPixel
; 
 425     dRed 
= (double *) malloc(colormapSize 
* sizeof(double)); 
 426     dGreen 
= (double *) malloc(colormapSize 
* sizeof(double)); 
 427     dBlue 
= (double *) malloc(colormapSize 
* sizeof(double)); 
 428     MCHECK(dRed
); MCHECK(dGreen
); MCHECK(dBlue
); 
 431      * If TIFFTAG_PHOTOMETRIC is not present then assign a reasonable default. 
 432      * The TIFF 5.0 specification doesn't give a default. 
 434     if (!TIFFGetField(tfFile
, TIFFTAG_PHOTOMETRIC
, 
 435             &tfPhotometricInterpretation
)) { 
 436         if (tfSamplesPerPixel 
!= 1) 
 437             tfPhotometricInterpretation 
= PHOTOMETRIC_RGB
; 
 438         else if (tfBitsPerSample 
== 1) 
 439             tfPhotometricInterpretation 
= PHOTOMETRIC_MINISBLACK
; 
 440         else if (TIFFGetField(tfFile
, TIFFTAG_COLORMAP
, 
 441                 &redMap
, &greenMap
, &blueMap
)) { 
 442             tfPhotometricInterpretation 
= PHOTOMETRIC_PALETTE
; 
 443             redMap 
= greenMap 
= blueMap 
= NULL
; 
 445             tfPhotometricInterpretation 
= PHOTOMETRIC_MINISBLACK
; 
 449      * Given TIFFTAG_PHOTOMETRIC extract or create the response curves. 
 451     switch (tfPhotometricInterpretation
) { 
 452     case PHOTOMETRIC_RGB
: 
 453         redMap 
= (u_short 
*) malloc(colormapSize 
* sizeof(u_short
)); 
 454         greenMap 
= (u_short 
*) malloc(colormapSize 
* sizeof(u_short
)); 
 455         blueMap 
= (u_short 
*) malloc(colormapSize 
* sizeof(u_short
)); 
 456         MCHECK(redMap
); MCHECK(greenMap
); MCHECK(blueMap
); 
 457         for (i 
= 0; i 
< colormapSize
; i
++) 
 458             dRed
[i
] = dGreen
[i
] = dBlue
[i
] 
 459                 = (double) SCALE(i
, colormapSize 
- 1); 
 461     case PHOTOMETRIC_PALETTE
: 
 462         if (!TIFFGetField(tfFile
, TIFFTAG_COLORMAP
, 
 463                 &redMap
, &greenMap
, &blueMap
)) { 
 464             redMap 
= (u_short 
*) malloc(colormapSize 
* sizeof(u_short
)); 
 465             greenMap 
= (u_short 
*) malloc(colormapSize 
* sizeof(u_short
)); 
 466             blueMap 
= (u_short 
*) malloc(colormapSize 
* sizeof(u_short
)); 
 467             MCHECK(redMap
); MCHECK(greenMap
); MCHECK(blueMap
); 
 468             for (i 
= 0; i 
< colormapSize
; i
++) 
 469                 dRed
[i
] = dGreen
[i
] = dBlue
[i
] 
 470                     = (double) SCALE(i
, colormapSize 
- 1); 
 472             CheckAndCorrectColormap(); 
 473             for (i 
= 0; i 
< colormapSize
; i
++) { 
 474                 dRed
[i
] = (double) redMap
[i
]; 
 475                 dGreen
[i
] = (double) greenMap
[i
]; 
 476                 dBlue
[i
] = (double) blueMap
[i
]; 
 480     case PHOTOMETRIC_MINISWHITE
: 
 481         redMap 
= (u_short 
*) malloc(colormapSize 
* sizeof(u_short
)); 
 482         greenMap 
= (u_short 
*) malloc(colormapSize 
* sizeof(u_short
)); 
 483         blueMap 
= (u_short 
*) malloc(colormapSize 
* sizeof(u_short
)); 
 484         MCHECK(redMap
); MCHECK(greenMap
); MCHECK(blueMap
); 
 485         for (i 
= 0; i 
< colormapSize
; i
++) 
 486             dRed
[i
] = dGreen
[i
] = dBlue
[i
] = (double) 
 487                  SCALE(colormapSize
-1-i
, colormapSize
-1); 
 489     case PHOTOMETRIC_MINISBLACK
: 
 490         redMap 
= (u_short 
*) malloc(colormapSize 
* sizeof(u_short
)); 
 491         greenMap 
= (u_short 
*) malloc(colormapSize 
* sizeof(u_short
)); 
 492         blueMap 
= (u_short 
*) malloc(colormapSize 
* sizeof(u_short
)); 
 493         MCHECK(redMap
); MCHECK(greenMap
); MCHECK(blueMap
); 
 494         for (i 
= 0; i 
< colormapSize
; i
++) 
 495             dRed
[i
] = dGreen
[i
] = dBlue
[i
] = (double) SCALE(i
, colormapSize
-1); 
 499             "xtiff: can't display photometric interpretation type %d\n", 
 500             tfPhotometricInterpretation
); 
 512         sprintf(buffer
, "%s - page %d", fileName
, tfDirectory
); 
 514         strcpy(buffer
, fileName
); 
 515     XtSetArg(args
[0], XtNlabel
, buffer
); 
 516     XtSetValues(labelWidget
, args
, 1); 
 520  * Many programs get TIFF colormaps wrong.  They use 8-bit colormaps instead of 
 521  * 16-bit colormaps.  This function is a heuristic to detect and correct this. 
 524 CheckAndCorrectColormap() 
 528     for (i 
= 0; i 
< colormapSize
; i
++) 
 529         if ((redMap
[i
] > 255) || (greenMap
[i
] > 255) || (blueMap
[i
] > 255)) 
 532     for (i 
= 0; i 
< colormapSize
; i
++) { 
 533         redMap
[i
] = SCALE(redMap
[i
], 255); 
 534         greenMap
[i
] = SCALE(greenMap
[i
], 255); 
 535         blueMap
[i
] = SCALE(blueMap
[i
], 255); 
 537     TIFFWarning(fileName
, "Assuming 8-bit colormap"); 
 541 SimpleGammaCorrection() 
 544     register double i_gamma 
= 1.0 / appData
.gamma
; 
 546     for (i 
= 0; i 
< colormapSize
; i
++) { 
 547         if (((tfPhotometricInterpretation 
== PHOTOMETRIC_MINISWHITE
) 
 548             && (i 
== colormapSize 
- 1)) 
 549             || ((tfPhotometricInterpretation 
== PHOTOMETRIC_MINISBLACK
) 
 551             redMap
[i
] = greenMap
[i
] = blueMap
[i
] = 0; 
 553             redMap
[i
] = ROUND((pow(dRed
[i
] / 65535.0, i_gamma
) * 65535.0)); 
 554             greenMap
[i
] = ROUND((pow(dGreen
[i
] / 65535.0, i_gamma
) * 65535.0)); 
 555             blueMap
[i
] = ROUND((pow(dBlue
[i
] / 65535.0, i_gamma
) * 65535.0)); 
 559     free(dRed
); free(dGreen
); free(dBlue
); 
 562 static char* classNames
[] = { 
 572  * Current limitation: the visual is set initially by the first file. 
 573  * It cannot be changed. 
 578     register XColor 
*colors 
= NULL
; 
 579     register u_long 
*pixels 
= NULL
; 
 582     switch (tfImageDepth
) { 
 584      * X really wants a 32-bit image with the fourth channel unused, 
 585      * but the visual structure thinks it's 24-bit.  bitmap_unit is 32. 
 589         if (SearchVisualList(24, DirectColor
, &xVisual
) == False
) { 
 590             fprintf(stderr
, "xtiff: 24-bit DirectColor visual not available\n"); 
 594         colors 
= (XColor 
*) malloc(3 * colormapSize 
* sizeof(XColor
)); 
 597         for (i 
= 0; i 
< colormapSize
; i
++) { 
 598             colors
[i
].pixel 
= (u_long
) (i 
<< 16) + (i 
<< 8) + i
; 
 599             colors
[i
].red 
= redMap
[i
]; 
 600             colors
[i
].green 
= greenMap
[i
]; 
 601             colors
[i
].blue 
= blueMap
[i
]; 
 602             colors
[i
].flags 
= DoRed 
| DoGreen 
| DoBlue
; 
 605         xColormap 
= XCreateColormap(xDisplay
, RootWindow(xDisplay
, xScreen
), 
 607         XStoreColors(xDisplay
, xColormap
, colors
, colormapSize
); 
 613          * We assume that systems with 24-bit visuals also have 8-bit visuals. 
 614          * We don't promote from 8-bit PseudoColor to 24/32 bit DirectColor. 
 616         switch (tfPhotometricInterpretation
) { 
 617         case PHOTOMETRIC_MINISWHITE
: 
 618         case PHOTOMETRIC_MINISBLACK
: 
 619             if (SearchVisualList((int) tfImageDepth
, GrayScale
, &xVisual
) == True
) 
 621         case PHOTOMETRIC_PALETTE
: 
 622             if (SearchVisualList((int) tfImageDepth
, PseudoColor
, &xVisual
) == True
) 
 625             fprintf(stderr
, "xtiff: Unsupported TIFF/X configuration\n"); 
 629         colors 
= (XColor 
*) malloc(colormapSize 
* sizeof(XColor
)); 
 632         for (i 
= 0; i 
< colormapSize
; i
++) { 
 633             colors
[i
].pixel 
= (u_long
) i
; 
 634             colors
[i
].red 
= redMap
[i
]; 
 635             colors
[i
].green 
= greenMap
[i
]; 
 636             colors
[i
].blue 
= blueMap
[i
]; 
 637             colors
[i
].flags 
= DoRed 
| DoGreen 
| DoBlue
; 
 641          * xtiff's colormap allocation is private.  It does not attempt 
 642          * to detect whether any existing colormap entries are suitable 
 643          * for its use.  This will cause colormap flashing.  Furthermore, 
 644          * background and foreground are taken from the environment. 
 645          * For example, the foreground color may be red when the visual 
 646          * is GrayScale.  If the colormap is completely populated, 
 647          * Xt will not be able to allocate fg and bg. 
 649         if (tfImageDepth 
== 8) 
 650             xColormap 
= XCreateColormap(xDisplay
, RootWindow(xDisplay
, xScreen
), 
 653             xColormap 
= XCreateColormap(xDisplay
, RootWindow(xDisplay
, xScreen
), 
 655             pixels 
= (u_long 
*) malloc(colormapSize 
* sizeof(u_long
)); 
 657             (void) XAllocColorCells(xDisplay
, xColormap
, True
, 
 658                 NULL
, 0, pixels
, colormapSize
); 
 659             basePixel 
= (u_char
) pixels
[0]; 
 662         XStoreColors(xDisplay
, xColormap
, colors
, colormapSize
); 
 666         xVisual 
= DefaultVisual(xDisplay
, xScreen
); 
 667         xColormap 
= DefaultColormap(xDisplay
, xScreen
); 
 670         fprintf(stderr
, "xtiff: unsupported image depth %d\n", tfImageDepth
); 
 674     if (appData
.verbose 
== True
) 
 675         fprintf(stderr
, "%s: Using %d-bit %s visual.\n", 
 676             fileName
, xImageDepth
, classNames
[xVisual
->class]); 
 684     if (greenMap 
!= NULL
) 
 689     colors 
= NULL
; grayMap 
= redMap 
= greenMap 
= blueMap 
= NULL
; 
 693  * Search for an appropriate visual.  Promote where necessary. 
 694  * Check to make sure that ENOUGH colormap entries are writeable. 
 695  * basePixel was determined when XAllocColorCells() contiguously 
 696  * allocated enough entries.  basePixel is used below in GetTIFFImage. 
 699 SearchVisualList(image_depth
, visual_class
, visual
) 
 700     int image_depth
, visual_class
; 
 703     XVisualInfo template_visual
, *visual_list
, *vl
; 
 706     template_visual
.screen 
= xScreen
; 
 707     vl 
= visual_list 
= XGetVisualInfo(xDisplay
, VisualScreenMask
, 
 708         &template_visual
, &n_visuals
); 
 710     if (n_visuals 
== 0) { 
 711         fprintf(stderr
, "xtiff: visual list not available\n"); 
 715     for (i 
= 0; i 
< n_visuals
; vl
++, i
++) { 
 716         if ((vl
->class == visual_class
) && (vl
->depth 
>= image_depth
) 
 717             && (vl
->visual
->map_entries 
>= (1 << vl
->depth
))) { 
 718             *visual 
= vl
->visual
; 
 719             xImageDepth 
= vl
->depth
; 
 720             xRedMask 
= vl
->red_mask
; 
 721             xGreenMask 
= vl
->green_mask
; 
 722             xBlueMask 
= vl
->blue_mask
; 
 723             XFree((char *) visual_list
); 
 728     XFree((char *) visual_list
); 
 735     int pixel_map
[3], red_shift
, green_shift
, blue_shift
; 
 736     register u_char 
*scan_line
, *output_p
, *input_p
; 
 737     register int i
, j
, s
; 
 739     scan_line 
= (u_char 
*) malloc(tfBytesPerRow 
= TIFFScanlineSize(tfFile
)); 
 742     if ((tfImageDepth 
== 32) || (tfImageDepth 
== 24)) { 
 743         output_p 
= imageMemory 
= (u_char 
*) 
 744             malloc(tfImageWidth 
* tfImageHeight 
* 4); 
 748          * Handle different color masks for different frame buffers. 
 750         if (ImageByteOrder(xDisplay
) == LSBFirst
) { /* DECstation 5000 */ 
 751             red_shift 
= pixel_map
[0] = xRedMask 
== 0xFF000000 ? 3 
 752                 : (xRedMask 
== 0xFF0000 ? 2 : (xRedMask 
== 0xFF00 ? 1 : 0)); 
 753             green_shift 
= pixel_map
[1] = xGreenMask 
== 0xFF000000 ? 3 
 754                 : (xGreenMask 
== 0xFF0000 ? 2 : (xGreenMask 
== 0xFF00 ? 1 : 0)); 
 755             blue_shift 
= pixel_map
[2] = xBlueMask 
== 0xFF000000 ? 3 
 756                 : (xBlueMask 
== 0xFF0000 ? 2 : (xBlueMask 
== 0xFF00 ? 1 : 0)); 
 757         } else { /* Ardent */ 
 758             red_shift 
= pixel_map
[0] = xRedMask 
== 0xFF000000 ? 0 
 759                 : (xRedMask 
== 0xFF0000 ? 1 : (xRedMask 
== 0xFF00 ? 2 : 3)); 
 760             green_shift 
= pixel_map
[0] = xGreenMask 
== 0xFF000000 ? 0 
 761                 : (xGreenMask 
== 0xFF0000 ? 1 : (xGreenMask 
== 0xFF00 ? 2 : 3)); 
 762             blue_shift 
= pixel_map
[0] = xBlueMask 
== 0xFF000000 ? 0 
 763                 : (xBlueMask 
== 0xFF0000 ? 1 : (xBlueMask 
== 0xFF00 ? 2 : 3)); 
 766         if (tfPlanarConfiguration 
== PLANARCONFIG_CONTIG
) { 
 767             for (i 
= 0; i 
< tfImageHeight
; i
++) { 
 768                 if (TIFFReadScanline(tfFile
, scan_line
, i
, 0) < 0) 
 770                 for (input_p 
= scan_line
, j 
= 0; j 
< tfImageWidth
; j
++) { 
 771                     *(output_p 
+ red_shift
) = *input_p
++; 
 772                     *(output_p 
+ green_shift
) = *input_p
++; 
 773                     *(output_p 
+ blue_shift
) = *input_p
++; 
 775                     if (tfSamplesPerPixel 
== 4) /* skip the fourth channel */ 
 780             for (s 
= 0; s 
< tfSamplesPerPixel
; s
++) { 
 781                 if (s 
== 3)             /* skip the fourth channel */ 
 783                 for (i 
= 0; i 
< tfImageHeight
; i
++) { 
 784                     if (TIFFReadScanline(tfFile
, scan_line
, i
, s
) < 0) 
 787                     output_p 
= imageMemory 
+ (i
*tfImageWidth
*4) + pixel_map
[s
]; 
 788                     for (j 
= 0; j 
< tfImageWidth
; j
++, output_p 
+= 4) 
 789                         *output_p 
= *input_p
++; 
 794         if (xImageDepth 
== tfImageDepth
) { 
 795             output_p 
= imageMemory 
= (u_char 
*) 
 796                 malloc(tfBytesPerRow 
* tfImageHeight
); 
 799             for (i 
= 0; i 
< tfImageHeight
; i
++, output_p 
+= tfBytesPerRow
) 
 800                 if (TIFFReadScanline(tfFile
, output_p
, i
, 0) < 0) 
 802         } else if ((xImageDepth 
== 8) && (tfImageDepth 
== 4)) { 
 803             output_p 
= imageMemory 
= (u_char 
*) 
 804                 malloc(tfBytesPerRow 
* 2 * tfImageHeight 
+ 2); 
 808              * If a scanline is of odd size the inner loop below will overshoot. 
 809              * This is handled very simply by recalculating the start point at 
 810              * each scanline and padding imageMemory a little at the end. 
 812             for (i 
= 0; i 
< tfImageHeight
; i
++) { 
 813                 if (TIFFReadScanline(tfFile
, scan_line
, i
, 0) < 0) 
 815                 output_p 
= &imageMemory
[i 
* tfImageWidth
]; 
 817                 for (j 
= 0; j 
< tfImageWidth
; j 
+= 2, input_p
++) { 
 818                     *output_p
++ = (*input_p 
>> 4) + basePixel
; 
 819                     *output_p
++ = (*input_p 
& 0xf) + basePixel
; 
 822         } else if ((xImageDepth 
== 8) && (tfImageDepth 
== 2)) { 
 823             output_p 
= imageMemory 
= (u_char 
*) 
 824                 malloc(tfBytesPerRow 
* 4 * tfImageHeight 
+ 4); 
 827             for (i 
= 0; i 
< tfImageHeight
; i
++) { 
 828                 if (TIFFReadScanline(tfFile
, scan_line
, i
, 0) < 0) 
 830                 output_p 
= &imageMemory
[i 
* tfImageWidth
]; 
 832                 for (j 
= 0; j 
< tfImageWidth
; j 
+= 4, input_p
++) { 
 833                     *output_p
++ = (*input_p 
>> 6) + basePixel
; 
 834                     *output_p
++ = ((*input_p 
>> 4) & 3) + basePixel
; 
 835                     *output_p
++ = ((*input_p 
>> 2) & 3) + basePixel
; 
 836                     *output_p
++ = (*input_p 
& 3) + basePixel
; 
 839         } else if ((xImageDepth 
== 4) && (tfImageDepth 
== 2)) { 
 840             output_p 
= imageMemory 
= (u_char 
*) 
 841                 malloc(tfBytesPerRow 
* 2 * tfImageHeight 
+ 2); 
 844             for (i 
= 0; i 
< tfImageHeight
; i
++) { 
 845                 if (TIFFReadScanline(tfFile
, scan_line
, i
, 0) < 0) 
 847                 output_p 
= &imageMemory
[i 
* tfBytesPerRow 
* 2]; 
 849                 for (j 
= 0; j 
< tfImageWidth
; j 
+= 4, input_p
++) { 
 850                     *output_p
++ = (((*input_p
>>6) << 4) 
 851                         | ((*input_p 
>> 4) & 3)) + basePixel
; 
 852                     *output_p
++ = ((((*input_p
>>2) & 3) << 4) 
 853                         | (*input_p 
& 3)) + basePixel
; 
 858                 "xtiff: can't handle %d-bit TIFF file on an %d-bit display\n", 
 859                 tfImageDepth
, xImageDepth
); 
 873     xOffset 
= yOffset 
= 0; 
 876     xImage 
= XCreateImage(xDisplay
, xVisual
, xImageDepth
, 
 877         xImageDepth 
== 1 ? XYBitmap 
: ZPixmap
, /* offset */ 0, 
 878         (char *) imageMemory
, tfImageWidth
, tfImageHeight
, 
 879         /* bitmap_pad */ 8, /* bytes_per_line */ 0); 
 882      * libtiff converts LSB data into MSB but doesn't change the FillOrder tag. 
 884     if (xImageDepth 
== 1) 
 885         xImage
->bitmap_bit_order 
= MSBFirst
; 
 886     if (xImageDepth 
<= 8) 
 887         xImage
->byte_order 
= MSBFirst
; 
 890      * create an appropriate GC 
 892     gc_values
.function 
= GXcopy
; 
 893     gc_values
.plane_mask 
= AllPlanes
; 
 894     if (tfPhotometricInterpretation 
== PHOTOMETRIC_MINISBLACK
) { 
 895         gc_values
.foreground 
= XWhitePixel(xDisplay
, xScreen
); 
 896         gc_values
.background 
= XBlackPixel(xDisplay
, xScreen
); 
 898         gc_values
.foreground 
= XBlackPixel(xDisplay
, xScreen
); 
 899         gc_values
.background 
= XWhitePixel(xDisplay
, xScreen
); 
 901     xWinGc 
= XCreateGC(xDisplay
, XtWindow(shellWidget
), 
 902         GCFunction 
| GCPlaneMask 
| GCForeground 
| GCBackground
, &gc_values
); 
 905      * create the pixmap and load the image 
 907     if (appData
.usePixmap 
== True
) { 
 908         xImagePixmap 
= XCreatePixmap(xDisplay
, RootWindow(xDisplay
, xScreen
), 
 909             xImage
->width
, xImage
->height
, xImageDepth
); 
 912          * According to the O'Reilly X Protocol Reference Manual, page 53, 
 913          * "A pixmap depth of one is always supported and listed, but windows 
 914          * of depth one might not be supported."  Therefore we create a pixmap 
 915          * of depth one and use XCopyPlane().  This is idiomatic. 
 917         if (xImageDepth 
== 1) {         /* just pass the bits through */ 
 918             gc_values
.foreground 
= 1;   /* foreground describes set bits */ 
 919             gc_values
.background 
= 0;   /* background describes clear bits */ 
 920             bitmap_gc 
= XCreateGC(xDisplay
, xImagePixmap
, 
 921                 GCForeground 
| GCBackground
, &gc_values
); 
 922             XPutImage(xDisplay
, xImagePixmap
, bitmap_gc
, xImage
, 
 923                 0, 0, 0, 0, xImage
->width
, xImage
->height
); 
 925             XPutImage(xDisplay
, xImagePixmap
, xWinGc
, xImage
, 
 926                 0, 0, 0, 0, xImage
->width
, xImage
->height
); 
 927         XDestroyImage(xImage
); 
 933 SelectProc(w
, unused_1
, unused_2
) 
 938     XawListReturnStruct 
*list_return
; 
 940     list_return 
= XawListShowCurrent(w
); 
 942     switch (list_return
->list_index
) { 
 946     case ButtonPreviousPage
: 
 953         fprintf(stderr
, "error in SelectProc\n"); 
 956     XawListUnhighlight(w
); 
 968     PageProc(ButtonNextPage
); 
 974     PageProc(ButtonPreviousPage
); 
 985     case ButtonPreviousPage
: 
 987             TIFFSetDirectory(tfFile
, --tfDirectory
); 
 992         if (TIFFReadDirectory(tfFile
) == True
) 
 998         fprintf(stderr
, "error in PageProc\n"); 
1002     xOffset 
= yOffset 
= 0; 
1009     if (appData
.usePixmap 
== True
) 
1010         XFreePixmap(xDisplay
, xImagePixmap
); 
1012         XDestroyImage(xImage
); 
1017      * Using XtSetValues() to set the widget size causes a resize. 
1018      * This resize gets propagated up to the parent shell. 
1019      * In order to disable this visually disconcerting effect, 
1020      * shell resizing is temporarily disabled. 
1022     XtSetArg(args
[0], XtNallowShellResize
, False
); 
1023     XtSetValues(shellWidget
, args
, 1); 
1025     XtSetArg(args
[0], XtNwidth
, tfImageWidth
); 
1026     XtSetArg(args
[1], XtNheight
, tfImageHeight
); 
1027     XtSetValues(imageWidget
, args
, 2); 
1029     XtSetArg(args
[0], XtNallowShellResize
, True
); 
1030     XtSetValues(shellWidget
, args
, 1); 
1032     XClearWindow(xDisplay
, XtWindow(imageWidget
)); 
1034     fake_event
.type 
= Expose
; 
1035     fake_event
.xexpose
.x 
= fake_event
.xexpose
.y 
= 0; 
1036     fake_event
.xexpose
.width 
= tfImageWidth
;    /* the window will clip */ 
1037     fake_event
.xexpose
.height 
= tfImageHeight
; 
1038     EventProc(imageWidget
, NULL
, &fake_event
); 
1042 EventProc(widget
, unused
, event
) 
1047     int ih
, iw
, ww
, wh
, sx
, sy
, w
, h
, dx
, dy
; 
1048     Dimension w_width
, w_height
; 
1052     if (event
->type 
== MappingNotify
) { 
1053         XRefreshKeyboardMapping((XMappingEvent 
*) event
); 
1057     if (!XtIsRealized(widget
)) 
1060     if ((event
->type 
== ButtonPress
) || (event
->type 
== ButtonRelease
)) 
1061         if (event
->xbutton
.button 
!= Button1
) 
1064     iw 
= tfImageWidth
;  /* avoid sign problems */ 
1068      * The grabX and grabY variables record where the user grabbed the image. 
1069      * They also record whether the mouse button is down or not. 
1071     if (event
->type 
== ButtonPress
) { 
1072         grabX 
= event
->xbutton
.x
; 
1073         grabY 
= event
->xbutton
.y
; 
1078      * imageWidget is a Core widget and doesn't get resized. 
1079      * So we calculate the size of its viewport here. 
1081     XtSetArg(args
[0], XtNwidth
, &w_width
); 
1082     XtSetArg(args
[1], XtNheight
, &w_height
); 
1083     XtGetValues(shellWidget
, args
, 2); 
1086     XtGetValues(listWidget
, args
, 2); 
1089     switch (event
->type
) { 
1091         dx 
= event
->xexpose
.x
; 
1092         dy 
= event
->xexpose
.y
; 
1095         w 
= MIN(event
->xexpose
.width
, iw
); 
1096         h 
= MIN(event
->xexpose
.height
, ih
); 
1099         if ((grabX 
>= 0) || (grabY 
>= 0)) /* Mouse button is still down */ 
1101         switch (XLookupKeysym((XKeyEvent 
*) event
, /* KeySyms index */ 0)) { 
1103             if (ih 
< wh
)    /* Don't scroll if the window fits the image. */ 
1105             sy 
= yOffset 
+ appData
.translate
; 
1106             sy 
= MIN(ih 
- wh
, sy
); 
1107             if (sy 
== yOffset
)  /* Filter redundant stationary refreshes. */ 
1117             sy 
= yOffset 
- appData
.translate
; 
1129             sx 
= xOffset 
+ appData
.translate
; 
1130             sx 
= MIN(iw 
- ww
, sx
); 
1141             sx 
= xOffset 
- appData
.translate
; 
1156          * MotionEvent compression.  Ignore multiple motion events. 
1157          * Ignore motion events if the mouse button is up. 
1159         if (XPending(xDisplay
)) /* Xlib doesn't flush the output buffer */ 
1160             if (XtPeekEvent(&next_event
)) 
1161                 if (next_event
.type 
== MotionNotify
) 
1163         if ((grabX 
< 0) || (grabY 
< 0)) 
1165         sx 
= xOffset 
+ grabX 
- (int) event
->xmotion
.x
; 
1166         if (sx 
>= (iw 
- ww
))    /* clamp x motion but allow y motion */ 
1169         sy 
= yOffset 
+ grabY 
- (int) event
->xmotion
.y
; 
1170         if (sy 
>= (ih 
- wh
)) /* clamp y motion but allow x motion */ 
1173         if ((sx 
== xOffset
) && (sy 
== yOffset
)) 
1179         xOffset 
= xOffset 
+ grabX 
- (int) event
->xbutton
.x
; 
1180         xOffset 
= MIN(iw 
- ww
, xOffset
); 
1181         xOffset 
= MAX(xOffset
, 0); 
1182         yOffset 
= yOffset 
+ grabY 
- (int) event
->xbutton
.y
; 
1183         yOffset 
= MIN(ih 
- wh
, yOffset
); 
1184         yOffset 
= MAX(yOffset
, 0); 
1190     if (appData
.usePixmap 
== True
) { 
1191         if (xImageDepth 
== 1) 
1192             XCopyPlane(xDisplay
, xImagePixmap
, XtWindow(widget
), 
1193                 xWinGc
, sx
, sy
, w
, h
, dx
, dy
, 1); 
1195             XCopyArea(xDisplay
, xImagePixmap
, XtWindow(widget
), 
1196                 xWinGc
, sx
, sy
, w
, h
, dx
, dy
); 
1198         XPutImage(xDisplay
, XtWindow(widget
), xWinGc
, xImage
, 
1199             sx
, sy
, dx
, dy
, w
, h
); 
1205     Dimension w_width
, w_height
; 
1210     if ((xOffset 
== 0) && (yOffset 
== 0)) 
1213     XtSetArg(args
[0], XtNwidth
, &w_width
); 
1214     XtSetArg(args
[1], XtNheight
, &w_height
); 
1215     XtGetValues(shellWidget
, args
, 2); 
1218     XtGetValues(listWidget
, args
, 2); 
1221     xo 
= xOffset
; yo 
= yOffset
; 
1223     if ((xOffset 
+ ww
) >= tfImageWidth
) 
1224         xOffset 
= MAX((int) tfImageWidth 
- ww
, 0); 
1225     if ((yOffset 
+ wh
) >= tfImageHeight
) 
1226         yOffset 
= MAX((int) tfImageHeight 
- wh
, 0); 
1229      * Send an ExposeEvent if the origin changed. 
1230      * We have to do this because of the use and semantics of bit gravity. 
1232     if ((xo 
!= xOffset
) || (yo 
!= yOffset
)) { 
1233         fake_event
.type 
= Expose
; 
1234         fake_event
.xexpose
.x 
= fake_event
.xexpose
.y 
= 0; 
1235         fake_event
.xexpose
.width 
= tfImageWidth
; 
1236         fake_event
.xexpose
.height 
= tfImageHeight
; 
1237         EventProc(imageWidget
, NULL
, &fake_event
); 
1242 XTiffErrorHandler(display
, error_event
) 
1244     XErrorEvent 
*error_event
; 
1249      * Some X servers limit the size of pixmaps. 
1251     if ((error_event
->error_code 
== BadAlloc
) 
1252             && (error_event
->request_code 
== X_CreatePixmap
)) 
1253         fprintf(stderr
, "xtiff: requested pixmap too big for display\n"); 
1255         XGetErrorText(display
, error_event
->error_code
, message
, 80); 
1256         fprintf(stderr
, "xtiff: error code %s\n", message
); 
1265     fprintf(stderr
, "Usage xtiff: [options] tiff-file\n"); 
1266     fprintf(stderr
, "\tstandard Xt options\n"); 
1267     fprintf(stderr
, "\t[-help]\n"); 
1268     fprintf(stderr
, "\t[-gamma gamma]\n"); 
1269     fprintf(stderr
, "\t[-usePixmap (True | False)]\n"); 
1270     fprintf(stderr
, "\t[-viewportWidth pixels]\n"); 
1271     fprintf(stderr
, "\t[-viewportHeight pixels]\n"); 
1272     fprintf(stderr
, "\t[-translate pixels]\n"); 
1273     fprintf(stderr
, "\t[-verbose (True | False)]\n");