~ubuntu-branches/ubuntu/karmic/tiff/karmic-security

« back to all changes in this revision

Viewing changes to contrib/dbs/xtiff/xtiff.c

  • Committer: Bazaar Package Importer
  • Author(s): Fabio M. Di Nitto
  • Date: 2004-10-14 07:57:59 UTC
  • Revision ID: james.westby@ubuntu.com-20041014075759-a77e7zuaetya8cp0
Tags: upstream-3.6.1
ImportĀ upstreamĀ versionĀ 3.6.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
87
void main PP((int argc, char **argv));
 
88
void OpenTIFFFile PP((void));
 
89
void GetTIFFHeader PP((void));
 
90
void SetNameLabel PP((void));
 
91
void CheckAndCorrectColormap PP((void));
 
92
void SimpleGammaCorrection PP((void));
 
93
void GetVisual PP((void));
 
94
Boolean SearchVisualList PP((int image_depth,
 
95
    int visual_class, Visual **visual));
 
96
void GetTIFFImage PP((void));
 
97
void CreateXImage PP((void));
 
98
XtCallbackProc SelectProc PP((Widget w, caddr_t unused_1, caddr_t unused_2));
 
99
void QuitProc PP((void));
 
100
void NextProc PP((void));
 
101
void PreviousProc PP((void));
 
102
void PageProc PP((int direction));
 
103
void EventProc PP((Widget widget, caddr_t unused, XEvent *event));
 
104
void ResizeProc PP((void));
 
105
int XTiffErrorHandler PP((Display *display, XErrorEvent *error_event));
 
106
void Usage PP((void));
 
107
 
 
108
int xtVersion = XtSpecificationRelease;     /* xtiff depends on R4 or higher */
 
109
 
 
110
/*
 
111
 * Xt data structures
 
112
 */
 
113
Widget shellWidget, formWidget, listWidget, labelWidget, imageWidget;
 
114
 
 
115
enum { ButtonQuit = 0, ButtonPreviousPage = 1, ButtonNextPage = 2 };
 
116
 
 
117
String buttonStrings[] = { "Quit", "Previous", "Next" };
 
118
 
 
119
static XrmOptionDescRec shellOptions[] = {
 
120
    { "-help", "*help", XrmoptionNoArg, (caddr_t) "True" },
 
121
    { "-gamma", "*gamma", XrmoptionSepArg, NULL },
 
122
    { "-usePixmap", "*usePixmap", XrmoptionSepArg, NULL },
 
123
    { "-viewportWidth", "*viewportWidth", XrmoptionSepArg, NULL },
 
124
    { "-viewportHeight", "*viewportHeight", XrmoptionSepArg, NULL },
 
125
    { "-translate", "*translate", XrmoptionSepArg, NULL },
 
126
    { "-verbose", "*verbose", XrmoptionSepArg, NULL }
 
127
};
 
128
 
 
129
typedef 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
 
 
139
AppData appData;
 
140
 
 
141
XtResource 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
 
 
168
Arg formArgs[] = {
 
169
    { XtNresizable, True }
 
170
};
 
171
 
 
172
Arg 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
 
 
185
Arg 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
 
 
196
Arg imageArgs[] = {
 
197
    { XtNresizable, True },
 
198
    { XtNborderWidth, 0 },
 
199
    { XtNtop, XtChainTop },
 
200
    { XtNleft, XtChainLeft },
 
201
    { XtNbottom, XtChainTop },
 
202
    { XtNright, XtChainLeft }
 
203
};
 
204
 
 
205
XtActionsRec actionsTable[] = {
 
206
    { "quit", QuitProc },
 
207
    { "next", NextProc },
 
208
    { "previous", PreviousProc },
 
209
    { "notifyresize", ResizeProc }
 
210
};
 
211
 
 
212
char 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
 */
 
224
Colormap            xColormap;
 
225
Display *           xDisplay;
 
226
Pixmap              xImagePixmap;
 
227
Visual *            xVisual;
 
228
XImage *            xImage;
 
229
GC                  xWinGc;
 
230
int                 xImageDepth, xScreen, xRedMask, xGreenMask, xBlueMask,
 
231
                    xOffset = 0, yOffset = 0, grabX = -1, grabY = -1;
 
232
u_char              basePixel = 0;
 
233
 
 
234
/*
 
235
 * TIFF data structures
 
236
 */
 
237
TIFF *              tfFile = NULL;
 
238
u_long              tfImageWidth, tfImageHeight;
 
239
u_short             tfBitsPerSample, tfSamplesPerPixel, tfPlanarConfiguration,
 
240
                    tfPhotometricInterpretation, tfGrayResponseUnit,
 
241
                    tfImageDepth, tfBytesPerRow;
 
242
int                 tfDirectory = 0, tfMultiPage = False;
 
243
double              tfUnitMap, tfGrayResponseUnitMap[] = {
 
244
                        -1, -10, -100, -1000, -10000, -100000
 
245
                    };
 
246
 
 
247
/*
 
248
 * display data structures
 
249
 */
 
250
double              *dRed, *dGreen, *dBlue;
 
251
 
 
252
/*
 
253
 * shared data structures
 
254
 */
 
255
u_short *           redMap = NULL, *greenMap = NULL, *blueMap = NULL,
 
256
                    *grayMap = NULL, colormapSize;
 
257
u_char *            imageMemory;
 
258
char *              fileName;
 
259
 
 
260
void
 
261
main(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
 
 
384
void
 
385
OpenTIFFFile()
 
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
 
 
399
void
 
400
GetTIFFHeader()
 
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
 
 
505
void
 
506
SetNameLabel()
 
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
 */
 
523
void
 
524
CheckAndCorrectColormap()
 
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
 
 
540
void
 
541
SimpleGammaCorrection()
 
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
 
 
562
static 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
 */
 
575
void
 
576
GetVisual()
 
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
 */
 
698
Boolean
 
699
SearchVisualList(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
 
 
732
void
 
733
GetTIFFImage()
 
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
 
 
867
void
 
868
CreateXImage()
 
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
 
 
932
XtCallbackProc
 
933
SelectProc(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
 
 
959
void
 
960
QuitProc(void)
 
961
{
 
962
    exit(0);
 
963
}
 
964
 
 
965
void
 
966
NextProc()
 
967
{
 
968
    PageProc(ButtonNextPage);
 
969
}
 
970
 
 
971
void
 
972
PreviousProc()
 
973
{
 
974
    PageProc(ButtonPreviousPage);
 
975
}
 
976
 
 
977
void
 
978
PageProc(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
 
 
1041
void
 
1042
EventProc(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
 
 
1202
void
 
1203
ResizeProc()
 
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
 
 
1241
int
 
1242
XTiffErrorHandler(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
 
 
1262
void
 
1263
Usage()
 
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
}