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 47extern char *optarg; 48extern int optind; 49 50/* Prototypes */ 51int kbd_action(int x, int y, char hotkey); 52 53struct action { 54 int zoom; 55 int rotate; 56 int Xshift,Yshift; 57}; 58 59struct menu_item { 60 char *descr; 61 char hotkey; 62 int (*func)(int x, int y, char hotkey); 63}; 64 65struct 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 80char *progname; 81VGLBitmap pic,bkg; 82struct action a; 83byte pal_red[256]; 84byte pal_green[256]; 85byte pal_blue[256]; 86byte pal_colors; 87double screen_gamma; 88int max_screen_colors=15; 89int quit,changed; 90char **pres; 91int nimg=0; 92int auto_chg=0; 93int cur_img=0; 94char act; 95FILE *log; 96 97void 98usage() 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 112int 113pop_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 187void 188display( 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 251int 252png_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 391void 392kbd_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 402int 403kbd_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 471int 472main(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