roentgen b75cab
/*
roentgen b75cab
 * $Id: xtiff.c,v 1.3 2010-06-08 18:55:15 bfriesen Exp $
roentgen b75cab
 *
roentgen b75cab
 * xtiff - view a TIFF file in an X window
roentgen b75cab
 *
roentgen b75cab
 * Dan Sears
roentgen b75cab
 * Chris Sears
roentgen b75cab
 *
roentgen b75cab
 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
roentgen b75cab
 *
roentgen b75cab
 *                      All Rights Reserved
roentgen b75cab
 *
roentgen b75cab
 * Permission to use, copy, modify, and distribute this software and its
roentgen b75cab
 * documentation for any purpose and without fee is hereby granted,
roentgen b75cab
 * provided that the above copyright notice appear in all copies and that
roentgen b75cab
 * both that copyright notice and this permission notice appear in
roentgen b75cab
 * supporting documentation, and that the name of Digital not be
roentgen b75cab
 * used in advertising or publicity pertaining to distribution of the
roentgen b75cab
 * software without specific, written prior permission.
roentgen b75cab
 *
roentgen b75cab
 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
roentgen b75cab
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
roentgen b75cab
 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
roentgen b75cab
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
roentgen b75cab
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
roentgen b75cab
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
roentgen b75cab
 * SOFTWARE.
roentgen b75cab
 *
roentgen b75cab
 * Revision 1.0  90/05/07
roentgen b75cab
 *      Initial release.
roentgen b75cab
 * Revision 2.0  90/12/20
roentgen b75cab
 *      Converted to use the Athena Widgets and the Xt Intrinsics.
roentgen b75cab
 *
roentgen b75cab
 * Notes:
roentgen b75cab
 *
roentgen b75cab
 * According to the TIFF 5.0 Specification, it is possible to have
roentgen b75cab
 * both a TIFFTAG_COLORMAP and a TIFFTAG_COLORRESPONSECURVE.  This
roentgen b75cab
 * doesn't make sense since a TIFFTAG_COLORMAP is 16 bits wide and
roentgen b75cab
 * a TIFFTAG_COLORRESPONSECURVE is tfBitsPerSample bits wide for each
roentgen b75cab
 * channel.  This is probably a bug in the specification.
roentgen b75cab
 * In this case, TIFFTAG_COLORRESPONSECURVE is ignored.
roentgen b75cab
 * This might make sense if TIFFTAG_COLORMAP was 8 bits wide.
roentgen b75cab
 *
roentgen b75cab
 * TIFFTAG_COLORMAP is often incorrectly written as ranging from
roentgen b75cab
 * 0 to 255 rather than from 0 to 65535.  CheckAndCorrectColormap()
roentgen b75cab
 * takes care of this.
roentgen b75cab
 *
roentgen b75cab
 * Only ORIENTATION_TOPLEFT is supported correctly.  This is the
roentgen b75cab
 * default TIFF and X orientation.  Other orientations will be
roentgen b75cab
 * displayed incorrectly.
roentgen b75cab
 *
roentgen b75cab
 * There is no support for or use of 3/3/2 DirectColor visuals.
roentgen b75cab
 * TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE are not supported.
roentgen b75cab
 *
roentgen b75cab
 * Only TIFFTAG_BITSPERSAMPLE values that are 1, 2, 4 or 8 are supported.
roentgen b75cab
 */
roentgen b75cab
#include <math.h></math.h>
roentgen b75cab
#include <stdio.h></stdio.h>
roentgen b75cab
#include <stdlib.h></stdlib.h>
roentgen b75cab
#include <tiffio.h></tiffio.h>
roentgen b75cab
#include <x11 xatom.h=""></x11>
roentgen b75cab
#include <x11 intrinsic.h=""></x11>
roentgen b75cab
#include <x11 stringdefs.h=""></x11>
roentgen b75cab
#include <x11 xproto.h=""></x11>
roentgen b75cab
#include <x11 shell.h=""></x11>
roentgen b75cab
#include <x11 form.h="" xaw=""></x11>
roentgen b75cab
#include <x11 list.h="" xaw=""></x11>
roentgen b75cab
#include <x11 label.h="" xaw=""></x11>
roentgen b75cab
#include <x11 cursorfont.h=""></x11>
roentgen b75cab
#define XK_MISCELLANY
roentgen b75cab
#include <x11 keysymdef.h=""></x11>
roentgen b75cab
#include "xtifficon.h"
roentgen b75cab
roentgen b75cab
#define TIFF_GAMMA      "2.2"     /* default gamma from the TIFF 5.0 spec */
roentgen b75cab
#define ROUND(x)        (uint16) ((x) + 0.5)
roentgen b75cab
#define SCALE(x, s)     (((x) * 65535L) / (s))
roentgen b75cab
#define MCHECK(m)       if (!m) { fprintf(stderr, "malloc failed\n"); exit(0); }
roentgen b75cab
#define MIN(a, b)       (((a) < (b)) ? (a) : (b))
roentgen b75cab
#define MAX(a, b)       (((a) > (b)) ? (a) : (b))
roentgen b75cab
#define VIEWPORT_WIDTH  700
roentgen b75cab
#define VIEWPORT_HEIGHT 500
roentgen b75cab
#define KEY_TRANSLATE   20
roentgen b75cab
roentgen b75cab
#ifdef __STDC__
roentgen b75cab
#define PP(args)    args
roentgen b75cab
#else
roentgen b75cab
#define PP(args)    ()
roentgen b75cab
#endif
roentgen b75cab
roentgen b75cab
int main PP((int argc, char **argv));
roentgen b75cab
void OpenTIFFFile PP((void));
roentgen b75cab
void GetTIFFHeader PP((void));
roentgen b75cab
void SetNameLabel PP((void));
roentgen b75cab
void CheckAndCorrectColormap PP((void));
roentgen b75cab
void SimpleGammaCorrection PP((void));
roentgen b75cab
void GetVisual PP((void));
roentgen b75cab
Boolean SearchVisualList PP((int image_depth,
roentgen b75cab
    int visual_class, Visual **visual));
roentgen b75cab
void GetTIFFImage PP((void));
roentgen b75cab
void CreateXImage PP((void));
roentgen b75cab
XtCallbackProc SelectProc PP((Widget w, caddr_t unused_1, caddr_t unused_2));
roentgen b75cab
void QuitProc PP((void));
roentgen b75cab
void NextProc PP((void));
roentgen b75cab
void PreviousProc PP((void));
roentgen b75cab
void PageProc PP((int direction));
roentgen b75cab
void EventProc PP((Widget widget, caddr_t unused, XEvent *event));
roentgen b75cab
void ResizeProc PP((void));
roentgen b75cab
int XTiffErrorHandler PP((Display *display, XErrorEvent *error_event));
roentgen b75cab
void Usage PP((void));
roentgen b75cab
roentgen b75cab
int xtVersion = XtSpecificationRelease;     /* xtiff depends on R4 or higher */
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * Xt data structures
roentgen b75cab
 */
