]> git.saurik.com Git - wxWidgets.git/blame - src/tiff/contrib/dbs/xtiff/xtiff.c
xti changes
[wxWidgets.git] / src / tiff / contrib / dbs / xtiff / xtiff.c
CommitLineData
8414a40c
VZ
1/*
2 * xtiff - view a TIFF file in an X window
3 *
4 * Dan Sears
5 * Chris Sears
6 *
7 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
8 *
9 * All Rights Reserved
10 *
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.
18 *
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
25 * SOFTWARE.
26 *
27 * Revision 1.0 90/05/07
28 * Initial release.
29 * Revision 2.0 90/12/20
30 * Converted to use the Athena Widgets and the Xt Intrinsics.
31 *
32 * Notes:
33 *
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.
41 *
42 * TIFFTAG_COLORMAP is often incorrectly written as ranging from
43 * 0 to 255 rather than from 0 to 65535. CheckAndCorrectColormap()
44 * takes care of this.
45 *
46 * Only ORIENTATION_TOPLEFT is supported correctly. This is the
47 * default TIFF and X orientation. Other orientations will be
48 * displayed incorrectly.
49 *
50 * There is no support for or use of 3/3/2 DirectColor visuals.
51 * TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE are not supported.
52 *
53 * Only TIFFTAG_BITSPERSAMPLE values that are 1, 2, 4 or 8 are supported.
54 */
55#include <math.h>
56#include <stdio.h>
57#include <tiffio.h>
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>
67#define XK_MISCELLANY
68#include <X11/keysymdef.h>
69#include "xtifficon.h"
70
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
80
81#ifdef __STDC__
82#define PP(args) args
83#else
84#define PP(args) ()
85#endif
86
87void main PP((int argc, char **argv));
88void OpenTIFFFile PP((void));
89void GetTIFFHeader PP((void));
90void SetNameLabel PP((void));
91void CheckAndCorrectColormap PP((void));
92void SimpleGammaCorrection PP((void));
93void GetVisual PP((void));
94Boolean SearchVisualList PP((int image_depth,
95 int visual_class, Visual **visual));
96void GetTIFFImage PP((void));
97void CreateXImage PP((void));
98XtCallbackProc SelectProc PP((Widget w, caddr_t unused_1, caddr_t unused_2));
99void QuitProc PP((void));
100void NextProc PP((void));
101void PreviousProc PP((void));
102void PageProc PP((int direction));
103void EventProc PP((Widget widget, caddr_t unused, XEvent *event));
104void ResizeProc PP((void));
105int XTiffErrorHandler PP((Display *display, XErrorEvent *error_event));
106void Usage PP((void));
107
108int xtVersion = XtSpecificationRelease; /* xtiff depends on R4 or higher */
109
110/*
111 * Xt data structures
112 */
113Widget shellWidget, formWidget, listWidget, labelWidget, imageWidget;
114
115enum { ButtonQuit = 0, ButtonPreviousPage = 1, ButtonNextPage = 2 };
116
117String buttonStrings[] = { "Quit", "Previous", "Next" };
118
119static 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 }
127};
128
129typedef struct {
130 Boolean help;
131 float gamma;
132 Boolean usePixmap;
133 int viewportWidth;
134 int viewportHeight;
135 int translate;
136 Boolean verbose;
137} AppData, *AppDataPtr;
138
139AppData appData;
140
141XtResource clientResources[] = {
142 {
143 "help", XtCBoolean, XtRBoolean, sizeof(Boolean),
144 XtOffset(AppDataPtr, help), XtRImmediate, (XtPointer) False
145 }, {
146 "gamma", "Gamma", XtRFloat, sizeof(float),
147 XtOffset(AppDataPtr, gamma), XtRString, (XtPointer) TIFF_GAMMA
148 }, {
149 "usePixmap", "UsePixmap", XtRBoolean, sizeof(Boolean),
150 XtOffset(AppDataPtr, usePixmap), XtRImmediate, (XtPointer) True
151 }, {
152 "viewportWidth", "ViewportWidth", XtRInt, sizeof(int),
153 XtOffset(AppDataPtr, viewportWidth), XtRImmediate,
154 (XtPointer) VIEWPORT_WIDTH
155 }, {
156 "viewportHeight", "ViewportHeight", XtRInt, sizeof(int),
157 XtOffset(AppDataPtr, viewportHeight), XtRImmediate,
158 (XtPointer) VIEWPORT_HEIGHT
159 }, {
160 "translate", "Translate", XtRInt, sizeof(int),
161 XtOffset(AppDataPtr, translate), XtRImmediate, (XtPointer) KEY_TRANSLATE
162 }, {
163 "verbose", "Verbose", XtRBoolean, sizeof(Boolean),
164 XtOffset(AppDataPtr, verbose), XtRImmediate, (XtPointer) True
165 }
166};
167
168Arg formArgs[] = {
169 { XtNresizable, True }
170};
171
172Arg listArgs[] = {
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 }
183};
184
185Arg labelArgs[] = {
186 { XtNresizable, False },
187 { XtNwidth, 200 },
188 { XtNborderWidth, 0 },
189 { XtNjustify, XtJustifyLeft },
190 { XtNtop, XtChainTop },
191 { XtNleft, XtChainLeft },
192 { XtNbottom, XtChainTop },
193 { XtNright, XtChainLeft }
194};
195
196Arg imageArgs[] = {
197 { XtNresizable, True },
198 { XtNborderWidth, 0 },
199 { XtNtop, XtChainTop },
200 { XtNleft, XtChainLeft },
201 { XtNbottom, XtChainTop },
202 { XtNright, XtChainLeft }
203};
204
205XtActionsRec actionsTable[] = {
206 { "quit", QuitProc },
207 { "next", NextProc },
208 { "previous", PreviousProc },
209 { "notifyresize", ResizeProc }
210};
211
212char translationsTable[] = "<Key>q: quit() \n \
213 <Key>Q: quit() \n \
214 <Message>WM_PROTOCOLS: quit()\n \
215 <Key>p: previous() \n \
216 <Key>P: previous() \n \
217 <Key>n: next() \n \
218 <Key>N: next() \n \
219 <Configure>: notifyresize()";
220
221/*
222 * X data structures
223 */
224Colormap xColormap;
225Display * xDisplay;
226Pixmap xImagePixmap;
227Visual * xVisual;
228XImage * xImage;
229GC xWinGc;
230int xImageDepth, xScreen, xRedMask, xGreenMask, xBlueMask,
231 xOffset = 0, yOffset = 0, grabX = -1, grabY = -1;
232u_char basePixel = 0;
233
234/*
235 * TIFF data structures
236 */
237TIFF * tfFile = NULL;
238u_long tfImageWidth, tfImageHeight;
239u_short tfBitsPerSample, tfSamplesPerPixel, tfPlanarConfiguration,
240 tfPhotometricInterpretation, tfGrayResponseUnit,
241 tfImageDepth, tfBytesPerRow;
242int tfDirectory = 0, tfMultiPage = False;
243double tfUnitMap, tfGrayResponseUnitMap[] = {
244 -1, -10, -100, -1000, -10000, -100000
245 };
246
247/*
248 * display data structures
249 */
250double *dRed, *dGreen, *dBlue;
251
252/*
253 * shared data structures
254 */
255u_short * redMap = NULL, *greenMap = NULL, *blueMap = NULL,
256 *grayMap = NULL, colormapSize;
257u_char * imageMemory;
258char * fileName;
259
260void
261main(argc, argv)
262 int argc;
263 char ** argv;
264{
265 XSetWindowAttributes window_attributes;
266 Widget widget_list[3];
267 Arg args[5];
268
269 setbuf(stdout, NULL); setbuf(stderr, NULL);
270
271 shellWidget = XtInitialize(argv[0], "XTiff", shellOptions,
272 XtNumber(shellOptions), &argc, argv);
273
274 XSetErrorHandler(XTiffErrorHandler);
275
276 XtGetApplicationResources(shellWidget, &appData,
277 (XtResourceList) clientResources, (Cardinal) XtNumber(clientResources),
278 (ArgList) NULL, (Cardinal) 0);
279
280 if ((argc <= 1) || (argc > 2) || appData.help)
281 Usage();
282
283 if (appData.verbose == False) {
284 TIFFSetErrorHandler(0);
285 TIFFSetWarningHandler(0);
286 }
287
288 fileName = argv[1];
289
290 xDisplay = XtDisplay(shellWidget);
291 xScreen = DefaultScreen(xDisplay);
292
293 OpenTIFFFile();
294 GetTIFFHeader();
295 SimpleGammaCorrection();
296 GetVisual();
297 GetTIFFImage();
298
299 /*
300 * Send visual, colormap, depth and iconPixmap to shellWidget.
301 * Sending the visual to the shell is only possible with the advent of R4.
302 */
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);
312
313 /*
314 * widget instance hierarchy
315 */
316 formWidget = XtCreateManagedWidget("form", formWidgetClass,
317 shellWidget, formArgs, XtNumber(formArgs));
318
319 widget_list[0] = listWidget = XtCreateWidget("list",
320 listWidgetClass, formWidget, listArgs, XtNumber(listArgs));
321
322 widget_list[1] = labelWidget = XtCreateWidget("label",
323 labelWidgetClass, formWidget, labelArgs, XtNumber(labelArgs));
324
325 widget_list[2] = imageWidget = XtCreateWidget("image",
326 widgetClass, formWidget, imageArgs, XtNumber(imageArgs));
327
328 XtManageChildren(widget_list, XtNumber(widget_list));
329
330 /*
331 * initial widget sizes - for small images let xtiff size itself
332 */
333 if (tfImageWidth >= appData.viewportWidth) {
334 XtSetArg(args[0], XtNwidth, appData.viewportWidth);
335 XtSetValues(shellWidget, args, 1);
336 }
337 if (tfImageHeight >= appData.viewportHeight) {
338 XtSetArg(args[0], XtNheight, appData.viewportHeight);
339 XtSetValues(shellWidget, args, 1);
340 }
341
342 XtSetArg(args[0], XtNwidth, tfImageWidth);
343 XtSetArg(args[1], XtNheight, tfImageHeight);
344 XtSetValues(imageWidget, args, 2);
345
346 /*
347 * formWidget uses these constraints but they are stored in the children.
348 */
349 XtSetArg(args[0], XtNfromVert, listWidget);
350 XtSetValues(imageWidget, args, 1);
351 XtSetArg(args[0], XtNfromHoriz, listWidget);
352 XtSetValues(labelWidget, args, 1);
353
354 SetNameLabel();
355
356 XtAddCallback(listWidget, XtNcallback, (XtCallbackProc) SelectProc,
357 (XtPointer) NULL);
358
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);
364
365 /*
366 * This is intended to be a little faster than going through
367 * the translation manager.
368 */
369 XtAddEventHandler(imageWidget, ExposureMask | ButtonPressMask
370 | ButtonReleaseMask | Button1MotionMask | KeyPressMask,
371 False, EventProc, NULL);
372
373 XtRealizeWidget(shellWidget);
374
375 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_fleur);
376 XChangeWindowAttributes(xDisplay, XtWindow(imageWidget),
377 CWCursor, &window_attributes);
378
379 CreateXImage();
380
381 XtMainLoop();
382}
383
384void
385OpenTIFFFile()
386{
387 if (tfFile != NULL)
388 TIFFClose(tfFile);
389
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);
393 exit(0);
394 }
395
396 tfMultiPage = (TIFFLastDirectory(tfFile) ? False : True);
397}
398
399void
400GetTIFFHeader()
401{
402 register int i;
403
404 if (!TIFFSetDirectory(tfFile, tfDirectory)) {
405 fprintf(stderr, "xtiff: can't seek to directory %d in %s\n",
406 tfDirectory, fileName);
407 exit(0);
408 }
409
410 TIFFGetField(tfFile, TIFFTAG_IMAGEWIDTH, &tfImageWidth);
411 TIFFGetField(tfFile, TIFFTAG_IMAGELENGTH, &tfImageHeight);
412
413 /*
414 * If the following tags aren't present then use the TIFF defaults.
415 */
416 TIFFGetFieldDefaulted(tfFile, TIFFTAG_BITSPERSAMPLE, &tfBitsPerSample);
417 TIFFGetFieldDefaulted(tfFile, TIFFTAG_SAMPLESPERPIXEL, &tfSamplesPerPixel);
418 TIFFGetFieldDefaulted(tfFile, TIFFTAG_PLANARCONFIG, &tfPlanarConfiguration);
419 TIFFGetFieldDefaulted(tfFile, TIFFTAG_GRAYRESPONSEUNIT, &tfGrayResponseUnit);
420
421 tfUnitMap = tfGrayResponseUnitMap[tfGrayResponseUnit];
422 colormapSize = 1 << tfBitsPerSample;
423 tfImageDepth = tfBitsPerSample * tfSamplesPerPixel;
424
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);
429
430 /*
431 * If TIFFTAG_PHOTOMETRIC is not present then assign a reasonable default.
432 * The TIFF 5.0 specification doesn't give a default.
433 */
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;
444 } else
445 tfPhotometricInterpretation = PHOTOMETRIC_MINISBLACK;
446 }
447
448 /*
449 * Given TIFFTAG_PHOTOMETRIC extract or create the response curves.
450 */
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);
460 break;
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);
471 } else {
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];
477 }
478 }
479 break;
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);
488 break;
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);
496 break;
497 default:
498 fprintf(stderr,
499 "xtiff: can't display photometric interpretation type %d\n",
500 tfPhotometricInterpretation);
501 exit(0);
502 }
503}
504
505void
506SetNameLabel()
507{
508 char buffer[BUFSIZ];
509 Arg args[1];
510
511 if (tfMultiPage)
512 sprintf(buffer, "%s - page %d", fileName, tfDirectory);
513 else
514 strcpy(buffer, fileName);
515 XtSetArg(args[0], XtNlabel, buffer);
516 XtSetValues(labelWidget, args, 1);
517}
518
519/*
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.
522 */
523void
524CheckAndCorrectColormap()
525{
526 register int i;
527
528 for (i = 0; i < colormapSize; i++)
529 if ((redMap[i] > 255) || (greenMap[i] > 255) || (blueMap[i] > 255))
530 return;
531
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);
536 }
537 TIFFWarning(fileName, "Assuming 8-bit colormap");
538}
539
540void
541SimpleGammaCorrection()
542{
543 register int i;
544 register double i_gamma = 1.0 / appData.gamma;
545
546 for (i = 0; i < colormapSize; i++) {
547 if (((tfPhotometricInterpretation == PHOTOMETRIC_MINISWHITE)
548 && (i == colormapSize - 1))
549 || ((tfPhotometricInterpretation == PHOTOMETRIC_MINISBLACK)
550 && (i == 0)))
551 redMap[i] = greenMap[i] = blueMap[i] = 0;
552 else {
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));
556 }
557 }
558
559 free(dRed); free(dGreen); free(dBlue);
560}
561
562static char* classNames[] = {
563 "StaticGray",
564 "GrayScale",
565 "StaticColor",
566 "PseudoColor",
567 "TrueColor",
568 "DirectColor"
569};
570
571/*
572 * Current limitation: the visual is set initially by the first file.
573 * It cannot be changed.
574 */
575void
576GetVisual()
577{
578 register XColor *colors = NULL;
579 register u_long *pixels = NULL;
580 register int i;
581
582 switch (tfImageDepth) {
583 /*
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.
586 */
587 case 32:
588 case 24:
589 if (SearchVisualList(24, DirectColor, &xVisual) == False) {
590 fprintf(stderr, "xtiff: 24-bit DirectColor visual not available\n");
591 exit(0);
592 }
593
594 colors = (XColor *) malloc(3 * colormapSize * sizeof(XColor));
595 MCHECK(colors);
596
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;
603 }
604
605 xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen),
606 xVisual, AllocAll);
607 XStoreColors(xDisplay, xColormap, colors, colormapSize);
608 break;
609 case 8:
610 case 4:
611 case 2:
612 /*
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.
615 */
616 switch (tfPhotometricInterpretation) {
617 case PHOTOMETRIC_MINISWHITE:
618 case PHOTOMETRIC_MINISBLACK:
619 if (SearchVisualList((int) tfImageDepth, GrayScale, &xVisual) == True)
620 break;
621 case PHOTOMETRIC_PALETTE:
622 if (SearchVisualList((int) tfImageDepth, PseudoColor, &xVisual) == True)
623 break;
624 default:
625 fprintf(stderr, "xtiff: Unsupported TIFF/X configuration\n");
626 exit(0);
627 }
628
629 colors = (XColor *) malloc(colormapSize * sizeof(XColor));
630 MCHECK(colors);
631
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;
638 }
639
640 /*
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.
648 */
649 if (tfImageDepth == 8)
650 xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen),
651 xVisual, AllocAll);
652 else {
653 xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen),
654 xVisual, AllocNone);
655 pixels = (u_long *) malloc(colormapSize * sizeof(u_long));
656 MCHECK(pixels);
657 (void) XAllocColorCells(xDisplay, xColormap, True,
658 NULL, 0, pixels, colormapSize);
659 basePixel = (u_char) pixels[0];
660 free(pixels);
661 }
662 XStoreColors(xDisplay, xColormap, colors, colormapSize);
663 break;
664 case 1:
665 xImageDepth = 1;
666 xVisual = DefaultVisual(xDisplay, xScreen);
667 xColormap = DefaultColormap(xDisplay, xScreen);
668 break;
669 default:
670 fprintf(stderr, "xtiff: unsupported image depth %d\n", tfImageDepth);
671 exit(0);
672 }
673
674 if (appData.verbose == True)
675 fprintf(stderr, "%s: Using %d-bit %s visual.\n",
676 fileName, xImageDepth, classNames[xVisual->class]);
677
678 if (colors != NULL)
679 free(colors);
680 if (grayMap != NULL)
681 free(grayMap);
682 if (redMap != NULL)
683 free(redMap);
684 if (greenMap != NULL)
685 free(greenMap);
686 if (blueMap != NULL)
687 free(blueMap);
688
689 colors = NULL; grayMap = redMap = greenMap = blueMap = NULL;
690}
691
692/*
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.
697 */
698Boolean
699SearchVisualList(image_depth, visual_class, visual)
700 int image_depth, visual_class;
701 Visual **visual;
702{
703 XVisualInfo template_visual, *visual_list, *vl;
704 int i, n_visuals;
705
706 template_visual.screen = xScreen;
707 vl = visual_list = XGetVisualInfo(xDisplay, VisualScreenMask,
708 &template_visual, &n_visuals);
709
710 if (n_visuals == 0) {
711 fprintf(stderr, "xtiff: visual list not available\n");
712 exit(0);
713 }
714
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);
724 return True;
725 }
726 }
727
728 XFree((char *) visual_list);
729 return False;
730}
731
732void
733GetTIFFImage()
734{
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;
738
739 scan_line = (u_char *) malloc(tfBytesPerRow = TIFFScanlineSize(tfFile));
740 MCHECK(scan_line);
741
742 if ((tfImageDepth == 32) || (tfImageDepth == 24)) {
743 output_p = imageMemory = (u_char *)
744 malloc(tfImageWidth * tfImageHeight * 4);
745 MCHECK(imageMemory);
746
747 /*
748 * Handle different color masks for different frame buffers.
749 */
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));
764 }
765
766 if (tfPlanarConfiguration == PLANARCONFIG_CONTIG) {
767 for (i = 0; i < tfImageHeight; i++) {
768 if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0)
769 break;
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++;
774 output_p += 4;
775 if (tfSamplesPerPixel == 4) /* skip the fourth channel */
776 input_p++;
777 }
778 }
779 } else {
780 for (s = 0; s < tfSamplesPerPixel; s++) {
781 if (s == 3) /* skip the fourth channel */
782 continue;
783 for (i = 0; i < tfImageHeight; i++) {
784 if (TIFFReadScanline(tfFile, scan_line, i, s) < 0)
785 break;
786 input_p = scan_line;
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++;
790 }
791 }
792 }
793 } else {
794 if (xImageDepth == tfImageDepth) {
795 output_p = imageMemory = (u_char *)
796 malloc(tfBytesPerRow * tfImageHeight);
797 MCHECK(imageMemory);
798
799 for (i = 0; i < tfImageHeight; i++, output_p += tfBytesPerRow)
800 if (TIFFReadScanline(tfFile, output_p, i, 0) < 0)
801 break;
802 } else if ((xImageDepth == 8) && (tfImageDepth == 4)) {
803 output_p = imageMemory = (u_char *)
804 malloc(tfBytesPerRow * 2 * tfImageHeight + 2);
805 MCHECK(imageMemory);
806
807 /*
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.
811 */
812 for (i = 0; i < tfImageHeight; i++) {
813 if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0)
814 break;
815 output_p = &imageMemory[i * tfImageWidth];
816 input_p = scan_line;
817 for (j = 0; j < tfImageWidth; j += 2, input_p++) {
818 *output_p++ = (*input_p >> 4) + basePixel;
819 *output_p++ = (*input_p & 0xf) + basePixel;
820 }
821 }
822 } else if ((xImageDepth == 8) && (tfImageDepth == 2)) {
823 output_p = imageMemory = (u_char *)
824 malloc(tfBytesPerRow * 4 * tfImageHeight + 4);
825 MCHECK(imageMemory);
826
827 for (i = 0; i < tfImageHeight; i++) {
828 if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0)
829 break;
830 output_p = &imageMemory[i * tfImageWidth];
831 input_p = scan_line;
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;
837 }
838 }
839 } else if ((xImageDepth == 4) && (tfImageDepth == 2)) {
840 output_p = imageMemory = (u_char *)
841 malloc(tfBytesPerRow * 2 * tfImageHeight + 2);
842 MCHECK(imageMemory);
843
844 for (i = 0; i < tfImageHeight; i++) {
845 if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0)
846 break;
847 output_p = &imageMemory[i * tfBytesPerRow * 2];
848 input_p = scan_line;
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;
854 }
855 }
856 } else {
857 fprintf(stderr,
858 "xtiff: can't handle %d-bit TIFF file on an %d-bit display\n",
859 tfImageDepth, xImageDepth);
860 exit(0);
861 }
862 }
863
864 free(scan_line);
865}
866
867void
868CreateXImage()
869{
870 XGCValues gc_values;
871 GC bitmap_gc;
872
873 xOffset = yOffset = 0;
874 grabX = grabY = -1;
875
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);
880
881 /*
882 * libtiff converts LSB data into MSB but doesn't change the FillOrder tag.
883 */
884 if (xImageDepth == 1)
885 xImage->bitmap_bit_order = MSBFirst;
886 if (xImageDepth <= 8)
887 xImage->byte_order = MSBFirst;
888
889 /*
890 * create an appropriate GC
891 */
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);
897 } else {
898 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
899 gc_values.background = XWhitePixel(xDisplay, xScreen);
900 }
901 xWinGc = XCreateGC(xDisplay, XtWindow(shellWidget),
902 GCFunction | GCPlaneMask | GCForeground | GCBackground, &gc_values);
903
904 /*
905 * create the pixmap and load the image
906 */
907 if (appData.usePixmap == True) {
908 xImagePixmap = XCreatePixmap(xDisplay, RootWindow(xDisplay, xScreen),
909 xImage->width, xImage->height, xImageDepth);
910
911 /*
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.
916 */
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);
924 } else
925 XPutImage(xDisplay, xImagePixmap, xWinGc, xImage,
926 0, 0, 0, 0, xImage->width, xImage->height);
927 XDestroyImage(xImage);
928 free(imageMemory);
929 }
930}
931
932XtCallbackProc
933SelectProc(w, unused_1, unused_2)
934 Widget w;
935 caddr_t unused_1;
936 caddr_t unused_2;
937{
938 XawListReturnStruct *list_return;
939
940 list_return = XawListShowCurrent(w);
941
942 switch (list_return->list_index) {
943 case ButtonQuit:
944 QuitProc();
945 break;
946 case ButtonPreviousPage:
947 PreviousProc();
948 break;
949 case ButtonNextPage:
950 NextProc();
951 break;
952 default:
953 fprintf(stderr, "error in SelectProc\n");
954 exit(0);
955 }
956 XawListUnhighlight(w);
957}
958
959void
960QuitProc(void)
961{
962 exit(0);
963}
964
965void
966NextProc()
967{
968 PageProc(ButtonNextPage);
969}
970
971void
972PreviousProc()
973{
974 PageProc(ButtonPreviousPage);
975}
976
977void
978PageProc(direction)
979 int direction;
980{
981 XEvent fake_event;
982 Arg args[4];
983
984 switch (direction) {
985 case ButtonPreviousPage:
986 if (tfDirectory > 0)
987 TIFFSetDirectory(tfFile, --tfDirectory);
988 else
989 return;
990 break;
991 case ButtonNextPage:
992 if (TIFFReadDirectory(tfFile) == True)
993 tfDirectory++;
994 else
995 return;
996 break;
997 default:
998 fprintf(stderr, "error in PageProc\n");
999 exit(0);
1000 }
1001
1002 xOffset = yOffset = 0;
1003 grabX = grabY = -1;
1004
1005 GetTIFFHeader();
1006 SetNameLabel();
1007 GetTIFFImage();
1008
1009 if (appData.usePixmap == True)
1010 XFreePixmap(xDisplay, xImagePixmap);
1011 else
1012 XDestroyImage(xImage);
1013
1014 CreateXImage();
1015
1016 /*
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.
1021 */
1022 XtSetArg(args[0], XtNallowShellResize, False);
1023 XtSetValues(shellWidget, args, 1);
1024
1025 XtSetArg(args[0], XtNwidth, tfImageWidth);
1026 XtSetArg(args[1], XtNheight, tfImageHeight);
1027 XtSetValues(imageWidget, args, 2);
1028
1029 XtSetArg(args[0], XtNallowShellResize, True);
1030 XtSetValues(shellWidget, args, 1);
1031
1032 XClearWindow(xDisplay, XtWindow(imageWidget));
1033
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);
1039}
1040
1041void
1042EventProc(widget, unused, event)
1043 Widget widget;
1044 caddr_t unused;
1045 XEvent *event;
1046{
1047 int ih, iw, ww, wh, sx, sy, w, h, dx, dy;
1048 Dimension w_width, w_height;
1049 XEvent next_event;
1050 Arg args[2];
1051
1052 if (event->type == MappingNotify) {
1053 XRefreshKeyboardMapping((XMappingEvent *) event);
1054 return;
1055 }
1056
1057 if (!XtIsRealized(widget))
1058 return;
1059
1060 if ((event->type == ButtonPress) || (event->type == ButtonRelease))
1061 if (event->xbutton.button != Button1)
1062 return;
1063
1064 iw = tfImageWidth; /* avoid sign problems */
1065 ih = tfImageHeight;
1066
1067 /*
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.
1070 */
1071 if (event->type == ButtonPress) {
1072 grabX = event->xbutton.x;
1073 grabY = event->xbutton.y;
1074 return;
1075 }
1076
1077 /*
1078 * imageWidget is a Core widget and doesn't get resized.
1079 * So we calculate the size of its viewport here.
1080 */
1081 XtSetArg(args[0], XtNwidth, &w_width);
1082 XtSetArg(args[1], XtNheight, &w_height);
1083 XtGetValues(shellWidget, args, 2);
1084 ww = w_width;
1085 wh = w_height;
1086 XtGetValues(listWidget, args, 2);
1087 wh -= w_height;
1088
1089 switch (event->type) {
1090 case Expose:
1091 dx = event->xexpose.x;
1092 dy = event->xexpose.y;
1093 sx = dx + xOffset;
1094 sy = dy + yOffset;
1095 w = MIN(event->xexpose.width, iw);
1096 h = MIN(event->xexpose.height, ih);
1097 break;
1098 case KeyPress:
1099 if ((grabX >= 0) || (grabY >= 0)) /* Mouse button is still down */
1100 return;
1101 switch (XLookupKeysym((XKeyEvent *) event, /* KeySyms index */ 0)) {
1102 case XK_Up:
1103 if (ih < wh) /* Don't scroll if the window fits the image. */
1104 return;
1105 sy = yOffset + appData.translate;
1106 sy = MIN(ih - wh, sy);
1107 if (sy == yOffset) /* Filter redundant stationary refreshes. */
1108 return;
1109 yOffset = sy;
1110 sx = xOffset;
1111 dx = dy = 0;
1112 w = ww; h = wh;
1113 break;
1114 case XK_Down:
1115 if (ih < wh)
1116 return;
1117 sy = yOffset - appData.translate;
1118 sy = MAX(sy, 0);
1119 if (sy == yOffset)
1120 return;
1121 yOffset = sy;
1122 sx = xOffset;
1123 dx = dy = 0;
1124 w = ww; h = wh;
1125 break;
1126 case XK_Left:
1127 if (iw < ww)
1128 return;
1129 sx = xOffset + appData.translate;
1130 sx = MIN(iw - ww, sx);
1131 if (sx == xOffset)
1132 return;
1133 xOffset = sx;
1134 sy = yOffset;
1135 dx = dy = 0;
1136 w = ww; h = wh;
1137 break;
1138 case XK_Right:
1139 if (iw < ww)
1140 return;
1141 sx = xOffset - appData.translate;
1142 sx = MAX(sx, 0);
1143 if (sx == xOffset)
1144 return;
1145 xOffset = sx;
1146 sy = yOffset;
1147 dx = dy = 0;
1148 w = ww; h = wh;
1149 break;
1150 default:
1151 return;
1152 }
1153 break;
1154 case MotionNotify:
1155 /*
1156 * MotionEvent compression. Ignore multiple motion events.
1157 * Ignore motion events if the mouse button is up.
1158 */
1159 if (XPending(xDisplay)) /* Xlib doesn't flush the output buffer */
1160 if (XtPeekEvent(&next_event))
1161 if (next_event.type == MotionNotify)
1162 return;
1163 if ((grabX < 0) || (grabY < 0))
1164 return;
1165 sx = xOffset + grabX - (int) event->xmotion.x;
1166 if (sx >= (iw - ww)) /* clamp x motion but allow y motion */
1167 sx = iw - ww;
1168 sx = MAX(sx, 0);
1169 sy = yOffset + grabY - (int) event->xmotion.y;
1170 if (sy >= (ih - wh)) /* clamp y motion but allow x motion */
1171 sy = ih - wh;
1172 sy = MAX(sy, 0);
1173 if ((sx == xOffset) && (sy == yOffset))
1174 return;
1175 dx = dy = 0;
1176 w = ww; h = wh;
1177 break;
1178 case ButtonRelease:
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);
1185 grabX = grabY = -1;
1186 default:
1187 return;
1188 }
1189
1190 if (appData.usePixmap == True) {
1191 if (xImageDepth == 1)
1192 XCopyPlane(xDisplay, xImagePixmap, XtWindow(widget),
1193 xWinGc, sx, sy, w, h, dx, dy, 1);
1194 else
1195 XCopyArea(xDisplay, xImagePixmap, XtWindow(widget),
1196 xWinGc, sx, sy, w, h, dx, dy);
1197 } else
1198 XPutImage(xDisplay, XtWindow(widget), xWinGc, xImage,
1199 sx, sy, dx, dy, w, h);
1200}
1201
1202void
1203ResizeProc()
1204{
1205 Dimension w_width, w_height;
1206 int xo, yo, ww, wh;
1207 XEvent fake_event;
1208 Arg args[2];
1209
1210 if ((xOffset == 0) && (yOffset == 0))
1211 return;
1212
1213 XtSetArg(args[0], XtNwidth, &w_width);
1214 XtSetArg(args[1], XtNheight, &w_height);
1215 XtGetValues(shellWidget, args, 2);
1216 ww = w_width;
1217 wh = w_height;
1218 XtGetValues(listWidget, args, 2);
1219 wh -= w_height;
1220
1221 xo = xOffset; yo = yOffset;
1222
1223 if ((xOffset + ww) >= tfImageWidth)
1224 xOffset = MAX((int) tfImageWidth - ww, 0);
1225 if ((yOffset + wh) >= tfImageHeight)
1226 yOffset = MAX((int) tfImageHeight - wh, 0);
1227
1228 /*
1229 * Send an ExposeEvent if the origin changed.
1230 * We have to do this because of the use and semantics of bit gravity.
1231 */
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);
1238 }
1239}
1240
1241int
1242XTiffErrorHandler(display, error_event)
1243 Display *display;
1244 XErrorEvent *error_event;
1245{
1246 char message[80];
1247
1248 /*
1249 * Some X servers limit the size of pixmaps.
1250 */
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");
1254 else {
1255 XGetErrorText(display, error_event->error_code, message, 80);
1256 fprintf(stderr, "xtiff: error code %s\n", message);
1257 }
1258
1259 exit(0);
1260}
1261
1262void
1263Usage()
1264{
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");
1274 exit(0);
1275}