1 /*
2  * $Id: xtiff.c,v 1.4 2013-05-02 14:44:29 tgl Exp $
3  *
4  * xtiff - view a TIFF file in an X window
5  *
6  * Dan Sears
7  * Chris Sears
8  *
9  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
10  *
11  *                      All Rights Reserved
12  *
13  * Permission to use, copy, modify, and distribute this software and its
14  * documentation for any purpose and without fee is hereby granted,
15  * provided that the above copyright notice appear in all copies and that
16  * both that copyright notice and this permission notice appear in
17  * supporting documentation, and that the name of Digital not be
18  * used in advertising or publicity pertaining to distribution of the
19  * software without specific, written prior permission.
20  *
21  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
22  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
23  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
24  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
25  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
26  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
27  * SOFTWARE.
28  *
29  * Revision 1.0  90/05/07
30  *      Initial release.
31  * Revision 2.0  90/12/20
32  *      Converted to use the Athena Widgets and the Xt Intrinsics.
33  *
34  * Notes:
35  *
36  * According to the TIFF 5.0 Specification, it is possible to have
37  * both a TIFFTAG_COLORMAP and a TIFFTAG_COLORRESPONSECURVE.  This
38  * doesn't make sense since a TIFFTAG_COLORMAP is 16 bits wide and
39  * a TIFFTAG_COLORRESPONSECURVE is tfBitsPerSample bits wide for each
40  * channel.  This is probably a bug in the specification.
41  * In this case, TIFFTAG_COLORRESPONSECURVE is ignored.
42  * This might make sense if TIFFTAG_COLORMAP was 8 bits wide.
43  *
44  * TIFFTAG_COLORMAP is often incorrectly written as ranging from
45  * 0 to 255 rather than from 0 to 65535.  CheckAndCorrectColormap()
46  * takes care of this.
47  *
48  * Only ORIENTATION_TOPLEFT is supported correctly.  This is the
49  * default TIFF and X orientation.  Other orientations will be
50  * displayed incorrectly.
51  *
52  * There is no support for or use of 3/3/2 DirectColor visuals.
53  * TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE are not supported.
54  *
55  * Only TIFFTAG_BITSPERSAMPLE values that are 1, 2, 4 or 8 are supported.
56  */
57 #include <math.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <tiffio.h>
61 #include <X11/Xatom.h>
62 #include <X11/Intrinsic.h>
63 #include <X11/StringDefs.h>
64 #include <X11/Xproto.h>
65 #include <X11/Shell.h>
66 #include <X11/Xaw/Form.h>
67 #include <X11/Xaw/List.h>
68 #include <X11/Xaw/Label.h>
69 #include <X11/cursorfont.h>
70 #define XK_MISCELLANY
71 #include <X11/keysymdef.h>
72 #include "xtifficon.h"
73 
74 #define TIFF_GAMMA      "2.2"     /* default gamma from the TIFF 5.0 spec */
75 #define ROUND(x)        (uint16) ((x) + 0.5)
76 #define SCALE(x, s)     (((x) * 65535L) / (s))
77 #define MCHECK(m)       if (!m) { fprintf(stderr, "malloc failed\n"); exit(0); }
78 #define MIN(a, b)       (((a) < (b)) ? (a) : (b))
79 #define MAX(a, b)       (((a) > (b)) ? (a) : (b))
80 #define VIEWPORT_WIDTH  700
81 #define VIEWPORT_HEIGHT 500
82 #define KEY_TRANSLATE   20
83 
84 #ifdef __STDC__
85 #define PP(args)    args
86 #else
87 #define PP(args)    ()
88 #endif
89 
90 int main PP((int argc, char **argv));
91 void OpenTIFFFile PP((void));
92 void GetTIFFHeader PP((void));
93 void SetNameLabel PP((void));
94 void CheckAndCorrectColormap PP((void));
95 void SimpleGammaCorrection PP((void));
96 void GetVisual PP((void));
97 Boolean SearchVisualList PP((int image_depth,
98     int visual_class, Visual **visual));
99 void GetTIFFImage PP((void));
100 void CreateXImage PP((void));
101 XtCallbackProc SelectProc PP((Widget w, caddr_t unused_1, caddr_t unused_2));
102 void QuitProc PP((void));
103 void NextProc PP((void));
104 void PreviousProc PP((void));
105 void PageProc PP((int direction));
106 void EventProc PP((Widget widget, caddr_t unused, XEvent *event));
107 void ResizeProc PP((void));
108 int XTiffErrorHandler PP((Display *display, XErrorEvent *error_event));
109 void Usage PP((void));
110 
111 int xtVersion = XtSpecificationRelease;     /* xtiff depends on R4 or higher */
112 
113 /*
114  * Xt data structures
115  */
116 Widget shellWidget, formWidget, listWidget, labelWidget, imageWidget;
117 
118 enum { ButtonQuit = 0, ButtonPreviousPage = 1, ButtonNextPage = 2 };
119 
120 String buttonStrings[] = { "Quit", "Previous", "Next" };
121 
122 static XrmOptionDescRec shellOptions[] = {
123     { "-help", "*help", XrmoptionNoArg, (caddr_t) "True" },
124     { "-gamma", "*gamma", XrmoptionSepArg, NULL },
125     { "-usePixmap", "*usePixmap", XrmoptionSepArg, NULL },
126     { "-viewportWidth", "*viewportWidth", XrmoptionSepArg, NULL },
127     { "-viewportHeight", "*viewportHeight", XrmoptionSepArg, NULL },
128     { "-translate", "*translate", XrmoptionSepArg, NULL },
129     { "-verbose", "*verbose", XrmoptionSepArg, NULL }
130 };
131 
132 typedef struct {
133     Boolean help;
134     float gamma;
135     Boolean usePixmap;
136     uint32 viewportWidth;
137     uint32 viewportHeight;
138     int translate;
139     Boolean verbose;
140 } AppData, *AppDataPtr;
141 
142 AppData appData;
143 
144 XtResource clientResources[] = {
145     {
146         "help", XtCBoolean, XtRBoolean, sizeof(Boolean),
147         XtOffset(AppDataPtr, help), XtRImmediate, (XtPointer) False
148     }, {
149         "gamma", "Gamma", XtRFloat, sizeof(float),
150         XtOffset(AppDataPtr, gamma), XtRString, (XtPointer) TIFF_GAMMA
151     }, {
152         "usePixmap", "UsePixmap", XtRBoolean, sizeof(Boolean),
153         XtOffset(AppDataPtr, usePixmap), XtRImmediate, (XtPointer) True
154     }, {
155         "viewportWidth", "ViewportWidth", XtRInt, sizeof(int),
156         XtOffset(AppDataPtr, viewportWidth), XtRImmediate,
157         (XtPointer) VIEWPORT_WIDTH
158     }, {
159         "viewportHeight", "ViewportHeight", XtRInt, sizeof(int),
160         XtOffset(AppDataPtr, viewportHeight), XtRImmediate,
161         (XtPointer) VIEWPORT_HEIGHT
162     }, {
163         "translate", "Translate", XtRInt, sizeof(int),
164         XtOffset(AppDataPtr, translate), XtRImmediate, (XtPointer) KEY_TRANSLATE
165     }, {
166         "verbose", "Verbose", XtRBoolean, sizeof(Boolean),
167         XtOffset(AppDataPtr, verbose), XtRImmediate, (XtPointer) True
168     }
169 };
170 
171 Arg formArgs[] = {
172     { XtNresizable, True }
173 };
174 
175 Arg listArgs[] = {
176     { XtNresizable, False },
177     { XtNborderWidth, 0 },
178     { XtNdefaultColumns, 3 },
179     { XtNforceColumns, True },
180     { XtNlist, (int) buttonStrings },
181     { XtNnumberStrings, XtNumber(buttonStrings) },
182     { XtNtop, XtChainTop },
183     { XtNleft, XtChainLeft },
184     { XtNbottom, XtChainTop },
185     { XtNright, XtChainLeft }
186 };
187 
188 Arg labelArgs[] = {
189     { XtNresizable, False },
190     { XtNwidth, 200 },
191     { XtNborderWidth, 0 },
192     { XtNjustify, XtJustifyLeft },
193     { XtNtop, XtChainTop },
194     { XtNleft, XtChainLeft },
195     { XtNbottom, XtChainTop },
196     { XtNright, XtChainLeft }
197 };
198 
199 Arg imageArgs[] = {
200     { XtNresizable, True },
201     { XtNborderWidth, 0 },
202     { XtNtop, XtChainTop },
203     { XtNleft, XtChainLeft },
204     { XtNbottom, XtChainTop },
205     { XtNright, XtChainLeft }
206 };
207 
208 XtActionsRec actionsTable[] = {
209     { "quit", QuitProc },
210     { "next", NextProc },
211     { "previous", PreviousProc },
212     { "notifyresize", ResizeProc }
213 };
214 
215 char translationsTable[] = "<Key>q:      quit() \n \
216                             <Key>Q:      quit() \n \
217                             <Message>WM_PROTOCOLS: quit()\n \
218                             <Key>p:      previous() \n \
219                             <Key>P:      previous() \n \
220                             <Key>n:      next() \n \
221                             <Key>N:      next() \n \
222                             <Configure>: notifyresize()";
223 
224 /*
225  * X data structures
226  */
227 Colormap            xColormap;
228 Display *           xDisplay;
229 Pixmap              xImagePixmap;
230 Visual *            xVisual;
231 XImage *            xImage;
232 GC                  xWinGc;
233 int                 xImageDepth, xScreen, xRedMask, xGreenMask, xBlueMask,
234                     xOffset = 0, yOffset = 0, grabX = -1, grabY = -1;
235 unsigned char       basePixel = 0;
236 
237 /*
238  * TIFF data structures
239  */
240 TIFF *              tfFile = NULL;
241 uint32              tfImageWidth, tfImageHeight;
242 uint16              tfBitsPerSample, tfSamplesPerPixel, tfPlanarConfiguration,
243                     tfPhotometricInterpretation, tfGrayResponseUnit,
244                     tfImageDepth, tfBytesPerRow;
245 int                 tfDirectory = 0, tfMultiPage = False;
246 double              tfUnitMap, tfGrayResponseUnitMap[] = {
247                         -1, -10, -100, -1000, -10000, -100000
248                     };
249 
250 /*
251  * display data structures
252  */
253 double              *dRed, *dGreen, *dBlue;
254 
255 /*
256  * shared data structures
257  */
258 uint16 *            redMap = NULL, *greenMap = NULL, *blueMap = NULL,
259                     *grayMap = NULL, colormapSize;
260 char *             imageMemory;
261 char *              fileName;
262 
263 int
main(int argc,char ** argv)264 main(int argc, char **argv)
265 {
266     XSetWindowAttributes window_attributes;
267     Widget widget_list[3];
268     Arg args[5];
269 
270     setbuf(stdout, NULL); setbuf(stderr, NULL);
271 
272     shellWidget = XtInitialize(argv[0], "XTiff", shellOptions,
273         XtNumber(shellOptions), &argc, argv);
274 
275     XSetErrorHandler(XTiffErrorHandler);
276 
277     XtGetApplicationResources(shellWidget, &appData,
278         (XtResourceList) clientResources, (Cardinal) XtNumber(clientResources),
279         (ArgList) NULL, (Cardinal) 0);
280 
281     if ((argc <= 1) || (argc > 2) || appData.help)
282         Usage();
283 
284     if (appData.verbose == False) {
285         TIFFSetErrorHandler(0);
286         TIFFSetWarningHandler(0);
287     }
288 
289     fileName = argv[1];
290 
291     xDisplay = XtDisplay(shellWidget);
292     xScreen = DefaultScreen(xDisplay);
293 
294     OpenTIFFFile();
295     GetTIFFHeader();
296     SimpleGammaCorrection();
297     GetVisual();
298     GetTIFFImage();
299 
300     /*
301      * Send visual, colormap, depth and iconPixmap to shellWidget.
302      * Sending the visual to the shell is only possible with the advent of R4.
303      */
304     XtSetArg(args[0], XtNvisual, xVisual);
305     XtSetArg(args[1], XtNcolormap, xColormap);
306     XtSetArg(args[2], XtNdepth,
307         xImageDepth == 1 ? DefaultDepth(xDisplay, xScreen) : xImageDepth);
308     XtSetArg(args[3], XtNiconPixmap,
309         XCreateBitmapFromData(xDisplay, RootWindow(xDisplay, xScreen),
310             xtifficon_bits, xtifficon_width, xtifficon_height));
311     XtSetArg(args[4], XtNallowShellResize, True);
312     XtSetValues(shellWidget, args, 5);
313 
314     /*
315      * widget instance hierarchy
316      */
317     formWidget = XtCreateManagedWidget("form", formWidgetClass,
318         shellWidget, formArgs, XtNumber(formArgs));
319 
320         widget_list[0] = listWidget = XtCreateWidget("list",
321             listWidgetClass, formWidget, listArgs, XtNumber(listArgs));
322 
323         widget_list[1] = labelWidget = XtCreateWidget("label",
324             labelWidgetClass, formWidget, labelArgs, XtNumber(labelArgs));
325 
326         widget_list[2] = imageWidget = XtCreateWidget("image",
327             widgetClass, formWidget, imageArgs, XtNumber(imageArgs));
328 
329     XtManageChildren(widget_list, XtNumber(widget_list));
330 
331     /*
332      * initial widget sizes - for small images let xtiff size itself
333      */
334     if (tfImageWidth >= appData.viewportWidth) {
335         XtSetArg(args[0], XtNwidth, appData.viewportWidth);
336         XtSetValues(shellWidget, args, 1);
337     }
338     if (tfImageHeight >= appData.viewportHeight) {
339         XtSetArg(args[0], XtNheight, appData.viewportHeight);
340         XtSetValues(shellWidget, args, 1);
341     }
342 
343     XtSetArg(args[0], XtNwidth, tfImageWidth);
344     XtSetArg(args[1], XtNheight, tfImageHeight);
345     XtSetValues(imageWidget, args, 2);
346 
347     /*
348      * formWidget uses these constraints but they are stored in the children.
349      */
350     XtSetArg(args[0], XtNfromVert, listWidget);
351     XtSetValues(imageWidget, args, 1);
352     XtSetArg(args[0], XtNfromHoriz, listWidget);
353     XtSetValues(labelWidget, args, 1);
354 
355     SetNameLabel();
356 
357     XtAddCallback(listWidget, XtNcallback, (XtCallbackProc) SelectProc,
358         (XtPointer) NULL);
359 
360     XtAddActions(actionsTable, XtNumber(actionsTable));
361     XtSetArg(args[0], XtNtranslations,
362         XtParseTranslationTable(translationsTable));
363     XtSetValues(formWidget, &args[0], 1);
364     XtSetValues(imageWidget, &args[0], 1);
365 
366     /*
367      * This is intended to be a little faster than going through
368      * the translation manager.
369      */
370     XtAddEventHandler(imageWidget, ExposureMask | ButtonPressMask
371         | ButtonReleaseMask | Button1MotionMask | KeyPressMask,
372         False, EventProc, NULL);
373 
374     XtRealizeWidget(shellWidget);
375 
376     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_fleur);
377     XChangeWindowAttributes(xDisplay, XtWindow(imageWidget),
378         CWCursor, &window_attributes);
379 
380     CreateXImage();
381 
382     XtMainLoop();
383 
384     return 0;
385 }
386 
387 void
OpenTIFFFile()388 OpenTIFFFile()
389 {
390     if (tfFile != NULL)
391         TIFFClose(tfFile);
392 
393     if ((tfFile = TIFFOpen(fileName, "r")) == NULL) {
394 	fprintf(appData.verbose ? stderr : stdout,
395 	    "xtiff: can't open %s as a TIFF file\n", fileName);
396         exit(0);
397     }
398 
399     tfMultiPage = (TIFFLastDirectory(tfFile) ? False : True);
400 }
401 
402 void
GetTIFFHeader()403 GetTIFFHeader()
404 {
405     register int i;
406 
407     if (!TIFFSetDirectory(tfFile, tfDirectory)) {
408         fprintf(stderr, "xtiff: can't seek to directory %d in %s\n",
409             tfDirectory, fileName);
410         exit(0);
411     }
412 
413     TIFFGetField(tfFile, TIFFTAG_IMAGEWIDTH, &tfImageWidth);
414     TIFFGetField(tfFile, TIFFTAG_IMAGELENGTH, &tfImageHeight);
415 
416     /*
417      * If the following tags aren't present then use the TIFF defaults.
418      */
419     TIFFGetFieldDefaulted(tfFile, TIFFTAG_BITSPERSAMPLE, &tfBitsPerSample);
420     TIFFGetFieldDefaulted(tfFile, TIFFTAG_SAMPLESPERPIXEL, &tfSamplesPerPixel);
421     TIFFGetFieldDefaulted(tfFile, TIFFTAG_PLANARCONFIG, &tfPlanarConfiguration);
422     TIFFGetFieldDefaulted(tfFile, TIFFTAG_GRAYRESPONSEUNIT, &tfGrayResponseUnit);
423 
424     tfUnitMap = tfGrayResponseUnitMap[tfGrayResponseUnit];
425     colormapSize = 1 << tfBitsPerSample;
426     tfImageDepth = tfBitsPerSample * tfSamplesPerPixel;
427 
428     dRed = (double *) malloc(colormapSize * sizeof(double));
429     dGreen = (double *) malloc(colormapSize * sizeof(double));
430     dBlue = (double *) malloc(colormapSize * sizeof(double));
431     MCHECK(dRed); MCHECK(dGreen); MCHECK(dBlue);
432 
433     /*
434      * If TIFFTAG_PHOTOMETRIC is not present then assign a reasonable default.
435      * The TIFF 5.0 specification doesn't give a default.
436      */
437     if (!TIFFGetField(tfFile, TIFFTAG_PHOTOMETRIC,
438             &tfPhotometricInterpretation)) {
439         if (tfSamplesPerPixel != 1)
440             tfPhotometricInterpretation = PHOTOMETRIC_RGB;
441         else if (tfBitsPerSample == 1)
442             tfPhotometricInterpretation = PHOTOMETRIC_MINISBLACK;
443         else if (TIFFGetField(tfFile, TIFFTAG_COLORMAP,
444                 &redMap, &greenMap, &blueMap)) {
445             tfPhotometricInterpretation = PHOTOMETRIC_PALETTE;
446             redMap = greenMap = blueMap = NULL;
447         } else
448             tfPhotometricInterpretation = PHOTOMETRIC_MINISBLACK;
449     }
450 
451     /*
452      * Given TIFFTAG_PHOTOMETRIC extract or create the response curves.
453      */
454     switch (tfPhotometricInterpretation) {
455     case PHOTOMETRIC_RGB:
456 	redMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
457 	greenMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
458 	blueMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
459 	MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap);
460 	for (i = 0; i < colormapSize; i++)
461 	    dRed[i] = dGreen[i] = dBlue[i]
462 		= (double) SCALE(i, colormapSize - 1);
463         break;
464     case PHOTOMETRIC_PALETTE:
465         if (!TIFFGetField(tfFile, TIFFTAG_COLORMAP,
466                 &redMap, &greenMap, &blueMap)) {
467             redMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
468             greenMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
469             blueMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
470             MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap);
471             for (i = 0; i < colormapSize; i++)
472                 dRed[i] = dGreen[i] = dBlue[i]
473                     = (double) SCALE(i, colormapSize - 1);
474         } else {
475             CheckAndCorrectColormap();
476             for (i = 0; i < colormapSize; i++) {
477                 dRed[i] = (double) redMap[i];
478                 dGreen[i] = (double) greenMap[i];
479                 dBlue[i] = (double) blueMap[i];
480             }
481         }
482         break;
483     case PHOTOMETRIC_MINISWHITE:
484         redMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
485         greenMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
486         blueMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
487         MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap);
488 	for (i = 0; i < colormapSize; i++)
489 	    dRed[i] = dGreen[i] = dBlue[i] = (double)
490 		 SCALE(colormapSize-1-i, colormapSize-1);
491         break;
492     case PHOTOMETRIC_MINISBLACK:
493         redMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
494         greenMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
495         blueMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
496         MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap);
497 	for (i = 0; i < colormapSize; i++)
498 	    dRed[i] = dGreen[i] = dBlue[i] = (double) SCALE(i, colormapSize-1);
499         break;
500     default:
501         fprintf(stderr,
502             "xtiff: can't display photometric interpretation type %d\n",
503             tfPhotometricInterpretation);
504         exit(0);
505     }
506 }
507 
508 void
SetNameLabel()509 SetNameLabel()
510 {
511     char buffer[BUFSIZ];
512     Arg args[1];
513 
514     if (tfMultiPage)
515         snprintf(buffer, sizeof(buffer), "%s - page %d", fileName, tfDirectory);
516     else
517         snprintf(buffer, sizeof(buffer), "%s", fileName);
518     XtSetArg(args[0], XtNlabel, buffer);
519     XtSetValues(labelWidget, args, 1);
520 }
521 
522 /*
523  * Many programs get TIFF colormaps wrong.  They use 8-bit colormaps instead of
524  * 16-bit colormaps.  This function is a heuristic to detect and correct this.
525  */
526 void
CheckAndCorrectColormap()527 CheckAndCorrectColormap()
528 {
529     register int i;
530 
531     for (i = 0; i < colormapSize; i++)
532         if ((redMap[i] > 255) || (greenMap[i] > 255) || (blueMap[i] > 255))
533             return;
534 
535     for (i = 0; i < colormapSize; i++) {
536         redMap[i] = SCALE(redMap[i], 255);
537         greenMap[i] = SCALE(greenMap[i], 255);
538         blueMap[i] = SCALE(blueMap[i], 255);
539     }
540     TIFFWarning(fileName, "Assuming 8-bit colormap");
541 }
542 
543 void
SimpleGammaCorrection()544 SimpleGammaCorrection()
545 {
546     register int i;
547     register double i_gamma = 1.0 / appData.gamma;
548 
549     for (i = 0; i < colormapSize; i++) {
550         if (((tfPhotometricInterpretation == PHOTOMETRIC_MINISWHITE)
551             && (i == colormapSize - 1))
552             || ((tfPhotometricInterpretation == PHOTOMETRIC_MINISBLACK)
553             && (i == 0)))
554             redMap[i] = greenMap[i] = blueMap[i] = 0;
555         else {
556             redMap[i] = ROUND((pow(dRed[i] / 65535.0, i_gamma) * 65535.0));
557             greenMap[i] = ROUND((pow(dGreen[i] / 65535.0, i_gamma) * 65535.0));
558             blueMap[i] = ROUND((pow(dBlue[i] / 65535.0, i_gamma) * 65535.0));
559         }
560     }
561 
562     free(dRed); free(dGreen); free(dBlue);
563 }
564 
565 static char* classNames[] = {
566     "StaticGray",
567     "GrayScale",
568     "StaticColor",
569     "PseudoColor",
570     "TrueColor",
571     "DirectColor"
572 };
573 
574 /*
575  * Current limitation: the visual is set initially by the first file.
576  * It cannot be changed.
577  */
578 void
GetVisual()579 GetVisual()
580 {
581     XColor *colors = NULL;
582     unsigned long *pixels = NULL;
583     unsigned long i;
584 
585     switch (tfImageDepth) {
586     /*
587      * X really wants a 32-bit image with the fourth channel unused,
588      * but the visual structure thinks it's 24-bit.  bitmap_unit is 32.
589      */
590     case 32:
591     case 24:
592         if (SearchVisualList(24, DirectColor, &xVisual) == False) {
593             fprintf(stderr, "xtiff: 24-bit DirectColor visual not available\n");
594             exit(0);
595         }
596 
597         colors = (XColor *) malloc(3 * colormapSize * sizeof(XColor));
598         MCHECK(colors);
599 
600         for (i = 0; i < colormapSize; i++) {
601             colors[i].pixel = (i << 16) + (i << 8) + i;
602             colors[i].red = redMap[i];
603             colors[i].green = greenMap[i];
604             colors[i].blue = blueMap[i];
605             colors[i].flags = DoRed | DoGreen | DoBlue;
606         }
607 
608         xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen),
609             xVisual, AllocAll);
610         XStoreColors(xDisplay, xColormap, colors, colormapSize);
611         break;
612     case 8:
613     case 4:
614     case 2:
615         /*
616          * We assume that systems with 24-bit visuals also have 8-bit visuals.
617          * We don't promote from 8-bit PseudoColor to 24/32 bit DirectColor.
618          */
619         switch (tfPhotometricInterpretation) {
620         case PHOTOMETRIC_MINISWHITE:
621         case PHOTOMETRIC_MINISBLACK:
622             if (SearchVisualList((int) tfImageDepth, GrayScale, &xVisual) == True)
623                 break;
624         case PHOTOMETRIC_PALETTE:
625             if (SearchVisualList((int) tfImageDepth, PseudoColor, &xVisual) == True)
626                 break;
627         default:
628             fprintf(stderr, "xtiff: Unsupported TIFF/X configuration\n");
629             exit(0);
630         }
631 
632         colors = (XColor *) malloc(colormapSize * sizeof(XColor));
633         MCHECK(colors);
634 
635         for (i = 0; i < colormapSize; i++) {
636             colors[i].pixel = i;
637             colors[i].red = redMap[i];
638             colors[i].green = greenMap[i];
639             colors[i].blue = blueMap[i];
640             colors[i].flags = DoRed | DoGreen | DoBlue;
641         }
642 
643         /*
644          * xtiff's colormap allocation is private.  It does not attempt
645          * to detect whether any existing colormap entries are suitable
646          * for its use.  This will cause colormap flashing.  Furthermore,
647          * background and foreground are taken from the environment.
648          * For example, the foreground color may be red when the visual
649          * is GrayScale.  If the colormap is completely populated,
650          * Xt will not be able to allocate fg and bg.
651          */
652         if (tfImageDepth == 8)
653             xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen),
654                 xVisual, AllocAll);
655         else {
656             xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen),
657                 xVisual, AllocNone);
658             pixels = (unsigned long *)
659                 malloc(colormapSize * sizeof(unsigned long));
660             MCHECK(pixels);
661             (void) XAllocColorCells(xDisplay, xColormap, True,
662                 NULL, 0, pixels, colormapSize);
663             basePixel = (unsigned char) pixels[0];
664             free(pixels);
665         }
666         XStoreColors(xDisplay, xColormap, colors, colormapSize);
667         break;
668     case 1:
669         xImageDepth = 1;
670         xVisual = DefaultVisual(xDisplay, xScreen);
671         xColormap = DefaultColormap(xDisplay, xScreen);
672         break;
673     default:
674         fprintf(stderr, "xtiff: unsupported image depth %d\n", tfImageDepth);
675         exit(0);
676     }
677 
678     if (appData.verbose == True)
679 	fprintf(stderr, "%s: Using %d-bit %s visual.\n",
680 	    fileName, xImageDepth, classNames[xVisual->class]);
681 
682     if (colors != NULL)
683         free(colors);
684     if (grayMap != NULL)
685         free(grayMap);
686     if (redMap != NULL)
687         free(redMap);
688     if (greenMap != NULL)
689         free(greenMap);
690     if (blueMap != NULL)
691         free(blueMap);
692 
693     colors = NULL; grayMap = redMap = greenMap = blueMap = NULL;
694 }
695 
696 /*
697  * Search for an appropriate visual.  Promote where necessary.
698  * Check to make sure that ENOUGH colormap entries are writeable.
699  * basePixel was determined when XAllocColorCells() contiguously
700  * allocated enough entries.  basePixel is used below in GetTIFFImage.
701  */
702 Boolean
SearchVisualList(image_depth,visual_class,visual)703 SearchVisualList(image_depth, visual_class, visual)
704     int image_depth, visual_class;
705     Visual **visual;
706 {
707     XVisualInfo template_visual, *visual_list, *vl;
708     int i, n_visuals;
709 
710     template_visual.screen = xScreen;
711     vl = visual_list = XGetVisualInfo(xDisplay, VisualScreenMask,
712         &template_visual, &n_visuals);
713 
714     if (n_visuals == 0) {
715         fprintf(stderr, "xtiff: visual list not available\n");
716         exit(0);
717     }
718 
719     for (i = 0; i < n_visuals; vl++, i++) {
720         if ((vl->class == visual_class) && (vl->depth >= image_depth)
721             && (vl->visual->map_entries >= (1 << vl->depth))) {
722             *visual = vl->visual;
723             xImageDepth = vl->depth;
724             xRedMask = vl->red_mask;
725             xGreenMask = vl->green_mask;
726             xBlueMask = vl->blue_mask;
727             XFree((char *) visual_list);
728             return True;
729         }
730     }
731 
732     XFree((char *) visual_list);
733     return False;
734 }
735 
736 void
GetTIFFImage()737 GetTIFFImage()
738 {
739     int pixel_map[3], red_shift, green_shift, blue_shift;
740     char *scan_line, *output_p, *input_p;
741     uint32 i, j;
742     uint16 s;
743 
744     scan_line = (char *) malloc(tfBytesPerRow = TIFFScanlineSize(tfFile));
745     MCHECK(scan_line);
746 
747     if ((tfImageDepth == 32) || (tfImageDepth == 24)) {
748         output_p = imageMemory = (char *)
749             malloc(tfImageWidth * tfImageHeight * 4);
750         MCHECK(imageMemory);
751 
752         /*
753          * Handle different color masks for different frame buffers.
754          */
755         if (ImageByteOrder(xDisplay) == LSBFirst) { /* DECstation 5000 */
756             red_shift = pixel_map[0] = xRedMask == 0xFF000000 ? 3
757                 : (xRedMask == 0xFF0000 ? 2 : (xRedMask == 0xFF00 ? 1 : 0));
758             green_shift = pixel_map[1] = xGreenMask == 0xFF000000 ? 3
759                 : (xGreenMask == 0xFF0000 ? 2 : (xGreenMask == 0xFF00 ? 1 : 0));
760             blue_shift = pixel_map[2] = xBlueMask == 0xFF000000 ? 3
761                 : (xBlueMask == 0xFF0000 ? 2 : (xBlueMask == 0xFF00 ? 1 : 0));
762         } else { /* Ardent */
763             red_shift = pixel_map[0] = xRedMask == 0xFF000000 ? 0
764                 : (xRedMask == 0xFF0000 ? 1 : (xRedMask == 0xFF00 ? 2 : 3));
765             green_shift = pixel_map[0] = xGreenMask == 0xFF000000 ? 0
766                 : (xGreenMask == 0xFF0000 ? 1 : (xGreenMask == 0xFF00 ? 2 : 3));
767             blue_shift = pixel_map[0] = xBlueMask == 0xFF000000 ? 0
768                 : (xBlueMask == 0xFF0000 ? 1 : (xBlueMask == 0xFF00 ? 2 : 3));
769         }
770 
771         if (tfPlanarConfiguration == PLANARCONFIG_CONTIG) {
772             for (i = 0; i < tfImageHeight; i++) {
773                 if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0)
774                     break;
775                 for (input_p = scan_line, j = 0; j < tfImageWidth; j++) {
776                     *(output_p + red_shift) = *input_p++;
777                     *(output_p + green_shift) = *input_p++;
778                     *(output_p + blue_shift) = *input_p++;
779                     output_p += 4;
780                     if (tfSamplesPerPixel == 4) /* skip the fourth channel */
781                         input_p++;
782                 }
783             }
784         } else {
785             for (s = 0; s < tfSamplesPerPixel; s++) {
786                 if (s == 3)             /* skip the fourth channel */
787                     continue;
788                 for (i = 0; i < tfImageHeight; i++) {
789                     if (TIFFReadScanline(tfFile, scan_line, i, s) < 0)
790                         break;
791                     input_p = scan_line;
792                     output_p = imageMemory + (i*tfImageWidth*4) + pixel_map[s];
793                     for (j = 0; j < tfImageWidth; j++, output_p += 4)
794                         *output_p = *input_p++;
795                 }
796             }
797         }
798     } else {
799         if (xImageDepth == tfImageDepth) {
800             output_p = imageMemory = (char *)
801                 malloc(tfBytesPerRow * tfImageHeight);
802             MCHECK(imageMemory);
803 
804             for (i = 0; i < tfImageHeight; i++, output_p += tfBytesPerRow)
805                 if (TIFFReadScanline(tfFile, output_p, i, 0) < 0)
806                     break;
807         } else if ((xImageDepth == 8) && (tfImageDepth == 4)) {
808             output_p = imageMemory = (char *)
809                 malloc(tfBytesPerRow * 2 * tfImageHeight + 2);
810             MCHECK(imageMemory);
811 
812             /*
813              * If a scanline is of odd size the inner loop below will overshoot.
814              * This is handled very simply by recalculating the start point at
815              * each scanline and padding imageMemory a little at the end.
816              */
817             for (i = 0; i < tfImageHeight; i++) {
818                 if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0)
819                     break;
820                 output_p = &imageMemory[i * tfImageWidth];
821                 input_p = scan_line;
822                 for (j = 0; j < tfImageWidth; j += 2, input_p++) {
823                     *output_p++ = (*input_p >> 4) + basePixel;
824                     *output_p++ = (*input_p & 0xf) + basePixel;
825                 }
826             }
827         } else if ((xImageDepth == 8) && (tfImageDepth == 2)) {
828             output_p = imageMemory = (char *)
829                 malloc(tfBytesPerRow * 4 * tfImageHeight + 4);
830             MCHECK(imageMemory);
831 
832             for (i = 0; i < tfImageHeight; i++) {
833                 if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0)
834                     break;
835                 output_p = &imageMemory[i * tfImageWidth];
836                 input_p = scan_line;
837                 for (j = 0; j < tfImageWidth; j += 4, input_p++) {
838                     *output_p++ = (*input_p >> 6) + basePixel;
839                     *output_p++ = ((*input_p >> 4) & 3) + basePixel;
840                     *output_p++ = ((*input_p >> 2) & 3) + basePixel;
841                     *output_p++ = (*input_p & 3) + basePixel;
842                 }
843             }
844         } else if ((xImageDepth == 4) && (tfImageDepth == 2)) {
845             output_p = imageMemory = (char *)
846                 malloc(tfBytesPerRow * 2 * tfImageHeight + 2);
847             MCHECK(imageMemory);
848 
849             for (i = 0; i < tfImageHeight; i++) {
850                 if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0)
851                     break;
852                 output_p = &imageMemory[i * tfBytesPerRow * 2];
853                 input_p = scan_line;
854                 for (j = 0; j < tfImageWidth; j += 4, input_p++) {
855                     *output_p++ = (((*input_p>>6) << 4)
856                         | ((*input_p >> 4) & 3)) + basePixel;
857                     *output_p++ = ((((*input_p>>2) & 3) << 4)
858                         | (*input_p & 3)) + basePixel;
859                 }
860             }
861         } else {
862             fprintf(stderr,
863                 "xtiff: can't handle %d-bit TIFF file on an %d-bit display\n",
864                 tfImageDepth, xImageDepth);
865             exit(0);
866         }
867     }
868 
869     free(scan_line);
870 }
871 
872 void
CreateXImage()873 CreateXImage()
874 {
875     XGCValues gc_values;
876     GC bitmap_gc;
877 
878     xOffset = yOffset = 0;
879     grabX = grabY = -1;
880 
881     xImage = XCreateImage(xDisplay, xVisual, xImageDepth,
882         xImageDepth == 1 ? XYBitmap : ZPixmap, /* offset */ 0,
883         (char *) imageMemory, tfImageWidth, tfImageHeight,
884         /* bitmap_pad */ 8, /* bytes_per_line */ 0);
885 
886     /*
887      * libtiff converts LSB data into MSB but doesn't change the FillOrder tag.
888      */
889     if (xImageDepth == 1)
890         xImage->bitmap_bit_order = MSBFirst;
891     if (xImageDepth <= 8)
892         xImage->byte_order = MSBFirst;
893 
894     /*
895      * create an appropriate GC
896      */
897     gc_values.function = GXcopy;
898     gc_values.plane_mask = AllPlanes;
899     if (tfPhotometricInterpretation == PHOTOMETRIC_MINISBLACK) {
900         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
901         gc_values.background = XBlackPixel(xDisplay, xScreen);
902     } else {
903         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
904         gc_values.background = XWhitePixel(xDisplay, xScreen);
905     }
906     xWinGc = XCreateGC(xDisplay, XtWindow(shellWidget),
907         GCFunction | GCPlaneMask | GCForeground | GCBackground, &gc_values);
908 
909     /*
910      * create the pixmap and load the image
911      */
912     if (appData.usePixmap == True) {
913         xImagePixmap = XCreatePixmap(xDisplay, RootWindow(xDisplay, xScreen),
914             xImage->width, xImage->height, xImageDepth);
915 
916         /*
917          * According to the O'Reilly X Protocol Reference Manual, page 53,
918          * "A pixmap depth of one is always supported and listed, but windows
919          * of depth one might not be supported."  Therefore we create a pixmap
920          * of depth one and use XCopyPlane().  This is idiomatic.
921          */
922         if (xImageDepth == 1) {         /* just pass the bits through */
923             gc_values.foreground = 1;   /* foreground describes set bits */
924             gc_values.background = 0;   /* background describes clear bits */
925             bitmap_gc = XCreateGC(xDisplay, xImagePixmap,
926                 GCForeground | GCBackground, &gc_values);
927             XPutImage(xDisplay, xImagePixmap, bitmap_gc, xImage,
928                 0, 0, 0, 0, xImage->width, xImage->height);
929         } else
930             XPutImage(xDisplay, xImagePixmap, xWinGc, xImage,
931                 0, 0, 0, 0, xImage->width, xImage->height);
932         XDestroyImage(xImage);
933         free(imageMemory);
934     }
935 }
936 
937 XtCallbackProc
SelectProc(w,unused_1,unused_2)938 SelectProc(w, unused_1, unused_2)
939     Widget w;
940     caddr_t unused_1;
941     caddr_t unused_2;
942 {
943     XawListReturnStruct *list_return;
944 
945     list_return = XawListShowCurrent(w);
946 
947     switch (list_return->list_index) {
948     case ButtonQuit:
949         QuitProc();
950         break;
951     case ButtonPreviousPage:
952         PreviousProc();
953         break;
954     case ButtonNextPage:
955         NextProc();
956         break;
957     default:
958         fprintf(stderr, "error in SelectProc\n");
959         exit(0);
960     }
961     XawListUnhighlight(w);
962 }
963 
964 void
QuitProc(void)965 QuitProc(void)
966 {
967     exit(0);
968 }
969 
970 void
NextProc()971 NextProc()
972 {
973     PageProc(ButtonNextPage);
974 }
975 
976 void
PreviousProc()977 PreviousProc()
978 {
979     PageProc(ButtonPreviousPage);
980 }
981 
982 void
PageProc(direction)983 PageProc(direction)
984     int direction;
985 {
986     XEvent fake_event;
987     Arg args[4];
988 
989     switch (direction) {
990     case ButtonPreviousPage:
991         if (tfDirectory > 0)
992             TIFFSetDirectory(tfFile, --tfDirectory);
993         else
994             return;
995         break;
996     case ButtonNextPage:
997         if (TIFFReadDirectory(tfFile) == True)
998             tfDirectory++;
999         else
1000             return;
1001         break;
1002     default:
1003         fprintf(stderr, "error in PageProc\n");
1004         exit(0);
1005     }
1006 
1007     xOffset = yOffset = 0;
1008     grabX = grabY = -1;
1009 
1010     GetTIFFHeader();
1011     SetNameLabel();
1012     GetTIFFImage();
1013 
1014     if (appData.usePixmap == True)
1015         XFreePixmap(xDisplay, xImagePixmap);
1016     else
1017         XDestroyImage(xImage);
1018 
1019     CreateXImage();
1020 
1021     /*
1022      * Using XtSetValues() to set the widget size causes a resize.
1023      * This resize gets propagated up to the parent shell.
1024      * In order to disable this visually disconcerting effect,
1025      * shell resizing is temporarily disabled.
1026      */
1027     XtSetArg(args[0], XtNallowShellResize, False);
1028     XtSetValues(shellWidget, args, 1);
1029 
1030     XtSetArg(args[0], XtNwidth, tfImageWidth);
1031     XtSetArg(args[1], XtNheight, tfImageHeight);
1032     XtSetValues(imageWidget, args, 2);
1033 
1034     XtSetArg(args[0], XtNallowShellResize, True);
1035     XtSetValues(shellWidget, args, 1);
1036 
1037     XClearWindow(xDisplay, XtWindow(imageWidget));
1038 
1039     fake_event.type = Expose;
1040     fake_event.xexpose.x = fake_event.xexpose.y = 0;
1041     fake_event.xexpose.width = tfImageWidth;    /* the window will clip */
1042     fake_event.xexpose.height = tfImageHeight;
1043     EventProc(imageWidget, NULL, &fake_event);
1044 }
1045 
1046 void
EventProc(widget,unused,event)1047 EventProc(widget, unused, event)
1048     Widget widget;
1049     caddr_t unused;
1050     XEvent *event;
1051 {
1052     int ih, iw, ww, wh, sx, sy, w, h, dx, dy;
1053     Dimension w_width, w_height;
1054     XEvent next_event;
1055     Arg args[2];
1056 
1057     if (event->type == MappingNotify) {
1058         XRefreshKeyboardMapping((XMappingEvent *) event);
1059         return;
1060     }
1061 
1062     if (!XtIsRealized(widget))
1063         return;
1064 
1065     if ((event->type == ButtonPress) || (event->type == ButtonRelease))
1066         if (event->xbutton.button != Button1)
1067             return;
1068 
1069     iw = tfImageWidth;  /* avoid sign problems */
1070     ih = tfImageHeight;
1071 
1072     /*
1073      * The grabX and grabY variables record where the user grabbed the image.
1074      * They also record whether the mouse button is down or not.
1075      */
1076     if (event->type == ButtonPress) {
1077         grabX = event->xbutton.x;
1078         grabY = event->xbutton.y;
1079         return;
1080     }
1081 
1082     /*
1083      * imageWidget is a Core widget and doesn't get resized.
1084      * So we calculate the size of its viewport here.
1085      */
1086     XtSetArg(args[0], XtNwidth, &w_width);
1087     XtSetArg(args[1], XtNheight, &w_height);
1088     XtGetValues(shellWidget, args, 2);
1089     ww = w_width;
1090     wh = w_height;
1091     XtGetValues(listWidget, args, 2);
1092     wh -= w_height;
1093 
1094     switch (event->type) {
1095     case Expose:
1096         dx = event->xexpose.x;
1097         dy = event->xexpose.y;
1098         sx = dx + xOffset;
1099         sy = dy + yOffset;
1100         w = MIN(event->xexpose.width, iw);
1101         h = MIN(event->xexpose.height, ih);
1102         break;
1103     case KeyPress:
1104         if ((grabX >= 0) || (grabY >= 0)) /* Mouse button is still down */
1105             return;
1106         switch (XLookupKeysym((XKeyEvent *) event, /* KeySyms index */ 0)) {
1107         case XK_Up:
1108             if (ih < wh)    /* Don't scroll if the window fits the image. */
1109                 return;
1110             sy = yOffset + appData.translate;
1111             sy = MIN(ih - wh, sy);
1112             if (sy == yOffset)  /* Filter redundant stationary refreshes. */
1113                 return;
1114             yOffset = sy;
1115             sx = xOffset;
1116             dx = dy = 0;
1117             w = ww; h = wh;
1118             break;
1119         case XK_Down:
1120             if (ih < wh)
1121                 return;
1122             sy = yOffset - appData.translate;
1123             sy = MAX(sy, 0);
1124             if (sy == yOffset)
1125                 return;
1126             yOffset = sy;
1127             sx = xOffset;
1128             dx = dy = 0;
1129             w = ww; h = wh;
1130             break;
1131         case XK_Left:
1132             if (iw < ww)
1133                 return;
1134             sx = xOffset + appData.translate;
1135             sx = MIN(iw - ww, sx);
1136             if (sx == xOffset)
1137                 return;
1138             xOffset = sx;
1139             sy = yOffset;
1140             dx = dy = 0;
1141             w = ww; h = wh;
1142             break;
1143         case XK_Right:
1144             if (iw < ww)
1145                 return;
1146             sx = xOffset - appData.translate;
1147             sx = MAX(sx, 0);
1148             if (sx == xOffset)
1149                 return;
1150             xOffset = sx;
1151             sy = yOffset;
1152             dx = dy = 0;
1153             w = ww; h = wh;
1154             break;
1155         default:
1156             return;
1157         }
1158         break;
1159     case MotionNotify:
1160         /*
1161          * MotionEvent compression.  Ignore multiple motion events.
1162          * Ignore motion events if the mouse button is up.
1163          */
1164         if (XPending(xDisplay)) /* Xlib doesn't flush the output buffer */
1165             if (XtPeekEvent(&next_event))
1166                 if (next_event.type == MotionNotify)
1167                     return;
1168         if ((grabX < 0) || (grabY < 0))
1169             return;
1170         sx = xOffset + grabX - (int) event->xmotion.x;
1171         if (sx >= (iw - ww))    /* clamp x motion but allow y motion */
1172             sx = iw - ww;
1173         sx = MAX(sx, 0);
1174         sy = yOffset + grabY - (int) event->xmotion.y;
1175         if (sy >= (ih - wh)) /* clamp y motion but allow x motion */
1176             sy = ih - wh;
1177         sy = MAX(sy, 0);
1178         if ((sx == xOffset) && (sy == yOffset))
1179             return;
1180         dx = dy = 0;
1181         w = ww; h = wh;
1182         break;
1183     case ButtonRelease:
1184         xOffset = xOffset + grabX - (int) event->xbutton.x;
1185         xOffset = MIN(iw - ww, xOffset);
1186         xOffset = MAX(xOffset, 0);
1187         yOffset = yOffset + grabY - (int) event->xbutton.y;
1188         yOffset = MIN(ih - wh, yOffset);
1189         yOffset = MAX(yOffset, 0);
1190         grabX = grabY = -1;
1191     default:
1192         return;
1193     }
1194 
1195     if (appData.usePixmap == True) {
1196         if (xImageDepth == 1)
1197             XCopyPlane(xDisplay, xImagePixmap, XtWindow(widget),
1198                 xWinGc, sx, sy, w, h, dx, dy, 1);
1199         else
1200             XCopyArea(xDisplay, xImagePixmap, XtWindow(widget),
1201                 xWinGc, sx, sy, w, h, dx, dy);
1202     } else
1203         XPutImage(xDisplay, XtWindow(widget), xWinGc, xImage,
1204             sx, sy, dx, dy, w, h);
1205 }
1206 
1207 void
ResizeProc()1208 ResizeProc()
1209 {
1210     Dimension w_width, w_height;
1211     int xo, yo, ww, wh;
1212     XEvent fake_event;
1213     Arg args[2];
1214 
1215     if ((xOffset == 0) && (yOffset == 0))
1216         return;
1217 
1218     XtSetArg(args[0], XtNwidth, &w_width);
1219     XtSetArg(args[1], XtNheight, &w_height);
1220     XtGetValues(shellWidget, args, 2);
1221     ww = w_width;
1222     wh = w_height;
1223     XtGetValues(listWidget, args, 2);
1224     wh -= w_height;
1225 
1226     xo = xOffset; yo = yOffset;
1227 
1228     if ((xOffset + ww) >= tfImageWidth)
1229         xOffset = MAX((int) tfImageWidth - ww, 0);
1230     if ((yOffset + wh) >= tfImageHeight)
1231         yOffset = MAX((int) tfImageHeight - wh, 0);
1232 
1233     /*
1234      * Send an ExposeEvent if the origin changed.
1235      * We have to do this because of the use and semantics of bit gravity.
1236      */
1237     if ((xo != xOffset) || (yo != yOffset)) {
1238         fake_event.type = Expose;
1239         fake_event.xexpose.x = fake_event.xexpose.y = 0;
1240         fake_event.xexpose.width = tfImageWidth;
1241         fake_event.xexpose.height = tfImageHeight;
1242         EventProc(imageWidget, NULL, &fake_event);
1243     }
1244 }
1245 
1246 int
XTiffErrorHandler(display,error_event)1247 XTiffErrorHandler(display, error_event)
1248     Display *display;
1249     XErrorEvent *error_event;
1250 {
1251     char message[80];
1252 
1253     /*
1254      * Some X servers limit the size of pixmaps.
1255      */
1256     if ((error_event->error_code == BadAlloc)
1257             && (error_event->request_code == X_CreatePixmap))
1258         fprintf(stderr, "xtiff: requested pixmap too big for display\n");
1259     else {
1260         XGetErrorText(display, error_event->error_code, message, 80);
1261         fprintf(stderr, "xtiff: error code %s\n", message);
1262     }
1263 
1264     exit(0);
1265 }
1266 
1267 void
Usage()1268 Usage()
1269 {
1270     fprintf(stderr, "Usage xtiff: [options] tiff-file\n");
1271     fprintf(stderr, "\tstandard Xt options\n");
1272     fprintf(stderr, "\t[-help]\n");
1273     fprintf(stderr, "\t[-gamma gamma]\n");
1274     fprintf(stderr, "\t[-usePixmap (True | False)]\n");
1275     fprintf(stderr, "\t[-viewportWidth pixels]\n");
1276     fprintf(stderr, "\t[-viewportHeight pixels]\n");
1277     fprintf(stderr, "\t[-translate pixels]\n");
1278     fprintf(stderr, "\t[-verbose (True | False)]\n");
1279     exit(0);
1280 }
1281 
1282 /* vim: set ts=8 sts=8 sw=8 noet: */
1283 
1284 /*
1285  * Local Variables:
1286  * mode: c
1287  * c-basic-offset: 8
1288  * fill-column: 78
1289  * End:
1290  */
1291