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");