roentgen b75cab
Widget shellWidget, formWidget, listWidget, labelWidget, imageWidget;
roentgen b75cab
roentgen b75cab
enum { ButtonQuit = 0, ButtonPreviousPage = 1, ButtonNextPage = 2 };
roentgen b75cab
roentgen b75cab
String buttonStrings[] = { "Quit", "Previous", "Next" };
roentgen b75cab
roentgen b75cab
static XrmOptionDescRec shellOptions[] = {
roentgen b75cab
    { "-help", "*help", XrmoptionNoArg, (caddr_t) "True" },
roentgen b75cab
    { "-gamma", "*gamma", XrmoptionSepArg, NULL },
roentgen b75cab
    { "-usePixmap", "*usePixmap", XrmoptionSepArg, NULL },
roentgen b75cab
    { "-viewportWidth", "*viewportWidth", XrmoptionSepArg, NULL },
roentgen b75cab
    { "-viewportHeight", "*viewportHeight", XrmoptionSepArg, NULL },
roentgen b75cab
    { "-translate", "*translate", XrmoptionSepArg, NULL },
roentgen b75cab
    { "-verbose", "*verbose", XrmoptionSepArg, NULL }
roentgen b75cab
};
roentgen b75cab
roentgen b75cab
typedef struct {
roentgen b75cab
    Boolean help;
roentgen b75cab
    float gamma;
roentgen b75cab
    Boolean usePixmap;
roentgen b75cab
    uint32 viewportWidth;
roentgen b75cab
    uint32 viewportHeight;
roentgen b75cab
    int translate;
roentgen b75cab
    Boolean verbose;
roentgen b75cab
} AppData, *AppDataPtr;
roentgen b75cab
roentgen b75cab
AppData appData;
roentgen b75cab
roentgen b75cab
XtResource clientResources[] = {
roentgen b75cab
    {
roentgen b75cab
        "help", XtCBoolean, XtRBoolean, sizeof(Boolean),
roentgen b75cab
        XtOffset(AppDataPtr, help), XtRImmediate, (XtPointer) False
roentgen b75cab
    }, {
roentgen b75cab
        "gamma", "Gamma", XtRFloat, sizeof(float),
roentgen b75cab
        XtOffset(AppDataPtr, gamma), XtRString, (XtPointer) TIFF_GAMMA
roentgen b75cab
    }, {
roentgen b75cab
        "usePixmap", "UsePixmap", XtRBoolean, sizeof(Boolean),
roentgen b75cab
        XtOffset(AppDataPtr, usePixmap), XtRImmediate, (XtPointer) True
roentgen b75cab
    }, {
roentgen b75cab
        "viewportWidth", "ViewportWidth", XtRInt, sizeof(int),
roentgen b75cab
        XtOffset(AppDataPtr, viewportWidth), XtRImmediate,
roentgen b75cab
        (XtPointer) VIEWPORT_WIDTH
roentgen b75cab
    }, {
roentgen b75cab
        "viewportHeight", "ViewportHeight", XtRInt, sizeof(int),
roentgen b75cab
        XtOffset(AppDataPtr, viewportHeight), XtRImmediate,
roentgen b75cab
        (XtPointer) VIEWPORT_HEIGHT
roentgen b75cab
    }, {
roentgen b75cab
        "translate", "Translate", XtRInt, sizeof(int),
roentgen b75cab
        XtOffset(AppDataPtr, translate), XtRImmediate, (XtPointer) KEY_TRANSLATE
roentgen b75cab
    }, {
roentgen b75cab
        "verbose", "Verbose", XtRBoolean, sizeof(Boolean),
roentgen b75cab
        XtOffset(AppDataPtr, verbose), XtRImmediate, (XtPointer) True
roentgen b75cab
    }
roentgen b75cab
};
roentgen b75cab
roentgen b75cab
Arg formArgs[] = {
roentgen b75cab
    { XtNresizable, True }
roentgen b75cab
};
roentgen b75cab
roentgen b75cab
Arg listArgs[] = {
roentgen b75cab
    { XtNresizable, False },
roentgen b75cab
    { XtNborderWidth, 0 },
roentgen b75cab
    { XtNdefaultColumns, 3 },
roentgen b75cab
    { XtNforceColumns, True },
roentgen b75cab
    { XtNlist, (int) buttonStrings },
roentgen b75cab
    { XtNnumberStrings, XtNumber(buttonStrings) },
roentgen b75cab
    { XtNtop, XtChainTop },
roentgen b75cab
    { XtNleft, XtChainLeft },
roentgen b75cab
    { XtNbottom, XtChainTop },
roentgen b75cab
    { XtNright, XtChainLeft }
roentgen b75cab
};
roentgen b75cab
roentgen b75cab
Arg labelArgs[] = {
roentgen b75cab
    { XtNresizable, False },
roentgen b75cab
    { XtNwidth, 200 },
roentgen b75cab
    { XtNborderWidth, 0 },
roentgen b75cab
    { XtNjustify, XtJustifyLeft },
roentgen b75cab
    { XtNtop, XtChainTop },
roentgen b75cab
    { XtNleft, XtChainLeft },
roentgen b75cab
    { XtNbottom, XtChainTop },
roentgen b75cab
    { XtNright, XtChainLeft }
roentgen b75cab
};
roentgen b75cab
roentgen b75cab
Arg imageArgs[] = {
roentgen b75cab
    { XtNresizable, True },
roentgen b75cab
    { XtNborderWidth, 0 },
roentgen b75cab
    { XtNtop, XtChainTop },
roentgen b75cab
    { XtNleft, XtChainLeft },
roentgen b75cab
    { XtNbottom, XtChainTop },
roentgen b75cab
    { XtNright, XtChainLeft }
roentgen b75cab
};
roentgen b75cab
roentgen b75cab
XtActionsRec actionsTable[] = {
roentgen b75cab
    { "quit", QuitProc },
roentgen b75cab
    { "next", NextProc },
roentgen b75cab
    { "previous", PreviousProc },
roentgen b75cab
    { "notifyresize", ResizeProc }
roentgen b75cab
};
roentgen b75cab
roentgen b75cab
char translationsTable[] = "<key>q:      quit() \n \</key>
roentgen b75cab
                            <key>Q:      quit() \n \</key>
roentgen b75cab
                            <message>WM_PROTOCOLS: quit()\n \</message>
roentgen b75cab
                            <key>p:      previous() \n \</key>
roentgen b75cab
                            <key>P:      previous() \n \</key>
roentgen b75cab
                            <key>n:      next() \n \</key>
roentgen b75cab
                            <key>N:      next() \n \</key>
roentgen b75cab
                            <configure>: notifyresize()";</configure>
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * X data structures
roentgen b75cab
 */
roentgen b75cab
Colormap            xColormap;
roentgen b75cab
Display *           xDisplay;
roentgen b75cab
Pixmap              xImagePixmap;
roentgen b75cab
Visual *            xVisual;
roentgen b75cab
XImage *            xImage;
roentgen b75cab
GC                  xWinGc;
roentgen b75cab
int                 xImageDepth, xScreen, xRedMask, xGreenMask, xBlueMask,
roentgen b75cab
                    xOffset = 0, yOffset = 0, grabX = -1, grabY = -1;
roentgen b75cab
unsigned char       basePixel = 0;
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * TIFF data structures
roentgen b75cab
 */
roentgen b75cab
TIFF *              tfFile = NULL;
roentgen b75cab
uint32              tfImageWidth, tfImageHeight;
roentgen b75cab
uint16              tfBitsPerSample, tfSamplesPerPixel, tfPlanarConfiguration,
roentgen b75cab
                    tfPhotometricInterpretation, tfGrayResponseUnit,
roentgen b75cab
                    tfImageDepth, tfBytesPerRow;
roentgen b75cab
int                 tfDirectory = 0, tfMultiPage = False;
roentgen b75cab
double              tfUnitMap, tfGrayResponseUnitMap[] = {
roentgen b75cab
                        -1, -10, -100, -1000, -10000, -100000
roentgen b75cab
                    };
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * display data structures
roentgen b75cab
 */
roentgen b75cab
double              *dRed, *dGreen, *dBlue;
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * shared data structures
roentgen b75cab
 */
roentgen b75cab
uint16 *            redMap = NULL, *greenMap = NULL, *blueMap = NULL,
roentgen b75cab
                    *grayMap = NULL, colormapSize;
