text: /* $Id: tiffgt.c,v 1.15 2015-09-06 20:42:20 bfriesen Exp $ */
  
  /*
   * Copyright (c) 1988-1997 Sam Leffler
   * Copyright (c) 1991-1997 Silicon Graphics, Inc.
   * Copyright (c) 2003, Andrey Kiselev <dron@ak4719.spb.edu>
   *
   * Permission to use, copy, modify, distribute, and sell this software and 
   * its documentation for any purpose is hereby granted without fee, provided
   * that (i) the above copyright notices and this permission notice appear in
   * all copies of the software and related documentation, and (ii) the names of
   * Sam Leffler and Silicon Graphics may not be used in any advertising or
   * publicity relating to the software without the specific, prior written
   * permission of Sam Leffler and Silicon Graphics.
   * 
   * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
   * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
   * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
   * 
   * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
   * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
   * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
   * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
   * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
   * OF THIS SOFTWARE.
   */
  
  #include "tif_config.h"
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
  
  #ifdef HAVE_OPENGL_GL_H
  # include <OpenGL/gl.h>
  #else
  # include <GL/gl.h>
  #endif
  #ifdef HAVE_GLUT_GLUT_H
  # include <GLUT/glut.h>
  #else
  # include <GL/glut.h>
  #endif
  
  #include "tiffio.h"
  #include "tiffiop.h"
  
  #ifndef HAVE_GETOPT
  extern int getopt(int, char**, char*);
  #endif
  
  static  uint32  width = 0, height = 0;          /* window width & height */
  static  uint32* raster = NULL;                  /* displayable image */
  static TIFFRGBAImage img;
  static int      order0 = 0, order;
  static uint16   photo0 = (uint16) -1, photo;
  static int      stoponerr = 0;                  /* stop on read error */
  static int      verbose = 0;
  #define TITLE_LENGTH    1024
  static char     title[TITLE_LENGTH];            /* window title line */
  static uint32   xmax, ymax;
  static char**   filelist = NULL;
  static int      fileindex;
  static int      filenum;
  static TIFFErrorHandler oerror;
  static TIFFErrorHandler owarning;
  
  static void	cleanup_and_exit(void);
  static int	initImage(void);
  static int	prevImage(void);
  static int	nextImage(void);
  static void	setWindowSize(void);
  static void	usage(void);
  static uint16	photoArg(const char*);
  static void	raster_draw(void);
  static void	raster_reshape(int, int);
  static void	raster_keys(unsigned char, int, int);
  static void	raster_special(int, int, int);
  
  #if !HAVE_DECL_OPTARG
  extern  char* optarg;
  extern  int optind;
  #endif
  
  /* GLUT framework on MacOS X produces deprecation warnings */
  # if defined(__GNUC__) && defined(__APPLE__)
  #  pragma GCC diagnostic push
  #  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  # endif
  
  static TIFF* tif = NULL;
  
  int
  main(int argc, char* argv[])
  {
          int c;
          int dirnum = -1;
          uint32 diroff = 0;
  
          oerror = TIFFSetErrorHandler(NULL);
          owarning = TIFFSetWarningHandler(NULL);
          while ((c = getopt(argc, argv, "d:o:p:eflmsvw?")) != -1)
              switch (c) {
              case 'd':
                  dirnum = atoi(optarg);
                  break;
              case 'e':
                  oerror = TIFFSetErrorHandler(oerror);
                  break;
              case 'l':
                  order0 = FILLORDER_LSB2MSB;
                  break;
              case 'm':
                  order0 = FILLORDER_MSB2LSB;
                  break;
              case 'o':
                  diroff = strtoul(optarg, NULL, 0);
                  break;
              case 'p':
                  photo0 = photoArg(optarg);
                  break;
              case 's':
                  stoponerr = 1;
                  break;
              case 'w':
                  owarning = TIFFSetWarningHandler(owarning);
                  break;
              case 'v':
                  verbose = 1;
                  break;
              case '?':
                  usage();
                  /*NOTREACHED*/
              }
          filenum = argc - optind;
          if ( filenum < 1)
                  usage();
  
          glutInit(&argc, argv);
          glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
  
          /*
           * Get the screen size
           */
          xmax = glutGet(GLUT_SCREEN_WIDTH);
          ymax = glutGet(GLUT_SCREEN_HEIGHT);
  
          /*
           * Use 90% of the screen size
           */
          xmax = xmax - xmax / 10.0;
          ymax = ymax - ymax / 10.0;
  
          filelist = (char **) _TIFFmalloc(filenum * sizeof(char*));
          if (!filelist) {
                  TIFFError(argv[0], "Can not allocate space for the file list.");
                  return 1;
          }
          _TIFFmemcpy(filelist, argv + optind, filenum * sizeof(char*));
          fileindex = -1;
          if (nextImage() < 0) {
                  _TIFFfree(filelist);
                  return 2;
          }
          /*
           * Set initial directory if user-specified
           * file was opened successfully.
           */
          if (dirnum != -1 && !TIFFSetDirectory(tif, dirnum))
              TIFFError(argv[0], "Error, seeking to directory %d", dirnum);
          if (diroff != 0 && !TIFFSetSubDirectory(tif, diroff))
              TIFFError(argv[0], "Error, setting subdirectory at %#x", diroff);
          order = order0;
          photo = photo0;
  	if (initImage() < 0){
                  _TIFFfree(filelist);
                  return 3;
          }
          /*
           * Create a new window or reconfigure an existing
           * one to suit the image to be displayed.
           */
          glutInitWindowSize(width, height);
          snprintf(title, TITLE_LENGTH - 1, "%s [%u]", filelist[fileindex],
                  (unsigned int) TIFFCurrentDirectory(tif));
          glutCreateWindow(title);
          glutDisplayFunc(raster_draw);
          glutReshapeFunc(raster_reshape);
          glutKeyboardFunc(raster_keys);
          glutSpecialFunc(raster_special);
          glutMainLoop();
  
          cleanup_and_exit();
          return 0;
  }
  
  static void 
  cleanup_and_exit(void)
  {
          TIFFRGBAImageEnd(&img);
          if (filelist != NULL)
                  _TIFFfree(filelist);
          if (raster != NULL)
                  _TIFFfree(raster);
          if (tif != NULL)
                  TIFFClose(tif);
          exit(0);
  }
  
  static int
  initImage(void)
  {
          uint32 w, h;
  
          if (order)
                  TIFFSetField(tif, TIFFTAG_FILLORDER, order);
          if (photo != (uint16) -1)
                  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photo);
          if (!TIFFRGBAImageBegin(&img, tif, stoponerr, title)) {
                  TIFFError(filelist[fileindex], "%s", title);
                  TIFFClose(tif);
                  tif = NULL;
                  return -1;
          }
  
          /*
           * Setup the image raster as required.
           */
          h = img.height;
          w = img.width;
          if (h > ymax) {
                  w = (int)(w * ((float)ymax / h));
                  h = ymax;
          }
          if (w > xmax) {
                  h = (int)(h * ((float)xmax / w));
                  w = xmax;
          }
  
  	if (w != width || h != height) {
  		uint32 rastersize =
  			_TIFFMultiply32(tif, img.width, img.height, "allocating raster buffer");
  		if (raster != NULL)
  			_TIFFfree(raster), raster = NULL;
  		raster = (uint32*) _TIFFCheckMalloc(tif, rastersize, sizeof (uint32),
  						    "allocating raster buffer");
  		if (raster == NULL) {
  			width = height = 0;
  			TIFFError(filelist[fileindex], "No space for raster buffer");
  			cleanup_and_exit();
  		}
  		width = w;
  		height = h;
  	}
  	TIFFRGBAImageGet(&img, raster, img.width, img.height);
  #if HOST_BIGENDIAN
  	TIFFSwabArrayOfLong(raster,img.width*img.height);
  #endif
  	return 0;
  }
  
  static int
  prevImage(void)
  {
          if (fileindex > 0)
                  fileindex--;
          else if (tif)
                  return fileindex;
          if (tif)
                  TIFFClose(tif);
          tif = TIFFOpen(filelist[fileindex], "r");
          if (tif == NULL)
                  return -1;
          return fileindex;
  }
  
  static int
  nextImage(void)
  {
          if (fileindex < filenum - 1)
                  fileindex++;
          else if (tif)
                  return fileindex;
          if (tif)
                  TIFFClose(tif);
          tif = TIFFOpen(filelist[fileindex], "r");
          if (tif == NULL)
                  return -1;
          return fileindex;
  }
  
  static void
  setWindowSize(void)
  {
          glutReshapeWindow(width, height);
  }
  
  static void
  raster_draw(void)
  {
    glDrawPixels(img.width, img.height, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid *) raster);
    glFlush();
  }
  
  static void
  raster_reshape(int win_w, int win_h)
  {
          GLfloat xratio = (GLfloat)win_w/img.width;
          GLfloat yratio = (GLfloat)win_h/img.height;
          int     ratio = (int)(((xratio > yratio)?xratio:yratio) * 100);
  
          glPixelZoom(xratio, yratio);
          glViewport(0, 0, win_w, win_h);
          snprintf(title, 1024, "%s [%u] %d%%", filelist[fileindex],
                  (unsigned int) TIFFCurrentDirectory(tif), ratio);
          glutSetWindowTitle(title);
  }
  
  static void
  raster_keys(unsigned char key, int x, int y)
  {
          (void) x;
          (void) y;
          switch (key) {
                  case 'b':                       /* photometric MinIsBlack */
                      photo = PHOTOMETRIC_MINISBLACK;
                      initImage();
                      break;
                  case 'l':                       /* lsb-to-msb FillOrder */
                      order = FILLORDER_LSB2MSB;
                      initImage();
                      break;
                  case 'm':                       /* msb-to-lsb FillOrder */
                      order = FILLORDER_MSB2LSB;
                      initImage();
                      break;
                  case 'w':                       /* photometric MinIsWhite */
                      photo = PHOTOMETRIC_MINISWHITE;
                      initImage();
                      break;
                  case 'W':                       /* toggle warnings */
                      owarning = TIFFSetWarningHandler(owarning);
                      initImage();
                      break;
                  case 'E':                       /* toggle errors */
                      oerror = TIFFSetErrorHandler(oerror);
                      initImage();
                      break;
                  case 'z':                       /* reset to defaults */
                  case 'Z':
                      order = order0;
                      photo = photo0;
                      if (owarning == NULL)
                          owarning = TIFFSetWarningHandler(NULL);
                      if (oerror == NULL)
                          oerror = TIFFSetErrorHandler(NULL);
                      initImage();
                      break;
                  case 'q':                       /* exit */
                  case '\033':
                      cleanup_and_exit();
          }
          glutPostRedisplay();
  }
  
  static void
  raster_special(int key, int x, int y)
  {
          (void) x;
          (void) y;
          switch (key) {
                  case GLUT_KEY_PAGE_UP:          /* previous logical image */
                      if (TIFFCurrentDirectory(tif) > 0) {
                              if (TIFFSetDirectory(tif,
                                                   TIFFCurrentDirectory(tif)-1)) {
                                      initImage();
                                      setWindowSize();
                          }
                      } else {
                              TIFFRGBAImageEnd(&img);
                              prevImage();
                              initImage();
                              setWindowSize();
                      }
                  break;
                  case GLUT_KEY_PAGE_DOWN:        /* next logical image */
                      if (!TIFFLastDirectory(tif)) {
                              if (TIFFReadDirectory(tif)) {
                                      initImage();
                                      setWindowSize();
                              }
                      } else {
                              TIFFRGBAImageEnd(&img);
                              nextImage();
                              initImage();
                              setWindowSize();
                      }
                  break;
                  case GLUT_KEY_HOME:             /* 1st image in current file */
                          if (TIFFSetDirectory(tif, 0)) {
                                  TIFFRGBAImageEnd(&img);
                                  initImage();
                                  setWindowSize();
                          }
                  break;
                  case GLUT_KEY_END:              /* last image in current file */
                          TIFFRGBAImageEnd(&img);
                          while (!TIFFLastDirectory(tif))
                                  TIFFReadDirectory(tif);
                          initImage();
                          setWindowSize();
                  break;
          }
          glutPostRedisplay();
  }
  
  /* GLUT framework on MacOS X produces deprecation warnings */
  # if defined(__GNUC__) && defined(__APPLE__)
  #  pragma GCC diagnostic pop
  # endif
  
  char* stuff[] = {
  "usage: tiffgt [options] file.tif",
  "where options are:",
  " -c            use colormap visual",
  " -d dirnum     set initial directory (default is 0)",
  " -e            enable display of TIFF error messages",
  " -l            force lsb-to-msb FillOrder",
  " -m            force msb-to-lsb FillOrder",
  " -o offset     set initial directory offset",
  " -p photo      override photometric interpretation",
  " -r            use fullcolor visual",
  " -s            stop decoding on first error (default is ignore errors)",
  " -v            enable verbose mode",
  " -w            enable display of TIFF warning messages",
  NULL
  };
  
  static void
  usage(void)
  {
          char buf[BUFSIZ];
          int i;
  
          setbuf(stderr, buf);
                  fprintf(stderr, "%s\n\n", TIFFGetVersion());
          for (i = 0; stuff[i] != NULL; i++)
                  fprintf(stderr, "%s\n", stuff[i]);
          exit(-1);
  }
  
  static uint16
  photoArg(const char* arg)
  {
          if (strcmp(arg, "miniswhite") == 0)
              return (PHOTOMETRIC_MINISWHITE);
          else if (strcmp(arg, "minisblack") == 0)
              return (PHOTOMETRIC_MINISBLACK);
          else if (strcmp(arg, "rgb") == 0)
              return (PHOTOMETRIC_RGB);
          else if (strcmp(arg, "palette") == 0)
              return (PHOTOMETRIC_PALETTE);
          else if (strcmp(arg, "mask") == 0)
              return (PHOTOMETRIC_MASK);
          else if (strcmp(arg, "separated") == 0)
              return (PHOTOMETRIC_SEPARATED);
          else if (strcmp(arg, "ycbcr") == 0)
              return (PHOTOMETRIC_YCBCR);
          else if (strcmp(arg, "cielab") == 0)
              return (PHOTOMETRIC_CIELAB);
          else if (strcmp(arg, "logl") == 0)
              return (PHOTOMETRIC_LOGL);
          else if (strcmp(arg, "logluv") == 0)
              return (PHOTOMETRIC_LOGLUV);
          else
              return ((uint16) -1);
  }
  
  /* vim: set ts=8 sts=8 sw=8 noet: */
  /*
   * Local Variables:
   * mode: c
   * c-basic-offset: 8
   * fill-column: 78
   * End:
   */
  