1 /* $Id: tiffgt.c,v 1.15 2015-09-06 20:42:20 bfriesen Exp $ */
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 #ifdef HAVE_OPENGL_GL_H
35 # include <OpenGL/gl.h>
36 #else
37 # include <GL/gl.h>
38 #endif
39 #ifdef HAVE_GLUT_GLUT_H
40 # include <GLUT/glut.h>
41 #else
42 # include <GL/glut.h>
43 #endif
44
45 #include "tiffio.h"
46 #include "tiffiop.h"
47
48 #ifndef HAVE_GETOPT
49 extern int getopt(int, char**, char*);
50 #endif
51
52 static uint32 width = 0, height = 0; /* window width & height */
53 static uint32* raster = NULL; /* displayable image */
54 static TIFFRGBAImage img;
55 static int order0 = 0, order;
56 static uint16 photo0 = (uint16) -1, photo;
57 static int stoponerr = 0; /* stop on read error */
58 static int verbose = 0;
59 #define TITLE_LENGTH 1024
60 static char title[TITLE_LENGTH]; /* window title line */
61 static uint32 xmax, ymax;
62 static char** filelist = NULL;
63 static int fileindex;
64 static int filenum;
65 static TIFFErrorHandler oerror;
66 static TIFFErrorHandler owarning;
67
68 static void cleanup_and_exit(void);
69 static int initImage(void);
70 static int prevImage(void);
71 static int nextImage(void);
72 static void setWindowSize(void);
73 static void usage(void);
74 static uint16 photoArg(const char*);
75 static void raster_draw(void);
76 static void raster_reshape(int, int);
77 static void raster_keys(unsigned char, int, int);
78 static void raster_special(int, int, int);
79
80 #if !HAVE_DECL_OPTARG
81 extern char* optarg;
82 extern int optind;
83 #endif
84
85 /* GLUT framework on MacOS X produces deprecation warnings */
86 # if defined(__GNUC__) && defined(__APPLE__)
87 # pragma GCC diagnostic push
88 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
89 # endif
90
91 static TIFF* tif = NULL;
92
93 int
main(int argc,char * argv[])94 main(int argc, char* argv[])
95 {
96 int c;
97 int dirnum = -1;
98 uint32 diroff = 0;
99
100 oerror = TIFFSetErrorHandler(NULL);
101 owarning = TIFFSetWarningHandler(NULL);
102 while ((c = getopt(argc, argv, "d:o:p:eflmsvw?")) != -1)
103 switch (c) {
104 case 'd':
105 dirnum = atoi(optarg);
106 break;
107 case 'e':
108 oerror = TIFFSetErrorHandler(oerror);
109 break;
110 case 'l':
111 order0 = FILLORDER_LSB2MSB;
112 break;
113 case 'm':
114 order0 = FILLORDER_MSB2LSB;
115 break;
116 case 'o':
117 diroff = strtoul(optarg, NULL, 0);
118 break;
119 case 'p':
120 photo0 = photoArg(optarg);
121 break;
122 case 's':
123 stoponerr = 1;
124 break;
125 case 'w':
126 owarning = TIFFSetWarningHandler(owarning);
127 break;
128 case 'v':
129 verbose = 1;
130 break;
131 case '?':
132 usage();
133 /*NOTREACHED*/
134 }
135 filenum = argc - optind;
136 if ( filenum < 1)
137 usage();
138
139 glutInit(&argc, argv);
140 glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
141
142 /*
143 * Get the screen size
144 */
145 xmax = glutGet(GLUT_SCREEN_WIDTH);
146 ymax = glutGet(GLUT_SCREEN_HEIGHT);
147
148 /*
149 * Use 90% of the screen size
150 */
151 xmax = xmax - xmax / 10.0;
152 ymax = ymax - ymax / 10.0;
153
154 filelist = (char **) _TIFFmalloc(filenum * sizeof(char*));
155 if (!filelist) {
156 TIFFError(argv[0], "Can not allocate space for the file list.");
157 return 1;
158 }
159 _TIFFmemcpy(filelist, argv + optind, filenum * sizeof(char*));
160 fileindex = -1;
161 if (nextImage() < 0) {
162 _TIFFfree(filelist);
163 return 2;
164 }
165 /*
166 * Set initial directory if user-specified
167 * file was opened successfully.
168 */
169 if (dirnum != -1 && !TIFFSetDirectory(tif, dirnum))
170 TIFFError(argv[0], "Error, seeking to directory %d", dirnum);
171 if (diroff != 0 && !TIFFSetSubDirectory(tif, diroff))
172 TIFFError(argv[0], "Error, setting subdirectory at %#x", diroff);
173 order = order0;
174 photo = photo0;
175 if (initImage() < 0){
176 _TIFFfree(filelist);
177 return 3;
178 }
179 /*
180 * Create a new window or reconfigure an existing
181 * one to suit the image to be displayed.
182 */
183 glutInitWindowSize(width, height);
184 snprintf(title, TITLE_LENGTH - 1, "%s [%u]", filelist[fileindex],
185 (unsigned int) TIFFCurrentDirectory(tif));
186 glutCreateWindow(title);
187 glutDisplayFunc(raster_draw);
188 glutReshapeFunc(raster_reshape);
189 glutKeyboardFunc(raster_keys);
190 glutSpecialFunc(raster_special);
191 glutMainLoop();
192
193 cleanup_and_exit();
194 return 0;
195 }
196
197 static void
cleanup_and_exit(void)198 cleanup_and_exit(void)
199 {
200 TIFFRGBAImageEnd(&img);
201 if (filelist != NULL)
202 _TIFFfree(filelist);
203 if (raster != NULL)
204 _TIFFfree(raster);
205 if (tif != NULL)
206 TIFFClose(tif);
207 exit(0);
208 }
209
210 static int
initImage(void)211 initImage(void)
212 {
213 uint32 w, h;
214
215 if (order)
216 TIFFSetField(tif, TIFFTAG_FILLORDER, order);
217 if (photo != (uint16) -1)
218 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photo);
219 if (!TIFFRGBAImageBegin(&img, tif, stoponerr, title)) {
220 TIFFError(filelist[fileindex], "%s", title);
221 TIFFClose(tif);
222 tif = NULL;
223 return -1;
224 }
225
226 /*
227 * Setup the image raster as required.
228 */
229 h = img.height;
230 w = img.width;
231 if (h > ymax) {
232 w = (int)(w * ((float)ymax / h));
233 h = ymax;
234 }
235 if (w > xmax) {
236 h = (int)(h * ((float)xmax / w));
237 w = xmax;
238 }
239
240 if (w != width || h != height) {
241 uint32 rastersize =
242 _TIFFMultiply32(tif, img.width, img.height, "allocating raster buffer");
243 if (raster != NULL)
244 _TIFFfree(raster), raster = NULL;
245 raster = (uint32*) _TIFFCheckMalloc(tif, rastersize, sizeof (uint32),
246 "allocating raster buffer");
247 if (raster == NULL) {
248 width = height = 0;
249 TIFFError(filelist[fileindex], "No space for raster buffer");
250 cleanup_and_exit();
251 }
252 width = w;
253 height = h;
254 }
255 TIFFRGBAImageGet(&img, raster, img.width, img.height);
256 #if HOST_BIGENDIAN
257 TIFFSwabArrayOfLong(raster,img.width*img.height);
258 #endif
259 return 0;
260 }
261
262 static int
prevImage(void)263 prevImage(void)
264 {
265 if (fileindex > 0)
266 fileindex--;
267 else if (tif)
268 return fileindex;
269 if (tif)
270 TIFFClose(tif);
271 tif = TIFFOpen(filelist[fileindex], "r");
272 if (tif == NULL)
273 return -1;
274 return fileindex;
275 }
276
277 static int
nextImage(void)278 nextImage(void)
279 {
280 if (fileindex < filenum - 1)
281 fileindex++;
282 else if (tif)
283 return fileindex;
284 if (tif)
285 TIFFClose(tif);
286 tif = TIFFOpen(filelist[fileindex], "r");
287 if (tif == NULL)
288 return -1;
289 return fileindex;
290 }
291
292 static void
setWindowSize(void)293 setWindowSize(void)
294 {
295 glutReshapeWindow(width, height);
296 }
297
298 static void
raster_draw(void)299 raster_draw(void)
300 {
301 glDrawPixels(img.width, img.height, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid *) raster);
302 glFlush();
303 }
304
305 static void
raster_reshape(int win_w,int win_h)306 raster_reshape(int win_w, int win_h)
307 {
308 GLfloat xratio = (GLfloat)win_w/img.width;
309 GLfloat yratio = (GLfloat)win_h/img.height;
310 int ratio = (int)(((xratio > yratio)?xratio:yratio) * 100);
311
312 glPixelZoom(xratio, yratio);
313 glViewport(0, 0, win_w, win_h);
314 snprintf(title, 1024, "%s [%u] %d%%", filelist[fileindex],
315 (unsigned int) TIFFCurrentDirectory(tif), ratio);
316 glutSetWindowTitle(title);
317 }
318
319 static void
raster_keys(unsigned char key,int x,int y)320 raster_keys(unsigned char key, int x, int y)
321 {
322 (void) x;
323 (void) y;
324 switch (key) {
325 case 'b': /* photometric MinIsBlack */
326 photo = PHOTOMETRIC_MINISBLACK;
327 initImage();
328 break;
329 case 'l': /* lsb-to-msb FillOrder */
330 order = FILLORDER_LSB2MSB;
331 initImage();
332 break;
333 case 'm': /* msb-to-lsb FillOrder */
334 order = FILLORDER_MSB2LSB;
335 initImage();
336 break;
337 case 'w': /* photometric MinIsWhite */
338 photo = PHOTOMETRIC_MINISWHITE;
339 initImage();
340 break;
341 case 'W': /* toggle warnings */
342 owarning = TIFFSetWarningHandler(owarning);
343 initImage();
344 break;
345 case 'E': /* toggle errors */
346 oerror = TIFFSetErrorHandler(oerror);
347 initImage();
348 break;
349 case 'z': /* reset to defaults */
350 case 'Z':
351 order = order0;
352 photo = photo0;
353 if (owarning == NULL)
354 owarning = TIFFSetWarningHandler(NULL);
355 if (oerror == NULL)
356 oerror = TIFFSetErrorHandler(NULL);
357 initImage();
358 break;
359 case 'q': /* exit */
360 case '\033':
361 cleanup_and_exit();
362 }
363 glutPostRedisplay();
364 }
365
366 static void
raster_special(int key,int x,int y)367 raster_special(int key, int x, int y)
368 {
369 (void) x;
370 (void) y;
371 switch (key) {
372 case GLUT_KEY_PAGE_UP: /* previous logical image */
373 if (TIFFCurrentDirectory(tif) > 0) {
374 if (TIFFSetDirectory(tif,
375 TIFFCurrentDirectory(tif)-1)) {
376 initImage();
377 setWindowSize();
378 }
379 } else {
380 TIFFRGBAImageEnd(&img);
381 prevImage();
382 initImage();
383 setWindowSize();
384 }
385 break;
386 case GLUT_KEY_PAGE_DOWN: /* next logical image */
387 if (!TIFFLastDirectory(tif)) {
388 if (TIFFReadDirectory(tif)) {
389 initImage();
390 setWindowSize();
391 }
392 } else {
393 TIFFRGBAImageEnd(&img);
394 nextImage();
395 initImage();
396 setWindowSize();
397 }
398 break;
399 case GLUT_KEY_HOME: /* 1st image in current file */
400 if (TIFFSetDirectory(tif, 0)) {
401 TIFFRGBAImageEnd(&img);
402 initImage();
403 setWindowSize();
404 }
405 break;
406 case GLUT_KEY_END: /* last image in current file */
407 TIFFRGBAImageEnd(&img);
408 while (!TIFFLastDirectory(tif))
409 TIFFReadDirectory(tif);
410 initImage();
411 setWindowSize();
412 break;
413 }
414 glutPostRedisplay();
415 }
416
417 /* GLUT framework on MacOS X produces deprecation warnings */
418 # if defined(__GNUC__) && defined(__APPLE__)
419 # pragma GCC diagnostic pop
420 # endif
421
422 char* stuff[] = {
423 "usage: tiffgt [options] file.tif",
424 "where options are:",
425 " -c use colormap visual",
426 " -d dirnum set initial directory (default is 0)",
427 " -e enable display of TIFF error messages",
428 " -l force lsb-to-msb FillOrder",
429 " -m force msb-to-lsb FillOrder",
430 " -o offset set initial directory offset",
431 " -p photo override photometric interpretation",
432 " -r use fullcolor visual",
433 " -s stop decoding on first error (default is ignore errors)",
434 " -v enable verbose mode",
435 " -w enable display of TIFF warning messages",
436 NULL
437 };
438
439 static void
usage(void)440 usage(void)
441 {
442 char buf[BUFSIZ];
443 int i;
444
445 setbuf(stderr, buf);
446 fprintf(stderr, "%s\n\n", TIFFGetVersion());
447 for (i = 0; stuff[i] != NULL; i++)
448 fprintf(stderr, "%s\n", stuff[i]);
449 exit(-1);
450 }
451
452 static uint16
photoArg(const char * arg)453 photoArg(const char* arg)
454 {
455 if (strcmp(arg, "miniswhite") == 0)
456 return (PHOTOMETRIC_MINISWHITE);
457 else if (strcmp(arg, "minisblack") == 0)
458 return (PHOTOMETRIC_MINISBLACK);
459 else if (strcmp(arg, "rgb") == 0)
460 return (PHOTOMETRIC_RGB);
461 else if (strcmp(arg, "palette") == 0)
462 return (PHOTOMETRIC_PALETTE);
463 else if (strcmp(arg, "mask") == 0)
464 return (PHOTOMETRIC_MASK);
465 else if (strcmp(arg, "separated") == 0)
466 return (PHOTOMETRIC_SEPARATED);
467 else if (strcmp(arg, "ycbcr") == 0)
468 return (PHOTOMETRIC_YCBCR);
469 else if (strcmp(arg, "cielab") == 0)
470 return (PHOTOMETRIC_CIELAB);
471 else if (strcmp(arg, "logl") == 0)
472 return (PHOTOMETRIC_LOGL);
473 else if (strcmp(arg, "logluv") == 0)
474 return (PHOTOMETRIC_LOGLUV);
475 else
476 return ((uint16) -1);
477 }
478
479 /* vim: set ts=8 sts=8 sw=8 noet: */
480 /*
481 * Local Variables:
482 * mode: c
483 * c-basic-offset: 8
484 * fill-column: 78
485 * End:
486 */
487