1 /*
2  * Copyright (c) 2002-2003 Michael David Adams.
3  * All rights reserved.
4  */
5 
6 /* __START_OF_JASPER_LICENSE__
7  *
8  * JasPer License Version 2.0
9  *
10  * Copyright (c) 2001-2006 Michael David Adams
11  * Copyright (c) 1999-2000 Image Power, Inc.
12  * Copyright (c) 1999-2000 The University of British Columbia
13  *
14  * All rights reserved.
15  *
16  * Permission is hereby granted, free of charge, to any person (the
17  * "User") obtaining a copy of this software and associated documentation
18  * files (the "Software"), to deal in the Software without restriction,
19  * including without limitation the rights to use, copy, modify, merge,
20  * publish, distribute, and/or sell copies of the Software, and to permit
21  * persons to whom the Software is furnished to do so, subject to the
22  * following conditions:
23  *
24  * 1.  The above copyright notices and this permission notice (which
25  * includes the disclaimer below) shall be included in all copies or
26  * substantial portions of the Software.
27  *
28  * 2.  The name of a copyright holder shall not be used to endorse or
29  * promote products derived from the Software without specific prior
30  * written permission.
31  *
32  * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS
33  * LICENSE.  NO USE OF THE SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
34  * THIS DISCLAIMER.  THE SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
35  * "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
36  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
37  * PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  IN NO
38  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
39  * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
40  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
41  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
42  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  NO ASSURANCES ARE
43  * PROVIDED BY THE COPYRIGHT HOLDERS THAT THE SOFTWARE DOES NOT INFRINGE
44  * THE PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OF ANY OTHER ENTITY.
45  * EACH COPYRIGHT HOLDER DISCLAIMS ANY LIABILITY TO THE USER FOR CLAIMS
46  * BROUGHT BY ANY OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL
47  * PROPERTY RIGHTS OR OTHERWISE.  AS A CONDITION TO EXERCISING THE RIGHTS
48  * GRANTED HEREUNDER, EACH USER HEREBY ASSUMES SOLE RESPONSIBILITY TO SECURE
49  * ANY OTHER INTELLECTUAL PROPERTY RIGHTS NEEDED, IF ANY.  THE SOFTWARE
50  * IS NOT FAULT-TOLERANT AND IS NOT INTENDED FOR USE IN MISSION-CRITICAL
51  * SYSTEMS, SUCH AS THOSE USED IN THE OPERATION OF NUCLEAR FACILITIES,
52  * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL
53  * SYSTEMS, DIRECT LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH
54  * THE FAILURE OF THE SOFTWARE OR SYSTEM COULD LEAD DIRECTLY TO DEATH,
55  * PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH
56  * RISK ACTIVITIES").  THE COPYRIGHT HOLDERS SPECIFICALLY DISCLAIM ANY
57  * EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.
58  *
59  * __END_OF_JASPER_LICENSE__
60  */
61 
62 /******************************************************************************\
63 * Includes
64 \******************************************************************************/
65 
66 #include <jasper/jasper.h>
67 #include <stdlib.h>
68 #include <math.h>
69 #include <inttypes.h>
70 #if defined(JAS_HAVE_GL_GLUT_H)
71 #include <GL/glut.h>
72 #else
73 #include <glut.h>
74 #endif
75 
76 /******************************************************************************\
77 *
78 \******************************************************************************/
79 
80 #define MAXCMPTS	256
81 #define BIGPANAMOUNT	0.90
82 #define SMALLPANAMOUNT	0.05
83 #define	BIGZOOMAMOUNT	2.0
84 #define	SMALLZOOMAMOUNT	1.41421356237310
85 
86 #define	min(x, y)	(((x) < (y)) ? (x) : (y))
87 #define	max(x, y)	(((x) > (y)) ? (x) : (y))
88 
89 typedef struct {
90 
91 	/* The number of image files to view. */
92 	int numfiles;
93 
94 	/* The names of the image files. */
95 	char **filenames;
96 
97 	/* The title for the window. */
98 	const char *title;
99 
100 	/* The time to wait before advancing to the next image (in ms). */
101 	int tmout;
102 
103 	/* Loop indefinitely over all images. */
104 	int loop;
105 
106 	int verbose;
107 
108 #if defined(JAS_DEFAULT_MAX_MEM_USAGE)
109 	size_t max_mem;
110 #endif
111 
112 } cmdopts_t;
113 
114 typedef struct {
115 
116 	int width;
117 
118 	int height;
119 
120 	GLshort *data;
121 
122 } pixmap_t;
123 
124 typedef struct {
125 
126 	/* The index of the current image file. */
127 	int filenum;
128 
129 	/* The image. */
130 	jas_image_t *image;
131 	jas_image_t *altimage;
132 
133 	float botleftx;
134 	float botlefty;
135 	float toprightx;
136 	float toprighty;
137 
138 	int viewportwidth;
139 	int viewportheight;
140 
141 	/* The image for display. */
142 	pixmap_t vp;
143 
144 	/* The active timer ID. */
145 	int activetmid;
146 
147 	/* The next available timer ID. */
148 	int nexttmid;
149 
150 	int monomode;
151 
152 	unsigned cmptno;
153 
154 } gs_t;
155 
156 /******************************************************************************\
157 *
158 \******************************************************************************/
159 
160 static void displayfunc(void);
161 static void reshapefunc(int w, int h);
162 static void keyboardfunc(unsigned char key, int x, int y);
163 static void specialfunc(int key, int x, int y);
164 static void timerfunc(int value);
165 
166 static void usage(void);
167 static void nextimage(void);
168 static void previmage(void);
169 static void nextcmpt(void);
170 static void prevcmpt(void);
171 static int loadimage(void);
172 static void unloadimage(void);
173 static int jas_image_render2(jas_image_t *image, unsigned cmptno, float vtlx, float vtly,
174   float vsx, float vsy, int vw, int vh, GLshort *vdata);
175 static int jas_image_render(jas_image_t *image, float vtlx, float vtly,
176   float vsx, float vsy, int vw, int vh, GLshort *vdata);
177 
178 static void dumpstate(void);
179 static int pixmap_resize(pixmap_t *p, int w, int h);
180 static void cmdinfo(void);
181 
182 static void cleanupandexit(int);
183 static void init(void);
184 
185 static void zoom(float sx, float sy);
186 static void pan(float dx, float dy);
187 static void panzoom(float dx, float dy, float sx, float sy);
188 static void render(void);
189 
190 /******************************************************************************\
191 *
192 \******************************************************************************/
193 
194 static const jas_opt_t opts[] = {
195 	{'V', "version", 0},
196 	{'v', "v", 0},
197 	{'h', "help", 0},
198 	{'w', "wait", JAS_OPT_HASARG},
199 	{'l', "loop", 0},
200 	{'t', "title", JAS_OPT_HASARG},
201 #if defined(JAS_DEFAULT_MAX_MEM_USAGE)
202 	{'m', "memory-limit", JAS_OPT_HASARG},
203 #endif
204 	{-1, 0, 0}
205 };
206 
207 char *cmdname = 0;
208 cmdopts_t cmdopts;
209 gs_t gs;
210 jas_stream_t *streamin = 0;
211 
212 /******************************************************************************\
213 *
214 \******************************************************************************/
215 
main(int argc,char ** argv)216 int main(int argc, char **argv)
217 {
218 	int c;
219 
220 	init();
221 
222 	/* Determine the base name of this command. */
223 	if ((cmdname = strrchr(argv[0], '/'))) {
224 		++cmdname;
225 	} else {
226 		cmdname = argv[0];
227 	}
228 
229 	/* Initialize the JasPer library. */
230 	if (jas_init()) {
231 		abort();
232 	}
233 
234 	glutInit(&argc, argv);
235 	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
236 	glutCreateWindow(cmdname);
237 	glutReshapeFunc(reshapefunc);
238 	glutDisplayFunc(displayfunc);
239 	glutSpecialFunc(specialfunc);
240 	glutKeyboardFunc(keyboardfunc);
241 
242 	cmdopts.numfiles = 0;
243 	cmdopts.filenames = 0;
244 	cmdopts.title = 0;
245 	cmdopts.tmout = 0;
246 	cmdopts.loop = 0;
247 	cmdopts.verbose = 0;
248 #if defined(JAS_DEFAULT_MAX_MEM_USAGE)
249 	cmdopts.max_mem = JAS_DEFAULT_MAX_MEM_USAGE;
250 #endif
251 
252 	while ((c = jas_getopt(argc, argv, opts)) != EOF) {
253 		switch (c) {
254 		case 'w':
255 			cmdopts.tmout = atof(jas_optarg) * 1000;
256 			break;
257 		case 'l':
258 			cmdopts.loop = 1;
259 			break;
260 		case 't':
261 			cmdopts.title = jas_optarg;
262 			break;
263 		case 'v':
264 			cmdopts.verbose = 1;
265 			break;
266 		case 'm':
267 #if defined(JAS_DEFAULT_MAX_MEM_USAGE)
268 			cmdopts.max_mem = strtoull(jas_optarg, 0, 10);
269 #endif
270 			break;
271 		case 'V':
272 			printf("%s\n", JAS_VERSION);
273 			fprintf(stderr, "libjasper %s\n", jas_getversion());
274 			cleanupandexit(EXIT_SUCCESS);
275 			break;
276 		default:
277 		case 'h':
278 			usage();
279 			break;
280 		}
281 	}
282 
283 	if (jas_optind < argc) {
284 		/* The images are to be read from one or more explicitly named
285 		  files. */
286 		cmdopts.numfiles = argc - jas_optind;
287 		cmdopts.filenames = &argv[jas_optind];
288 	} else {
289 		/* The images are to be read from standard input. */
290 		static char *null = 0;
291 		cmdopts.filenames = &null;
292 		cmdopts.numfiles = 1;
293 	}
294 
295 #if defined(JAS_DEFAULT_MAX_MEM_USAGE)
296 	jas_set_max_mem_usage(cmdopts.max_mem);
297 #endif
298 
299 	streamin = jas_stream_fdopen(0, "rb");
300 
301 	/* Load the next image. */
302 	nextimage();
303 
304 	/* Start the GLUT main event handler loop. */
305 	glutMainLoop();
306 
307 	return EXIT_SUCCESS;
308 }
309 
310 /******************************************************************************\
311 *
312 \******************************************************************************/
313 
cmdinfo()314 static void cmdinfo()
315 {
316 	fprintf(stderr, "JasPer Image Viewer (Version %s).\n",
317 	  JAS_VERSION);
318 	fprintf(stderr, "Copyright (c) 2002-2003 Michael David Adams.\n"
319 	  "All rights reserved.\n");
320 	fprintf(stderr, "%s\n", JAS_NOTES);
321 }
322 
323 static const char *const helpinfo[] = {
324 "The following options are supported:\n",
325 "    --help                  Print this help information and exit.\n",
326 "    --version               Print version information and exit.\n",
327 "    --loop                  Loop indefinitely through images.\n",
328 "    --wait N                Advance to next image after N seconds.\n",
329 0
330 };
331 
usage()332 static void usage()
333 {
334 	const char *s;
335 	int i;
336 	cmdinfo();
337 	fprintf(stderr, "usage: %s [options] [file1 file2 ...]\n", cmdname);
338 	for (i = 0, s = helpinfo[i]; s; ++i, s = helpinfo[i]) {
339 		fprintf(stderr, "%s", s);
340 	}
341 	cleanupandexit(EXIT_FAILURE);
342 }
343 
344 /******************************************************************************\
345 * GLUT Callback Functions
346 \******************************************************************************/
347 
348 /* Display callback function. */
349 
displayfunc()350 static void displayfunc()
351 {
352 
353 	float w;
354 	float h;
355 	int regbotleftx;
356 	int regbotlefty;
357 	int regtoprightx;
358 	int regtoprighty;
359 	int regwidth;
360 	int regheight;
361 	float x;
362 	float y;
363 	float xx;
364 	float yy;
365 
366 	if (cmdopts.verbose) {
367 		fprintf(stderr, "displayfunc()\n");
368 	}
369 
370 	regbotleftx = max(ceil(gs.botleftx), 0);
371 	regbotlefty = max(ceil(gs.botlefty), 0);
372 	regtoprightx = min(gs.vp.width, floor(gs.toprightx));
373 	regtoprighty = min(gs.vp.height, floor(gs.toprighty));
374 	regwidth = regtoprightx - regbotleftx;
375 	regheight = regtoprighty - regbotlefty;
376 	w = gs.toprightx - gs.botleftx;
377 	h = gs.toprighty - gs.botlefty;
378 	x = (regbotleftx - gs.botleftx) / w;
379 	y = (regbotlefty - gs.botlefty) / h;
380 	xx = (regtoprightx - gs.botleftx) / w;
381 	yy = (regtoprighty - gs.botlefty) / h;
382 
383 	assert(regwidth > 0);
384 	assert(regheight > 0);
385 
386 	glClear(GL_COLOR_BUFFER_BIT);
387 	glPixelStorei(GL_UNPACK_ALIGNMENT, sizeof(GLshort));
388 	glPixelStorei(GL_UNPACK_ROW_LENGTH, gs.vp.width);
389 	glPixelStorei(GL_UNPACK_SKIP_PIXELS, regbotleftx);
390 	glPixelStorei(GL_UNPACK_SKIP_ROWS, regbotlefty);
391 	glRasterPos2f(x * gs.viewportwidth, y * gs.viewportheight);
392 	glPixelZoom((xx - x) * ((double) gs.viewportwidth) / regwidth, (yy - y) * ((double) gs.viewportheight) / regheight);
393 	glDrawPixels(regwidth, regheight, GL_RGBA, GL_UNSIGNED_SHORT,
394 	  gs.vp.data);
395 	glFlush();
396 	glutSwapBuffers();
397 
398 }
399 
400 /* Reshape callback function. */
401 
reshapefunc(int w,int h)402 static void reshapefunc(int w, int h)
403 {
404 	if (cmdopts.verbose) {
405 		fprintf(stderr, "reshapefunc(%d, %d)\n", w, h);
406 		dumpstate();
407 	}
408 
409 	glViewport(0, 0, w, h);
410 	glMatrixMode(GL_PROJECTION);
411 	glLoadIdentity();
412 	gluOrtho2D(0, w, 0, h);
413 	glMatrixMode(GL_MODELVIEW);
414 	glLoadIdentity();
415 	glTranslatef(0, 0, 0);
416 	glRasterPos2i(0, 0);
417 
418 	zoom((double) gs.viewportwidth / w, (double) gs.viewportheight / h);
419 	gs.viewportwidth = w;
420 	gs.viewportheight = h;
421 
422 }
423 
424 /* Keyboard callback function. */
425 
keyboardfunc(unsigned char key,int x,int y)426 static void keyboardfunc(unsigned char key, int x, int y)
427 {
428 	if (cmdopts.verbose) {
429 		fprintf(stderr, "keyboardfunc(%d, %d, %d)\n", key, x, y);
430 	}
431 
432 	switch (key) {
433 	case ' ':
434 		nextimage();
435 		break;
436 	case '\b':
437 		previmage();
438 		break;
439 	case '>':
440 		zoom(BIGZOOMAMOUNT, BIGZOOMAMOUNT);
441 		glutPostRedisplay();
442 		break;
443 	case '.':
444 		zoom(SMALLZOOMAMOUNT, SMALLZOOMAMOUNT);
445 		glutPostRedisplay();
446 		break;
447 	case '<':
448 		zoom(1.0 / BIGZOOMAMOUNT, 1.0 / BIGZOOMAMOUNT);
449 		glutPostRedisplay();
450 		break;
451 	case ',':
452 		zoom(1.0 / SMALLZOOMAMOUNT, 1.0 / SMALLZOOMAMOUNT);
453 		glutPostRedisplay();
454 		break;
455 	case 'c':
456 		nextcmpt();
457 		break;
458 	case 'C':
459 		prevcmpt();
460 		break;
461 	case 'h':
462 		fprintf(stderr, "h             help\n");
463 		fprintf(stderr, ">             zoom in (large)\n");
464 		fprintf(stderr, ",             zoom in (small)\n");
465 		fprintf(stderr, "<             zoom out (large)\n");
466 		fprintf(stderr, ".             zoom out (small)\n");
467 		fprintf(stderr, "down arrow    pan down\n");
468 		fprintf(stderr, "up arrow      pan up\n");
469 		fprintf(stderr, "left arrow    pan left\n");
470 		fprintf(stderr, "right arrow   pan right\n");
471 		fprintf(stderr, "space         next image\n");
472 		fprintf(stderr, "backspace     previous image\n");
473 		fprintf(stderr, "q             quit\n");
474 		break;
475 	case 'q':
476 		cleanupandexit(EXIT_SUCCESS);
477 		break;
478 	}
479 }
480 
481 /* Special keyboard callback function. */
482 
specialfunc(int key,int x,int y)483 static void specialfunc(int key, int x, int y)
484 {
485 	if (cmdopts.verbose) {
486 		fprintf(stderr, "specialfunc(%d, %d, %d)\n", key, x, y);
487 	}
488 
489 	switch (key) {
490 	case GLUT_KEY_UP:
491 		{
492 			float panamount;
493 			panamount = (glutGetModifiers() & GLUT_ACTIVE_SHIFT) ?
494 			  BIGPANAMOUNT : SMALLPANAMOUNT;
495 			pan(0.0, panamount * (gs.toprighty - gs.botlefty));
496 			glutPostRedisplay();
497 		}
498 		break;
499 	case GLUT_KEY_DOWN:
500 		{
501 			float panamount;
502 			panamount = (glutGetModifiers() & GLUT_ACTIVE_SHIFT) ?
503 			  BIGPANAMOUNT : SMALLPANAMOUNT;
504 			pan(0.0, -panamount * (gs.toprighty - gs.botlefty));
505 			glutPostRedisplay();
506 		}
507 		break;
508 	case GLUT_KEY_LEFT:
509 		{
510 			float panamount;
511 			panamount = (glutGetModifiers() & GLUT_ACTIVE_SHIFT) ?
512 			  BIGPANAMOUNT : SMALLPANAMOUNT;
513 			pan(-panamount * (gs.toprightx - gs.botleftx), 0.0);
514 			glutPostRedisplay();
515 		}
516 		break;
517 	case GLUT_KEY_RIGHT:
518 		{
519 			float panamount;
520 			panamount = (glutGetModifiers() & GLUT_ACTIVE_SHIFT) ?
521 			  BIGPANAMOUNT : SMALLPANAMOUNT;
522 			pan(panamount * (gs.toprightx - gs.botleftx), 0.0);
523 			glutPostRedisplay();
524 		}
525 		break;
526 	default:
527 		break;
528 	}
529 }
530 
531 /* Timer callback function. */
532 
timerfunc(int value)533 static void timerfunc(int value)
534 {
535 	if (cmdopts.verbose) {
536 		fprintf(stderr, "timerfunc(%d)\n", value);
537 	}
538 	if (value == gs.activetmid) {
539 		nextimage();
540 	}
541 }
542 
543 /******************************************************************************\
544 *
545 \******************************************************************************/
546 
zoom(float sx,float sy)547 static void zoom(float sx, float sy)
548 {
549 	panzoom(0, 0, sx, sy);
550 }
551 
pan(float dx,float dy)552 static void pan(float dx, float dy)
553 {
554 	panzoom(dx, dy, 1.0, 1.0);
555 }
556 
panzoom(float dx,float dy,float sx,float sy)557 static void panzoom(float dx, float dy, float sx, float sy)
558 {
559 	float cx;
560 	float cy;
561 	int reginh;
562 	int reginv;
563 
564 	reginh = (gs.botleftx >= 0 && gs.toprightx <= gs.vp.width);
565 	reginv = (gs.botlefty >= 0 && gs.toprighty <= gs.vp.height);
566 
567 	if (cmdopts.verbose) {
568 		fprintf(stderr, "start of panzoom\n");
569 		dumpstate();
570 		fprintf(stderr, "reginh=%d reginv=%d\n", reginh, reginv);
571 	}
572 
573 	if (dx || dy) {
574 		gs.botleftx += dx;
575 		gs.botlefty += dy;
576 		gs.toprightx += dx;
577 		gs.toprighty += dy;
578 	}
579 
580 	if (sx != 1.0 || sy != 1.0) {
581 		cx = (gs.botleftx + gs.toprightx) / 2.0;
582 		cy = (gs.botlefty + gs.toprighty) / 2.0;
583 		float w = gs.toprightx - gs.botleftx;
584 		float h = gs.toprighty - gs.botlefty;
585 		gs.botleftx = cx - 0.5 * w / sx;
586 		gs.botlefty = cy - 0.5 * h / sy;
587 		gs.toprightx = cx + 0.5 * w / sx;
588 		gs.toprighty = cy + 0.5 * h / sy;
589 	}
590 
591 	if (reginh) {
592 		if (gs.botleftx < 0) {
593 			dx = -gs.botleftx;
594 			gs.botleftx += dx;
595 			gs.toprightx += dx;
596 		} else if (gs.toprightx > gs.vp.width) {
597 			dx = gs.vp.width - gs.toprightx;
598 			gs.botleftx += dx;
599 			gs.toprightx += dx;
600 		}
601 	}
602 	if (gs.botleftx < 0 || gs.toprightx > gs.vp.width) {
603 		float w = gs.toprightx - gs.botleftx;
604 		gs.botleftx = 0.5 * gs.vp.width - 0.5 * w;
605 		gs.toprightx = 0.5 * gs.vp.width + 0.5 * w;
606 	}
607 
608 	if (reginv) {
609 		if (gs.botlefty < 0) {
610 			dy = -gs.botlefty;
611 			gs.botlefty += dy;
612 			gs.toprighty += dy;
613 		} else if (gs.toprighty > gs.vp.height) {
614 			dy = gs.vp.height - gs.toprighty;
615 			gs.botlefty += dy;
616 			gs.toprighty += dy;
617 		}
618 	}
619 	if (gs.botlefty < 0 || gs.toprighty > gs.vp.height) {
620 		float h = gs.toprighty - gs.botlefty;
621 		gs.botlefty = 0.5 * gs.vp.height - 0.5 * h;
622 		gs.toprighty = 0.5 * gs.vp.height + 0.5 * h;
623 	}
624 
625 	if (cmdopts.verbose) {
626 		fprintf(stderr, "end of panzoom\n");
627 		dumpstate();
628 	}
629 }
630 
nextcmpt()631 static void nextcmpt()
632 {
633 	if (gs.monomode) {
634 		if (gs.cmptno == jas_image_numcmpts(gs.image) - 1) {
635 			if (gs.altimage) {
636 				gs.monomode = 0;
637 			} else {
638 				gs.cmptno = 0;
639 			}
640 		} else {
641 			++gs.cmptno;
642 		}
643 	} else {
644 		gs.monomode = 1;
645 		gs.cmptno = 0;
646 	}
647 	render();
648 	glutPostRedisplay();
649 }
650 
prevcmpt()651 static void prevcmpt()
652 {
653 	if (gs.monomode) {
654 		if (!gs.cmptno) {
655 			gs.monomode = 0;
656 		} else {
657 			--gs.cmptno;
658 		}
659 	} else {
660 		gs.monomode = 1;
661 		gs.cmptno = jas_image_numcmpts(gs.image) - 1;
662 	}
663 	render();
664 	glutPostRedisplay();
665 }
666 
nextimage()667 static void nextimage()
668 {
669 	int n;
670 	unloadimage();
671 	for (n = cmdopts.numfiles; n > 0; --n) {
672 		++gs.filenum;
673 		if (gs.filenum >= cmdopts.numfiles) {
674 			if (cmdopts.loop) {
675 				gs.filenum = 0;
676 			} else {
677 				cleanupandexit(EXIT_SUCCESS);
678 			}
679 		}
680 		if (!loadimage()) {
681 			return;
682 		}
683 		fprintf(stderr, "cannot load image\n");
684 	}
685 	cleanupandexit(EXIT_SUCCESS);
686 }
687 
previmage()688 static void previmage()
689 {
690 	int n;
691 	unloadimage();
692 	for (n = cmdopts.numfiles; n > 0; --n) {
693 		--gs.filenum;
694 		if (gs.filenum < 0) {
695 			if (cmdopts.loop) {
696 				gs.filenum = cmdopts.numfiles - 1;
697 			} else {
698 				cleanupandexit(EXIT_SUCCESS);
699 			}
700 		}
701 		if (!loadimage()) {
702 			return;
703 		}
704 	}
705 	cleanupandexit(EXIT_SUCCESS);
706 }
707 
loadimage()708 static int loadimage()
709 {
710 	jas_stream_t *in;
711 	int vh;
712 	int vw;
713 	char *pathname;
714 	jas_cmprof_t *outprof = NULL;
715 
716 	assert(!gs.image);
717 	assert(!gs.altimage);
718 
719 	gs.image = 0;
720 	gs.altimage = 0;
721 
722 	pathname = cmdopts.filenames[gs.filenum];
723 
724 	if (pathname && pathname[0] != '\0') {
725 		if (cmdopts.verbose) {
726 			fprintf(stderr, "opening file %s\n", pathname);
727 		}
728 		/* The input image is to be read from a file. */
729 		if (!(in = jas_stream_fopen(pathname, "rb"))) {
730 			fprintf(stderr, "error: cannot open file %s\n", pathname);
731 			goto error;
732 		}
733 	} else {
734 		/* The input image is to be read from standard input. */
735 		in = streamin;
736 	}
737 
738 	if (cmdopts.verbose) {
739 		fprintf(stderr, "decoding image\n");
740 	}
741 
742 	/* Get the input image data. */
743 	if (!(gs.image = jas_image_decode(in, -1, 0))) {
744 		fprintf(stderr, "error: cannot load image data\n");
745 		goto error;
746 	}
747 
748 	/* Close the input stream. */
749 	if (in != streamin) {
750 		jas_stream_close(in);
751 	}
752 
753 	if (cmdopts.verbose) {
754 		fprintf(stderr, "creating color profile\n");
755 	}
756 
757 	if (!(outprof = jas_cmprof_createfromclrspc(JAS_CLRSPC_SRGB)))
758 		goto error;
759 	if (!(gs.altimage = jas_image_chclrspc(gs.image, outprof, JAS_CMXFORM_INTENT_PER)))
760 		goto error;
761 
762 	jas_cmprof_destroy(outprof);
763 	outprof = NULL;
764 
765 	vw = jas_image_width(gs.image);
766 	vh = jas_image_height(gs.image);
767 
768 	gs.botleftx = jas_image_tlx(gs.image);
769 	gs.botlefty = jas_image_tly(gs.image);
770 	gs.toprightx = jas_image_brx(gs.image);
771 	gs.toprighty = jas_image_bry(gs.image);
772 	if (gs.altimage) {
773 		gs.monomode = 0;
774 	} else {
775 		gs.monomode = 1;
776 		gs.cmptno = 0;
777 	}
778 
779 
780 	if (cmdopts.verbose) {
781 		fprintf(stderr, "num of components %d\n", jas_image_numcmpts(gs.image));
782 		fprintf(stderr, "dimensions %"PRIiFAST32" %"PRIiFAST32"\n",
783 		  jas_image_width(gs.image), jas_image_height(gs.image));
784 	}
785 
786 	gs.viewportwidth = vw;
787 	gs.viewportheight = vh;
788 	pixmap_resize(&gs.vp, vw, vh);
789 	if (cmdopts.verbose) {
790 		fprintf(stderr, "preparing image for viewing\n");
791 	}
792 	render();
793 	if (cmdopts.verbose) {
794 		fprintf(stderr, "done preparing image for viewing\n");
795 	}
796 
797 	if (vw != glutGet(GLUT_WINDOW_WIDTH) ||
798 	  vh != glutGet(GLUT_WINDOW_HEIGHT)) {
799 		glutReshapeWindow(vw, vh);
800 	}
801 	if (cmdopts.title) {
802 		glutSetWindowTitle(cmdopts.title);
803 	} else {
804 		glutSetWindowTitle((pathname && pathname[0] != '\0') ? pathname :
805 		  "stdin");
806 	}
807 	/* If we reshaped the window, GLUT will automatically invoke both
808 	  the reshape and display callback (in this order).  Therefore, we
809 	  only need to explicitly force the display callback to be invoked
810 	  if the window was not reshaped. */
811 	glutPostRedisplay();
812 
813 	if (cmdopts.tmout != 0) {
814 		glutTimerFunc(cmdopts.tmout, timerfunc, gs.nexttmid);
815 		gs.activetmid = gs.nexttmid;
816 		++gs.nexttmid;
817 	}
818 
819 	return 0;
820 
821  error:
822 	if (outprof != NULL)
823 		jas_cmprof_destroy(outprof);
824 	unloadimage();
825 	return -1;
826 }
827 
unloadimage()828 static void unloadimage()
829 {
830 	if (gs.image) {
831 		jas_image_destroy(gs.image);
832 		gs.image = 0;
833 	}
834 	if (gs.altimage) {
835 		jas_image_destroy(gs.altimage);
836 		gs.altimage = 0;
837 	}
838 }
839 
840 /******************************************************************************\
841 *
842 \******************************************************************************/
843 
pixmap_resize(pixmap_t * p,int w,int h)844 static int pixmap_resize(pixmap_t *p, int w, int h)
845 {
846 	GLshort* buf;
847 	if (!(buf = realloc(p->data, w * h * 4 * sizeof(GLshort)))) {
848 		return -1;
849 	}
850 	p->width = w;
851 	p->height = h;
852 	p->data = buf;
853 	return 0;
854 }
855 
dumpstate()856 static void dumpstate()
857 {
858 	printf("blx=%f bly=%f trx=%f try=%f\n", gs.botleftx, gs.botlefty, gs.toprightx, gs.toprighty);
859 }
860 
861 #define	vctocc(i, co, cs, vo, vs) \
862   (((vo) + (i) * (vs) - (co)) / (cs))
863 
jas_image_render(jas_image_t * image,float vtlx,float vtly,float vsx,float vsy,int vw,int vh,GLshort * vdata)864 static int jas_image_render(jas_image_t *image, float vtlx, float vtly,
865   float vsx, float vsy, int vw, int vh, GLshort *vdata)
866 {
867 	int i;
868 	int j;
869 	int k;
870 	int x;
871 	int y;
872 	int v[3];
873 	GLshort *vdatap;
874 	int cmptlut[3];
875 	int width;
876 	int height;
877 	int hs;
878 	int vs;
879 	int tlx;
880 	int tly;
881 
882 	if ((cmptlut[0] = jas_image_getcmptbytype(image,
883 	  JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R))) < 0 ||
884 	  (cmptlut[1] = jas_image_getcmptbytype(image,
885 	  JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G))) < 0 ||
886 	  (cmptlut[2] = jas_image_getcmptbytype(image,
887 	  JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B))) < 0)
888 		goto error;
889 	width = jas_image_cmptwidth(image, cmptlut[0]);
890 	height = jas_image_cmptheight(image, cmptlut[0]);
891 	tlx = jas_image_cmpttlx(image, cmptlut[0]);
892 	tly = jas_image_cmpttly(image, cmptlut[0]);
893 	vs = jas_image_cmptvstep(image, cmptlut[0]);
894 	hs = jas_image_cmpthstep(image, cmptlut[0]);
895 	for (i = 1; i < 3; ++i) {
896 		if (jas_image_cmptwidth(image, cmptlut[i]) != width ||
897 		  jas_image_cmptheight(image, cmptlut[i]) != height)
898 			goto error;
899 	}
900 	for (i = 0; i < vh; ++i) {
901 		vdatap = &vdata[(vh - 1 - i) * (4 * vw)];
902 		for (j = 0; j < vw; ++j) {
903 			x = vctocc(j, tlx, hs, vtlx, vsx);
904 			y = vctocc(i, tly, vs, vtly, vsy);
905 			if (x >= 0 && x < width && y >= 0 && y < height) {
906 				for (k = 0; k < 3; ++k) {
907 					v[k] = jas_image_readcmptsample(image, cmptlut[k], x, y);
908 					v[k] <<= 16 - jas_image_cmptprec(image, cmptlut[k]);
909 					if (v[k] < 0) {
910 						v[k] = 0;
911 					} else if (v[k] > 65535) {
912 						v[k] = 65535;
913 					}
914 				}
915 			} else {
916 				v[0] = 0;
917 				v[1] = 0;
918 				v[2] = 0;
919 			}
920 			*vdatap++ = v[0];
921 			*vdatap++ = v[1];
922 			*vdatap++ = v[2];
923 			*vdatap++ = 0;
924 		}
925 	}
926 	return 0;
927 error:
928 	return -1;
929 }
930 
jas_image_render2(jas_image_t * image,unsigned cmptno,float vtlx,float vtly,float vsx,float vsy,int vw,int vh,GLshort * vdata)931 static int jas_image_render2(jas_image_t *image, unsigned cmptno, float vtlx,
932   float vtly, float vsx, float vsy, int vw, int vh, GLshort *vdata)
933 {
934 	int i;
935 	int j;
936 	int x;
937 	int y;
938 	int v;
939 	GLshort *vdatap;
940 
941 	if (cmptno >= image->numcmpts_) {
942 		fprintf(stderr, "bad parameter\n");
943 		goto error;
944 	}
945 	for (i = 0; i < vh; ++i) {
946 		vdatap = &vdata[(vh - 1 - i) * (4 * vw)];
947 		for (j = 0; j < vw; ++j) {
948 			x = vctocc(j, jas_image_cmpttlx(image, cmptno), jas_image_cmpthstep(image, cmptno), vtlx, vsx);
949 			y = vctocc(i, jas_image_cmpttly(image, cmptno), jas_image_cmptvstep(image, cmptno), vtly, vsy);
950 			v = (x >= 0 && x < jas_image_cmptwidth(image, cmptno) && y >=0 && y < jas_image_cmptheight(image, cmptno)) ? jas_image_readcmptsample(image, cmptno, x, y) : 0;
951 			v <<= 16 - jas_image_cmptprec(image, cmptno);
952 			if (v < 0) {
953 				v = 0;
954 			} else if (v > 65535) {
955 				v = 65535;
956 			}
957 			*vdatap++ = v;
958 			*vdatap++ = v;
959 			*vdatap++ = v;
960 			*vdatap++ = 0;
961 		}
962 	}
963 	return 0;
964 error:
965 	return -1;
966 }
967 
968 
render()969 static void render()
970 {
971 	float vtlx;
972 	float vtly;
973 
974 	vtlx = gs.botleftx;
975 	vtly = gs.toprighty;
976 	if (cmdopts.verbose) {
977 //		fprintf(stderr, "vtlx=%f, vtly=%f, vsx=%f, vsy=%f\n",
978 //		  vtlx, vtly, gs.sx, gs.sy);
979 		/* suppress -Wunused-but-set-variable */
980 		(void)vtlx; (void)vtly;
981 	}
982 
983 	if (gs.monomode) {
984 		if (cmdopts.verbose) {
985 			fprintf(stderr, "component %d\n", gs.cmptno);
986 		}
987 		jas_image_render2(gs.image, gs.cmptno, 0.0, 0.0,
988 		  1.0, 1.0, gs.vp.width, gs.vp.height, gs.vp.data);
989 	} else {
990 		if (cmdopts.verbose) {
991 			fprintf(stderr, "color\n");
992 		}
993 		jas_image_render(gs.altimage, 0.0, 0.0, 1.0, 1.0,
994 		  gs.vp.width, gs.vp.height, gs.vp.data);
995 	}
996 
997 }
998 
999 #if 0
1000 
1001 #define	vctocc(i, co, cs, vo, vs) \
1002   (((vo) + (i) * (vs) - (co)) / (cs))
1003 
1004 static void drawview(jas_image_t *image, float vtlx, float vtly,
1005   float sx, float sy, pixmap_t *p)
1006 {
1007 	int i;
1008 	int j;
1009 	int k;
1010 	int red;
1011 	int grn;
1012 	int blu;
1013 	int lum;
1014 	GLshort *datap;
1015 	int x;
1016 	int y;
1017 	int *cmptlut;
1018 	int numcmpts;
1019 	int v[4];
1020 	int u[4];
1021 	int color;
1022 
1023 	cmptlut = gs.cmptlut;
1024 	switch (jas_image_colorspace(gs.image)) {
1025 	case JAS_IMAGE_CS_RGB:
1026 	case JAS_IMAGE_CS_YCBCR:
1027 		color = 1;
1028 		numcmpts = 3;
1029 		break;
1030 	case JAS_IMAGE_CS_GRAY:
1031 	default:
1032 		numcmpts = 1;
1033 		color = 0;
1034 		break;
1035 	}
1036 
1037 	for (i = 0; i < p->height; ++i) {
1038 		datap = &p->data[(p->height - 1 - i) * (4 * p->width)];
1039 		for (j = 0; j < p->width; ++j) {
1040 			if (!gs.monomode && color) {
1041 				for (k = 0; k < numcmpts; ++k) {
1042 					x = vctocc(j, jas_image_cmpttlx(gs.image, cmptlut[k]), jas_image_cmpthstep(gs.image, cmptlut[k]), vtlx, sx);
1043 					y = vctocc(i, jas_image_cmpttly(gs.image, cmptlut[k]), jas_image_cmptvstep(gs.image, cmptlut[k]), vtly, sy);
1044 					v[k] = (x >= 0 && x < jas_image_cmptwidth(gs.image, cmptlut[k]) && y >=0 && y < jas_image_cmptheight(gs.image, cmptlut[k])) ? jas_matrix_get(gs.cmpts[cmptlut[k]], y, x) : 0;
1045 					v[k] <<= 16 - jas_image_cmptprec(gs.image, cmptlut[k]);
1046 				}
1047 				switch (jas_image_colorspace(gs.image)) {
1048 				case JAS_IMAGE_CS_RGB:
1049 					break;
1050 				case JAS_IMAGE_CS_YCBCR:
1051 					u[0] = (1/1.772) * (v[0] + 1.402 * v[2]);
1052 					u[1] = (1/1.772) * (v[0] - 0.34413 * v[1] - 0.71414 * v[2]);
1053 					u[2] = (1/1.772) * (v[0] + 1.772 * v[1]);
1054 					v[0] = u[0];
1055 					v[1] = u[1];
1056 					v[2] = u[2];
1057 					break;
1058 				}
1059 			} else {
1060 				x = vctocc(j, jas_image_cmpttlx(gs.image, gs.cmptno), jas_image_cmpthstep(gs.image, gs.cmptno), vtlx, sx);
1061 				y = vctocc(i, jas_image_cmpttly(gs.image, gs.cmptno), jas_image_cmptvstep(gs.image, gs.cmptno), vtly, sy);
1062 				v[0] = (x >= 0 && x < jas_image_cmptwidth(gs.image, gs.cmptno) && y >=0 && y < jas_image_cmptheight(gs.image, gs.cmptno)) ? jas_matrix_get(gs.cmpts[gs.cmptno], y, x) : 0;
1063 				v[0] <<= 16 - jas_image_cmptprec(gs.image, gs.cmptno);
1064 				v[1] = v[0];
1065 				v[2] = v[0];
1066 				v[3] = 0;
1067 			}
1068 
1069 for (k = 0; k < 3; ++k) {
1070 	if (v[k] < 0) {
1071 		v[k] = 0;
1072 	} else if (v[k] > 65535) {
1073 		v[k] = 65535;
1074 	}
1075 }
1076 
1077 			*datap++ = v[0];
1078 			*datap++ = v[1];
1079 			*datap++ = v[2];
1080 			*datap++ = 0;
1081 		}
1082 	}
1083 }
1084 
1085 #endif
1086 
cleanupandexit(int status)1087 static void cleanupandexit(int status)
1088 {
1089 	unloadimage();
1090 	exit(status);
1091 }
1092 
init()1093 static void init()
1094 {
1095 	gs.filenum = -1;
1096 	gs.image = 0;
1097 	gs.altimage = 0;
1098 	gs.nexttmid = 0;
1099 	gs.vp.width = 0;
1100 	gs.vp.height = 0;
1101 	gs.vp.data = 0;
1102 	gs.viewportwidth = -1;
1103 	gs.viewportheight = -1;
1104 }
1105