1/* $Id: tiffgt.c 276 2010-06-30 12:18:30Z nijtmans $ */
2
3/*
4 * Copyright (c) 1988-1997 Sam Leffler
5 * Copyright (c) 1991-1997 Silicon Graphics, Inc.
6 * Copyright (c) 2003, Andrey Kiselev <dron@ak4719.spb.edu>
7 *
8 * Permission to use, copy, modify, distribute, and sell this software and
9 * its documentation for any purpose is hereby granted without fee, provided
10 * that (i) the above copyright notices and this permission notice appear in
11 * all copies of the software and related documentation, and (ii) the names of
12 * Sam Leffler and Silicon Graphics may not be used in any advertising or
13 * publicity relating to the software without the specific, prior written
14 * permission of Sam Leffler and Silicon Graphics.
15 *
16 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
18 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
21 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
22 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
23 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
24 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
25 * OF THIS SOFTWARE.
26 */
27
28#include "tif_config.h"
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33
34#if HAVE_APPLE_OPENGL_FRAMEWORK
35# include <OpenGL/gl.h>
36# include <GLUT/glut.h>
37#else
38# include <GL/gl.h>
39# include <GL/glut.h>
40#endif
41
42#include "tiffio.h"
43
44#ifndef HAVE_GETOPT
45extern int getopt(int, char**, char*);
46#endif
47
48static  uint32  width = 0, height = 0;          /* window width & height */
49static  uint32* raster = NULL;                  /* displayable image */
50static TIFFRGBAImage img;
51static int      order0 = 0, order;
52static uint16   photo0 = (uint16) -1, photo;
53static int      stoponerr = 0;                  /* stop on read error */
54static int      verbose = 0;
55#define TITLE_LENGTH    1024
56static char     title[TITLE_LENGTH];            /* window title line */
57static uint32   xmax, ymax;
58static char**   filelist = NULL;
59static int      fileindex;
60static int      filenum;
61static TIFFErrorHandler oerror;
62static TIFFErrorHandler owarning;
63
64static void	cleanup_and_exit(void);
65static int	initImage(void);
66static int	prevImage(void);
67static int	nextImage(void);
68static void	setWindowSize(void);
69static void	usage(void);
70static uint16	photoArg(const char*);
71static void	raster_draw(void);
72static void	raster_reshape(int, int);
73static void	raster_keys(unsigned char, int, int);
74static void	raster_special(int, int, int);
75
76extern  char* optarg;
77extern  int optind;
78static TIFF* tif = NULL;
79
80int
81main(int argc, char* argv[])
82{
83        int c;
84        int dirnum = -1;
85        uint32 diroff = 0;
86
87        oerror = TIFFSetErrorHandler(NULL);
88        owarning = TIFFSetWarningHandler(NULL);
89        while ((c = getopt(argc, argv, "d:o:p:eflmsvw?")) != -1)
90            switch (c) {
91            case 'd':
92                dirnum = atoi(optarg);
93                break;
94            case 'e':
95                oerror = TIFFSetErrorHandler(oerror);
96                break;
97            case 'l':
98                order0 = FILLORDER_LSB2MSB;
99                break;
100            case 'm':
101                order0 = FILLORDER_MSB2LSB;
102                break;
103            case 'o':
104                diroff = strtoul(optarg, NULL, 0);
105                break;
106            case 'p':
107                photo0 = photoArg(optarg);
108                break;
109            case 's':
110                stoponerr = 1;
111                break;
112            case 'w':
113                owarning = TIFFSetWarningHandler(owarning);
114                break;
115            case 'v':
116                verbose = 1;
117                break;
118            case '?':
119                usage();
120                /*NOTREACHED*/
121            }
122        filenum = argc - optind;
123        if ( filenum < 1)
124                usage();
125
126        glutInit(&argc, argv);
127        glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
128
129        /*
130         * Get the screen size
131         */
132        xmax = glutGet(GLUT_SCREEN_WIDTH);
133        ymax = glutGet(GLUT_SCREEN_HEIGHT);
134
135        /*
136         * Use 90% of the screen size
137         */
138        xmax = xmax - xmax / 10.0;
139        ymax = ymax - ymax / 10.0;
140
141        filelist = (char **) _TIFFmalloc(filenum * sizeof(char*));
142        if (!filelist) {
143                TIFFError(argv[0], "Can not allocate space for the file list.");
144                return 1;
145        }
146        _TIFFmemcpy(filelist, argv + optind, filenum * sizeof(char*));
147        fileindex = -1;
148        if (nextImage() < 0) {
149                _TIFFfree(filelist);
150                return 2;
151        }
152        /*
153         * Set initial directory if user-specified
154         * file was opened successfully.
155         */
156        if (dirnum != -1 && !TIFFSetDirectory(tif, dirnum))
157            TIFFError(argv[0], "Error, seeking to directory %d", dirnum);
158        if (diroff != 0 && !TIFFSetSubDirectory(tif, diroff))
159            TIFFError(argv[0], "Error, setting subdirectory at %#x", diroff);
160        order = order0;
161        photo = photo0;
162	if (initImage() < 0){
163                _TIFFfree(filelist);
164                return 3;
165        }
166        /*
167         * Create a new window or reconfigure an existing
168         * one to suit the image to be displayed.
169         */
170        glutInitWindowSize(width, height);
171        snprintf(title, TITLE_LENGTH - 1, "%s [%u]", filelist[fileindex],
172                (unsigned int) TIFFCurrentDirectory(tif));
173        glutCreateWindow(title);
174        glutDisplayFunc(raster_draw);
175        glutReshapeFunc(raster_reshape);
176        glutKeyboardFunc(raster_keys);
177        glutSpecialFunc(raster_special);
178        glutMainLoop();
179
180        cleanup_and_exit();
181        return 0;
182}
183
184static void
185cleanup_and_exit(void)
186{
187        TIFFRGBAImageEnd(&img);
188        if (filelist != NULL)
189                _TIFFfree(filelist);
190        if (raster != NULL)
191                _TIFFfree(raster);
192        if (tif != NULL)
193                TIFFClose(tif);
194        exit(0);
195}
196
197static int
198initImage(void)
199{
200        uint32 w, h;
201
202        if (order)
203                TIFFSetField(tif, TIFFTAG_FILLORDER, order);
204        if (photo != (uint16) -1)
205                TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photo);
206        if (!TIFFRGBAImageBegin(&img, tif, stoponerr, title)) {
207                TIFFError(filelist[fileindex], "%s", title);
208                TIFFClose(tif);
209                tif = NULL;
210                return -1;
211        }
212
213        /*
214         * Setup the image raster as required.
215         */
216        h = img.height;
217        w = img.width;
218        if (h > ymax) {
219                w = (int)(w * ((float)ymax / h));
220                h = ymax;
221        }
222        if (w > xmax) {
223                h = (int)(h * ((float)xmax / w));
224                w = xmax;
225        }
226
227        if (w != width || h != height) {
228            if (raster != NULL)
229                _TIFFfree(raster), raster = NULL;
230            raster = (uint32*) _TIFFmalloc(img.width * img.height * sizeof (uint32));
231            if (raster == NULL) {
232                width = height = 0;
233                TIFFError(filelist[fileindex], "No space for raster buffer");
234                cleanup_and_exit();
235            }
236            width = w;
237            height = h;
238        }
239        TIFFRGBAImageGet(&img, raster, img.width, img.height);
240#if HOST_BIGENDIAN
241        TIFFSwabArrayOfLong(raster,img.width*img.height);
242#endif
243	return 0;
244}
245
246static int
247prevImage(void)
248{
249        if (fileindex > 0)
250                fileindex--;
251        else if (tif)
252                return fileindex;
253        if (tif)
254                TIFFClose(tif);
255        tif = TIFFOpen(filelist[fileindex], "r");
256        if (tif == NULL)
257                return -1;
258        return fileindex;
259}
260
261static int
262nextImage(void)
263{
264        if (fileindex < filenum - 1)
265                fileindex++;
266        else if (tif)
267                return fileindex;
268        if (tif)
269                TIFFClose(tif);
270        tif = TIFFOpen(filelist[fileindex], "r");
271        if (tif == NULL)
272                return -1;
273        return fileindex;
274}
275
276static void
277setWindowSize(void)
278{
279        glutReshapeWindow(width, height);
280}
281
282static void
283raster_draw(void)
284{
285  glDrawPixels(img.width, img.height, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid *) raster);
286}
287
288static void
289raster_reshape(int win_w, int win_h)
290{
291        GLfloat xratio = (GLfloat)win_w/img.width;
292        GLfloat yratio = (GLfloat)win_h/img.height;
293        int     ratio = (int)(((xratio > yratio)?xratio:yratio) * 100);
294
295        glPixelZoom(xratio, yratio);
296        glViewport(0, 0, win_w, win_h);
297        snprintf(title, 1024, "%s [%u] %d%%", filelist[fileindex],
298                (unsigned int) TIFFCurrentDirectory(tif), ratio);
299        glutSetWindowTitle(title);
300}
301
302static void
303raster_keys(unsigned char key, int x, int y)
304{
305        switch (key) {
306                case 'b':                       /* photometric MinIsBlack */
307                    photo = PHOTOMETRIC_MINISBLACK;
308                    initImage();
309                    break;
310                case 'l':                       /* lsb-to-msb FillOrder */
311                    order = FILLORDER_LSB2MSB;
312                    initImage();
313                    break;
314                case 'm':                       /* msb-to-lsb FillOrder */
315                    order = FILLORDER_MSB2LSB;
316                    initImage();
317                    break;
318                case 'w':                       /* photometric MinIsWhite */
319                    photo = PHOTOMETRIC_MINISWHITE;
320                    initImage();
321                    break;
322                case 'W':                       /* toggle warnings */
323                    owarning = TIFFSetWarningHandler(owarning);
324                    initImage();
325                    break;
326                case 'E':                       /* toggle errors */
327                    oerror = TIFFSetErrorHandler(oerror);
328                    initImage();
329                    break;
330                case 'z':                       /* reset to defaults */
331                case 'Z':
332                    order = order0;
333                    photo = photo0;
334                    if (owarning == NULL)
335                        owarning = TIFFSetWarningHandler(NULL);
336                    if (oerror == NULL)
337                        oerror = TIFFSetErrorHandler(NULL);
338                    initImage();
339                    break;
340                case 'q':                       /* exit */
341                case '\033':
342                    cleanup_and_exit();
343        }
344        glutPostRedisplay();
345}
346
347static void
348raster_special(int key, int x, int y)
349{
350        switch (key) {
351                case GLUT_KEY_PAGE_UP:          /* previous logical image */
352                    if (TIFFCurrentDirectory(tif) > 0) {
353                            if (TIFFSetDirectory(tif,
354                                                 TIFFCurrentDirectory(tif)-1)) {
355                                    initImage();
356                                    setWindowSize();
357                        }
358                    } else {
359                            TIFFRGBAImageEnd(&img);
360                            prevImage();
361                            initImage();
362                            setWindowSize();
363                    }
364                break;
365                case GLUT_KEY_PAGE_DOWN:        /* next logical image */
366                    if (!TIFFLastDirectory(tif)) {
367                            if (TIFFReadDirectory(tif)) {
368                                    initImage();
369                                    setWindowSize();
370                            }
371                    } else {
372                            TIFFRGBAImageEnd(&img);
373                            nextImage();
374                            initImage();
375                            setWindowSize();
376                    }
377                break;
378                case GLUT_KEY_HOME:             /* 1st image in current file */
379                        if (TIFFSetDirectory(tif, 0)) {
380                                TIFFRGBAImageEnd(&img);
381                                initImage();
382                                setWindowSize();
383                        }
384                break;
385                case GLUT_KEY_END:              /* last image in current file */
386                        TIFFRGBAImageEnd(&img);
387                        while (!TIFFLastDirectory(tif))
388                                TIFFReadDirectory(tif);
389                        initImage();
390                        setWindowSize();
391                break;
392        }
393        glutPostRedisplay();
394}
395
396
397
398char* stuff[] = {
399"usage: tiffgt [options] file.tif",
400"where options are:",
401" -c            use colormap visual",
402" -d dirnum     set initial directory (default is 0)",
403" -e            enable display of TIFF error messages",
404" -l            force lsb-to-msb FillOrder",
405" -m            force msb-to-lsb FillOrder",
406" -o offset     set initial directory offset",
407" -p photo      override photometric interpretation",
408" -r            use fullcolor visual",
409" -s            stop decoding on first error (default is ignore errors)",
410" -v            enable verbose mode",
411" -w            enable display of TIFF warning messages",
412NULL
413};
414
415static void
416usage(void)
417{
418        char buf[BUFSIZ];
419        int i;
420
421        setbuf(stderr, buf);
422                fprintf(stderr, "%s\n\n", TIFFGetVersion());
423        for (i = 0; stuff[i] != NULL; i++)
424                fprintf(stderr, "%s\n", stuff[i]);
425        exit(-1);
426}
427
428static uint16
429photoArg(const char* arg)
430{
431        if (strcmp(arg, "miniswhite") == 0)
432            return (PHOTOMETRIC_MINISWHITE);
433        else if (strcmp(arg, "minisblack") == 0)
434            return (PHOTOMETRIC_MINISBLACK);
435        else if (strcmp(arg, "rgb") == 0)
436            return (PHOTOMETRIC_RGB);
437        else if (strcmp(arg, "palette") == 0)
438            return (PHOTOMETRIC_PALETTE);
439        else if (strcmp(arg, "mask") == 0)
440            return (PHOTOMETRIC_MASK);
441        else if (strcmp(arg, "separated") == 0)
442            return (PHOTOMETRIC_SEPARATED);
443        else if (strcmp(arg, "ycbcr") == 0)
444            return (PHOTOMETRIC_YCBCR);
445        else if (strcmp(arg, "cielab") == 0)
446            return (PHOTOMETRIC_CIELAB);
447        else if (strcmp(arg, "logl") == 0)
448            return (PHOTOMETRIC_LOGL);
449        else if (strcmp(arg, "logluv") == 0)
450            return (PHOTOMETRIC_LOGLUV);
451        else
452            return ((uint16) -1);
453}
454
455/* vim: set ts=8 sts=8 sw=8 noet: */
456/*
457 * Local Variables:
458 * mode: c
459 * c-basic-offset: 8
460 * fill-column: 78
461 * End:
462 */
463