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