roentgen b75cab
char *             imageMemory;
roentgen b75cab
char *              fileName;
roentgen b75cab
roentgen b75cab
int
roentgen b75cab
main(int argc, char **argv)
roentgen b75cab
{
roentgen b75cab
    XSetWindowAttributes window_attributes;
roentgen b75cab
    Widget widget_list[3];
roentgen b75cab
    Arg args[5];
roentgen b75cab
roentgen b75cab
    setbuf(stdout, NULL); setbuf(stderr, NULL);
roentgen b75cab
roentgen b75cab
    shellWidget = XtInitialize(argv[0], "XTiff", shellOptions,
roentgen b75cab
        XtNumber(shellOptions), &argc, argv);
roentgen b75cab
roentgen b75cab
    XSetErrorHandler(XTiffErrorHandler);
roentgen b75cab
roentgen b75cab
    XtGetApplicationResources(shellWidget, &appData,
roentgen b75cab
        (XtResourceList) clientResources, (Cardinal) XtNumber(clientResources),
roentgen b75cab
        (ArgList) NULL, (Cardinal) 0);
roentgen b75cab
roentgen b75cab
    if ((argc <= 1) || (argc > 2) || appData.help)
roentgen b75cab
        Usage();
roentgen b75cab
roentgen b75cab
    if (appData.verbose == False) {
roentgen b75cab
        TIFFSetErrorHandler(0);
roentgen b75cab
        TIFFSetWarningHandler(0);
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    fileName = argv[1];
roentgen b75cab
roentgen b75cab
    xDisplay = XtDisplay(shellWidget);
roentgen b75cab
    xScreen = DefaultScreen(xDisplay);
roentgen b75cab
roentgen b75cab
    OpenTIFFFile();
roentgen b75cab
    GetTIFFHeader();
roentgen b75cab
    SimpleGammaCorrection();
roentgen b75cab
    GetVisual();
roentgen b75cab
    GetTIFFImage();
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * Send visual, colormap, depth and iconPixmap to shellWidget.
roentgen b75cab
     * Sending the visual to the shell is only possible with the advent of R4.
roentgen b75cab
     */
roentgen b75cab
    XtSetArg(args[0], XtNvisual, xVisual);
roentgen b75cab
    XtSetArg(args[1], XtNcolormap, xColormap);
roentgen b75cab
    XtSetArg(args[2], XtNdepth,
roentgen b75cab
        xImageDepth == 1 ? DefaultDepth(xDisplay, xScreen) : xImageDepth);
roentgen b75cab
    XtSetArg(args[3], XtNiconPixmap,
roentgen b75cab
        XCreateBitmapFromData(xDisplay, RootWindow(xDisplay, xScreen),
roentgen b75cab
            xtifficon_bits, xtifficon_width, xtifficon_height));
roentgen b75cab
    XtSetArg(args[4], XtNallowShellResize, True);
roentgen b75cab
    XtSetValues(shellWidget, args, 5);
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * widget instance hierarchy
roentgen b75cab
     */
roentgen b75cab
    formWidget = XtCreateManagedWidget("form", formWidgetClass,
roentgen b75cab
        shellWidget, formArgs, XtNumber(formArgs));
roentgen b75cab
roentgen b75cab
        widget_list[0] = listWidget = XtCreateWidget("list",
roentgen b75cab
            listWidgetClass, formWidget, listArgs, XtNumber(listArgs));
roentgen b75cab
roentgen b75cab
        widget_list[1] = labelWidget = XtCreateWidget("label",
roentgen b75cab
            labelWidgetClass, formWidget, labelArgs, XtNumber(labelArgs));
roentgen b75cab
roentgen b75cab
        widget_list[2] = imageWidget = XtCreateWidget("image",
roentgen b75cab
            widgetClass, formWidget, imageArgs, XtNumber(imageArgs));
roentgen b75cab
roentgen b75cab
    XtManageChildren(widget_list, XtNumber(widget_list));
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * initial widget sizes - for small images let xtiff size itself
roentgen b75cab
     */
roentgen b75cab
    if (tfImageWidth >= appData.viewportWidth) {
roentgen b75cab
        XtSetArg(args[0], XtNwidth, appData.viewportWidth);
roentgen b75cab
        XtSetValues(shellWidget, args, 1);
roentgen b75cab
    }
roentgen b75cab
    if (tfImageHeight >= appData.viewportHeight) {
roentgen b75cab
        XtSetArg(args[0], XtNheight, appData.viewportHeight);
roentgen b75cab
        XtSetValues(shellWidget, args, 1);
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    XtSetArg(args[0], XtNwidth, tfImageWidth);
roentgen b75cab
    XtSetArg(args[1], XtNheight, tfImageHeight);
roentgen b75cab
    XtSetValues(imageWidget, args, 2);
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * formWidget uses these constraints but they are stored in the children.
roentgen b75cab
     */
roentgen b75cab
    XtSetArg(args[0], XtNfromVert, listWidget);
roentgen b75cab
    XtSetValues(imageWidget, args, 1);
roentgen b75cab
    XtSetArg(args[0], XtNfromHoriz, listWidget);
roentgen b75cab
    XtSetValues(labelWidget, args, 1);
roentgen b75cab
roentgen b75cab
    SetNameLabel();
roentgen b75cab
roentgen b75cab
    XtAddCallback(listWidget, XtNcallback, (XtCallbackProc) SelectProc,
roentgen b75cab
        (XtPointer) NULL);
roentgen b75cab
roentgen b75cab
    XtAddActions(actionsTable, XtNumber(actionsTable));
roentgen b75cab
    XtSetArg(args[0], XtNtranslations,
roentgen b75cab
        XtParseTranslationTable(translationsTable));
roentgen b75cab
    XtSetValues(formWidget, &args[0], 1);
roentgen b75cab
    XtSetValues(imageWidget, &args[0], 1);
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * This is intended to be a little faster than going through
roentgen b75cab
     * the translation manager.
roentgen b75cab
     */
roentgen b75cab
    XtAddEventHandler(imageWidget, ExposureMask | ButtonPressMask
roentgen b75cab
        | ButtonReleaseMask | Button1MotionMask | KeyPressMask,
roentgen b75cab
        False, EventProc, NULL);
roentgen b75cab
roentgen b75cab
    XtRealizeWidget(shellWidget);
roentgen b75cab
roentgen b75cab
    window_attributes.cursor = XCreateFontCursor(xDisplay, XC_fleur);
roentgen b75cab
    XChangeWindowAttributes(xDisplay, XtWindow(imageWidget),
roentgen b75cab
        CWCursor, &window_attributes);
roentgen b75cab
roentgen b75cab
    CreateXImage();
roentgen b75cab
roentgen b75cab
    XtMainLoop();
roentgen b75cab
roentgen b75cab
    return 0;
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
OpenTIFFFile()
roentgen b75cab
{
roentgen b75cab
    if (tfFile != NULL)
roentgen b75cab
        TIFFClose(tfFile);
roentgen b75cab
roentgen b75cab
    if ((tfFile = TIFFOpen(fileName, "r")) == NULL) {
roentgen b75cab
	fprintf(appData.verbose ? stderr : stdout,
roentgen b75cab
	    "xtiff: can't open %s as a TIFF file\n", fileName);
roentgen b75cab
        exit(0);
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    tfMultiPage = (TIFFLastDirectory(tfFile) ? False : True);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
GetTIFFHeader()
roentgen b75cab
{
roentgen b75cab
    register int i;
roentgen b75cab
roentgen b75cab
    if (!TIFFSetDirectory(tfFile, tfDirectory)) {
roentgen b75cab
        fprintf(stderr, "xtiff: can't seek to directory %d in %s\n",
roentgen b75cab
            tfDirectory, fileName);
roentgen b75cab
        exit(0);
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    TIFFGetField(tfFile, TIFFTAG_IMAGEWIDTH, &tfImageWidth);
roentgen b75cab
    TIFFGetField(tfFile, TIFFTAG_IMAGELENGTH, &tfImageHeight);
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * If the following tags aren't present then use the TIFF defaults.
roentgen b75cab
     */
roentgen b75cab
    TIFFGetFieldDefaulted(tfFile, TIFFTAG_BITSPERSAMPLE, &tfBitsPerSample);
roentgen b75cab
    TIFFGetFieldDefaulted(tfFile, TIFFTAG_SAMPLESPERPIXEL, &tfSamplesPerPixel);
roentgen b75cab
    TIFFGetFieldDefaulted(tfFile, TIFFTAG_PLANARCONFIG, &tfPlanarConfiguration);
roentgen b75cab
    TIFFGetFieldDefaulted(tfFile, TIFFTAG_GRAYRESPONSEUNIT, &tfGrayResponseUnit);
roentgen b75cab
roentgen b75cab
    tfUnitMap = tfGrayResponseUnitMap[tfGrayResponseUnit];
roentgen b75cab
    colormapSize = 1 << tfBitsPerSample;
roentgen b75cab
    tfImageDepth = tfBitsPerSample * tfSamplesPerPixel;
roentgen b75cab
roentgen b75cab
    dRed = (double *) malloc(colormapSize * sizeof(double));
roentgen b75cab
    dGreen = (double *) malloc(colormapSize * sizeof(double));
roentgen b75cab
    dBlue = (double *) malloc(colormapSize * sizeof(double));
roentgen b75cab
    MCHECK(dRed); MCHECK(dGreen); MCHECK(dBlue);
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * If TIFFTAG_PHOTOMETRIC is not present then assign a reasonable default.
roentgen b75cab
     * The TIFF 5.0 specification doesn't give a default.
roentgen b75cab
     */
roentgen b75cab
    if (!TIFFGetField(tfFile, TIFFTAG_PHOTOMETRIC,
roentgen b75cab
            &tfPhotometricInterpretation)) {
roentgen b75cab
        if (tfSamplesPerPixel != 1)
roentgen b75cab
            tfPhotometricInterpretation = PHOTOMETRIC_RGB;
roentgen b75cab
        else if (tfBitsPerSample == 1)
roentgen b75cab
            tfPhotometricInterpretation = PHOTOMETRIC_MINISBLACK;
roentgen b75cab
        else if (TIFFGetField(tfFile, TIFFTAG_COLORMAP,
roentgen b75cab
                &redMap, &greenMap, &blueMap)) {
roentgen b75cab
            tfPhotometricInterpretation = PHOTOMETRIC_PALETTE;
roentgen b75cab
            redMap = greenMap = blueMap = NULL;
roentgen b75cab
        } else
roentgen b75cab
            tfPhotometricInterpretation = PHOTOMETRIC_MINISBLACK;
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * Given TIFFTAG_PHOTOMETRIC extract or create the response curves.
roentgen b75cab
     */
roentgen b75cab
    switch (tfPhotometricInterpretation) {
roentgen b75cab
    case PHOTOMETRIC_RGB:
roentgen b75cab
	redMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
roentgen b75cab
	greenMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
roentgen b75cab
	blueMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
roentgen b75cab
	MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap);
roentgen b75cab
	for (i = 0; i < colormapSize; i++)
roentgen b75cab
	    dRed[i] = dGreen[i] = dBlue[i]
roentgen b75cab
		= (double) SCALE(i, colormapSize - 1);
roentgen b75cab
        break;
roentgen b75cab
    case PHOTOMETRIC_PALETTE:
roentgen b75cab
        if (!TIFFGetField(tfFile, TIFFTAG_COLORMAP,
roentgen b75cab
                &redMap, &greenMap, &blueMap)) {
roentgen b75cab
            redMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
roentgen b75cab
            greenMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
roentgen b75cab
            blueMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
roentgen b75cab
            MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap);
roentgen b75cab
            for (i = 0; i < colormapSize; i++)
roentgen b75cab
                dRed[i] = dGreen[i] = dBlue[i]
roentgen b75cab
                    = (double) SCALE(i, colormapSize - 1);
roentgen b75cab
        } else {
roentgen b75cab
            CheckAndCorrectColormap();
roentgen b75cab
            for (i = 0; i < colormapSize; i++) {
roentgen b75cab
                dRed[i] = (double) redMap[i];
roentgen b75cab
                dGreen[i] = (double) greenMap[i];
roentgen b75cab
                dBlue[i] = (double) blueMap[i];
roentgen b75cab
            }
roentgen b75cab
        }
roentgen b75cab
        break;
roentgen b75cab
    case PHOTOMETRIC_MINISWHITE:
roentgen b75cab
        redMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
roentgen b75cab
        greenMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
roentgen b75cab
        blueMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
roentgen b75cab
        MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap);
roentgen b75cab
	for (i = 0; i < colormapSize; i++)
roentgen b75cab
	    dRed[i] = dGreen[i] = dBlue[i] = (double)
roentgen b75cab
		 SCALE(colormapSize-1-i, colormapSize-1);
roentgen b75cab
        break;
roentgen b75cab
    case PHOTOMETRIC_MINISBLACK:
roentgen b75cab
        redMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
roentgen b75cab
        greenMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
roentgen b75cab
        blueMap = (uint16 *) malloc(colormapSize * sizeof(uint16));
roentgen b75cab
        MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap);
roentgen b75cab
	for (i = 0; i < colormapSize; i++)
roentgen b75cab
	    dRed[i] = dGreen[i] = dBlue[i] = (double) SCALE(i, colormapSize-1);
roentgen b75cab
        break;
roentgen b75cab
    default:
roentgen b75cab
        fprintf(stderr,
roentgen b75cab
            "xtiff: can't display photometric interpretation type %d\n",
roentgen b75cab
            tfPhotometricInterpretation);
roentgen b75cab
        exit(0);
roentgen b75cab
    }
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
SetNameLabel()
roentgen b75cab
{
roentgen b75cab
    char buffer[BUFSIZ];
roentgen b75cab
    Arg args[1];
roentgen b75cab
roentgen b75cab
    if (tfMultiPage)
roentgen b75cab
        sprintf(buffer, "%s - page %d", fileName, tfDirectory);
roentgen b75cab
    else
roentgen b75cab
        strcpy(buffer, fileName);
roentgen b75cab
    XtSetArg(args[0], XtNlabel, buffer);
roentgen b75cab
    XtSetValues(labelWidget, args, 1);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * Many programs get TIFF colormaps wrong.  They use 8-bit colormaps instead of
roentgen b75cab
 * 16-bit colormaps.  This function is a heuristic to detect and correct this.
roentgen b75cab
 */
roentgen b75cab
void
roentgen b75cab
CheckAndCorrectColormap()
roentgen b75cab
{
roentgen b75cab
    register int i;
roentgen b75cab
roentgen b75cab
    for (i = 0; i < colormapSize; i++)
roentgen b75cab
        if ((redMap[i] > 255) || (greenMap[i] > 255) || (blueMap[i] > 255))
roentgen b75cab
            return;
roentgen b75cab
roentgen b75cab
    for (i = 0; i < colormapSize; i++) {
roentgen b75cab
        redMap[i] = SCALE(redMap[i], 255);
roentgen b75cab
        greenMap[i] = SCALE(greenMap[i], 255);
roentgen b75cab
        blueMap[i] = SCALE(blueMap[i], 255);
roentgen b75cab
    }
roentgen b75cab
    TIFFWarning(fileName, "Assuming 8-bit colormap");
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
SimpleGammaCorrection()
roentgen b75cab
{
roentgen b75cab
    register int i;
roentgen b75cab
    register double i_gamma = 1.0 / appData.gamma;
roentgen b75cab
roentgen b75cab
    for (i = 0; i < colormapSize; i++) {
roentgen b75cab
        if (((tfPhotometricInterpretation == PHOTOMETRIC_MINISWHITE)
roentgen b75cab
            && (i == colormapSize - 1))
roentgen b75cab
            || ((tfPhotometricInterpretation == PHOTOMETRIC_MINISBLACK)
roentgen b75cab
            && (i == 0)))
roentgen b75cab
            redMap[i] = greenMap[i] = blueMap[i] = 0;
roentgen b75cab
        else {
roentgen b75cab
            redMap[i] = ROUND((pow(dRed[i] / 65535.0, i_gamma) * 65535.0));
roentgen b75cab
            greenMap[i] = ROUND((pow(dGreen[i] / 65535.0, i_gamma) * 65535.0));
roentgen b75cab
            blueMap[i] = ROUND((pow(dBlue[i] / 65535.0, i_gamma) * 65535.0));
roentgen b75cab
        }
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    free(dRed); free(dGreen); free(dBlue);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static char* classNames[] = {
roentgen b75cab
    "StaticGray",
roentgen b75cab
    "GrayScale",
roentgen b75cab
    "StaticColor",
roentgen b75cab
    "PseudoColor",
roentgen b75cab
    "TrueColor",
roentgen b75cab
    "DirectColor"
roentgen b75cab
};
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * Current limitation: the visual is set initially by the first file.
roentgen b75cab
 * It cannot be changed.
roentgen b75cab
 */
roentgen b75cab
void
roentgen b75cab
GetVisual()
roentgen b75cab
{
roentgen b75cab
    XColor *colors = NULL;
roentgen b75cab
    unsigned long *pixels = NULL;
roentgen b75cab
    unsigned long i;
roentgen b75cab
roentgen b75cab
    switch (tfImageDepth) {
roentgen b75cab
    /*
roentgen b75cab
     * X really wants a 32-bit image with the fourth channel unused,
roentgen b75cab
     * but the visual structure thinks it's 24-bit.  bitmap_unit is 32.
roentgen b75cab
     */
roentgen b75cab
    case 32:
roentgen b75cab
    case 24:
roentgen b75cab
        if (SearchVisualList(24, DirectColor, &xVisual) == False) {
roentgen b75cab
            fprintf(stderr, "xtiff: 24-bit DirectColor visual not available\n");
roentgen b75cab
            exit(0);
roentgen b75cab
        }
roentgen b75cab
roentgen b75cab
        colors = (XColor *) malloc(3 * colormapSize * sizeof(XColor));
roentgen b75cab
        MCHECK(colors);
roentgen b75cab
roentgen b75cab
        for (i = 0; i < colormapSize; i++) {
roentgen b75cab
            colors[i].pixel = (i << 16) + (i << 8) + i;
roentgen b75cab
            colors[i].red = redMap[i];
roentgen b75cab
            colors[i].green = greenMap[i];
roentgen b75cab
            colors[i].blue = blueMap[i];
roentgen b75cab
            colors[i].flags = DoRed | DoGreen | DoBlue;
roentgen b75cab
        }
roentgen b75cab
roentgen b75cab
        xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen),
roentgen b75cab
            xVisual, AllocAll);
roentgen b75cab
        XStoreColors(xDisplay, xColormap, colors, colormapSize);
roentgen b75cab
        break;
roentgen b75cab
    case 8:
roentgen b75cab
    case 4:
roentgen b75cab
    case 2:
roentgen b75cab
        /*
roentgen b75cab
         * We assume that systems with 24-bit visuals also have 8-bit visuals.
roentgen b75cab
         * We don't promote from 8-bit PseudoColor to 24/32 bit DirectColor.
roentgen b75cab
         */
roentgen b75cab
        switch (tfPhotometricInterpretation) {
roentgen b75cab
        case PHOTOMETRIC_MINISWHITE:
roentgen b75cab
        case PHOTOMETRIC_MINISBLACK:
roentgen b75cab
            if (SearchVisualList((int) tfImageDepth, GrayScale, &xVisual) == True)
roentgen b75cab
                break;
roentgen b75cab
        case PHOTOMETRIC_PALETTE:
roentgen b75cab
            if (SearchVisualList((int) tfImageDepth, PseudoColor, &xVisual) == True)
roentgen b75cab
                break;
roentgen b75cab
        default:
roentgen b75cab
            fprintf(stderr, "xtiff: Unsupported TIFF/X configuration\n");
roentgen b75cab
            exit(0);
roentgen b75cab
        }
roentgen b75cab
roentgen b75cab
        colors = (XColor *) malloc(colormapSize * sizeof(XColor));
roentgen b75cab
        MCHECK(colors);
roentgen b75cab
roentgen b75cab
        for (i = 0; i < colormapSize; i++) {
roentgen b75cab
            colors[i].pixel = i;
roentgen b75cab
            colors[i].red = redMap[i];
roentgen b75cab
            colors[i].green = greenMap[i];
roentgen b75cab
            colors[i].blue = blueMap[i];
roentgen b75cab
            colors[i].flags = DoRed | DoGreen | DoBlue;
roentgen b75cab
        }
roentgen b75cab
roentgen b75cab
        /*
roentgen b75cab
         * xtiff's colormap allocation is private.  It does not attempt
roentgen b75cab
         * to detect whether any existing colormap entries are suitable
roentgen b75cab
         * for its use.  This will cause colormap flashing.  Furthermore,
roentgen b75cab
         * background and foreground are taken from the environment.
roentgen b75cab
         * For example, the foreground color may be red when the visual
roentgen b75cab
         * is GrayScale.  If the colormap is completely populated,
roentgen b75cab
         * Xt will not be able to allocate fg and bg.
roentgen b75cab
         */
roentgen b75cab
        if (tfImageDepth == 8)
roentgen b75cab
            xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen),
roentgen b75cab
                xVisual, AllocAll);
roentgen b75cab
        else {
roentgen b75cab
            xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen),
roentgen b75cab
                xVisual, AllocNone);
roentgen b75cab
            pixels = (unsigned long *)
roentgen b75cab
                malloc(colormapSize * sizeof(unsigned long));
roentgen b75cab
            MCHECK(pixels);
roentgen b75cab
            (void) XAllocColorCells(xDisplay, xColormap, True,
roentgen b75cab
                NULL, 0, pixels, colormapSize);
roentgen b75cab
            basePixel = (unsigned char) pixels[0];
roentgen b75cab
            free(pixels);
roentgen b75cab
        }
roentgen b75cab
        XStoreColors(xDisplay, xColormap, colors, colormapSize);
roentgen b75cab
        break;
roentgen b75cab
    case 1:
roentgen b75cab
        xImageDepth = 1;
roentgen b75cab
        xVisual = DefaultVisual(xDisplay, xScreen);
roentgen b75cab
        xColormap = DefaultColormap(xDisplay, xScreen);
roentgen b75cab
        break;
roentgen b75cab
    default:
roentgen b75cab
        fprintf(stderr, "xtiff: unsupported image depth %d\n", tfImageDepth);
roentgen b75cab
        exit(0);
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    if (appData.verbose == True)
roentgen b75cab
	fprintf(stderr, "%s: Using %d-bit %s visual.\n",
roentgen b75cab
	    fileName, xImageDepth, classNames[xVisual->class]);
roentgen b75cab
roentgen b75cab
    if (colors != NULL)
roentgen b75cab
        free(colors);
roentgen b75cab
    if (grayMap != NULL)
roentgen b75cab
        free(grayMap);
roentgen b75cab
    if (redMap != NULL)
roentgen b75cab
        free(redMap);
roentgen b75cab
    if (greenMap != NULL)
roentgen b75cab
        free(greenMap);
roentgen b75cab
    if (blueMap != NULL)
roentgen b75cab
        free(blueMap);
roentgen b75cab
roentgen b75cab
    colors = NULL; grayMap = redMap = greenMap = blueMap = NULL;
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * Search for an appropriate visual.  Promote where necessary.
roentgen b75cab
 * Check to make sure that ENOUGH colormap entries are writeable.
roentgen b75cab
 * basePixel was determined when XAllocColorCells() contiguously
roentgen b75cab
 * allocated enough entries.  basePixel is used below in GetTIFFImage.
roentgen b75cab
 */
roentgen b75cab
Boolean
roentgen b75cab
SearchVisualList(image_depth, visual_class, visual)
roentgen b75cab
    int image_depth, visual_class;
roentgen b75cab
    Visual **visual;
roentgen b75cab
{
roentgen b75cab
    XVisualInfo template_visual, *visual_list, *vl;
roentgen b75cab
    int i, n_visuals;
roentgen b75cab
roentgen b75cab
    template_visual.screen = xScreen;
roentgen b75cab
    vl = visual_list = XGetVisualInfo(xDisplay, VisualScreenMask,
roentgen b75cab
        &template_visual, &n_visuals);
roentgen b75cab
roentgen b75cab
    if (n_visuals == 0) {
roentgen b75cab
        fprintf(stderr, "xtiff: visual list not available\n");
roentgen b75cab
        exit(0);
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    for (i = 0; i < n_visuals; vl++, i++) {
roentgen b75cab
        if ((vl->class == visual_class) && (vl->depth >= image_depth)
roentgen b75cab
            && (vl->visual->map_entries >= (1 << vl->depth))) {
roentgen b75cab
            *visual = vl->visual;
roentgen b75cab
            xImageDepth = vl->depth;
roentgen b75cab
            xRedMask = vl->red_mask;
roentgen b75cab
            xGreenMask = vl->green_mask;
roentgen b75cab
            xBlueMask = vl->blue_mask;
roentgen b75cab
            XFree((char *) visual_list);
roentgen b75cab
            return True;
roentgen b75cab
        }
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    XFree((char *) visual_list);
roentgen b75cab
    return False;
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
GetTIFFImage()
roentgen b75cab
{
roentgen b75cab
    int pixel_map[3], red_shift, green_shift, blue_shift;
roentgen b75cab
    char *scan_line, *output_p, *input_p;
roentgen b75cab
    uint32 i, j;
roentgen b75cab
    uint16 s;
roentgen b75cab
roentgen b75cab
    scan_line = (char *) malloc(tfBytesPerRow = TIFFScanlineSize(tfFile));
roentgen b75cab
    MCHECK(scan_line);
roentgen b75cab
roentgen b75cab
    if ((tfImageDepth == 32) || (tfImageDepth == 24)) {
roentgen b75cab
        output_p = imageMemory = (char *)
roentgen b75cab
            malloc(tfImageWidth * tfImageHeight * 4);
roentgen b75cab
        MCHECK(imageMemory);
roentgen b75cab
roentgen b75cab
        /*
roentgen b75cab
         * Handle different color masks for different frame buffers.
roentgen b75cab
         */
roentgen b75cab
        if (ImageByteOrder(xDisplay) == LSBFirst) { /* DECstation 5000 */
roentgen b75cab
            red_shift = pixel_map[0] = xRedMask == 0xFF000000 ? 3
roentgen b75cab
                : (xRedMask == 0xFF0000 ? 2 : (xRedMask == 0xFF00 ? 1 : 0));
roentgen b75cab
            green_shift = pixel_map[1] = xGreenMask == 0xFF000000 ? 3
roentgen b75cab
                : (xGreenMask == 0xFF0000 ? 2 : (xGreenMask == 0xFF00 ? 1 : 0));
roentgen b75cab
            blue_shift = pixel_map[2] = xBlueMask == 0xFF000000 ? 3
roentgen b75cab
                : (xBlueMask == 0xFF0000 ? 2 : (xBlueMask == 0xFF00 ? 1 : 0));
roentgen b75cab
        } else { /* Ardent */
roentgen b75cab
            red_shift = pixel_map[0] = xRedMask == 0xFF000000 ? 0
roentgen b75cab
                : (xRedMask == 0xFF0000 ? 1 : (xRedMask == 0xFF00 ? 2 : 3));
roentgen b75cab
            green_shift = pixel_map[0] = xGreenMask == 0xFF000000 ? 0
roentgen b75cab
                : (xGreenMask == 0xFF0000 ? 1 : (xGreenMask == 0xFF00 ? 2 : 3));
roentgen b75cab
            blue_shift = pixel_map[0] = xBlueMask == 0xFF000000 ? 0
roentgen b75cab
                : (xBlueMask == 0xFF0000 ? 1 : (xBlueMask == 0xFF00 ? 2 : 3));
roentgen b75cab
        }
roentgen b75cab
roentgen b75cab
        if (tfPlanarConfiguration == PLANARCONFIG_CONTIG) {
roentgen b75cab
            for (i = 0; i < tfImageHeight; i++) {
roentgen b75cab
                if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0)
roentgen b75cab
                    break;
roentgen b75cab
                for (input_p = scan_line, j = 0; j < tfImageWidth; j++) {
roentgen b75cab
                    *(output_p + red_shift) = *input_p++;
roentgen b75cab
                    *(output_p + green_shift) = *input_p++;
roentgen b75cab
                    *(output_p + blue_shift) = *input_p++;
roentgen b75cab
                    output_p += 4;
roentgen b75cab
                    if (tfSamplesPerPixel == 4) /* skip the fourth channel */
roentgen b75cab
                        input_p++;
roentgen b75cab
                }
roentgen b75cab
            }
roentgen b75cab
        } else {
roentgen b75cab
            for (s = 0; s < tfSamplesPerPixel; s++) {
roentgen b75cab
                if (s == 3)             /* skip the fourth channel */
roentgen b75cab
                    continue;
roentgen b75cab
                for (i = 0; i < tfImageHeight; i++) {
roentgen b75cab
                    if (TIFFReadScanline(tfFile, scan_line, i, s) < 0)
roentgen b75cab
                        break;
roentgen b75cab
                    input_p = scan_line;
roentgen b75cab
                    output_p = imageMemory + (i*tfImageWidth*4) + pixel_map[s];
roentgen b75cab
                    for (j = 0; j < tfImageWidth; j++, output_p += 4)
roentgen b75cab
                        *output_p = *input_p++;
roentgen b75cab
                }
roentgen b75cab
            }
roentgen b75cab
        }
roentgen b75cab
    } else {
roentgen b75cab
        if (xImageDepth == tfImageDepth) {
roentgen b75cab
            output_p = imageMemory = (char *)
roentgen b75cab
                malloc(tfBytesPerRow * tfImageHeight);
roentgen b75cab
            MCHECK(imageMemory);
roentgen b75cab
roentgen b75cab
            for (i = 0; i < tfImageHeight; i++, output_p += tfBytesPerRow)
roentgen b75cab
                if (TIFFReadScanline(tfFile, output_p, i, 0) < 0)
roentgen b75cab
                    break;
roentgen b75cab
        } else if ((xImageDepth == 8) && (tfImageDepth == 4)) {
roentgen b75cab
            output_p = imageMemory = (char *)
roentgen b75cab
                malloc(tfBytesPerRow * 2 * tfImageHeight + 2);
roentgen b75cab
            MCHECK(imageMemory);
roentgen b75cab
roentgen b75cab
            /*
roentgen b75cab
             * If a scanline is of odd size the inner loop below will overshoot.
roentgen b75cab
             * This is handled very simply by recalculating the start point at
roentgen b75cab
             * each scanline and padding imageMemory a little at the end.
roentgen b75cab
             */
roentgen b75cab
            for (i = 0; i < tfImageHeight; i++) {
roentgen b75cab
                if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0)
roentgen b75cab
                    break;
roentgen b75cab
                output_p = &imageMemory[i * tfImageWidth];
roentgen b75cab
                input_p = scan_line;
roentgen b75cab
                for (j = 0; j < tfImageWidth; j += 2, input_p++) {
roentgen b75cab
                    *output_p++ = (*input_p >> 4) + basePixel;
roentgen b75cab
                    *output_p++ = (*input_p & 0xf) + basePixel;
roentgen b75cab
                }
roentgen b75cab
            }
roentgen b75cab
        } else if ((xImageDepth == 8) && (tfImageDepth == 2)) {
roentgen b75cab
            output_p = imageMemory = (char *)
roentgen b75cab
                malloc(tfBytesPerRow * 4 * tfImageHeight + 4);
roentgen b75cab
            MCHECK(imageMemory);
roentgen b75cab
roentgen b75cab
            for (i = 0; i < tfImageHeight; i++) {
roentgen b75cab
                if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0)
roentgen b75cab
                    break;
roentgen b75cab
                output_p = &imageMemory[i * tfImageWidth];
roentgen b75cab
                input_p = scan_line;
roentgen b75cab
                for (j = 0; j < tfImageWidth; j += 4, input_p++) {
roentgen b75cab
                    *output_p++ = (*input_p >> 6) + basePixel;
roentgen b75cab
                    *output_p++ = ((*input_p >> 4) & 3) + basePixel;
roentgen b75cab
                    *output_p++ = ((*input_p >> 2) & 3) + basePixel;
roentgen b75cab
                    *output_p++ = (*input_p & 3) + basePixel;
roentgen b75cab
                }
roentgen b75cab
            }
roentgen b75cab
        } else if ((xImageDepth == 4) && (tfImageDepth == 2)) {
roentgen b75cab
            output_p = imageMemory = (char *)
roentgen b75cab
                malloc(tfBytesPerRow * 2 * tfImageHeight + 2);
roentgen b75cab
            MCHECK(imageMemory);
roentgen b75cab
roentgen b75cab
            for (i = 0; i < tfImageHeight; i++) {
roentgen b75cab
                if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0)
roentgen b75cab
                    break;
roentgen b75cab
                output_p = &imageMemory[i * tfBytesPerRow * 2];
roentgen b75cab
                input_p = scan_line;
roentgen b75cab
                for (j = 0; j < tfImageWidth; j += 4, input_p++) {
roentgen b75cab
                    *output_p++ = (((*input_p>>6) << 4)
roentgen b75cab
                        | ((*input_p >> 4) & 3)) + basePixel;
roentgen b75cab
                    *output_p++ = ((((*input_p>>2) & 3) << 4)
roentgen b75cab
                        | (*input_p & 3)) + basePixel;
roentgen b75cab
                }
roentgen b75cab
            }
roentgen b75cab
        } else {
roentgen b75cab
            fprintf(stderr,
roentgen b75cab
                "xtiff: can't handle %d-bit TIFF file on an %d-bit display\n",
roentgen b75cab
                tfImageDepth, xImageDepth);
roentgen b75cab
            exit(0);
roentgen b75cab
        }
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    free(scan_line);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
CreateXImage()
roentgen b75cab
{
roentgen b75cab
    XGCValues gc_values;
roentgen b75cab
    GC bitmap_gc;
roentgen b75cab
roentgen b75cab
    xOffset = yOffset = 0;
roentgen b75cab
    grabX = grabY = -1;
roentgen b75cab
roentgen b75cab
    xImage = XCreateImage(xDisplay, xVisual, xImageDepth,
roentgen b75cab
        xImageDepth == 1 ? XYBitmap : ZPixmap, /* offset */ 0,
roentgen b75cab
        (char *) imageMemory, tfImageWidth, tfImageHeight,
roentgen b75cab
        /* bitmap_pad */ 8, /* bytes_per_line */ 0);
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * libtiff converts LSB data into MSB but doesn't change the FillOrder tag.
roentgen b75cab
     */
roentgen b75cab
    if (xImageDepth == 1)
roentgen b75cab
        xImage->bitmap_bit_order = MSBFirst;
roentgen b75cab
    if (xImageDepth <= 8)
roentgen b75cab
        xImage->byte_order = MSBFirst;
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * create an appropriate GC
roentgen b75cab
     */
roentgen b75cab
    gc_values.function = GXcopy;
roentgen b75cab
    gc_values.plane_mask = AllPlanes;
roentgen b75cab
    if (tfPhotometricInterpretation == PHOTOMETRIC_MINISBLACK) {
roentgen b75cab
        gc_values.foreground = XWhitePixel(xDisplay, xScreen);
roentgen b75cab
        gc_values.background = XBlackPixel(xDisplay, xScreen);
roentgen b75cab
    } else {
roentgen b75cab
        gc_values.foreground = XBlackPixel(xDisplay, xScreen);
roentgen b75cab
        gc_values.background = XWhitePixel(xDisplay, xScreen);
roentgen b75cab
    }
roentgen b75cab
    xWinGc = XCreateGC(xDisplay, XtWindow(shellWidget),
roentgen b75cab
        GCFunction | GCPlaneMask | GCForeground | GCBackground, &gc_values);
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * create the pixmap and load the image
roentgen b75cab
     */
roentgen b75cab
    if (appData.usePixmap == True) {
roentgen b75cab
        xImagePixmap = XCreatePixmap(xDisplay, RootWindow(xDisplay, xScreen),
roentgen b75cab
            xImage->width, xImage->height, xImageDepth);
roentgen b75cab
roentgen b75cab
        /*
roentgen b75cab
         * According to the O'Reilly X Protocol Reference Manual, page 53,
roentgen b75cab
         * "A pixmap depth of one is always supported and listed, but windows
roentgen b75cab
         * of depth one might not be supported."  Therefore we create a pixmap
roentgen b75cab
         * of depth one and use XCopyPlane().  This is idiomatic.
roentgen b75cab
         */
roentgen b75cab
        if (xImageDepth == 1) {         /* just pass the bits through */
roentgen b75cab
            gc_values.foreground = 1;   /* foreground describes set bits */
roentgen b75cab
            gc_values.background = 0;   /* background describes clear bits */
roentgen b75cab
            bitmap_gc = XCreateGC(xDisplay, xImagePixmap,
roentgen b75cab
                GCForeground | GCBackground, &gc_values);
roentgen b75cab
            XPutImage(xDisplay, xImagePixmap, bitmap_gc, xImage,
roentgen b75cab
                0, 0, 0, 0, xImage->width, xImage->height);
roentgen b75cab
        } else
roentgen b75cab
            XPutImage(xDisplay, xImagePixmap, xWinGc, xImage,
roentgen b75cab
                0, 0, 0, 0, xImage->width, xImage->height);
roentgen b75cab
        XDestroyImage(xImage);
roentgen b75cab
        free(imageMemory);
roentgen b75cab
    }
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
XtCallbackProc
roentgen b75cab
SelectProc(w, unused_1, unused_2)
roentgen b75cab
    Widget w;
roentgen b75cab
    caddr_t unused_1;
roentgen b75cab
    caddr_t unused_2;
roentgen b75cab
{
roentgen b75cab
    XawListReturnStruct *list_return;
roentgen b75cab
roentgen b75cab
    list_return = XawListShowCurrent(w);
roentgen b75cab
roentgen b75cab
    switch (list_return->list_index) {
roentgen b75cab
    case ButtonQuit:
roentgen b75cab
        QuitProc();
roentgen b75cab
        break;
roentgen b75cab
    case ButtonPreviousPage:
roentgen b75cab
        PreviousProc();
roentgen b75cab
        break;
roentgen b75cab
    case ButtonNextPage:
roentgen b75cab
        NextProc();
roentgen b75cab
        break;
roentgen b75cab
    default:
roentgen b75cab
        fprintf(stderr, "error in SelectProc\n");
roentgen b75cab
        exit(0);
roentgen b75cab
    }
roentgen b75cab
    XawListUnhighlight(w);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
QuitProc(void)
roentgen b75cab
{
roentgen b75cab
    exit(0);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
NextProc()
roentgen b75cab
{
roentgen b75cab
    PageProc(ButtonNextPage);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
PreviousProc()
roentgen b75cab
{
roentgen b75cab
    PageProc(ButtonPreviousPage);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
PageProc(direction)
roentgen b75cab
    int direction;
roentgen b75cab
{
roentgen b75cab
    XEvent fake_event;
roentgen b75cab
    Arg args[4];
roentgen b75cab
roentgen b75cab
    switch (direction) {
roentgen b75cab
    case ButtonPreviousPage:
roentgen b75cab
        if (tfDirectory > 0)
roentgen b75cab
            TIFFSetDirectory(tfFile, --tfDirectory);
roentgen b75cab
        else
roentgen b75cab
            return;
roentgen b75cab
        break;
roentgen b75cab
    case ButtonNextPage:
roentgen b75cab
        if (TIFFReadDirectory(tfFile) == True)
roentgen b75cab
            tfDirectory++;
roentgen b75cab
        else
roentgen b75cab
            return;
roentgen b75cab
        break;
roentgen b75cab
    default:
roentgen b75cab
        fprintf(stderr, "error in PageProc\n");
roentgen b75cab
        exit(0);
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    xOffset = yOffset = 0;
roentgen b75cab
    grabX = grabY = -1;
roentgen b75cab
roentgen b75cab
    GetTIFFHeader();
roentgen b75cab
    SetNameLabel();
roentgen b75cab
    GetTIFFImage();
roentgen b75cab
roentgen b75cab
    if (appData.usePixmap == True)
roentgen b75cab
        XFreePixmap(xDisplay, xImagePixmap);
roentgen b75cab
    else
roentgen b75cab
        XDestroyImage(xImage);
roentgen b75cab
roentgen b75cab
    CreateXImage();
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * Using XtSetValues() to set the widget size causes a resize.
roentgen b75cab
     * This resize gets propagated up to the parent shell.
roentgen b75cab
     * In order to disable this visually disconcerting effect,
roentgen b75cab
     * shell resizing is temporarily disabled.
roentgen b75cab
     */
roentgen b75cab
    XtSetArg(args[0], XtNallowShellResize, False);
roentgen b75cab
    XtSetValues(shellWidget, args, 1);
roentgen b75cab
roentgen b75cab
    XtSetArg(args[0], XtNwidth, tfImageWidth);
roentgen b75cab
    XtSetArg(args[1], XtNheight, tfImageHeight);
roentgen b75cab
    XtSetValues(imageWidget, args, 2);
roentgen b75cab
roentgen b75cab
    XtSetArg(args[0], XtNallowShellResize, True);
roentgen b75cab
    XtSetValues(shellWidget, args, 1);
roentgen b75cab
roentgen b75cab
    XClearWindow(xDisplay, XtWindow(imageWidget));
roentgen b75cab
roentgen b75cab
    fake_event.type = Expose;
roentgen b75cab
    fake_event.xexpose.x = fake_event.xexpose.y = 0;
roentgen b75cab
    fake_event.xexpose.width = tfImageWidth;    /* the window will clip */
roentgen b75cab
    fake_event.xexpose.height = tfImageHeight;
roentgen b75cab
    EventProc(imageWidget, NULL, &fake_event);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
EventProc(widget, unused, event)
roentgen b75cab
    Widget widget;
roentgen b75cab
    caddr_t unused;
roentgen b75cab
    XEvent *event;
roentgen b75cab
{
roentgen b75cab
    int ih, iw, ww, wh, sx, sy, w, h, dx, dy;
roentgen b75cab
    Dimension w_width, w_height;
roentgen b75cab
    XEvent next_event;
roentgen b75cab
    Arg args[2];
roentgen b75cab
roentgen b75cab
    if (event->type == MappingNotify) {
roentgen b75cab
        XRefreshKeyboardMapping((XMappingEvent *) event);
roentgen b75cab
        return;
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    if (!XtIsRealized(widget))
roentgen b75cab
        return;
roentgen b75cab
roentgen b75cab
    if ((event->type == ButtonPress) || (event->type == ButtonRelease))
roentgen b75cab
        if (event->xbutton.button != Button1)
roentgen b75cab
            return;
roentgen b75cab
roentgen b75cab
    iw = tfImageWidth;  /* avoid sign problems */
roentgen b75cab
    ih = tfImageHeight;
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * The grabX and grabY variables record where the user grabbed the image.
roentgen b75cab
     * They also record whether the mouse button is down or not.
roentgen b75cab
     */
roentgen b75cab
    if (event->type == ButtonPress) {
roentgen b75cab
        grabX = event->xbutton.x;
roentgen b75cab
        grabY = event->xbutton.y;
roentgen b75cab
        return;
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * imageWidget is a Core widget and doesn't get resized.
roentgen b75cab
     * So we calculate the size of its viewport here.
roentgen b75cab
     */
roentgen b75cab
    XtSetArg(args[0], XtNwidth, &w_width);
roentgen b75cab
    XtSetArg(args[1], XtNheight, &w_height);
roentgen b75cab
    XtGetValues(shellWidget, args, 2);
roentgen b75cab
    ww = w_width;
roentgen b75cab
    wh = w_height;
roentgen b75cab
    XtGetValues(listWidget, args, 2);
roentgen b75cab
    wh -= w_height;
roentgen b75cab
roentgen b75cab
    switch (event->type) {
roentgen b75cab
    case Expose:
roentgen b75cab
        dx = event->xexpose.x;
roentgen b75cab
        dy = event->xexpose.y;
roentgen b75cab
        sx = dx + xOffset;
roentgen b75cab
        sy = dy + yOffset;
roentgen b75cab
        w = MIN(event->xexpose.width, iw);
roentgen b75cab
        h = MIN(event->xexpose.height, ih);
roentgen b75cab
        break;
roentgen b75cab
    case KeyPress:
roentgen b75cab
        if ((grabX >= 0) || (grabY >= 0)) /* Mouse button is still down */
roentgen b75cab
            return;
roentgen b75cab
        switch (XLookupKeysym((XKeyEvent *) event, /* KeySyms index */ 0)) {
roentgen b75cab
        case XK_Up:
roentgen b75cab
            if (ih < wh)    /* Don't scroll if the window fits the image. */
roentgen b75cab
                return;
roentgen b75cab
            sy = yOffset + appData.translate;
roentgen b75cab
            sy = MIN(ih - wh, sy);
roentgen b75cab
            if (sy == yOffset)  /* Filter redundant stationary refreshes. */
roentgen b75cab
                return;
roentgen b75cab
            yOffset = sy;
roentgen b75cab
            sx = xOffset;
roentgen b75cab
            dx = dy = 0;
roentgen b75cab
            w = ww; h = wh;
roentgen b75cab
            break;
roentgen b75cab
        case XK_Down:
roentgen b75cab
            if (ih < wh)
roentgen b75cab
                return;
roentgen b75cab
            sy = yOffset - appData.translate;
roentgen b75cab
            sy = MAX(sy, 0);
roentgen b75cab
            if (sy == yOffset)
roentgen b75cab
                return;
roentgen b75cab
            yOffset = sy;
roentgen b75cab
            sx = xOffset;
roentgen b75cab
            dx = dy = 0;
roentgen b75cab
            w = ww; h = wh;
roentgen b75cab
            break;
roentgen b75cab
        case XK_Left:
roentgen b75cab
            if (iw < ww)
roentgen b75cab
                return;
roentgen b75cab
            sx = xOffset + appData.translate;
roentgen b75cab
            sx = MIN(iw - ww, sx);
roentgen b75cab
            if (sx == xOffset)
roentgen b75cab
                return;
roentgen b75cab
            xOffset = sx;
roentgen b75cab
            sy = yOffset;
roentgen b75cab
            dx = dy = 0;
roentgen b75cab
            w = ww; h = wh;
roentgen b75cab
            break;
roentgen b75cab
        case XK_Right:
roentgen b75cab
            if (iw < ww)
roentgen b75cab
                return;
roentgen b75cab
            sx = xOffset - appData.translate;
roentgen b75cab
            sx = MAX(sx, 0);
roentgen b75cab
            if (sx == xOffset)
roentgen b75cab
                return;
roentgen b75cab
            xOffset = sx;
roentgen b75cab
            sy = yOffset;
roentgen b75cab
            dx = dy = 0;
roentgen b75cab
            w = ww; h = wh;
roentgen b75cab
            break;
roentgen b75cab
        default:
roentgen b75cab
            return;
roentgen b75cab
        }
roentgen b75cab
        break;
roentgen b75cab
    case MotionNotify:
roentgen b75cab
        /*
roentgen b75cab
         * MotionEvent compression.  Ignore multiple motion events.
roentgen b75cab
         * Ignore motion events if the mouse button is up.
roentgen b75cab
         */
roentgen b75cab
        if (XPending(xDisplay)) /* Xlib doesn't flush the output buffer */
roentgen b75cab
            if (XtPeekEvent(&next_event))
roentgen b75cab
                if (next_event.type == MotionNotify)
roentgen b75cab
                    return;
roentgen b75cab
        if ((grabX < 0) || (grabY < 0))
roentgen b75cab
            return;
roentgen b75cab
        sx = xOffset + grabX - (int) event->xmotion.x;
roentgen b75cab
        if (sx >= (iw - ww))    /* clamp x motion but allow y motion */
roentgen b75cab
            sx = iw - ww;
roentgen b75cab
        sx = MAX(sx, 0);
roentgen b75cab
        sy = yOffset + grabY - (int) event->xmotion.y;
roentgen b75cab
        if (sy >= (ih - wh)) /* clamp y motion but allow x motion */
roentgen b75cab
            sy = ih - wh;
roentgen b75cab
        sy = MAX(sy, 0);
roentgen b75cab
        if ((sx == xOffset) && (sy == yOffset))
roentgen b75cab
            return;
roentgen b75cab
        dx = dy = 0;
roentgen b75cab
        w = ww; h = wh;
roentgen b75cab
        break;
roentgen b75cab
    case ButtonRelease:
roentgen b75cab
        xOffset = xOffset + grabX - (int) event->xbutton.x;
roentgen b75cab
        xOffset = MIN(iw - ww, xOffset);
roentgen b75cab
        xOffset = MAX(xOffset, 0);
roentgen b75cab
        yOffset = yOffset + grabY - (int) event->xbutton.y;
roentgen b75cab
        yOffset = MIN(ih - wh, yOffset);
roentgen b75cab
        yOffset = MAX(yOffset, 0);
roentgen b75cab
        grabX = grabY = -1;
roentgen b75cab
    default:
roentgen b75cab
        return;
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    if (appData.usePixmap == True) {
roentgen b75cab
        if (xImageDepth == 1)
roentgen b75cab
            XCopyPlane(xDisplay, xImagePixmap, XtWindow(widget),
roentgen b75cab
                xWinGc, sx, sy, w, h, dx, dy, 1);
roentgen b75cab
        else
roentgen b75cab
            XCopyArea(xDisplay, xImagePixmap, XtWindow(widget),
roentgen b75cab
                xWinGc, sx, sy, w, h, dx, dy);
roentgen b75cab
    } else
roentgen b75cab
        XPutImage(xDisplay, XtWindow(widget), xWinGc, xImage,
roentgen b75cab
            sx, sy, dx, dy, w, h);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
ResizeProc()
roentgen b75cab
{
roentgen b75cab
    Dimension w_width, w_height;
roentgen b75cab
    int xo, yo, ww, wh;
roentgen b75cab
    XEvent fake_event;
roentgen b75cab
    Arg args[2];
roentgen b75cab
roentgen b75cab
    if ((xOffset == 0) && (yOffset == 0))
roentgen b75cab
        return;
roentgen b75cab
roentgen b75cab
    XtSetArg(args[0], XtNwidth, &w_width);
roentgen b75cab
    XtSetArg(args[1], XtNheight, &w_height);
roentgen b75cab
    XtGetValues(shellWidget, args, 2);
roentgen b75cab
    ww = w_width;
roentgen b75cab
    wh = w_height;
roentgen b75cab
    XtGetValues(listWidget, args, 2);
roentgen b75cab
    wh -= w_height;
roentgen b75cab
roentgen b75cab
    xo = xOffset; yo = yOffset;
roentgen b75cab
roentgen b75cab
    if ((xOffset + ww) >= tfImageWidth)
roentgen b75cab
        xOffset = MAX((int) tfImageWidth - ww, 0);
roentgen b75cab
    if ((yOffset + wh) >= tfImageHeight)
roentgen b75cab
        yOffset = MAX((int) tfImageHeight - wh, 0);
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * Send an ExposeEvent if the origin changed.
roentgen b75cab
     * We have to do this because of the use and semantics of bit gravity.
roentgen b75cab
     */
roentgen b75cab
    if ((xo != xOffset) || (yo != yOffset)) {
roentgen b75cab
        fake_event.type = Expose;
roentgen b75cab
        fake_event.xexpose.x = fake_event.xexpose.y = 0;
roentgen b75cab
        fake_event.xexpose.width = tfImageWidth;
roentgen b75cab
        fake_event.xexpose.height = tfImageHeight;
roentgen b75cab
        EventProc(imageWidget, NULL, &fake_event);
roentgen b75cab
    }
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
int
roentgen b75cab
XTiffErrorHandler(display, error_event)
roentgen b75cab
    Display *display;
roentgen b75cab
    XErrorEvent *error_event;
roentgen b75cab
{
roentgen b75cab
    char message[80];
roentgen b75cab
roentgen b75cab
    /*
roentgen b75cab
     * Some X servers limit the size of pixmaps.
roentgen b75cab
     */
roentgen b75cab
    if ((error_event->error_code == BadAlloc)
roentgen b75cab
            && (error_event->request_code == X_CreatePixmap))
roentgen b75cab
        fprintf(stderr, "xtiff: requested pixmap too big for display\n");
roentgen b75cab
    else {
roentgen b75cab
        XGetErrorText(display, error_event->error_code, message, 80);
roentgen b75cab
        fprintf(stderr, "xtiff: error code %s\n", message);
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    exit(0);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
void
roentgen b75cab
Usage()
roentgen b75cab
{
roentgen b75cab
    fprintf(stderr, "Usage xtiff: [options] tiff-file\n");
roentgen b75cab
    fprintf(stderr, "\tstandard Xt options\n");
roentgen b75cab
    fprintf(stderr, "\t[-help]\n");
roentgen b75cab
    fprintf(stderr, "\t[-gamma gamma]\n");
roentgen b75cab
    fprintf(stderr, "\t[-usePixmap (True | False)]\n");
roentgen b75cab
    fprintf(stderr, "\t[-viewportWidth pixels]\n");
roentgen b75cab
    fprintf(stderr, "\t[-viewportHeight pixels]\n");
roentgen b75cab
    fprintf(stderr, "\t[-translate pixels]\n");
roentgen b75cab
    fprintf(stderr, "\t[-verbose (True | False)]\n");
roentgen b75cab
    exit(0);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
/* vim: set ts=8 sts=8 sw=8 noet: */
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * Local Variables:
roentgen b75cab
 * mode: c
roentgen b75cab
 * c-basic-offset: 8
roentgen b75cab
 * fill-column: 78
roentgen b75cab
 * End:
roentgen b75cab
 */