1 /*-
2  * Copyright (c) 1998 Andrzej Bialecki
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 /*
30  * Small PNG viewer with scripting abilities
31  */
32 
33 #include <stdio.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <signal.h>
37 #include <termios.h>
38 #include <sys/types.h>
39 #include <sys/fbio.h>
40 #include <sys/consio.h>
41 #include <sys/mouse.h>
42 #include <vgl.h>
43 #include <png.h>
44 
45 #define NUMBER	8
46 
47 extern char *optarg;
48 extern int optind;
49 
50 /* Prototypes */
51 int kbd_action(int x, int y, char hotkey);
52 
53 struct action {
54 	int zoom;
55 	int rotate;
56 	int Xshift,Yshift;
57 };
58 
59 struct menu_item {
60 	char *descr;
61 	char hotkey;
62 	int (*func)(int x, int y, char hotkey);
63 };
64 
65 struct menu_item std_menu[]= {
66 	{"q  Quit",'q',kbd_action},
67 	{"n  Next",'n',kbd_action},
68 	{"p  Previous",'p',kbd_action},
69 	{"Z  Zoom in",'Z',kbd_action},
70 	{"z  Zoom out",'z',kbd_action},
71 	{"r  Rotate",'r',kbd_action},
72 	{"R  Refresh",'R',kbd_action},
73 	{"l  Left",'l',kbd_action},
74 	{"h  Right",'h',kbd_action},
75 	{"j  Up",'j',kbd_action},
76 	{"k  Down",'k',kbd_action},
77 	{NULL,0,NULL}
78 };
79 
80 char *progname;
81 VGLBitmap pic,bkg;
82 struct action a;
83 byte pal_red[256];
84 byte pal_green[256];
85 byte pal_blue[256];
86 byte pal_colors;
87 double screen_gamma;
88 int max_screen_colors=15;
89 int quit,changed;
90 char **pres;
91 int nimg=0;
92 int auto_chg=0;
93 int cur_img=0;
94 char act;
95 FILE *log;
96 
97 void
usage()98 usage()
99 {
100 	fprintf(stderr,"\nVGL graphics viewer, 1.0 (c) Andrzej Bialecki.\n");
101 	fprintf(stderr,"\nUsage:\n");
102 	fprintf(stderr,"\t%s [-r n] [-g n.n] filename\n",progname);
103 	fprintf(stderr,"\nwhere:\n");
104 	fprintf(stderr,"\t-r n\tchoose resolution:\n");
105 	fprintf(stderr,"\t\t0 - 640x480x16 (default)\n");
106 	fprintf(stderr,"\t\t1 - 640x200x256\n");
107 	fprintf(stderr,"\t\t2 - 320x240x256\n");
108 	fprintf(stderr,"\t-g n.n\tset screen gamma (1.3 by default)\n");
109 	fprintf(stderr,"\n");
110 }
111 
112 int
pop_up(char * title,int x,int y)113 pop_up(char *title,int x, int y)
114 {
115 	VGLBitmap sav,clr;
116 	int x1,y1,width,height,i,j;
117 	int last_pos,cur_pos,max_item;
118 	char buttons;
119 	char *t;
120 
121 	sav.Type=VGLDisplay->Type;
122 	clr.Type=VGLDisplay->Type;
123 	width=0;
124 	height=0;
125 	max_item=0;
126 	i=0;
127 	while(std_menu[i].descr!=NULL) {
128 		height++;
129 		max_item++;
130 		if(strlen(std_menu[i].descr)>width) width=strlen(std_menu[i].descr);
131 		i++;
132 	}
133 	width=width*8+2;
134 	height=height*9+4+8;
135 	sav.Xsize=width;
136 	sav.Ysize=height;
137 	clr.Xsize=width;
138 	clr.Ysize=height;
139 	sav.Bitmap=(byte *)calloc(width*height,1);
140 	clr.Bitmap=(byte *)calloc(width*height,1);
141 	if(x>(VGLDisplay->Xsize-width)) x1=VGLDisplay->Xsize-width;
142 	else x1=x;
143 	if(y>(VGLDisplay->Ysize-height)) y1=VGLDisplay->Ysize-height;
144 	else y1=y;
145 	VGLMouseMode(VGL_MOUSEHIDE);
146 	VGLBitmapCopy(VGLDisplay,x1,y1,&sav,0,0,width,height);
147 	VGLFilledBox(VGLDisplay,x1,y1,x1+width-1,y1+height-1,pal_colors-1);
148 	VGLBitmapString(VGLDisplay,x1+1,y1+1,title,0,pal_colors-1,0,0);
149 	VGLLine(VGLDisplay,x1,y1+9,x1+width,y1+9,0);
150 	i=0;
151 	while(std_menu[i].descr!=NULL) {
152 		VGLBitmapString(VGLDisplay,x1+1,y1+11+i*9,std_menu[i].descr,0,pal_colors-1,0,0);
153 		i++;
154 	}
155 	last_pos=-1;
156 	VGLMouseMode(VGL_MOUSESHOW);
157 	do {
158 		pause();
159 		VGLMouseStatus(&x,&y,&buttons);
160 		cur_pos=(y-y1-11)/9;
161 		if((cur_pos<0)||(cur_pos>max_item-1)) {
162 			if(last_pos==-1) last_pos=0;
163 			VGLBitmapString(VGLDisplay,x1+1,y1+11+last_pos*9,std_menu[last_pos].descr,0,pal_colors-1,0,0);
164 			last_pos=-1;
165 		} else if(last_pos!=cur_pos) {
166 			if(last_pos==-1) last_pos=0;
167 			VGLBitmapString(VGLDisplay,x1+1,y1+11+last_pos*9,std_menu[last_pos].descr,0,pal_colors-1,0,0);
168 			VGLBitmapString(VGLDisplay,x1+1,y1+11+cur_pos*9,std_menu[cur_pos].descr,pal_colors/2+1,pal_colors-1,0,0);
169 			last_pos=cur_pos;
170 		}
171 	} while (buttons & MOUSE_BUTTON3DOWN);
172 	VGLMouseMode(VGL_MOUSEHIDE);
173 	/* XXX Screws up totally when r==3. Libvgl bug! */
174 	VGLBitmapCopy(&clr,0,0,VGLDisplay,x1,y1,width,height);
175 	VGLBitmapCopy(&sav,0,0,VGLDisplay,x1,y1,width,height);
176 	VGLMouseMode(VGL_MOUSESHOW);
177 	free(sav.Bitmap);
178 	free(clr.Bitmap);
179 	changed++;
180 	if((cur_pos>=0) && (cur_pos<max_item)) {
181 		std_menu[cur_pos].func(x,y,std_menu[cur_pos].hotkey);
182 	}
183 	changed++;
184 	return(0);
185 }
186 
187 void
display(VGLBitmap * pic,byte * red,byte * green,byte * blue,struct action * e)188 display(	VGLBitmap *pic,
189 		byte *red,
190 		byte *green,
191 		byte *blue,
192 		struct action *e)
193 {
194 	VGLBitmap target;
195 	int x,y,i=0,j=0;
196 
197 	VGLMouseMode(VGL_MOUSEHIDE);
198 	VGLRestorePalette();
199 	/* XXX Broken in r!=2. Libvgl bug. */
200 	//VGLClear(VGLDisplay,0);
201 	VGLBitmapCopy(&bkg,0,0,VGLDisplay,0,0,bkg.Xsize,bkg.Ysize);
202 
203 	if(e!=NULL) {
204 		if(e->zoom!=1 || e->rotate) {
205 			target.Bitmap=(byte *)calloc(pic->Xsize*pic->Ysize*e->zoom*e->zoom,1);
206 			if(e->rotate) {
207 				target.Xsize=pic->Ysize*e->zoom;
208 				target.Ysize=pic->Xsize*e->zoom;
209 			} else {
210 				target.Xsize=pic->Xsize*e->zoom;
211 				target.Ysize=pic->Ysize*e->zoom;
212 			}
213 			target.Type=pic->Type;
214 			for(x=0;x<pic->Xsize;x++) {
215 				for(y=0;y<pic->Ysize;y++) {
216 					for(i=0;i<e->zoom;i++) {
217 						for(j=0;j<e->zoom;j++) {
218 							if(e->rotate) {
219 								VGLSetXY(&target,target.Xsize-(e->zoom*y+i),e->zoom*x+j,VGLGetXY(pic,x,y));
220 							} else {
221 								VGLSetXY(&target,e->zoom*x+i,e->zoom*y+j,VGLGetXY(pic,x,y));
222 							}
223 						}
224 					}
225 				}
226 			}
227 		} else {
228 			target.Bitmap=(byte *)calloc(pic->Xsize*pic->Ysize,sizeof(byte));
229 			target.Xsize=pic->Xsize;
230 			target.Ysize=pic->Ysize;
231 			target.Type=pic->Type;
232 			VGLBitmapCopy(pic,0,0,&target,0,0,pic->Xsize,pic->Ysize);
233 		}
234 	} else {
235 		target.Bitmap=(byte *)calloc(pic->Xsize*pic->Ysize,sizeof(byte));
236 		target.Xsize=pic->Xsize;
237 		target.Ysize=pic->Ysize;
238 		target.Type=pic->Type;
239 		VGLBitmapCopy(pic,0,0,&target,0,0,pic->Xsize,pic->Ysize);
240 	}
241 	VGLSetPalette(red, green, blue);
242 	if(e!=NULL) {
243 		VGLBitmapCopy(&target,0,0,VGLDisplay,e->Xshift,e->Yshift,target.Xsize,target.Ysize);
244 	} else {
245 		VGLBitmapCopy(&target,0,0,VGLDisplay,0,0,target.Xsize,target.Ysize);
246 	}
247 	VGLMouseMode(VGL_MOUSESHOW);
248 	free(target.Bitmap);
249 }
250 
251 int
png_load(char * filename)252 png_load(char *filename)
253 {
254 	int i,j,k;
255 	FILE *fd;
256 	u_char header[NUMBER];
257 	png_structp png_ptr;
258 	png_infop info_ptr,end_info;
259 	png_uint_32 width,height;
260 	int bit_depth,color_type,interlace_type;
261 	int compression_type,filter_type;
262 	int channels,rowbytes;
263 	double gamma;
264 	png_colorp palette;
265 	int num_palette;
266 	png_bytep *row_pointers;
267 	char c;
268 	int res=0;
269 
270 	fd=fopen(filename,"rb");
271 
272 	if(fd==NULL) {
273 		VGLEnd();
274 		perror("fopen");
275 		exit(1);
276 	}
277 	fread(header,1,NUMBER,fd);
278 	if(!png_check_sig(header,NUMBER)) {
279 		fprintf(stderr,"Not a PNG file.\n");
280 		return(-1);
281 	}
282 	png_ptr=png_create_read_struct(PNG_LIBPNG_VER_STRING,(void *)NULL,
283 		NULL,NULL);
284 	info_ptr=png_create_info_struct(png_ptr);
285 	end_info=png_create_info_struct(png_ptr);
286 	if(!png_ptr || !info_ptr || !end_info) {
287 		VGLEnd();
288 		fprintf(stderr,"failed to allocate needed structs!\n");
289 		png_destroy_read_struct(&png_ptr,&info_ptr,&end_info);
290 		return(-1);
291 	}
292 	png_set_sig_bytes(png_ptr,NUMBER);
293 	png_init_io(png_ptr,fd);
294 	png_read_info(png_ptr,info_ptr);
295 	png_get_IHDR(png_ptr,info_ptr,&width,&height,&bit_depth,
296 		&color_type,&interlace_type,&compression_type,&filter_type);
297 	png_get_PLTE(png_ptr,info_ptr,&palette,&num_palette);
298 	channels=png_get_channels(png_ptr,info_ptr);
299 	rowbytes=png_get_rowbytes(png_ptr,info_ptr);
300 	if(bit_depth==16)
301 		png_set_strip_16(png_ptr);
302 	if(color_type & PNG_COLOR_MASK_ALPHA)
303 		png_set_strip_alpha(png_ptr);
304 	if(png_get_gAMA(png_ptr,info_ptr,&gamma))
305 		png_set_gamma(png_ptr,screen_gamma,gamma);
306 	else
307 	png_set_gamma(png_ptr,screen_gamma,0.45);
308 	if(res==0) {
309 		/* Dither */
310 		if(color_type & PNG_COLOR_MASK_COLOR) {
311 			if(png_get_valid(png_ptr,info_ptr,PNG_INFO_PLTE)) {
312 				png_uint_16p histogram;
313 				png_get_hIST(png_ptr,info_ptr,&histogram);
314 				png_set_dither(png_ptr,palette,num_palette,max_screen_colors,histogram,0);
315 			} else {
316 				png_color std_color_cube[16]={
317 					{0x00,0x00,0x00},
318 					{0x02,0x02,0x02},
319 					{0x04,0x04,0x04},
320 					{0x06,0x06,0x06},
321 					{0x08,0x08,0x08},
322 					{0x0a,0x0a,0x0a},
323 					{0x0c,0x0c,0x0c},
324 					{0x0e,0x0e,0x0e},
325 					{0x10,0x10,0x10},
326 					{0x12,0x12,0x12},
327 					{0x14,0x14,0x14},
328 					{0x16,0x16,0x16},
329 					{0x18,0x18,0x18},
330 					{0x1a,0x1a,0x1a},
331 					{0x1d,0x1d,0x1d},
332 					{0xff,0xff,0xff},
333 				};
334 				png_set_dither(png_ptr,std_color_cube,max_screen_colors,max_screen_colors,NULL,0);
335 			}
336 		}
337 	}
338 	png_set_packing(png_ptr);
339 	if(png_get_valid(png_ptr,info_ptr,PNG_INFO_sBIT)) {
340 		png_color_8p sig_bit;
341 
342 		png_get_sBIT(png_ptr,info_ptr,&sig_bit);
343 		png_set_shift(png_ptr,sig_bit);
344 	}
345 	png_read_update_info(png_ptr,info_ptr);
346 	png_get_IHDR(png_ptr,info_ptr,&width,&height,&bit_depth,
347 		&color_type,&interlace_type,&compression_type,&filter_type);
348 	png_get_PLTE(png_ptr,info_ptr,&palette,&num_palette);
349 	channels=png_get_channels(png_ptr,info_ptr);
350 	rowbytes=png_get_rowbytes(png_ptr,info_ptr);
351 	row_pointers=malloc(height*sizeof(png_bytep));
352 	for(i=0;i<height;i++) {
353 		row_pointers[i]=malloc(rowbytes);
354 	}
355 	png_read_image(png_ptr,row_pointers);
356 	png_read_end(png_ptr,end_info);
357 	png_destroy_read_struct(&png_ptr,&info_ptr,&end_info);
358 	fclose(fd);
359 	/* Set palette */
360 	if(res) k=2;
361 	else k=2;
362 	for(i=0;i<256;i++) {
363 	 	pal_red[i]=255;
364 	 	pal_green[i]=255;
365 	 	pal_blue[i]=255;
366 	}
367 	for(i=0;i<num_palette;i++) {
368 	 	pal_red[i]=(palette+i)->red>>k;
369 	 	pal_green[i]=(palette+i)->green>>k;
370 	 	pal_blue[i]=(palette+i)->blue>>k;
371 	}
372 	pal_colors=num_palette;
373 	if(pic.Bitmap!=NULL) free(pic.Bitmap);
374 	pic.Bitmap=(byte *)calloc(rowbytes*height,sizeof(byte));
375 	pic.Type=MEMBUF;
376 	pic.Xsize=rowbytes;
377 	pic.Ysize=height;
378 	for(i=0;i<rowbytes;i++) {
379 		for(j=0;j<height;j++) {
380 			VGLSetXY(&pic,
381 			i,j,row_pointers[j][i]);
382 		}
383 	}
384 	a.zoom=1;
385 	a.Xshift=(VGLDisplay->Xsize-pic.Xsize)/2;
386 	a.Yshift=(VGLDisplay->Ysize-pic.Ysize)/2;
387 	a.rotate=0;
388 	return(0);
389 }
390 
391 void
kbd_handler(int sig)392 kbd_handler(int sig)
393 {
394 	u_char buf[10];
395 	int res;
396 
397 	res=read(0,&buf,10);
398 	changed++;
399 	act=buf[res-1];
400 }
401 
402 int
kbd_action(int x,int y,char key)403 kbd_action(int x, int y, char key)
404 {
405 	changed=0;
406 	if(key!='n') auto_chg=0;
407 	switch(key) {
408 	case 'q':
409 		quit=1;
410 		break;
411 	case 'Z':
412 		a.zoom++;
413 		changed++;
414 		break;
415 	case 'z':
416 		a.zoom--;
417 		if(a.zoom<1) a.zoom=1;
418 		changed++;
419 		break;
420 	case 'l':
421 		a.Xshift+=VGLDisplay->Xsize/5;
422 		changed++;
423 		break;
424 	case 'h':
425 		a.Xshift-=VGLDisplay->Xsize/5;
426 		changed++;
427 		break;
428 	case 'k':
429 		a.Yshift+=VGLDisplay->Ysize/5;
430 		changed++;
431 		break;
432 	case 'j':
433 		a.Yshift-=VGLDisplay->Ysize/5;
434 		changed++;
435 		break;
436 	case 'R':
437 		changed++;
438 		break;
439 	case 'r':
440 		if(a.rotate) a.rotate=0;
441 		else a.rotate=1;
442 		changed++;
443 		break;
444 	case '\n':
445 	case 'n':
446 		if(nimg>0) {
447 			if(cur_img<nimg-1) {
448 				cur_img++;
449 			} else {
450 				cur_img=0;
451 			}
452 			png_load(pres[cur_img]);
453 			changed++;
454 		}
455 		break;
456 	case 'p':
457 		if(nimg>0) {
458 			if(cur_img>0) {
459 				cur_img--;
460 			} else {
461 				cur_img=nimg-1;
462 			}
463 			png_load(pres[cur_img]);
464 			changed++;
465 		}
466 		break;
467 	}
468 	act=0;
469 }
470 
471 int
main(int argc,char * argv[])472 main(int argc, char *argv[])
473 {
474 	int i,j,k;
475 	char c;
476 	int res=0;
477 	int x,y;
478 	char buttons;
479 	struct termios t_new,t_old;
480 	FILE *fsc;
481 
482 	char buf[100];
483 
484 	progname=argv[0];
485 	screen_gamma=1.5;
486 #ifdef DEBUG
487 	log=fopen("/png/view.log","w");
488 #endif
489 	while((c=getopt(argc,argv,"r:g:"))!=-1) {
490 		switch(c) {
491 		case 'r':
492 			res=atoi(optarg);
493 			if(res>0) max_screen_colors=256;
494 			break;
495 		case 'g':
496 			screen_gamma=atof(optarg);
497 			break;
498 		case '?':
499 		default:
500 			usage();
501 			exit(0);
502 		}
503 	}
504 	switch(res) {
505 	case 0:
506 		VGLInit(SW_CG640x480);
507 		break;
508 	case 1:
509 		VGLInit(SW_VGA_CG320);
510 		break;
511 	case 2:
512 		VGLInit(SW_VGA_MODEX);
513 		break;
514 	default:
515 		fprintf(stderr,"No such resolution!\n");
516 		usage();
517 		exit(-1);
518 	}
519 #ifdef DEBUG
520 	fprintf(log,"VGL initialised\n");
521 #endif
522 	VGLSavePalette();
523 	if(argc>optind) {
524 		res=png_load(argv[optind]);
525 	} else {
526 		VGLEnd();
527 		usage();
528 		exit(0);
529 	}
530 	if(res) {
531 		/* Hmm... Script? */
532 		fsc=fopen(argv[optind],"r");
533 #ifdef DEBUG
534 		fprintf(log,"Trying script %s\n",argv[optind]);
535 #endif
536 		fgets(buf,99,fsc);
537 		buf[strlen(buf)-1]='\0';
538 		if(strncmp("VIEW SCRIPT",buf,11)!=NULL) {
539 			VGLEnd();
540 			usage();
541 		}
542 		if(strlen(buf)>12) {
543 			auto_chg=atoi(buf+12);
544 		}
545 		fgets(buf,99,fsc);
546 		buf[strlen(buf)-1]='\0';
547 		nimg=atoi(buf);
548 		if(nimg==0) {
549 			VGLEnd();
550 			usage();
551 		}
552 		pres=(char **)calloc(nimg,sizeof(char *));
553 		for(i=0;i<nimg;i++) {
554 			fgets(buf,99,fsc);
555 			buf[strlen(buf)-1]='\0';
556 			pres[i]=strdup(buf);
557 		}
558 		fclose(fsc);
559 		cur_img=0;
560 #ifdef DEBUG
561 		fprintf(log,"Script with %d entries\n",nimg);
562 #endif
563 		png_load(pres[cur_img]);
564 	}
565 	VGLMouseInit(VGL_MOUSEHIDE);
566 	/* Prepare the keyboard */
567 	tcgetattr(0,&t_old);
568 	memcpy(&t_new,&t_old,sizeof(struct termios));
569 	cfmakeraw(&t_new);
570 	tcsetattr(0,TCSAFLUSH,&t_new);
571 	fcntl(0,F_SETFL,O_ASYNC);
572 	/* XXX VGLClear doesn't work.. :-(( Prepare a blank background */
573 	bkg.Bitmap=(byte *)calloc(VGLDisplay->Xsize*VGLDisplay->Ysize,1);
574 	bkg.Xsize=VGLDisplay->Xsize;
575 	bkg.Ysize=VGLDisplay->Ysize;
576 	bkg.Type=VGLDisplay->Type;
577 	signal(SIGIO,kbd_handler);
578 	a.zoom=1;
579 	a.Xshift=(VGLDisplay->Xsize-pic.Xsize)/2;
580 	a.Yshift=(VGLDisplay->Ysize-pic.Ysize)/2;
581 	a.rotate=0;
582 	quit=0;
583 	changed=0;
584 	display(&pic,pal_red,pal_green,pal_blue,&a);
585 	while(!quit) {
586 		if(act) {
587 #ifdef DEBUG
588 			fprintf(log,"kbd_action(%c)\n",act);
589 #endif
590 			kbd_action(x,y,act);
591 		}
592 		if(quit) break;
593 		if(changed) {
594 #ifdef DEBUG
595 			fprintf(log,"changed, redisplaying\n");
596 #endif
597 			display(&pic,pal_red,pal_green,pal_blue,&a);
598 			changed=0;
599 		}
600 		if(auto_chg) {
601 			sleep(auto_chg);
602 			kbd_action(x,y,'n');
603 		} else {
604 			pause();
605 		}
606 		VGLMouseStatus(&x,&y,&buttons);
607 		if(buttons & MOUSE_BUTTON3DOWN) {
608 #ifdef DEBUG
609 			fprintf(log,"pop_up called\n");
610 #endif
611 			pop_up("View",x,y);
612 		}
613 	}
614 	VGLEnd();
615 #ifdef DEBUG
616 	fclose(log);
617 #endif
618 	exit(0);
619 }
620