]> git.saurik.com Git - wxWidgets.git/blob - src/tiff/contrib/dbs/xtiff/xtiff.c
Don't cache HDC used by wxPaintDCEx in wxMSW.
[wxWidgets.git] / src / tiff / contrib / dbs / xtiff / xtiff.c
1 /*
2 * $Id$
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
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
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
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
509 SetNameLabel()
510 {
511 char buffer[BUFSIZ];
512 Arg args[1];
513
514 if (tfMultiPage)
515 sprintf(buffer, "%s - page %d", fileName, tfDirectory);
516 else
517 strcpy(buffer, 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
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
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
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
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
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
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
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
965 QuitProc(void)
966 {
967 exit(0);
968 }
969
970 void
971 NextProc()
972 {
973 PageProc(ButtonNextPage);
974 }
975
976 void
977 PreviousProc()
978 {
979 PageProc(ButtonPreviousPage);
980 }
981
982 void
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
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
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
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
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 */