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: */