This is whirlgif.c in view mode; [Download] [Up]
/* * whirlgif.c * * Copyright (C) 1995,1996 by Kevin Kadow (kadokev@msg.net) * * Based on txtmerge.c * Copyright (C) 1990,1991,1992,1993 by Mark Podlipec. * All rights reserved. * * This software may be freely copied, modified and redistributed * without fee provided that this copyright notice is preserved * intact on all copies and modified copies. * * There is no warranty or other guarantee of fitness of this software. * It is provided solely "as is". The author(s) disclaim(s) all * responsibility and liability with respect to this software's usage * or its effect upon hardware or computer systems. * */ /* * Description: * * This program reads in a sequence of single-image GIF format files and * outputs a single multi-image GIF file, suitable for use as an animation. * * TODO: * * More options for dealing with the colormap * * Eventually, I'd like to have this program compare the current image * with the previous image and check to see if only a small section of * the screen changed from the previous image. Worth a shot. */ /* * Rev 2.01 31Aug96 Kevin Kadow * disposal * Rev 2.00 05Feb96 Kevin Kadow * transparency, gif comments, * Rev 1.10 29Jan96 Kevin Kadow * first release of whirlgif * * txtmerge: * Rev 1.00 23Jul91 Mark Podlipec * creation * Rev 1.01 08Jan92 Mark Podlipec * use all colormaps, not just 1st. * * */ #define DA_REV 2.01 #include <stdio.h> #include <stdlib.h> #ifdef _USE_STRINGS_H #include <strings.h> #else #include <string.h> #endif #include "whirlgif.h" #define MAXVAL 4100 /* maxval of lzw coding size */ #define MAXVALP 4200 /* * Set some defaults, these can be changed on the command line */ unsigned int loop=DEFAULT_LOOP,loopcount=0, use_colormap=DEFAULT_USE_COLORMAP, debug_flag=0, verbose=0; int imagex = 0; int imagey = 0; int imagec = 0; /* global settings for offset, transparency */ Global global; GIF_Color gif_cmap[256]; ULONG GIF_Get_Code(); void GIF_Decompress(); void GIF_Get_Next_Entry(); void GIF_Add_To_Table(); void GIF_Send_Data(); void GIF_Clear_Table(); void GIF_Screen_Header(); void GIF_Image_Header(); void GIF_Read_File(); void GIF_Comment(); void GIF_Loop(); void GIF_GCL(); void Calc_Trans(); void set_offset(); GIF_Screen_Hdr gifscrn; GIF_Image_Hdr gifimage; GIF_Table table[MAXVALP]; ULONG root_code_size,code_size,CLEAR,EOI,INCSIZE; ULONG nextab; ULONG gif_mask[16] = {1,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,0,0}; ULONG gif_ptwo[16] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,0,0}; UBYTE gif_buff[MAXVALP]; ULONG gif_block_size; int num_bits,bits; int pic_i; char gif_file_name[BIGSTRING]; int screen_was_last; void TheEnd() { exit(0); } void TheEnd1(p) char *p; { fprintf(stderr,"%s",p); TheEnd(); } Usage() { fprintf(stderr,"\nUsage: whirlgif [-o outfile] [-loop [count]] [-time #delay]\n"); fprintf(stderr,"\t[-disp_none | -disp_back | -disp_prev | -disp_not]\n"); fprintf(stderr,"\t[ -i listfile] file1 [ -time #delay] file2 ...\n"); exit(0); } main(argc,argv) int argc; char *argv[]; { FILE * infile, *fout; char temp[BIGSTRING]; int ret,i; int count=0; fprintf(stderr,"whirlgif Rev %2.2f (C) 1996 by Kevin Kadow\n",DA_REV); fprintf(stderr," (C) 1991,1992 by Mark Podlipec\n"); if (argc < 2) Usage(); /* set global values */ screen_was_last = FALSE; global.trans.type=TRANS_NONE; global.trans.valid=FALSE; global.time=DEFAULT_TIME; global.left=0; global.top=0; global.disposal=DEFAULT_DISPOSAL; fout=stdout; i = 1; while( i < argc) { char *p; p = argv[i]; /*fprintf(stderr,"Option: %s\n",p);*/ if ( (p[0] == '-') || (p[0] == '+') ) { ++p; /* strip off the - */ switch(p[0]) { case 'v': /* Give lots of information */ verbose++; i++; fprintf(stderr,"Verbose output\n"); break; case 'd': /* either Debug mode or disposal setting */ i++; if(!strncmp("disp",p,4)) { i++; p=argv[1]; p++; if(verbose) fprintf(stderr,"Disposal method set\n"); if(!strcmp("none",p)) global.disposal = DISP_NONE; else if(!strcmp("not",p)) global.disposal = DISP_NOT; else if(!strcmp("back",p)) global.disposal = DISP_BACK; else if(!strcmp("prev",p)) global.disposal = DISP_PREV; else global.disposal = DISP_NONE; } else { debug_flag++; fprintf(stderr,"DEBUG: Debug Level %d\n",debug_flag); } break; case 'l': /* Enable looping */ loop=TRUE; i++; if(*argv[i] !='-') { /* a loop count was given */ loopcount=atoi(argv[i++]); if(verbose) fprintf(stderr,"Loop %d times\n",loopcount); } else { /* default to infinite loop */ loopcount=0; if(verbose) fputs("Looping enabled\n",stderr); } break; case 'u': /* Use colormap? true or false */ i++; if(atoi(argv[i]) || !strcmp("true",argv[i])) use_colormap=1; else use_colormap=0; i++; break; case 't': /* either time or transparent */ i++; if(!strcmp("time",p)) { /* Delay time in 1/100's of a second */ global.time=atoi(argv[i++]); } else if(!strncmp("trans",p,4)) Calc_Trans(argv[i++]); break; case 'o': /* Output file - send output to a given filename */ i++; if(!strncmp("off",p,3)) set_offset(argv[i]); else /* It must be 'output, so do that */ if(NULL==(fout=fopen(argv[i],"w"))) { fprintf(stderr,"Cannot open %s for output\n",argv[i]); exit(1); } i++; break; case 'i': /* input file - file with a list of images */ i++; if(NULL != (infile=fopen(argv[i],"r"))) { while(fgets(gif_file_name,BIGSTRING,infile)) { strtok(gif_file_name,"\n"); if(!count) GIF_Read_File(fout,gif_file_name,1); else GIF_Read_File(fout,gif_file_name,0); count++; } fclose(infile); } else fprintf(stderr,"Cannot read list file %s\n",argv[i]); i++; break; default: Usage(); exit(0); break; } continue; } /* Not an option, must be the name of an input file */ if(!count) GIF_Read_File(fout,argv[i],1); else GIF_Read_File(fout,argv[i],0); count++; i++; } /* We're done with all the options, finish up */ if(count >0) { fputc(';',fout); /* image separator */ sprintf(temp,"whirlgif %2.2f (C) kadokev@msg.net. %d images",DA_REV,count); GIF_Comment(fout,temp); } fclose(fout); fprintf(stderr,"Processed %d files.\n",count); exit(0); } /* * Read a GIF file, outputting to fname as we go. * It would be faster to read and write the individual blocks, * but eventually we'd like to optimize based on changes from * previous images(ie only a small section of the image changed. */ void GIF_Read_File(fout,fname,first_image) FILE * fout; char *fname; int first_image; { FILE *fp; int ret,i,exit_flag; if ( (fp=fopen(fname,"r"))==0) { fprintf(stderr,"Can't open %s for reading.\n",fname); TheEnd(); } GIF_Screen_Header(fp,fout,first_image); /*** read until , separator */ do { i=fgetc(fp); if ( (i<0) && feof(fp)) { fclose(fp); TheEnd1("GIF_Read_Header: Unexpected End of File\n"); } } while(i != ','); if(first_image) { /* stuff we only do once */ if(loop) GIF_Loop(fout,loopcount); } if(global.time||(global.trans.type!=TRANS_NONE && global.trans.valid)) GIF_GCL(fout,global.time); fputc(',',fout); /* image separator */ GIF_Image_Header(fp,fout,first_image); /*FOO*/ /*** Setup ACTION for IMAGE */ GIF_Decompress(fp,fout,0); fputc(0,fout); /* block count of zero */ fclose(fp); } void GIF_Decompress(fp,fout) FILE *fp,*fout; { register ULONG code,old; pic_i = 0; bits=0; num_bits=0; gif_block_size=0; /* starting code size of LZW */ root_code_size=(fgetc(fp) & 0xff); fputc(root_code_size,fout); GIF_Clear_Table(); /* clear decoding symbol table */ code=GIF_Get_Code(fp,fout); if (code==CLEAR) { GIF_Clear_Table(); code=GIF_Get_Code(fp,fout); } /* write code(or what it currently stands for) to file */ GIF_Send_Data(code); old=code; code=GIF_Get_Code(fp,fout); do { if (table[code].valid==1) /* if known code */ { /* send it's associated string to file */ GIF_Send_Data(code); GIF_Get_Next_Entry(fp); /* get next table entry (nextab) */ GIF_Add_To_Table(old,code,nextab); /* add old+code to table */ old=code; } else /* code doesn't exist */ { GIF_Add_To_Table(old,old,code); /* add old+old to table */ GIF_Send_Data(code); old=code; } code=GIF_Get_Code(fp,fout); if (code==CLEAR) { GIF_Clear_Table(); code=GIF_Get_Code(fp,fout); GIF_Send_Data(code); old=code; code=GIF_Get_Code(fp,fout); } } while(code!=EOI); } void GIF_Get_Next_Entry(fp) FILE *fp; { /* table walk to empty spot */ while( (table[nextab].valid==1) &&(nextab<MAXVAL) ) nextab++; /* * Ran out of space?! Something's gone sour... */ if (nextab>=MAXVAL) { fprintf(stderr,"Error: GetNext nextab=%d\n",nextab); fclose(fp); TheEnd(); } if (nextab==INCSIZE) /* go to next table size (and LZW code size ) */ { /* fprintf(stderr,"GetNext INCSIZE was %d ",nextab); */ code_size++; INCSIZE=(INCSIZE*2)+1; if (code_size>=12) code_size=12; /* fprintf(stderr,"<%d>",INCSIZE); */ } } /* body is associated string next is code to add to that string to form associated string for index */ void GIF_Add_To_Table(body,next,index) register ULONG body,next,index; { if (index>MAXVAL) { fprintf(stderr,"Error index=%d\n",index); } else { table[index].valid=1; table[index].data=table[next].first; table[index].first=table[body].first; table[index].last=body; } } void GIF_Send_Data(index) register int index; { register int i,j; i=0; do /* table walk to retrieve string associated with index */ { gif_buff[i]=table[index].data; i++; index=table[index].last; if (i>MAXVAL) { fprintf(stderr,"Error: Sending i=%d index=%d\n",i,index); TheEnd(); } } while(index>=0); /* now invert that string since we retreived it backwards */ i--; for(j=i;j>=0;j--) { /*pic[pic_i] = gif_buff[j] | gif_pix_offset;*/ pic_i++; } } /* * initialize string table */ void GIF_Init_Table() { register int maxi,i; if (debug_flag) fprintf(stderr,"Initing Table..."); maxi=gif_ptwo[root_code_size]; for(i=0; i<maxi; i++) { table[i].data=i; table[i].first=i; table[i].valid=1; table[i].last = -1; } CLEAR=maxi; EOI=maxi+1; nextab=maxi+2; INCSIZE = (2*maxi)-1; code_size=root_code_size+1; } /* * clear table */ void GIF_Clear_Table() { register int i; if (debug_flag) fprintf(stderr,"Clearing Table...\n"); for(i=0;i<MAXVAL;i++) table[i].valid=0; GIF_Init_Table(); } /*CODE*/ ULONG GIF_Get_Code(fp,fout) /* get code depending of current LZW code size */ FILE *fp,*fout; { ULONG code; register int tmp; while(num_bits < code_size) { /**** if at end of a block, start new block */ if (gif_block_size==0) { tmp = fgetc(fp); if (tmp >= 0 ) { fputc(tmp,fout); gif_block_size=(ULONG)(tmp); } else TheEnd1("EOF in data stream\n"); } tmp = fgetc(fp); gif_block_size--; if (tmp >= 0) { fputc(tmp,fout); bits |= ( ((ULONG)(tmp) & 0xff) << num_bits ); num_bits+=8; } else TheEnd1("EOF in data stream\n"); } code = bits & gif_mask[code_size]; bits >>= code_size; num_bits -= code_size; if (code>MAXVAL) { fprintf(stderr,"\nError! in stream=%x \n",code); fprintf(stderr,"CLEAR=%x INCSIZE=%x EOI=%x code_size=%x \n", CLEAR,INCSIZE,EOI,code_size); code=EOI; } if (code==INCSIZE) { if (code_size<12) { code_size++; INCSIZE=(INCSIZE*2)+1; } else if (debug_flag) fprintf(stderr,"<13?>"); } return(code); } /* * read GIF header */ void GIF_Screen_Header(fp,fout,first_time) FILE *fp,*fout; int first_time; { int temp,i; for(i=0;i<6;i++) { temp = fgetc(fp); if(i==4 && temp == '7') temp='9'; if (first_time==TRUE) fputc(temp,fout); } gifscrn.width = GIF_Get_Short(fp,fout,first_time); gifscrn.height = GIF_Get_Short(fp,fout,first_time); temp=fgetc(fp); if (first_time==TRUE) fputc(temp,fout); gifscrn.m = temp & 0x80; gifscrn.cres = (temp & 0x70) >> 4; gifscrn.pixbits = temp & 0x07; gifscrn.bc = fgetc(fp); if (first_time==TRUE) { /* we really should set the background color to the transparent color */ fputc(gifscrn.bc,fout); } temp=fgetc(fp); if (first_time==TRUE) fputc(temp,fout); imagec=gif_ptwo[(1+gifscrn.pixbits)]; if (verbose) fprintf(stderr,"Screen: %dx%dx%d m=%d cres=%d bkgnd=%d pix=%d\n", gifscrn.width,gifscrn.height,imagec,gifscrn.m,gifscrn.cres, gifscrn.bc,gifscrn.pixbits); if(global.trans.type==TRANS_RGB) global.trans.valid=0; if (gifscrn.m) { for(i=0;i<imagec;i++) { gif_cmap[i].cmap.red = temp = fgetc(fp); if (first_time==TRUE) fputc(temp,fout); gif_cmap[i].cmap.green = temp = fgetc(fp); if (first_time==TRUE) fputc(temp,fout); gif_cmap[i].cmap.blue = temp = fgetc(fp); if (first_time==TRUE) fputc(temp,fout); if(global.trans.type==TRANS_RGB && !global.trans.valid) if(global.trans.red==gif_cmap[i].cmap.red && global.trans.green==gif_cmap[i].cmap.green && global.trans.blue==gif_cmap[i].cmap.blue) { if(debug_flag>1) fprintf(stderr," Transparent match at %d\n",i); global.trans.map=i; global.trans.valid=TRUE; } } } screen_was_last = TRUE; } void GIF_Image_Header(fp,fout,first_time) FILE *fp,*fout; int first_time; { int temp,tnum,i,r,g,b; gifimage.left = GIF_Get_Short(fp,fout,1); if(global.left) gifimage.left+=global.left; gifimage.top = GIF_Get_Short(fp,fout,1); if(global.top) gifimage.top+=global.top; gifimage.width = GIF_Get_Short(fp,fout,1); gifimage.height = GIF_Get_Short(fp,fout,1); temp=fgetc(fp); gifimage.i = temp & 0x40; gifimage.pixbits = temp & 0x07; gifimage.m = temp & 0x80; /* this sets the local colormap bit to true */ if (screen_was_last && (first_time==FALSE)) temp |= 0x80; temp &= 0xf8; temp |= gifscrn.pixbits; fputc(temp,fout); imagex=gifimage.width; imagey=gifimage.height; tnum=gif_ptwo[(1+gifimage.pixbits)]; if (verbose) fprintf(stderr,"Image: %dx%dx%d (%d,%d) m=%d i=%d pix=%d \n", imagex,imagey,tnum,gifimage.left,gifimage.top, gifimage.m,gifimage.i,gifimage.pixbits); /* if there is an image cmap, then use it */ if (gifimage.m) { if(debug_flag) fprintf(stderr,"DEBUG:Transferring colormap of %d colors\n", imagec); for(i=0;i<tnum;i++) { gif_cmap[i].cmap.red = r = fgetc(fp); gif_cmap[i].cmap.green = g = fgetc(fp); gif_cmap[i].cmap.blue = b = fgetc(fp); fputc(r,fout); fputc(g,fout); fputc(b,fout); } } /* else if screen was last not 1st time */ else if (screen_was_last && (first_time==FALSE)) { if(debug_flag>1) fprintf(stderr,"DEBUG:Writing colormap of %d colors\n", imagec); for(i=0;i<imagec;i++) { fputc(gif_cmap[i].cmap.red ,fout); fputc(gif_cmap[i].cmap.green,fout); fputc(gif_cmap[i].cmap.blue ,fout); } } screen_was_last = FALSE; } /* * */ int GIF_Get_Short(fp,fout,first_time) FILE *fp,*fout; int first_time; { register int temp,tmp1; temp=fgetc(fp); if (first_time==TRUE) fputc(temp,fout); tmp1=fgetc(fp); if (first_time==TRUE) fputc(tmp1,fout); return(temp|( (tmp1) << 8 )); } void GIF_Comment(fout,string) FILE *fout; char *string; { if(!string || !strlen(string)) { /* Bogus string */ if(debug_flag) fprintf(stderr,"GIF_Comment: invalid argument"); return; } fputc(0x21,fout); fputc(0xFE,fout); fputs(string,fout); fputc(0,fout); } /* * Write a Netscape loop marker. */ void GIF_Loop(fout,repeats) FILE *fout; unsigned int repeats; { UBYTE low=0,high=0; if(repeats) { /* non-zero repeat count- Netscape hasn't implemented this yet */ high=repeats / 256; low=repeats % 256; } fputc(0x21,fout); fputc(0xFF,fout); fputc(0x0B,fout); fputs("NETSCAPE2.0",fout); fputc(0x03,fout); fputc(0x01,fout); fputc(low,fout); /* the delay count - 0 for infinite */ fputc(high,fout); /* second byte of delay count */ fputc(0x00,fout); /* terminator */ if(verbose) fprintf(stderr,"Wrote loop extension\n"); } /* * GIF_GCL - add a Control Label to set an inter-frame delay value. */ void GIF_GCL(fout,delay) FILE * fout; unsigned int delay; { UBYTE low=0,high=0,flag; if(delay) { /* non-zero delay, figure out low/high bytes */ high=delay / 256; low=delay % 256; } fputc(0x21,fout); fputc(0xF9,fout); fputc(0x04,fout); flag = global.disposal <<2; if(delay) flag |=0x80; if(global.trans.valid) flag |=0x01; fputc(flag,fout); fputc(low,fout); /* the delay speed - 0 is instantaneous */ fputc(high,fout); /* second byte of delay count */ fputc(global.trans.map,fout); fputc(0,fout); if(debug_flag>1) { fprintf(stderr,"GCL: delay %d",delay); if(global.trans.valid) fprintf(stderr," Transparent: %d",global.trans.map); fputc('\n',stderr); } } void Calc_Trans(string) char * string; { if(string[0] != '#') { global.trans.type=TRANS_MAP; global.trans.map=atoi(string); global.trans.valid=1; } else { /* it's an RGB value */ int r,g,b; string++; if(3==sscanf(string,"%2x%2x%2x",&r,&g,&b)) { global.trans.red=r; global.trans.green=g; global.trans.blue=b; global.trans.type=TRANS_RGB; if(debug_flag) fprintf(stderr,"Transparent RGB=(%x,%x,%x)\n",r,g,b); } } if(debug_flag) fprintf(stderr,"DEBUG:Calc_trans is %d\n",global.trans.type); } void set_offset(string) char * string; { char *off_x,*off_y; off_x=(char *) strtok(string,","); off_y=(char *)strtok((char *)NULL,","); if(off_x && off_y) { /* set the offset */ global.left=atoi(off_x); global.top=atoi(off_y); if(debug_flag>1) fprintf(stderr,"Offset changed to %d,%d\n", global.left,global.top); return; } if(debug_flag>1) fprintf(stderr,"Couldn't parse offset values.\n"); exit(1); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.