3 * xtiff - view a TIFF file in an X window
8 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
12 * Permission to use, copy, modify, and distribute this software and its
13 * documentation for any purpose and without fee is hereby granted,
14 * provided that the above copyright notice appear in all copies and that
15 * both that copyright notice and this permission notice appear in
16 * supporting documentation, and that the name of Digital not be
17 * used in advertising or publicity pertaining to distribution of the
18 * software without specific, written prior permission.
20 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
21 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
22 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
23 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
24 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
25 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
28 * Revision 1.0 90/05/07
30 * Revision 2.0 90/12/20
31 * Converted to use the Athena Widgets and the Xt Intrinsics.
35 * According to the TIFF 5.0 Specification, it is possible to have
36 * both a TIFFTAG_COLORMAP and a TIFFTAG_COLORRESPONSECURVE. This
37 * doesn't make sense since a TIFFTAG_COLORMAP is 16 bits wide and
38 * a TIFFTAG_COLORRESPONSECURVE is tfBitsPerSample bits wide for each
39 * channel. This is probably a bug in the specification.
40 * In this case, TIFFTAG_COLORRESPONSECURVE is ignored.
41 * This might make sense if TIFFTAG_COLORMAP was 8 bits wide.
43 * TIFFTAG_COLORMAP is often incorrectly written as ranging from
44 * 0 to 255 rather than from 0 to 65535. CheckAndCorrectColormap()
47 * Only ORIENTATION_TOPLEFT is supported correctly. This is the
48 * default TIFF and X orientation. Other orientations will be
49 * displayed incorrectly.
51 * There is no support for or use of 3/3/2 DirectColor visuals.
52 * TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE are not supported.
54 * Only TIFFTAG_BITSPERSAMPLE values that are 1, 2, 4 or 8 are supported.
60 #include <X11/Xatom.h>
61 #include <X11/Intrinsic.h>
62 #include <X11/StringDefs.h>
63 #include <X11/Xproto.h>
64 #include <X11/Shell.h>
65 #include <X11/Xaw/Form.h>
66 #include <X11/Xaw/List.h>
67 #include <X11/Xaw/Label.h>
68 #include <X11/cursorfont.h>
70 #include <X11/keysymdef.h>
71 #include "xtifficon.h"
73 #define TIFF_GAMMA "2.2" /* default gamma from the TIFF 5.0 spec */
74 #define ROUND(x) (uint16) ((x) + 0.5)
75 #define SCALE(x, s) (((x) * 65535L) / (s))
76 #define MCHECK(m) if (!m) { fprintf(stderr, "malloc failed\n"); exit(0); }
77 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
78 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
79 #define VIEWPORT_WIDTH 700
80 #define VIEWPORT_HEIGHT 500
81 #define KEY_TRANSLATE 20
89 int main
PP((int argc
, char **argv
));
90 void OpenTIFFFile
PP((void));
91 void GetTIFFHeader
PP((void));
92 void SetNameLabel
PP((void));
93 void CheckAndCorrectColormap
PP((void));
94 void SimpleGammaCorrection
PP((void));
95 void GetVisual
PP((void));
96 Boolean SearchVisualList
PP((int image_depth
,
97 int visual_class
, Visual
**visual
));
98 void GetTIFFImage
PP((void));
99 void CreateXImage
PP((void));
100 XtCallbackProc SelectProc
PP((Widget w
, caddr_t unused_1
, caddr_t unused_2
));
101 void QuitProc
PP((void));
102 void NextProc
PP((void));
103 void PreviousProc
PP((void));
104 void PageProc
PP((int direction
));
105 void EventProc
PP((Widget widget
, caddr_t unused
, XEvent
*event
));
106 void ResizeProc
PP((void));
107 int XTiffErrorHandler
PP((Display
*display
, XErrorEvent
*error_event
));
108 void Usage
PP((void));
110 int xtVersion
= XtSpecificationRelease
; /* xtiff depends on R4 or higher */
115 Widget shellWidget
, formWidget
, listWidget
, labelWidget
, imageWidget
;
117 enum { ButtonQuit
= 0, ButtonPreviousPage
= 1, ButtonNextPage
= 2 };
119 String buttonStrings
[] = { "Quit", "Previous", "Next" };
121 static XrmOptionDescRec shellOptions
[] = {
122 { "-help", "*help", XrmoptionNoArg
, (caddr_t
) "True" },
123 { "-gamma", "*gamma", XrmoptionSepArg
, NULL
},
124 { "-usePixmap", "*usePixmap", XrmoptionSepArg
, NULL
},
125 { "-viewportWidth", "*viewportWidth", XrmoptionSepArg
, NULL
},
126 { "-viewportHeight", "*viewportHeight", XrmoptionSepArg
, NULL
},
127 { "-translate", "*translate", XrmoptionSepArg
, NULL
},
128 { "-verbose", "*verbose", XrmoptionSepArg
, NULL
}
135 uint32 viewportWidth
;
136 uint32 viewportHeight
;
139 } AppData
, *AppDataPtr
;
143 XtResource clientResources
[] = {
145 "help", XtCBoolean
, XtRBoolean
, sizeof(Boolean
),
146 XtOffset(AppDataPtr
, help
), XtRImmediate
, (XtPointer
) False
148 "gamma", "Gamma", XtRFloat
, sizeof(float),
149 XtOffset(AppDataPtr
, gamma
), XtRString
, (XtPointer
) TIFF_GAMMA
151 "usePixmap", "UsePixmap", XtRBoolean
, sizeof(Boolean
),
152 XtOffset(AppDataPtr
, usePixmap
), XtRImmediate
, (XtPointer
) True
154 "viewportWidth", "ViewportWidth", XtRInt
, sizeof(int),
155 XtOffset(AppDataPtr
, viewportWidth
), XtRImmediate
,
156 (XtPointer
) VIEWPORT_WIDTH
158 "viewportHeight", "ViewportHeight", XtRInt
, sizeof(int),
159 XtOffset(AppDataPtr
, viewportHeight
), XtRImmediate
,
160 (XtPointer
) VIEWPORT_HEIGHT
162 "translate", "Translate", XtRInt
, sizeof(int),
163 XtOffset(AppDataPtr
, translate
), XtRImmediate
, (XtPointer
) KEY_TRANSLATE
165 "verbose", "Verbose", XtRBoolean
, sizeof(Boolean
),
166 XtOffset(AppDataPtr
, verbose
), XtRImmediate
, (XtPointer
) True
171 { XtNresizable
, True
}
175 { XtNresizable
, False
},
176 { XtNborderWidth
, 0 },
177 { XtNdefaultColumns
, 3 },
178 { XtNforceColumns
, True
},
179 { XtNlist
, (int) buttonStrings
},
180 { XtNnumberStrings
, XtNumber(buttonStrings
) },
181 { XtNtop
, XtChainTop
},
182 { XtNleft
, XtChainLeft
},
183 { XtNbottom
, XtChainTop
},
184 { XtNright
, XtChainLeft
}
188 { XtNresizable
, False
},
190 { XtNborderWidth
, 0 },
191 { XtNjustify
, XtJustifyLeft
},
192 { XtNtop
, XtChainTop
},
193 { XtNleft
, XtChainLeft
},
194 { XtNbottom
, XtChainTop
},
195 { XtNright
, XtChainLeft
}
199 { XtNresizable
, True
},
200 { XtNborderWidth
, 0 },
201 { XtNtop
, XtChainTop
},
202 { XtNleft
, XtChainLeft
},
203 { XtNbottom
, XtChainTop
},
204 { XtNright
, XtChainLeft
}
207 XtActionsRec actionsTable
[] = {
208 { "quit", QuitProc
},
209 { "next", NextProc
},
210 { "previous", PreviousProc
},
211 { "notifyresize", ResizeProc
}
214 char translationsTable
[] = "<Key>q: quit() \n \
216 <Message>WM_PROTOCOLS: quit()\n \
217 <Key>p: previous() \n \
218 <Key>P: previous() \n \
221 <Configure>: notifyresize()";
232 int xImageDepth
, xScreen
, xRedMask
, xGreenMask
, xBlueMask
,
233 xOffset
= 0, yOffset
= 0, grabX
= -1, grabY
= -1;
234 unsigned char basePixel
= 0;
237 * TIFF data structures
239 TIFF
* tfFile
= NULL
;
240 uint32 tfImageWidth
, tfImageHeight
;
241 uint16 tfBitsPerSample
, tfSamplesPerPixel
, tfPlanarConfiguration
,
242 tfPhotometricInterpretation
, tfGrayResponseUnit
,
243 tfImageDepth
, tfBytesPerRow
;
244 int tfDirectory
= 0, tfMultiPage
= False
;
245 double tfUnitMap
, tfGrayResponseUnitMap
[] = {
246 -1, -10, -100, -1000, -10000, -100000
250 * display data structures
252 double *dRed
, *dGreen
, *dBlue
;
255 * shared data structures
257 uint16
* redMap
= NULL
, *greenMap
= NULL
, *blueMap
= NULL
,
258 *grayMap
= NULL
, colormapSize
;
263 main(int argc
, char **argv
)
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
);
392 if ((tfFile
= TIFFOpen(fileName
, "r")) == NULL
) {
393 fprintf(appData
.verbose
? stderr
: stdout
,
394 "xtiff: can't open %s as a TIFF file\n", fileName
);
398 tfMultiPage
= (TIFFLastDirectory(tfFile
) ? False
: True
);
406 if (!TIFFSetDirectory(tfFile
, tfDirectory
)) {
407 fprintf(stderr
, "xtiff: can't seek to directory %d in %s\n",
408 tfDirectory
, fileName
);
412 TIFFGetField(tfFile
, TIFFTAG_IMAGEWIDTH
, &tfImageWidth
);
413 TIFFGetField(tfFile
, TIFFTAG_IMAGELENGTH
, &tfImageHeight
);
416 * If the following tags aren't present then use the TIFF defaults.
418 TIFFGetFieldDefaulted(tfFile
, TIFFTAG_BITSPERSAMPLE
, &tfBitsPerSample
);
419 TIFFGetFieldDefaulted(tfFile
, TIFFTAG_SAMPLESPERPIXEL
, &tfSamplesPerPixel
);
420 TIFFGetFieldDefaulted(tfFile
, TIFFTAG_PLANARCONFIG
, &tfPlanarConfiguration
);
421 TIFFGetFieldDefaulted(tfFile
, TIFFTAG_GRAYRESPONSEUNIT
, &tfGrayResponseUnit
);
423 tfUnitMap
= tfGrayResponseUnitMap
[tfGrayResponseUnit
];
424 colormapSize
= 1 << tfBitsPerSample
;
425 tfImageDepth
= tfBitsPerSample
* tfSamplesPerPixel
;
427 dRed
= (double *) malloc(colormapSize
* sizeof(double));
428 dGreen
= (double *) malloc(colormapSize
* sizeof(double));
429 dBlue
= (double *) malloc(colormapSize
* sizeof(double));
430 MCHECK(dRed
); MCHECK(dGreen
); MCHECK(dBlue
);
433 * If TIFFTAG_PHOTOMETRIC is not present then assign a reasonable default.
434 * The TIFF 5.0 specification doesn't give a default.
436 if (!TIFFGetField(tfFile
, TIFFTAG_PHOTOMETRIC
,
437 &tfPhotometricInterpretation
)) {
438 if (tfSamplesPerPixel
!= 1)
439 tfPhotometricInterpretation
= PHOTOMETRIC_RGB
;
440 else if (tfBitsPerSample
== 1)
441 tfPhotometricInterpretation
= PHOTOMETRIC_MINISBLACK
;
442 else if (TIFFGetField(tfFile
, TIFFTAG_COLORMAP
,
443 &redMap
, &greenMap
, &blueMap
)) {
444 tfPhotometricInterpretation
= PHOTOMETRIC_PALETTE
;
445 redMap
= greenMap
= blueMap
= NULL
;
447 tfPhotometricInterpretation
= PHOTOMETRIC_MINISBLACK
;
451 * Given TIFFTAG_PHOTOMETRIC extract or create the response curves.
453 switch (tfPhotometricInterpretation
) {
454 case PHOTOMETRIC_RGB
:
455 redMap
= (uint16
*) malloc(colormapSize
* sizeof(uint16
));
456 greenMap
= (uint16
*) malloc(colormapSize
* sizeof(uint16
));
457 blueMap
= (uint16
*) malloc(colormapSize
* sizeof(uint16
));
458 MCHECK(redMap
); MCHECK(greenMap
); MCHECK(blueMap
);
459 for (i
= 0; i
< colormapSize
; i
++)
460 dRed
[i
] = dGreen
[i
] = dBlue
[i
]
461 = (double) SCALE(i
, colormapSize
- 1);
463 case PHOTOMETRIC_PALETTE
:
464 if (!TIFFGetField(tfFile
, TIFFTAG_COLORMAP
,
465 &redMap
, &greenMap
, &blueMap
)) {
466 redMap
= (uint16
*) malloc(colormapSize
* sizeof(uint16
));
467 greenMap
= (uint16
*) malloc(colormapSize
* sizeof(uint16
));
468 blueMap
= (uint16
*) malloc(colormapSize
* sizeof(uint16
));
469 MCHECK(redMap
); MCHECK(greenMap
); MCHECK(blueMap
);
470 for (i
= 0; i
< colormapSize
; i
++)
471 dRed
[i
] = dGreen
[i
] = dBlue
[i
]
472 = (double) SCALE(i
, colormapSize
- 1);
474 CheckAndCorrectColormap();
475 for (i
= 0; i
< colormapSize
; i
++) {
476 dRed
[i
] = (double) redMap
[i
];
477 dGreen
[i
] = (double) greenMap
[i
];
478 dBlue
[i
] = (double) blueMap
[i
];
482 case PHOTOMETRIC_MINISWHITE
:
483 redMap
= (uint16
*) malloc(colormapSize
* sizeof(uint16
));
484 greenMap
= (uint16
*) malloc(colormapSize
* sizeof(uint16
));
485 blueMap
= (uint16
*) malloc(colormapSize
* sizeof(uint16
));
486 MCHECK(redMap
); MCHECK(greenMap
); MCHECK(blueMap
);
487 for (i
= 0; i
< colormapSize
; i
++)
488 dRed
[i
] = dGreen
[i
] = dBlue
[i
] = (double)
489 SCALE(colormapSize
-1-i
, colormapSize
-1);
491 case PHOTOMETRIC_MINISBLACK
:
492 redMap
= (uint16
*) malloc(colormapSize
* sizeof(uint16
));
493 greenMap
= (uint16
*) malloc(colormapSize
* sizeof(uint16
));
494 blueMap
= (uint16
*) malloc(colormapSize
* sizeof(uint16
));
495 MCHECK(redMap
); MCHECK(greenMap
); MCHECK(blueMap
);
496 for (i
= 0; i
< colormapSize
; i
++)
497 dRed
[i
] = dGreen
[i
] = dBlue
[i
] = (double) SCALE(i
, colormapSize
-1);
501 "xtiff: can't display photometric interpretation type %d\n",
502 tfPhotometricInterpretation
);
514 sprintf(buffer
, "%s - page %d", fileName
, tfDirectory
);
516 strcpy(buffer
, fileName
);
517 XtSetArg(args
[0], XtNlabel
, buffer
);
518 XtSetValues(labelWidget
, args
, 1);
522 * Many programs get TIFF colormaps wrong. They use 8-bit colormaps instead of
523 * 16-bit colormaps. This function is a heuristic to detect and correct this.
526 CheckAndCorrectColormap()
530 for (i
= 0; i
< colormapSize
; i
++)
531 if ((redMap
[i
] > 255) || (greenMap
[i
] > 255) || (blueMap
[i
] > 255))
534 for (i
= 0; i
< colormapSize
; i
++) {
535 redMap
[i
] = SCALE(redMap
[i
], 255);
536 greenMap
[i
] = SCALE(greenMap
[i
], 255);
537 blueMap
[i
] = SCALE(blueMap
[i
], 255);
539 TIFFWarning(fileName
, "Assuming 8-bit colormap");
543 SimpleGammaCorrection()
546 register double i_gamma
= 1.0 / appData
.gamma
;
548 for (i
= 0; i
< colormapSize
; i
++) {
549 if (((tfPhotometricInterpretation
== PHOTOMETRIC_MINISWHITE
)
550 && (i
== colormapSize
- 1))
551 || ((tfPhotometricInterpretation
== PHOTOMETRIC_MINISBLACK
)
553 redMap
[i
] = greenMap
[i
] = blueMap
[i
] = 0;
555 redMap
[i
] = ROUND((pow(dRed
[i
] / 65535.0, i_gamma
) * 65535.0));
556 greenMap
[i
] = ROUND((pow(dGreen
[i
] / 65535.0, i_gamma
) * 65535.0));
557 blueMap
[i
] = ROUND((pow(dBlue
[i
] / 65535.0, i_gamma
) * 65535.0));
561 free(dRed
); free(dGreen
); free(dBlue
);
564 static char* classNames
[] = {
574 * Current limitation: the visual is set initially by the first file.
575 * It cannot be changed.
580 XColor
*colors
= NULL
;
581 unsigned long *pixels
= NULL
;
584 switch (tfImageDepth
) {
586 * X really wants a 32-bit image with the fourth channel unused,
587 * but the visual structure thinks it's 24-bit. bitmap_unit is 32.
591 if (SearchVisualList(24, DirectColor
, &xVisual
) == False
) {
592 fprintf(stderr
, "xtiff: 24-bit DirectColor visual not available\n");
596 colors
= (XColor
*) malloc(3 * colormapSize
* sizeof(XColor
));
599 for (i
= 0; i
< colormapSize
; i
++) {
600 colors
[i
].pixel
= (i
<< 16) + (i
<< 8) + i
;
601 colors
[i
].red
= redMap
[i
];
602 colors
[i
].green
= greenMap
[i
];
603 colors
[i
].blue
= blueMap
[i
];
604 colors
[i
].flags
= DoRed
| DoGreen
| DoBlue
;
607 xColormap
= XCreateColormap(xDisplay
, RootWindow(xDisplay
, xScreen
),
609 XStoreColors(xDisplay
, xColormap
, colors
, colormapSize
);
615 * We assume that systems with 24-bit visuals also have 8-bit visuals.
616 * We don't promote from 8-bit PseudoColor to 24/32 bit DirectColor.
618 switch (tfPhotometricInterpretation
) {
619 case PHOTOMETRIC_MINISWHITE
:
620 case PHOTOMETRIC_MINISBLACK
:
621 if (SearchVisualList((int) tfImageDepth
, GrayScale
, &xVisual
) == True
)
623 case PHOTOMETRIC_PALETTE
:
624 if (SearchVisualList((int) tfImageDepth
, PseudoColor
, &xVisual
) == True
)
627 fprintf(stderr
, "xtiff: Unsupported TIFF/X configuration\n");
631 colors
= (XColor
*) malloc(colormapSize
* sizeof(XColor
));
634 for (i
= 0; i
< colormapSize
; i
++) {
636 colors
[i
].red
= redMap
[i
];
637 colors
[i
].green
= greenMap
[i
];
638 colors
[i
].blue
= blueMap
[i
];
639 colors
[i
].flags
= DoRed
| DoGreen
| DoBlue
;
643 * xtiff's colormap allocation is private. It does not attempt
644 * to detect whether any existing colormap entries are suitable
645 * for its use. This will cause colormap flashing. Furthermore,
646 * background and foreground are taken from the environment.
647 * For example, the foreground color may be red when the visual
648 * is GrayScale. If the colormap is completely populated,
649 * Xt will not be able to allocate fg and bg.
651 if (tfImageDepth
== 8)
652 xColormap
= XCreateColormap(xDisplay
, RootWindow(xDisplay
, xScreen
),
655 xColormap
= XCreateColormap(xDisplay
, RootWindow(xDisplay
, xScreen
),
657 pixels
= (unsigned long *)
658 malloc(colormapSize
* sizeof(unsigned long));
660 (void) XAllocColorCells(xDisplay
, xColormap
, True
,
661 NULL
, 0, pixels
, colormapSize
);
662 basePixel
= (unsigned char) pixels
[0];
665 XStoreColors(xDisplay
, xColormap
, colors
, colormapSize
);
669 xVisual
= DefaultVisual(xDisplay
, xScreen
);
670 xColormap
= DefaultColormap(xDisplay
, xScreen
);
673 fprintf(stderr
, "xtiff: unsupported image depth %d\n", tfImageDepth
);
677 if (appData
.verbose
== True
)
678 fprintf(stderr
, "%s: Using %d-bit %s visual.\n",
679 fileName
, xImageDepth
, classNames
[xVisual
->class]);
687 if (greenMap
!= NULL
)
692 colors
= NULL
; grayMap
= redMap
= greenMap
= blueMap
= NULL
;
696 * Search for an appropriate visual. Promote where necessary.
697 * Check to make sure that ENOUGH colormap entries are writeable.
698 * basePixel was determined when XAllocColorCells() contiguously
699 * allocated enough entries. basePixel is used below in GetTIFFImage.
702 SearchVisualList(image_depth
, visual_class
, visual
)
703 int image_depth
, visual_class
;
706 XVisualInfo template_visual
, *visual_list
, *vl
;
709 template_visual
.screen
= xScreen
;
710 vl
= visual_list
= XGetVisualInfo(xDisplay
, VisualScreenMask
,
711 &template_visual
, &n_visuals
);
713 if (n_visuals
== 0) {
714 fprintf(stderr
, "xtiff: visual list not available\n");
718 for (i
= 0; i
< n_visuals
; vl
++, i
++) {
719 if ((vl
->class == visual_class
) && (vl
->depth
>= image_depth
)
720 && (vl
->visual
->map_entries
>= (1 << vl
->depth
))) {
721 *visual
= vl
->visual
;
722 xImageDepth
= vl
->depth
;
723 xRedMask
= vl
->red_mask
;
724 xGreenMask
= vl
->green_mask
;
725 xBlueMask
= vl
->blue_mask
;
726 XFree((char *) visual_list
);
731 XFree((char *) visual_list
);
738 int pixel_map
[3], red_shift
, green_shift
, blue_shift
;
739 char *scan_line
, *output_p
, *input_p
;
743 scan_line
= (char *) malloc(tfBytesPerRow
= TIFFScanlineSize(tfFile
));
746 if ((tfImageDepth
== 32) || (tfImageDepth
== 24)) {
747 output_p
= imageMemory
= (char *)
748 malloc(tfImageWidth
* tfImageHeight
* 4);
752 * Handle different color masks for different frame buffers.
754 if (ImageByteOrder(xDisplay
) == LSBFirst
) { /* DECstation 5000 */
755 red_shift
= pixel_map
[0] = xRedMask
== 0xFF000000 ? 3
756 : (xRedMask
== 0xFF0000 ? 2 : (xRedMask
== 0xFF00 ? 1 : 0));
757 green_shift
= pixel_map
[1] = xGreenMask
== 0xFF000000 ? 3
758 : (xGreenMask
== 0xFF0000 ? 2 : (xGreenMask
== 0xFF00 ? 1 : 0));
759 blue_shift
= pixel_map
[2] = xBlueMask
== 0xFF000000 ? 3
760 : (xBlueMask
== 0xFF0000 ? 2 : (xBlueMask
== 0xFF00 ? 1 : 0));
761 } else { /* Ardent */
762 red_shift
= pixel_map
[0] = xRedMask
== 0xFF000000 ? 0
763 : (xRedMask
== 0xFF0000 ? 1 : (xRedMask
== 0xFF00 ? 2 : 3));
764 green_shift
= pixel_map
[0] = xGreenMask
== 0xFF000000 ? 0
765 : (xGreenMask
== 0xFF0000 ? 1 : (xGreenMask
== 0xFF00 ? 2 : 3));
766 blue_shift
= pixel_map
[0] = xBlueMask
== 0xFF000000 ? 0
767 : (xBlueMask
== 0xFF0000 ? 1 : (xBlueMask
== 0xFF00 ? 2 : 3));
770 if (tfPlanarConfiguration
== PLANARCONFIG_CONTIG
) {
771 for (i
= 0; i
< tfImageHeight
; i
++) {
772 if (TIFFReadScanline(tfFile
, scan_line
, i
, 0) < 0)
774 for (input_p
= scan_line
, j
= 0; j
< tfImageWidth
; j
++) {
775 *(output_p
+ red_shift
) = *input_p
++;
776 *(output_p
+ green_shift
) = *input_p
++;
777 *(output_p
+ blue_shift
) = *input_p
++;
779 if (tfSamplesPerPixel
== 4) /* skip the fourth channel */
784 for (s
= 0; s
< tfSamplesPerPixel
; s
++) {
785 if (s
== 3) /* skip the fourth channel */
787 for (i
= 0; i
< tfImageHeight
; i
++) {
788 if (TIFFReadScanline(tfFile
, scan_line
, i
, s
) < 0)
791 output_p
= imageMemory
+ (i
*tfImageWidth
*4) + pixel_map
[s
];
792 for (j
= 0; j
< tfImageWidth
; j
++, output_p
+= 4)
793 *output_p
= *input_p
++;
798 if (xImageDepth
== tfImageDepth
) {
799 output_p
= imageMemory
= (char *)
800 malloc(tfBytesPerRow
* tfImageHeight
);
803 for (i
= 0; i
< tfImageHeight
; i
++, output_p
+= tfBytesPerRow
)
804 if (TIFFReadScanline(tfFile
, output_p
, i
, 0) < 0)
806 } else if ((xImageDepth
== 8) && (tfImageDepth
== 4)) {
807 output_p
= imageMemory
= (char *)
808 malloc(tfBytesPerRow
* 2 * tfImageHeight
+ 2);
812 * If a scanline is of odd size the inner loop below will overshoot.
813 * This is handled very simply by recalculating the start point at
814 * each scanline and padding imageMemory a little at the end.
816 for (i
= 0; i
< tfImageHeight
; i
++) {
817 if (TIFFReadScanline(tfFile
, scan_line
, i
, 0) < 0)
819 output_p
= &imageMemory
[i
* tfImageWidth
];
821 for (j
= 0; j
< tfImageWidth
; j
+= 2, input_p
++) {
822 *output_p
++ = (*input_p
>> 4) + basePixel
;
823 *output_p
++ = (*input_p
& 0xf) + basePixel
;
826 } else if ((xImageDepth
== 8) && (tfImageDepth
== 2)) {
827 output_p
= imageMemory
= (char *)
828 malloc(tfBytesPerRow
* 4 * tfImageHeight
+ 4);
831 for (i
= 0; i
< tfImageHeight
; i
++) {
832 if (TIFFReadScanline(tfFile
, scan_line
, i
, 0) < 0)
834 output_p
= &imageMemory
[i
* tfImageWidth
];
836 for (j
= 0; j
< tfImageWidth
; j
+= 4, input_p
++) {
837 *output_p
++ = (*input_p
>> 6) + basePixel
;
838 *output_p
++ = ((*input_p
>> 4) & 3) + basePixel
;
839 *output_p
++ = ((*input_p
>> 2) & 3) + basePixel
;
840 *output_p
++ = (*input_p
& 3) + basePixel
;
843 } else if ((xImageDepth
== 4) && (tfImageDepth
== 2)) {
844 output_p
= imageMemory
= (char *)
845 malloc(tfBytesPerRow
* 2 * tfImageHeight
+ 2);
848 for (i
= 0; i
< tfImageHeight
; i
++) {
849 if (TIFFReadScanline(tfFile
, scan_line
, i
, 0) < 0)
851 output_p
= &imageMemory
[i
* tfBytesPerRow
* 2];
853 for (j
= 0; j
< tfImageWidth
; j
+= 4, input_p
++) {
854 *output_p
++ = (((*input_p
>>6) << 4)
855 | ((*input_p
>> 4) & 3)) + basePixel
;
856 *output_p
++ = ((((*input_p
>>2) & 3) << 4)
857 | (*input_p
& 3)) + basePixel
;
862 "xtiff: can't handle %d-bit TIFF file on an %d-bit display\n",
863 tfImageDepth
, xImageDepth
);
877 xOffset
= yOffset
= 0;
880 xImage
= XCreateImage(xDisplay
, xVisual
, xImageDepth
,
881 xImageDepth
== 1 ? XYBitmap
: ZPixmap
, /* offset */ 0,
882 (char *) imageMemory
, tfImageWidth
, tfImageHeight
,
883 /* bitmap_pad */ 8, /* bytes_per_line */ 0);
886 * libtiff converts LSB data into MSB but doesn't change the FillOrder tag.
888 if (xImageDepth
== 1)
889 xImage
->bitmap_bit_order
= MSBFirst
;
890 if (xImageDepth
<= 8)
891 xImage
->byte_order
= MSBFirst
;
894 * create an appropriate GC
896 gc_values
.function
= GXcopy
;
897 gc_values
.plane_mask
= AllPlanes
;
898 if (tfPhotometricInterpretation
== PHOTOMETRIC_MINISBLACK
) {
899 gc_values
.foreground
= XWhitePixel(xDisplay
, xScreen
);
900 gc_values
.background
= XBlackPixel(xDisplay
, xScreen
);
902 gc_values
.foreground
= XBlackPixel(xDisplay
, xScreen
);
903 gc_values
.background
= XWhitePixel(xDisplay
, xScreen
);
905 xWinGc
= XCreateGC(xDisplay
, XtWindow(shellWidget
),
906 GCFunction
| GCPlaneMask
| GCForeground
| GCBackground
, &gc_values
);
909 * create the pixmap and load the image
911 if (appData
.usePixmap
== True
) {
912 xImagePixmap
= XCreatePixmap(xDisplay
, RootWindow(xDisplay
, xScreen
),
913 xImage
->width
, xImage
->height
, xImageDepth
);
916 * According to the O'Reilly X Protocol Reference Manual, page 53,
917 * "A pixmap depth of one is always supported and listed, but windows
918 * of depth one might not be supported." Therefore we create a pixmap
919 * of depth one and use XCopyPlane(). This is idiomatic.
921 if (xImageDepth
== 1) { /* just pass the bits through */
922 gc_values
.foreground
= 1; /* foreground describes set bits */
923 gc_values
.background
= 0; /* background describes clear bits */
924 bitmap_gc
= XCreateGC(xDisplay
, xImagePixmap
,
925 GCForeground
| GCBackground
, &gc_values
);
926 XPutImage(xDisplay
, xImagePixmap
, bitmap_gc
, xImage
,
927 0, 0, 0, 0, xImage
->width
, xImage
->height
);
929 XPutImage(xDisplay
, xImagePixmap
, xWinGc
, xImage
,
930 0, 0, 0, 0, xImage
->width
, xImage
->height
);
931 XDestroyImage(xImage
);
937 SelectProc(w
, unused_1
, unused_2
)
942 XawListReturnStruct
*list_return
;
944 list_return
= XawListShowCurrent(w
);
946 switch (list_return
->list_index
) {
950 case ButtonPreviousPage
:
957 fprintf(stderr
, "error in SelectProc\n");
960 XawListUnhighlight(w
);
972 PageProc(ButtonNextPage
);
978 PageProc(ButtonPreviousPage
);
989 case ButtonPreviousPage
:
991 TIFFSetDirectory(tfFile
, --tfDirectory
);
996 if (TIFFReadDirectory(tfFile
) == True
)
1002 fprintf(stderr
, "error in PageProc\n");
1006 xOffset
= yOffset
= 0;
1013 if (appData
.usePixmap
== True
)
1014 XFreePixmap(xDisplay
, xImagePixmap
);
1016 XDestroyImage(xImage
);
1021 * Using XtSetValues() to set the widget size causes a resize.
1022 * This resize gets propagated up to the parent shell.
1023 * In order to disable this visually disconcerting effect,
1024 * shell resizing is temporarily disabled.
1026 XtSetArg(args
[0], XtNallowShellResize
, False
);
1027 XtSetValues(shellWidget
, args
, 1);
1029 XtSetArg(args
[0], XtNwidth
, tfImageWidth
);
1030 XtSetArg(args
[1], XtNheight
, tfImageHeight
);
1031 XtSetValues(imageWidget
, args
, 2);
1033 XtSetArg(args
[0], XtNallowShellResize
, True
);
1034 XtSetValues(shellWidget
, args
, 1);
1036 XClearWindow(xDisplay
, XtWindow(imageWidget
));
1038 fake_event
.type
= Expose
;
1039 fake_event
.xexpose
.x
= fake_event
.xexpose
.y
= 0;
1040 fake_event
.xexpose
.width
= tfImageWidth
; /* the window will clip */
1041 fake_event
.xexpose
.height
= tfImageHeight
;
1042 EventProc(imageWidget
, NULL
, &fake_event
);
1046 EventProc(widget
, unused
, event
)
1051 int ih
, iw
, ww
, wh
, sx
, sy
, w
, h
, dx
, dy
;
1052 Dimension w_width
, w_height
;
1056 if (event
->type
== MappingNotify
) {
1057 XRefreshKeyboardMapping((XMappingEvent
*) event
);
1061 if (!XtIsRealized(widget
))
1064 if ((event
->type
== ButtonPress
) || (event
->type
== ButtonRelease
))
1065 if (event
->xbutton
.button
!= Button1
)
1068 iw
= tfImageWidth
; /* avoid sign problems */
1072 * The grabX and grabY variables record where the user grabbed the image.
1073 * They also record whether the mouse button is down or not.
1075 if (event
->type
== ButtonPress
) {
1076 grabX
= event
->xbutton
.x
;
1077 grabY
= event
->xbutton
.y
;
1082 * imageWidget is a Core widget and doesn't get resized.
1083 * So we calculate the size of its viewport here.
1085 XtSetArg(args
[0], XtNwidth
, &w_width
);
1086 XtSetArg(args
[1], XtNheight
, &w_height
);
1087 XtGetValues(shellWidget
, args
, 2);
1090 XtGetValues(listWidget
, args
, 2);
1093 switch (event
->type
) {
1095 dx
= event
->xexpose
.x
;
1096 dy
= event
->xexpose
.y
;
1099 w
= MIN(event
->xexpose
.width
, iw
);
1100 h
= MIN(event
->xexpose
.height
, ih
);
1103 if ((grabX
>= 0) || (grabY
>= 0)) /* Mouse button is still down */
1105 switch (XLookupKeysym((XKeyEvent
*) event
, /* KeySyms index */ 0)) {
1107 if (ih
< wh
) /* Don't scroll if the window fits the image. */
1109 sy
= yOffset
+ appData
.translate
;
1110 sy
= MIN(ih
- wh
, sy
);
1111 if (sy
== yOffset
) /* Filter redundant stationary refreshes. */
1121 sy
= yOffset
- appData
.translate
;
1133 sx
= xOffset
+ appData
.translate
;
1134 sx
= MIN(iw
- ww
, sx
);
1145 sx
= xOffset
- appData
.translate
;
1160 * MotionEvent compression. Ignore multiple motion events.
1161 * Ignore motion events if the mouse button is up.
1163 if (XPending(xDisplay
)) /* Xlib doesn't flush the output buffer */
1164 if (XtPeekEvent(&next_event
))
1165 if (next_event
.type
== MotionNotify
)
1167 if ((grabX
< 0) || (grabY
< 0))
1169 sx
= xOffset
+ grabX
- (int) event
->xmotion
.x
;
1170 if (sx
>= (iw
- ww
)) /* clamp x motion but allow y motion */
1173 sy
= yOffset
+ grabY
- (int) event
->xmotion
.y
;
1174 if (sy
>= (ih
- wh
)) /* clamp y motion but allow x motion */
1177 if ((sx
== xOffset
) && (sy
== yOffset
))
1183 xOffset
= xOffset
+ grabX
- (int) event
->xbutton
.x
;
1184 xOffset
= MIN(iw
- ww
, xOffset
);
1185 xOffset
= MAX(xOffset
, 0);
1186 yOffset
= yOffset
+ grabY
- (int) event
->xbutton
.y
;
1187 yOffset
= MIN(ih
- wh
, yOffset
);
1188 yOffset
= MAX(yOffset
, 0);
1194 if (appData
.usePixmap
== True
) {
1195 if (xImageDepth
== 1)
1196 XCopyPlane(xDisplay
, xImagePixmap
, XtWindow(widget
),
1197 xWinGc
, sx
, sy
, w
, h
, dx
, dy
, 1);
1199 XCopyArea(xDisplay
, xImagePixmap
, XtWindow(widget
),
1200 xWinGc
, sx
, sy
, w
, h
, dx
, dy
);
1202 XPutImage(xDisplay
, XtWindow(widget
), xWinGc
, xImage
,
1203 sx
, sy
, dx
, dy
, w
, h
);
1209 Dimension w_width
, w_height
;
1214 if ((xOffset
== 0) && (yOffset
== 0))
1217 XtSetArg(args
[0], XtNwidth
, &w_width
);
1218 XtSetArg(args
[1], XtNheight
, &w_height
);
1219 XtGetValues(shellWidget
, args
, 2);
1222 XtGetValues(listWidget
, args
, 2);
1225 xo
= xOffset
; yo
= yOffset
;
1227 if ((xOffset
+ ww
) >= tfImageWidth
)
1228 xOffset
= MAX((int) tfImageWidth
- ww
, 0);
1229 if ((yOffset
+ wh
) >= tfImageHeight
)
1230 yOffset
= MAX((int) tfImageHeight
- wh
, 0);
1233 * Send an ExposeEvent if the origin changed.
1234 * We have to do this because of the use and semantics of bit gravity.
1236 if ((xo
!= xOffset
) || (yo
!= yOffset
)) {
1237 fake_event
.type
= Expose
;
1238 fake_event
.xexpose
.x
= fake_event
.xexpose
.y
= 0;
1239 fake_event
.xexpose
.width
= tfImageWidth
;
1240 fake_event
.xexpose
.height
= tfImageHeight
;
1241 EventProc(imageWidget
, NULL
, &fake_event
);
1246 XTiffErrorHandler(display
, error_event
)
1248 XErrorEvent
*error_event
;
1253 * Some X servers limit the size of pixmaps.
1255 if ((error_event
->error_code
== BadAlloc
)
1256 && (error_event
->request_code
== X_CreatePixmap
))
1257 fprintf(stderr
, "xtiff: requested pixmap too big for display\n");
1259 XGetErrorText(display
, error_event
->error_code
, message
, 80);
1260 fprintf(stderr
, "xtiff: error code %s\n", message
);
1269 fprintf(stderr
, "Usage xtiff: [options] tiff-file\n");
1270 fprintf(stderr
, "\tstandard Xt options\n");
1271 fprintf(stderr
, "\t[-help]\n");
1272 fprintf(stderr
, "\t[-gamma gamma]\n");
1273 fprintf(stderr
, "\t[-usePixmap (True | False)]\n");
1274 fprintf(stderr
, "\t[-viewportWidth pixels]\n");
1275 fprintf(stderr
, "\t[-viewportHeight pixels]\n");
1276 fprintf(stderr
, "\t[-translate pixels]\n");
1277 fprintf(stderr
, "\t[-verbose (True | False)]\n");
1281 /* vim: set ts=8 sts=8 sw=8 noet: */