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