1 
2 /*
3  * whirlgif.c
4  *
5  * Copyright (C) 1995,1996 by Kevin Kadow (kadokev@msg.net)
6  *
7  * Based on txtmerge.c
8  * Copyright (C) 1990,1991,1992,1993 by Mark Podlipec.
9  * All rights reserved.
10  *
11  * This software may be freely copied, modified and redistributed
12  * without fee provided that this copyright notice is preserved
13  * intact on all copies and modified copies.
14  *
15  * There is no warranty or other guarantee of fitness of this software.
16  * It is provided solely "as is". The author(s) disclaim(s) all
17  * responsibility and liability with respect to this software's usage
18  * or its effect upon hardware or computer systems.
19  *
20  */
21  /*
22   * Description:
23   *
24   * This program reads in a sequence of single-image GIF format files and
25   * outputs a single multi-image GIF file, suitable for use as an animation.
26   *
27   * TODO:
28   *
29   * More options for dealing with the colormap
30   *
31   * Eventually, I'd like to have this program compare the current image
32   * with the previous image and check to see if only a small section of
33   * the screen changed from the previous image. Worth a shot.
34   */
35 
36  /*
37   * Rev 2.00	05Feb96 Kevin Kadow
38   *	transparency, gif comments,
39   * Rev 1.10	29Jan96 Kevin Kadow
40   *	first release of whirlgif
41   *
42   * txtmerge:
43   * Rev 1.00	23Jul91	Mark Podlipec
44   *	creation
45   * Rev 1.01	08Jan92	Mark Podlipec
46   *     use all colormaps, not just 1st.
47   *
48   *
49   */
50 #define DA_REV 1.00
51 
52 #include "whirlgif.h"
53 #include <stdlib.h>
54 #include <string.h>
55 
56 #define MAXVAL  4100            /* maxval of lzw coding size */
57 #define MAXVALP 4200
58 
59 /*
60  * Set some defaults, these can be changed on the command line
61  */
62 unsigned int loop=DEFAULT_LOOP,loopcount=0,
63 	use_colormap=DEFAULT_USE_COLORMAP,
64 	debug_flag=0,
65 	verbose=0;
66 
67 int imagex = 0;
68 int imagey = 0;
69 int imagec = 0;
70 
71 /* global settings for offset, transparency */
72 Global global;
73 
74 GIF_Color gif_cmap[256];
75 
76 ULONG GIF_Get_Code();
77 void GIF_Decompress();
78 void GIF_Get_Next_Entry();
79 void GIF_Add_To_Table();
80 void GIF_Send_Data();
81 void GIF_Clear_Table();
82 void GIF_Screen_Header();
83 void GIF_Image_Header();
84 void GIF_Read_File();
85 void GIF_Comment();
86 void GIF_Loop();
87 void GIF_GCL();
88 void Calc_Trans();
89 void set_offset();
90 
91 GIF_Screen_Hdr gifscrn;
92 GIF_Image_Hdr gifimage;
93 GIF_Table table[MAXVALP];
94 
95 ULONG root_code_size,code_size,CLEAR,EOI,INCSIZE;
96 ULONG nextab;
97 ULONG gif_mask[16] = {1,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,0,0};
98 ULONG gif_ptwo[16] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,0,0};
99 
100 
101 UBYTE gif_buff[MAXVALP];
102 ULONG gif_block_size;
103 int num_bits,bits;
104 
105 int pic_i;
106 char gif_file_name[BIGSTRING];
107 int screen_was_last;
108 
109 void big_Usage(void);
110 int GIF_Get_Short(FILE *fp, FILE *fout, int first_time);
111 
TheEnd()112 void TheEnd()
113 {
114  exit(0);
115 }
116 
TheEnd1(p)117 void TheEnd1(p)
118 char *p;
119 {
120  fprintf(stderr,"%s",p);
121  TheEnd();
122 }
123 
Usage()124 Usage()
125 {
126   fprintf(stderr,"\nUsage: whirlgif [-o outfile] [-loop [count]] [-time #delay]\n");
127   fprintf(stderr,"\t[ -i listfile] file1 [ -time #delay] file2 ...\n");
128   fprintf(stderr,"\nTry whirlgif -help for more information\n") ;
129   exit(0);
130 }
131 
main(argc,argv)132 main(argc,argv)
133 int argc;
134 char *argv[];
135 {
136  FILE * infile, *fout;
137  char temp[BIGSTRING];
138  int ret,i;
139  int count=0;
140 
141  fprintf(stderr,"whirlgif Rev %2.2f (C) 1996 by Kevin Kadow\n",DA_REV);
142  fprintf(stderr,"                  (C) 1991,1992 by Mark Podlipec\n");
143 
144  if (argc < 2) Usage();
145 
146  /* set global values */
147  screen_was_last = FALSE;
148  global.trans.type=TRANS_NONE;
149  global.trans.valid=FALSE;
150  global.time=DEFAULT_TIME;
151  global.left=0;
152  global.top=0;
153 
154 
155  fout=stdout;
156  i = 1;
157  while( i < argc)
158  {
159   char *p;
160   p = argv[i];
161   /*fprintf(stderr,"Option: %s\n",p);*/
162   if ( (p[0] == '-') || (p[0] == '+') )
163   {
164    ++p; /* strip off the - */
165    switch(p[0])
166    {
167     case 'v':	/* Give lots of information */
168 	        verbose++;
169 	        i++;
170 		break;
171 
172     case 'd':	/* Debug mode */
173 		debug_flag++;
174 		i++;
175 		fprintf(stderr,"DEBUG: Debug Level %d\n",debug_flag);
176 		break;
177 
178     case 'l':	/* Enable looping */
179 		loop=TRUE;
180 		i++;
181 		if(*argv[i] !='-') {
182 		  /* a loop count was given */
183 		  loopcount=atoi(argv[i++]);
184 		  if(verbose) fprintf(stderr,"Loop %d times\n",loopcount);
185 		  }
186 		else {
187 		  /* default to infinite loop */
188 		  loopcount=0;
189 		  if(verbose) fputs("Looping enabled\n",stderr);
190 		  }
191 		break;
192 
193     case 'u':	/* Use colormap? true or false */
194 		i++;
195 		if(atoi(argv[i]) || !strcmp("true",argv[i])) use_colormap=1;
196 		else use_colormap=0;
197 		i++;
198 		break;
199 
200     case 't':	/* either time or transparent */
201 		i++;
202 		if(!strcmp("time",p)) {
203 			/* Delay time in 1/100's of a second */
204 			global.time=atoi(argv[i++]);
205 			}
206 		else if(!strncmp("trans",p,4)) Calc_Trans(argv[i++]);
207 		break;
208 
209     case 'o':	/* Output file - send output to a given filename */
210 		i++;
211 		if(!strncmp("off",p,3)) set_offset(argv[i]);
212 		else
213 		/* It must be 'output, so do that */
214 		if(NULL==(fout=fopen(argv[i],"w")))
215 			{
216 			fprintf(stderr,"Cannot open %s for output\n",argv[i]);
217 			exit(1);
218 			}
219 		i++;
220 		break;
221     case 'i':	/* input file - file with a list of images */
222 		i++;
223 		if(NULL != (infile=fopen(argv[i],"r"))) {
224 			while(fgets(gif_file_name,BIGSTRING,infile)) {
225 				strtok(gif_file_name,"\n");
226   				if(!count) GIF_Read_File(fout,gif_file_name,1);
227   				else       GIF_Read_File(fout,gif_file_name,0);
228   				count++;
229 				}
230 			fclose(infile);
231 			}
232 		else fprintf(stderr,"Cannot read list file %s\n",argv[i]);
233 		i++;
234 		break;
235 
236     case 'h':   /* big help, added by RWCox */
237                 big_Usage() ;
238                 exit(0) ;
239                 break ;
240 
241     default:
242 		Usage();
243 		exit(0);
244 		break;
245    }
246    continue;
247   }
248   /* Not an option, must be the name of an input file */
249   if(!count) GIF_Read_File(fout,argv[i],1);
250   else       GIF_Read_File(fout,argv[i],0);
251   count++;
252   i++;
253  }
254  /* We're done with all the options, finish up */
255  if(count >0)
256   {
257   fputc(';',fout); /* image separator */
258   sprintf(temp,"whirlgif %2.2f (C) kadokev@msg.net. %d images",DA_REV,count);
259   GIF_Comment(fout,temp);
260   }
261 
262  fclose(fout);
263  fprintf(stderr,"Processed %d files.\n",count);
264 }
265 
266 
267 /*
268  * Read a GIF file, outputting to fname as we go.
269  * It would be faster to read and write the individual blocks,
270  * but eventually we'd like to optimize based on changes from
271  * previous images(ie only a small section of the image changed.
272  */
273 void
GIF_Read_File(fout,fname,first_image)274 GIF_Read_File(fout,fname,first_image)
275 FILE * fout;
276 char *fname;
277 int first_image;
278 {
279  FILE *fp;
280  int ret,i,exit_flag;
281 
282  if ( (fp=fopen(fname,"r"))==0)
283  {
284   fprintf(stderr,"Can't open %s for reading.\n",fname);
285   TheEnd();
286  }
287 
288  GIF_Screen_Header(fp,fout,first_image);
289 
290  /*** read until  ,  separator */
291  do
292  {
293   i=fgetc(fp);
294   if ( (i<0) && feof(fp))
295   {
296    fclose(fp);
297    TheEnd1("GIF_Read_Header: Unexpected End of File\n");
298   }
299  } while(i != ',');
300 
301  if(first_image)
302   {
303    /* stuff we only do once */
304    if(loop) GIF_Loop(fout,loopcount);
305    }
306  if(global.time||(global.trans.type!=TRANS_NONE && global.trans.valid))
307 	GIF_GCL(fout,global.time);
308 
309  fputc(',',fout); /* image separator */
310 
311  GIF_Image_Header(fp,fout,first_image);
312 
313  /*FOO*/
314 
315  /*** Setup ACTION for IMAGE */
316 
317  GIF_Decompress(fp,fout,0);
318  fputc(0,fout);  /* block count of zero */
319 
320  fclose(fp);
321 }
322 
GIF_Decompress(fp,fout)323 void GIF_Decompress(fp,fout)
324 FILE *fp,*fout;
325 {
326  register ULONG code,old;
327 
328  pic_i = 0;
329  bits=0;
330  num_bits=0;
331  gif_block_size=0;
332     /* starting code size of LZW */
333  root_code_size=(fgetc(fp) & 0xff); fputc(root_code_size,fout);
334  GIF_Clear_Table();                /* clear decoding symbol table */
335 
336  code=GIF_Get_Code(fp,fout);
337 
338  if (code==CLEAR)
339  {
340   GIF_Clear_Table();
341   code=GIF_Get_Code(fp,fout);
342  }
343  /* write code(or what it currently stands for) to file */
344  GIF_Send_Data(code);
345  old=code;
346  code=GIF_Get_Code(fp,fout);
347  do
348  {
349   if (table[code].valid==1)    /* if known code */
350   {
351        /* send it's associated string to file */
352     GIF_Send_Data(code);
353     GIF_Get_Next_Entry(fp);       /* get next table entry (nextab) */
354     GIF_Add_To_Table(old,code,nextab);  /* add old+code to table */
355     old=code;
356   }
357   else      /* code doesn't exist */
358   {
359     GIF_Add_To_Table(old,old,code);   /* add old+old to table */
360     GIF_Send_Data(code);
361     old=code;
362   }
363   code=GIF_Get_Code(fp,fout);
364   if (code==CLEAR)
365   {
366    GIF_Clear_Table();
367    code=GIF_Get_Code(fp,fout);
368    GIF_Send_Data(code);
369    old=code;
370    code=GIF_Get_Code(fp,fout);
371   }
372  } while(code!=EOI);
373 }
374 
GIF_Get_Next_Entry(fp)375 void GIF_Get_Next_Entry(fp)
376 FILE *fp;
377 {
378    /* table walk to empty spot */
379  while(  (table[nextab].valid==1)
380        &&(nextab<MAXVAL)
381       ) nextab++;
382  /*
383   * Ran out of space??!?  Something's roached
384   */
385  if (nextab>=MAXVAL)
386  {
387   fprintf(stderr,"Error: GetNext nextab=%ld\n",(long)nextab);
388   fclose(fp);
389   TheEnd();
390  }
391  if (nextab==INCSIZE)   /* go to next table size (and LZW code size ) */
392  {
393    /* fprintf(stderr,"GetNext INCSIZE was %ld ",nextab); */
394    code_size++; INCSIZE=(INCSIZE*2)+1;
395    if (code_size>=12) code_size=12;
396 /*   fprintf(stderr,"<%ld>",INCSIZE); */
397  }
398 
399 }
400 /*  body is associated string
401     next is code to add to that string to form associated string for
402     index
403 */
404 
GIF_Add_To_Table(body,next,index)405 void GIF_Add_To_Table(body,next,index)
406 register ULONG body,next,index;
407 {
408  if (index>MAXVAL)
409  {
410   fprintf(stderr,"Error index=%ld\n",(long)index);
411  }
412  else
413  {
414   table[index].valid=1;
415   table[index].data=table[next].first;
416   table[index].first=table[body].first;
417   table[index].last=body;
418  }
419 }
420 
GIF_Send_Data(index)421 void GIF_Send_Data(index)
422 register int index;
423 {
424  register int i,j;
425  i=0;
426  do         /* table walk to retrieve string associated with index */
427  {
428   gif_buff[i]=table[index].data;
429   i++;
430   index=table[index].last;
431   if (i>MAXVAL)
432   {
433    fprintf(stderr,"Error: Sending i=%ld index=%ld\n",(long)i,(long)index);
434    TheEnd();
435   }
436  } while(index>=0);
437 
438  /* now invert that string since we retreived it backwards */
439  i--;
440  for(j=i;j>=0;j--)
441  {
442   /*pic[pic_i] = gif_buff[j] | gif_pix_offset;*/
443   pic_i++;
444  }
445 }
446 
447 
448 /*
449  * initialize string table
450  */
GIF_Init_Table()451 void GIF_Init_Table()
452 {
453  register int maxi,i;
454 
455 if (debug_flag) fprintf(stderr,"Initing Table...");
456  maxi=gif_ptwo[root_code_size];
457  for(i=0; i<maxi; i++)
458  {
459   table[i].data=i;
460   table[i].first=i;
461   table[i].valid=1;
462   table[i].last = -1;
463  }
464  CLEAR=maxi;
465  EOI=maxi+1;
466  nextab=maxi+2;
467  INCSIZE = (2*maxi)-1;
468  code_size=root_code_size+1;
469 }
470 
471 
472 /*
473  * clear table
474  */
GIF_Clear_Table()475 void GIF_Clear_Table()
476 {
477  register int i;
478 if (debug_flag) fprintf(stderr,"Clearing Table...\n");
479  for(i=0;i<MAXVAL;i++) table[i].valid=0;
480  GIF_Init_Table();
481 }
482 
483 /*CODE*/
GIF_Get_Code(fp,fout)484 ULONG GIF_Get_Code(fp,fout) /* get code depending of current LZW code size */
485 FILE *fp,*fout;
486 {
487  ULONG code;
488  register int tmp;
489 
490  while(num_bits < code_size)
491  {
492   /**** if at end of a block, start new block */
493   if (gif_block_size==0)
494   {
495    tmp = fgetc(fp);
496    if (tmp >= 0 )
497    {
498     fputc(tmp,fout);
499     gif_block_size=(ULONG)(tmp);
500    }
501    else TheEnd1("EOF in data stream\n");
502   }
503 
504   tmp = fgetc(fp);   gif_block_size--;
505   if (tmp >= 0)
506   {
507    fputc(tmp,fout);
508    bits |= ( ((ULONG)(tmp) & 0xff) << num_bits );
509    num_bits+=8;
510   }
511   else TheEnd1("EOF in data stream\n");
512  }
513 
514  code = bits & gif_mask[code_size];
515  bits >>= code_size;
516  num_bits -= code_size;
517 
518 
519  if (code>MAXVAL)
520  {
521   fprintf(stderr,"\nError! in stream=%lx \n",(unsigned long)code);
522   fprintf(stderr,"CLEAR=%lx INCSIZE=%lx EOI=%lx code_size=%lx \n",
523           (unsigned long)CLEAR,(unsigned long)INCSIZE,(unsigned long)EOI,(unsigned long)code_size);
524   code=EOI;
525  }
526 
527  if (code==INCSIZE)
528  {
529   if (code_size<12)
530   {
531    code_size++; INCSIZE=(INCSIZE*2)+1;
532   }
533   else if (debug_flag) fprintf(stderr,"<13?>");
534  }
535 
536  return(code);
537 }
538 
539 
540 /*
541  * read GIF header
542  */
GIF_Screen_Header(fp,fout,first_time)543 void GIF_Screen_Header(fp,fout,first_time)
544 FILE *fp,*fout;
545 int first_time;
546 {
547  int temp,i;
548 
549  for(i=0;i<6;i++)
550  {
551   temp = fgetc(fp);
552   if(i==4 && temp == '7') temp='9';
553   if (first_time==TRUE) fputc(temp,fout);
554  }
555 
556  gifscrn.width  = GIF_Get_Short(fp,fout,first_time);
557  gifscrn.height = GIF_Get_Short(fp,fout,first_time);
558  temp=fgetc(fp);                 if (first_time==TRUE) fputc(temp,fout);
559  gifscrn.m       =  temp & 0x80;
560  gifscrn.cres    = (temp & 0x70) >> 4;
561  gifscrn.pixbits =  temp & 0x07;
562 
563  gifscrn.bc  = fgetc(fp);
564  if (first_time==TRUE)
565 	{
566 	/* we really should set the background color to the transparent color */
567 	fputc(gifscrn.bc,fout);
568 	}
569 
570  temp=fgetc(fp);                 if (first_time==TRUE) fputc(temp,fout);
571  imagec=gif_ptwo[(1+gifscrn.pixbits)];
572 
573  if (verbose)
574   fprintf(stderr,"Screen: %ldx%ldx%ld m=%ld cres=%ld bkgnd=%ld pix=%ld\n",
575     (long)gifscrn.width,(long)gifscrn.height,(long)imagec,(long)gifscrn.m,(long)gifscrn.cres,
576     (long)gifscrn.bc,(long)gifscrn.pixbits);
577 
578  if(global.trans.type==TRANS_RGB) global.trans.valid=0;
579  if (gifscrn.m)
580  {
581   for(i=0;i<imagec;i++)
582   {
583    gif_cmap[i].cmap.red   = temp = fgetc(fp);
584            if (first_time==TRUE) fputc(temp,fout);
585    gif_cmap[i].cmap.green = temp = fgetc(fp);
586            if (first_time==TRUE) fputc(temp,fout);
587    gif_cmap[i].cmap.blue  = temp = fgetc(fp);
588            if (first_time==TRUE) fputc(temp,fout);
589 
590    if(global.trans.type==TRANS_RGB && !global.trans.valid)
591 	if(global.trans.red==gif_cmap[i].cmap.red &&
592 	global.trans.green==gif_cmap[i].cmap.green &&
593 	global.trans.blue==gif_cmap[i].cmap.blue) {
594 	  if(debug_flag>1) fprintf(stderr," Transparent match at %d\n",i);
595 	    global.trans.map=i;
596 	global.trans.valid=TRUE;
597 	}
598   }
599  }
600  screen_was_last = TRUE;
601 }
602 
GIF_Image_Header(fp,fout,first_time)603 void GIF_Image_Header(fp,fout,first_time)
604 FILE *fp,*fout;
605 int first_time;
606 {
607  int temp,tnum,i,r,g,b;
608 
609  gifimage.left   = GIF_Get_Short(fp,fout,1);
610  if(global.left) gifimage.left+=global.left;
611 
612  gifimage.top    = GIF_Get_Short(fp,fout,1);
613  if(global.top) gifimage.top+=global.top;
614 
615  gifimage.width  = GIF_Get_Short(fp,fout,1);
616  gifimage.height = GIF_Get_Short(fp,fout,1);
617  temp=fgetc(fp);
618 
619 
620 
621  gifimage.i        = temp & 0x40;
622  gifimage.pixbits  = temp & 0x07;
623  gifimage.m        = temp & 0x80;
624 
625  /* this sets the local colormap bit to true */
626  if (screen_was_last && (first_time==FALSE)) temp |= 0x80;
627 
628  temp &= 0xf8;
629  temp |= gifscrn.pixbits;
630  fputc(temp,fout);
631 
632  imagex=gifimage.width;
633  imagey=gifimage.height;
634  tnum=gif_ptwo[(1+gifimage.pixbits)];
635  if (verbose)
636   fprintf(stderr,"Image: %ldx%ldx%ld (%ld,%ld) m=%ld i=%ld pix=%ld \n",
637     (long)imagex,(long)imagey,(long)tnum,(long)gifimage.left,(long)gifimage.top,
638 	(long)gifimage.m,(long)gifimage.i,(long)gifimage.pixbits);
639 
640  /* if there is an image cmap, then use it */
641 
642  if (gifimage.m)
643  {
644 /*  if(debug_flag) fprintf(stderr,"DEBUG:Transferring colormap of %d colors\n"); */
645   for(i=0;i<tnum;i++)
646   {
647    gif_cmap[i].cmap.red   = r = fgetc(fp);
648    gif_cmap[i].cmap.green = g = fgetc(fp);
649    gif_cmap[i].cmap.blue  = b = fgetc(fp);
650    fputc(r,fout);
651    fputc(g,fout);
652    fputc(b,fout);
653   }
654  }  /* else if screen was last not 1st time */
655  else if (screen_was_last && (first_time==FALSE))
656  {
657 /*  if(debug_flag>1) fprintf(stderr,"DEBUG:Writing colormap of %d colors\n"); */
658   for(i=0;i<imagec;i++)
659   {
660    fputc(gif_cmap[i].cmap.red  ,fout);
661    fputc(gif_cmap[i].cmap.green,fout);
662    fputc(gif_cmap[i].cmap.blue ,fout);
663   }
664  }
665  screen_was_last = FALSE;
666 }
667 
668 
669 /*
670  *
671  */
GIF_Get_Short(fp,fout,first_time)672 int GIF_Get_Short(fp,fout,first_time)
673 FILE *fp,*fout;
674 int first_time;
675 {
676  register int temp,tmp1;
677  temp=fgetc(fp);	 if (first_time==TRUE) fputc(temp,fout);
678  tmp1=fgetc(fp);	 if (first_time==TRUE) fputc(tmp1,fout);
679  return(temp|( (tmp1) << 8 ));
680 }
681 
GIF_Comment(fout,string)682 void GIF_Comment(fout,string)
683 FILE *fout;
684 char *string;
685 {
686 if(!string || !strlen(string))
687         {
688         /* Bogus string */
689         if(debug_flag) fprintf(stderr,"GIF_Comment: invalid argument");
690         return;
691         }
692 fputc(0x21,fout);
693 fputc(0xF9,fout);
694 fputs(string,fout);
695 fputc(0,fout);
696 }
697 
698 /*
699  * Write a Netscape loop marker.
700  */
GIF_Loop(fout,repeats)701 void GIF_Loop(fout,repeats)
702 FILE *fout;
703 unsigned int repeats;
704 {
705 UBYTE low=0,high=0;
706 if(repeats) {
707 	/* non-zero repeat count- Netscape hasn't implemented this yet */
708 	high=repeats / 256;
709 	low=repeats % 256;
710 	}
711 
712 fputc(0x21,fout);
713 fputc(0xFF,fout);
714 fputc(0x0B,fout);
715 fputs("NETSCAPE2.0",fout);
716 fputc(0x03,fout);
717 fputc(0x01,fout);
718 fputc(0x00,fout);
719 
720 
721 fputc(low,fout); /* the delay count - 0 for infinite */
722 fputc(high,fout); /* second byte of delay count */
723 
724 if(verbose) fprintf(stderr,"Wrote loop extension\n");
725 }
726 
727 /*
728  * GIF_GCL - add a Control Label to set an inter-frame delay value.
729  */
GIF_GCL(fout,delay)730 void GIF_GCL(fout,delay)
731 FILE * fout;
732 unsigned int delay;
733 {
734 UBYTE low=0,high=0,flag=0;
735 if(delay) {
736 	/* non-zero delay, figure out low/high bytes */
737 	high=delay / 256;
738 	low=delay % 256;
739 	}
740 
741 fputc(0x21,fout);
742 fputc(0xF9,fout);
743 fputc(0x04,fout);
744 
745 if(delay) flag |=0x80;
746 if(global.trans.valid) flag |=0x01;
747 fputc(flag,fout);
748 
749 fputc(low,fout); /* the delay speed - 0 is instantaneous */
750 fputc(high,fout); /* second byte of delay count */
751 
752 fputc(global.trans.map,fout);
753 fputc(0,fout);
754 if(debug_flag>1) {
755   fprintf(stderr,"GCL: delay %d",delay);
756   if(global.trans.valid) fprintf(stderr," Transparent: %d",global.trans.map);
757   fputc('\n',stderr);
758   }
759 }
760 
761 
Calc_Trans(string)762 void Calc_Trans(string)
763 char * string;
764 {
765 if(string[0] != '#') {
766   global.trans.type=TRANS_MAP;
767   global.trans.map=atoi(string);
768   global.trans.valid=1;
769   }
770 else
771   {
772   /* it's an RGB value */
773   int r,g,b;
774   string++;
775   if(3==sscanf(string,"%2x%2x%2x",&r,&g,&b)) {
776 	global.trans.red=r;
777 	global.trans.green=g;
778 	global.trans.blue=b;
779 	global.trans.type=TRANS_RGB;
780 	if(debug_flag) fprintf(stderr,"Transparent RGB=(%x,%x,%x)\n",r,g,b);
781 	}
782   }
783 if(debug_flag) fprintf(stderr,"DEBUG:Calc_trans is %d\n",global.trans.type);
784 }
785 
set_offset(string)786 void set_offset(string)
787 char * string;
788 {
789 char *off_x,*off_y;
790 off_x=(char *) strtok(string,",");
791 off_y=(char *)strtok((char *)NULL,",");
792 if(off_x && off_y) {
793 	/* set the offset */
794 	global.left=atoi(off_x);
795 	global.top=atoi(off_y);
796 	if(debug_flag>1) fprintf(stderr,"Offset changed to %d,%d\n",
797 		global.left,global.top);
798 	return;
799 	}
800 if(debug_flag>1) fprintf(stderr,"Couldn't parse offset values.\n");
801 exit(0);
802 }
803 
big_Usage(void)804 void big_Usage(void)
805 {
806    printf("\n"
807      "whirlgif is a quick program that reads a series of GIF files, and produces\n"
808      "a single gif file composed of those images.\n"
809      "\n"
810      "Usage: whirlgif [-v] [-trans index ] [-time delay] [-o outfile]\n"
811      "                [-loop] [-i incfile] file1 [ -time delay] file2\n"
812      "\n"
813      "options:\n"
814      "   -v              verbose mode\n"
815      "   -loop [count]   add the Netscape 'loop' extension.\n"
816      "   -time delay     inter-frame timing.\n"
817      "   -trans index    set the colormap index 'index' to be transparent\n"
818      "   -o outfile      write the results to 'outfile'\n"
819      "   -i incfile      read a list of names from 'incfile'\n"
820      "\n"
821      "TIPS\n"
822      "\n"
823      "If you don't specify an output file, the GIF will be sent to stdout. This is\n"
824      "a good thing if you're using this in a CGI script, a very bad thing if you\n"
825      "run this from a terminal and forget to redirect stdout.\n"
826      "\n"
827      "The output file (if any) and -loop _MUST_ be specified before any gif images.\n"
828      "\n"
829      "You can specify several delay statements on the command line to change\n"
830      "the delay between images in the middle of an animation, e.g.\n"
831      "\n"
832      "      whirlgif -time 5 a.gif b.gif c.gif -time 100 d.gif -time 5 e.gif f.gif\n"
833      "\n"
834      "Although it's generally considered to be evil, you can also specify\n"
835      "several transparency statements on the command line, to change the transparent\n"
836      "color in the middle of an animation. This may cause problems for some programs.\n"
837      "\n"
838      "\n"
839      "BUGS\n"
840      "  + The loop 'count' is ineffective because Netspcape always loops infinitely.\n"
841      "  + Should be able to specify delay in an 'incfile' list (see next bug).\n"
842      "  + Does not handle filenames starting with a - (hypen), except in 'incfile'.\n"
843      "\n"
844      "This program is (possibly) available from:\n"
845      "  http://web.mit.edu/javalib/working/animated-gifs/whirlgif/whirlgif.newdoc\n"
846      "  https://www.freshports.org/graphics/whirlgif/\n"
847      "-------------------------------------------------------------------\n"
848      "Kevin Kadow     kadokev@msg.net\n"
849      "Based on 'txtmerge' written by:\n"
850      "Mark Podlipec   podlipec@wellfleet.com\n"
851    ) ;
852    exit(0) ;
853 }
854