home *** CD-ROM | disk | FTP | other *** search
/ Practical Algorithms for Image Analysis / Practical Algorithms for Image Analysis.iso / TARFILE.GZ / tarfile / libtiff / contrib / dbs / xtiff / xtiff.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-09-11  |  40.8 KB  |  1,276 lines

  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. }
  1276.