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