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