ftp.nice.ch/pub/next/graphics/convertors/AnimGIF.N.bs.tar.gz#/AnimGIF/gifmerge.src/gifmerge.c

This is gifmerge.c in view mode; [Download] [Up]

/*
 * gifmerge.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 a gif91(see XAnim docs) file and merges the listed
  * gif files into one gif file. 
  *
  * 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 1.00	23Jul91	Mark Podlipec
  *	creation
  * Rev 1.01	08Jan92	Mark Podlipec
  *     use all colormaps, not just 1st.
  *
  * Rev 1.2    20Dec95  Rene Mueller
  *     command-line input (no longer txtfile needed)
  *
  * Rev 1.3    05Feb96  Rene Mueller (kiwi@iis.ee.ethz.ch)
  *     GIF89a transparency, and "Netscape2.0" application extension (looping)
  * Rev 1.31   14May96  Rene Mueller (kiwi@iis.ee.ethz.ch)
  *     disposal selectable
  * Rev 1.32   16Jul96  Rene Mueller (kiwi@iis.ee.ethz.ch)
  *     logical position per image manipulating
  * Rev 1.33   22Jul96  Rene Mueller (kiwi@iis.ee.ethz.ch)
  *     -notransp and -nopos added
  */

#define DA_REV 1.33

#include "gifmerge.h"

#define MAXVAL  4100            /* maxval of lzw coding size */
#define MAXVALP 4200

int debug_flag = 0;  /* make these options */
int verbose = 0;  /* make these options */
int imagex = 0;
int imagey = 0;
int imagec = 0;

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();

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[256];
int screen_was_last;

int disposal = 2;
int repeats = -1;
int delay = 50;
int transp = -1;
int pos_set = 0;
int xpos = 0, ypos = 0;

void TheEnd()
{
 exit(0);
}

void TheEnd1(p)
char *p;
{
 fprintf(stderr,"%s",p);
 TheEnd();
}

Usage()
{
  fprintf(stderr,"\nUsage:\ngifmerge [-<r>,<g>,<b>] [-<delay>] [-l<loops>] [-d<disp>] *.gif > anim.gif\n\n");
  fprintf(stderr,"   -<r>,<g>,<b>   set transparency, ie -192,192,192, default: no transparency\n");
  fprintf(stderr,"   -notransp      set transparency off (after you used -r,g,b before)\n");
  fprintf(stderr,"   -<delay>       set delay of between images in 1/100th secs, ie -100\n                  default 50 (0.5secs)\n");
  fprintf(stderr,"   -l<loops>      set loop counter, ie -l0 (ever) or -l1000, default no loops\n");
  fprintf(stderr,"   -d<disposal>   set disposal 0 = no action, 1 = no dispose,\n");
  fprintf(stderr,"                               2 = restore background (default)\n");
  fprintf(stderr,"                               3 = restore previous\n");
  fprintf(stderr,"   -pos<x>,<y>    set offset position for image\n");
  fprintf(stderr,"   -nopos         reset offset position (default)\n");
  fprintf(stderr,"\n   or look at http://www.iis.ee.ethz.ch/~kiwi/GIFMerge/\n\n");
  exit(0);
}

main(argc,argv)
int argc;
char *argv[];
{
 int first, i;
 int num_of_files,num_of_frames;

 fprintf(stderr,"=== GIFMerge Rev %2.2f (C) 1991,1992 by Mark Podlipec\n    Improvements by Rene K. Mueller 1996\n",DA_REV);
 
 if (argc < 2) Usage();
 
 screen_was_last = FALSE;

 num_of_files = argc-1;

 /* Read files, save colormap of 1st one only
  */
 first = 1;
 for(i=0; i<num_of_files; i++)
 {
   unsigned int r,g,b;
   if(sscanf(argv[i+1],"-%u,%u,%u",&r,&g,&b)==3) {
      transp = r|(g<<8)|(b<<16);
      fprintf(stderr,"Transparency: %d %d %d\n",r,g,b);
   } else if(!strcmp(argv[i+1],"-notransp"))
      transp = -1, fprintf(stderr,"NoTransparency\n");
   else if(sscanf(argv[i+1],"-%u",&delay)==1)
      fprintf(stderr,"Delay: %d (%.2f secs)\n",delay,delay/100.);
   else if(sscanf(argv[i+1],"-l%d",&repeats)==1)
      fprintf(stderr,"Loops: %d\n",repeats);
   else if(sscanf(argv[i+1],"-d%d",&disposal)==1)
      fprintf(stderr,"DisposalMode: %d\n",disposal);
   else if(sscanf(argv[i+1],"-pos%d,%d",&xpos,&ypos)==2)
      fprintf(stderr,"Position: %d %d\n",xpos,ypos), pos_set = 1;
   else if(!strcmp(argv[i+1],"-nopos"))
      pos_set = 0, fprintf(stderr,"NoPositioning\n");
   else if(argv[i+1][0]=='-')
      Usage();
   else {
     strcpy(gif_file_name,argv[i+1]);
     fprintf(stderr,"Merging %s ...\n",gif_file_name);
     GIF_Read_File(gif_file_name,first);
     first = 0;
   }
 }
 fputc(';',stdout); /* image separator */
 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(fname,first_image)
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,stdout,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 != ',');

 fputc(',',stdout); /* image separator */

 GIF_Image_Header(fp,stdout,first_image);

 /*** Setup ACTION for IMAGE */

 GIF_Decompress(fp,stdout,0);
 fputc(0,stdout);  /* 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 roached 
  */
 if (nextab>=MAXVAL)    
 { 
  fprintf(stderr,"Error: GetNext nextab=%ld\n",nextab);
  fclose(fp);
  TheEnd();
 }
 if (nextab==INCSIZE)   /* go to next table size (and LZW code size ) */
 {
   /* fprintf(stderr,"GetNext INCSIZE was %ld ",nextab); */
   code_size++; INCSIZE=(INCSIZE*2)+1;
   if (code_size>=12) code_size=12;
/*   fprintf(stderr,"<%ld>",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=%ld\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=%ld index=%ld\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=%lx \n",code); 
  fprintf(stderr,"CLEAR=%lx INCSIZE=%lx EOI=%lx code_size=%lx \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 (first_time==TRUE) fputc(temp,fout); */
 }
 if(first_time) 
   fputs("GIF89a",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) 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: %ldx%ldx%ld m=%ld cres=%ld bkgnd=%ld pix=%ld\n",
    gifscrn.width,gifscrn.height,imagec,gifscrn.m,gifscrn.cres,
    gifscrn.bc,gifscrn.pixbits);

 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);
  }
 }
 screen_was_last = TRUE;
 if(gifscrn.m&&(transp>=0||delay>=0)) {
   int ix = 0, max_dist = 3*256;
   if(transp>=0) {
      for(i=0; i<imagec; i++) {
         int dist = 
            abs(gif_cmap[i].cmap.red-(transp&255))+
            abs(gif_cmap[i].cmap.green-((transp>>8)&255))+
            abs(gif_cmap[i].cmap.blue-(transp>>16));
         if(dist<max_dist) 
            ix = i, max_dist = dist;
      } 
      if(max_dist==0)   /* info at http://www.iis.ee.ethz.ch/~kiwi/GIFMerge/gifspecs.txt */   
         ;
/*          fprintf(stderr,"Transparent color matched fully\n"); */
      else
         fprintf(stderr,"Transparent not matched fully, col #%d (%d,%d,%d) used now\n",ix,
            gif_cmap[ix].cmap.red,gif_cmap[ix].cmap.green,gif_cmap[ix].cmap.blue);
   }
   fputc(0x21,fout);
   fputc(0xF9,fout);
   fputc(0x04,fout);
   fputc((transp>=0?0x01:0x00)|(disposal<<2),fout);
   fputc(delay&255,fout);
   fputc((unsigned)delay>>8,fout);
   fputc(ix,fout);
   fputc(0x00,fout);
 }
 if(first_time&&repeats>=0) { /* documentation look at */
   fputc(0x21,fout);          /* http://www.reiworld.com/royalef/gifabout.htm */
   fputc(0xFF,fout);
   fputc(0x0B,fout);
   fputs("NETSCAPE2.0",fout);
   fputc(0x03,fout);
   fputc(0x01,fout);
   fputc(repeats&255,fout);
   fputc((unsigned)repeats>>8,fout);
   fputc(0x00,fout);
 }
}

void GIF_Image_Header(fp,fout,first_time)
FILE *fp,*fout;
int first_time;
{
 int temp,tnum,i,tmp;

 tmp = GIF_Get_Short(fp,fout,0); if(!pos_set) xpos = tmp;
 gifimage.left   = xpos; GIF_Put_Short(fout,xpos);
 tmp = GIF_Get_Short(fp,fout,0); if(!pos_set) ypos = tmp;
 gifimage.top    = ypos; GIF_Put_Short(fout,ypos);
 gifimage.width  = GIF_Get_Short(fp,fout,1);
 gifimage.height = GIF_Get_Short(fp,fout,1);
 temp=fgetc(fp); 

 gifimage.m        = temp & 0x80;
 gifimage.i        = temp & 0x40;
 gifimage.pixbits  = temp & 0x07;

 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: %ldx%ldx%ld m=%ld i=%ld pix=%ld \n",
    imagex,imagey,tnum,gifimage.m,gifimage.i,gifimage.pixbits);

 /* if there is an image cmap, then use it */
 if (gifimage.m)
 {
  for(i=0;i<tnum;i++)
  {
   gif_cmap[i].cmap.red   = temp = fgetc(fp); fputc(temp,fout);
   gif_cmap[i].cmap.green = temp = fgetc(fp); fputc(temp,fout);
   gif_cmap[i].cmap.blue  = temp = fgetc(fp); fputc(temp,fout);
  }
 }  /* else if screen was last not 1st time */
 else if (screen_was_last && (first_time==FALSE))
 {
  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 ));
}


/*
 *
 */
int GIF_Put_Short(fout,data)
FILE *fout; unsigned int data;
{
 fputc((unsigned char)(data&255),fout);      /* lo */
 fputc((unsigned char)((data>>8)&255),fout); /* hi */
}


These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.