Back to index

wims  3.65+svn20090927
whirlgif.c
Go to the documentation of this file.
00001 /*
00002  * whirlgif.c
00003  *
00004  * Copyright (c) 1997,1998,1999 by Hans Dinsen-Hansen (dino@danbbs.dk)
00005  * Copyright (c) 1995,1996 by Kevin Kadow (kadokev@msg.net)
00006  * Based on txtmerge.c
00007  * Copyright (c) 1990,1991,1992,1993 by Mark Podlipec (podlipec@BayNetworks.com).
00008  * All rights reserved.
00009  *
00010  * This software may be freely copied, modified and redistributed
00011  * without fee provided that above copyright notices are preserved
00012  * intact on all copies and modified copies.
00013  *
00014  * There is no warranty or other guarantee of fitness of this software.
00015  * It is provided solely "as is". The author(s) disclaim(s) all
00016  * responsibility and liability with respect to this software's usage
00017  * or its effect upon hardware or computer systems.
00018  *
00019  * The Graphics Interchange format (c) is the Copyright property of
00020  * Compuserve Incorporated.  Gif(sm) is a Service Mark property of
00021  * Compuserve Incorporated.
00022  *
00023  */
00024 /*
00025  * Description:
00026  *
00027  * This program reads in a sequence of single-image Gif format files and
00028  * outputs a single multi-image Gif file, suitable for use as an animation.
00029  *
00030  * TODO:
00031  *
00032  * More options for dealing with the colormap
00033  *
00034  */
00035 
00036 /*
00037  * Rev 3.04    21Feb99 Hans Dinsen-Hansen
00038  * RunLength & Amiga.
00039  * Rev 3.03    02Feb99 Hans Dinsen-Hansen
00040  * Published as a patch.  Better error messages.
00041  * Rev 3.02    01Oct98 Hans Dinsen-Hansen
00042  * Loop. Verbose -> DEBUG. Further minimizing.
00043  * Rev 3.01      Oct98 Hans Dinsen-Hansen
00044  * Never published.  Various experiments with Windows versions.
00045  * Rev 3.00    29jul98 Hans Dinsen-Hansen
00046  * Gif-repacking; unification of color map; only output diff.
00047  * Rev 2.02    09Sep97 Hans Dinsen-Hansen
00048  * Gif89a input; use global colormap whenever possible; background index
00049  * Rev 2.01    31Aug96 Kevin Kadow
00050  * disposal
00051  * Rev 2.00    05Feb96 Kevin Kadow
00052  * transparency, gif comments,
00053  * Rev 1.10    29Jan96 Kevin Kadow
00054  * first release of whirlgif
00055  *
00056  * txtmerge:
00057  * Rev 1.01    08Jan92 Mark Podlipec
00058  * use all colormaps, not just 1st.
00059  * Rev 1.00    23Jul91 Mark Podlipec
00060  * creation
00061  *
00062  *
00063  */
00064 
00065 #include "whirlgif.h"
00066 
00067 /*
00068  * Set some defaults, these can be changed on the command line
00069  */
00070 unsigned int loop=DEFAULT_LOOP, loopcount=0,
00071         useColormap=DEFAULT_USE_COLORMAP, debugFlag=0,
00072         globmap=0, minimize=0;
00073 
00074 int imagex = 0, imagey = 0, imagec = 0, GifBgcolor=0, count=0;
00075 
00076 /* global settings for offset, transparency */
00077 
00078 Global global;
00079 
00080 GifColor gifGmap[256], gifCmap[256];
00081 GifScreenHdr globscrn, gifscrn;
00082 
00083 GifImageHdr gifimage, gifimageold;
00084 
00085 extern ULONG gifMask[];
00086 extern int picI;
00087 UBYTE *pixold=NULL;
00088 ULONG gifMask[16]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,0,0}, obits;
00089 ULONG gifPtwo[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,0,0};
00090 
00091 char gifFileName[BIGSTRING];
00092 FILE *ff;
00093 
00094 long sq(UBYTE i,UBYTE j)
00095 {
00096   return((i-j)*(i-j));
00097 }
00098 
00099 void main(argc, argv)
00100  int            argc;
00101  char           *argv[];
00102 {
00103   FILE * infile, *fout;
00104   char temp[BIGSTRING], *cmt;
00105   int i;
00106 
00107   fprintf(stderr, "whirlgif Rev %2.2f (c) 1997-1999 by %s\n%s\n%s\n",
00108               DA_REV,"Hans Dinsen-Hansen",
00109   "                  (c) 1995-1996 by Kevin Kadow",
00110   "                  (c) 1990-1993 by Mark Podlipec");
00111   cmt = temp;
00112   /* if there is no comment option, let cmt point at the final message */
00113 
00114   if (argc < 2) Usage();
00115 
00116   /* set global values */
00117   global.trans.type = TRANS_NONE;
00118   global.trans.valid = 0;
00119   global.time = DEFAULT_TIME;
00120   global.left = 0;
00121   global.top = 0;
00122   global.disposal = DEFAULT_DISPOSAL;
00123 
00124   fout = stdout;
00125   i = 1;
00126   while( i < argc) {
00127     char *p;
00128     p = argv[i];
00129     if (debugFlag > 1) fprintf(stderr, "Option: %s\n", p);
00130     if ( (p[0] == '-') || (p[0] == '+') ) {
00131       ++p; /* strip off the - */
00132       switch(p[0]) {
00133        case 'v': /* Give lots of information */
00134          debugFlag++;
00135          i++;
00136          fprintf(stderr, "Verbose output; debug level: %d\n", debugFlag);
00137          break;
00138        case 'g': /* Use the global colormap throughout */
00139          globmap++;
00140          i++;
00141          if ( debugFlag > 1) fprintf(stderr, "globmap\n");
00142          break;
00143        case 'm': /* minimize output */
00144          minimize++;
00145          globmap++;
00146          i++;
00147          if ( debugFlag > 1) fprintf(stderr, "minimize \n");
00148          break;
00149        case 'd': /* disposal setting */
00150          i++;
00151          p = argv[i++];
00152          if(!strcmp("not",  p)) global.disposal = DISP_NOT;
00153          else if(!strcmp("back", p)) global.disposal = DISP_BACK;
00154          else if(!strcmp("prev", p)) global.disposal = DISP_PREV;
00155          else if(!strcmp("none", p)) global.disposal = DISP_NONE;
00156          else global.disposal = DEFAULT_DISPOSAL;
00157          if(debugFlag) fprintf(stderr, "Disposal method set to %s = %d\n",
00158                     p, global.disposal);
00159          break;
00160        case 'D': /* Debug setting */
00161          i++;
00162          debugFlag = 2;
00163          fprintf(stderr, "DEBUG: Debug Level %d\n", debugFlag);
00164          break;
00165        case 'c': /* set comment pointer */
00166          i++;
00167          cmt = argv[i++];
00168          if(debugFlag) fprintf(stderr, "Comment: '%s'\n", cmt);
00169          break;
00170        case 'b': /* set Background color index */
00171          i++;
00172          GifBgcolor = atoi(argv[i++]) | 0x100;
00173          if (debugFlag) fprintf(stderr, "BACKGROUND = %d\n", GifBgcolor & 0xff);
00174          break;
00175        case 'l': /* Enable looping */
00176          loop = TRUE;
00177          i++;
00178          if(*argv[i] != '-') {
00179            /* a loop count was perhaps given */
00180            loopcount = atoi(argv[i]);
00181            if(debugFlag) {
00182              fprintf(stderr, loopcount != 0 ? "Loop %d times\n"
00183                      : "Loop forever, count = %d\n", loopcount);
00184            }
00185            if( (loopcount > 0) | ((loopcount == 0) & (*argv[i] == '0'))) i++;
00186          }
00187          else {
00188            /* default to infinite loop */
00189            loopcount = 0;
00190            if(debugFlag) fprintf(stderr, "Looping enabled\n");
00191          }
00192          break;
00193        case 't': /* either time or transparent */
00194          i++;
00195          if(!strncmp("time", p, 2)) {
00196            /* Delay time in 1/100's of a second */
00197            global.time = atoi(argv[i++]);
00198          }
00199          else if(!strncmp("trans", p, 2)) CalcTrans(argv[i++]);
00200          break;
00201        case 'o': /* Output file - send output to a given filename */
00202          i++;
00203          if(!strncmp("off", p, 2)) SetOffset(argv[i]);
00204          else if(NULL == (fout = fopen(argv[i], WRIBIN))) {
00205            /* It must be 'output, so we do that */
00206            fprintf(stderr, "Cannot open %s for output\n", argv[i]);
00207            exit(1);
00208          }
00209          i++;
00210          break;
00211        case 'i': /* input file - file with a list of images */
00212          i++;
00213          if(NULL != (infile = fopen(argv[i], REATXT))) {
00214            while(fgets(gifFileName, BIGSTRING, infile)) {
00215              strtok(gifFileName, "\n");
00216              GifReadFile(fout, gifFileName, count++ == 0);
00217            }
00218            fclose(infile);
00219            global.left = global.top = 0;
00220          }
00221          else fprintf(stderr, "Cannot read list file %s\n", argv[i]);
00222          i++;
00223          break;
00224        default:
00225          Usage();
00226          exit(0);
00227          break;
00228       }
00229       continue;
00230     }
00231     /* Not an option, must be the name of an input file */
00232     GifReadFile(fout, argv[i], count++ == 0);
00233     global.left = global.top = 0;
00234     i++;
00235   }
00236  /* We're done with all options and file names, finish up */
00237   if(count >0) {
00238     sprintf(temp, "whirlgif %2.2f (c) %s\r\n%d %s",
00239            DA_REV, "dino@danbbs.dk", count,
00240            count == 1 ? "image" : "images");
00241     /* Either output above std. mess. or a possible user defined comment */
00242     GifComment(fout, cmt);
00243    }
00244    fputc(';', fout); /* End of Gif file */
00245 
00246    fclose(fout);
00247    fprintf(stderr, "Processed %d files.\n", count);
00248    exit(0);
00249 }
00250 
00251 
00252 /*
00253  * Read a Gif file.
00254  */
00255 void GifReadFile(FILE *fout, char *fname, int firstImage)
00256 {
00257   FILE *fp;
00258   UBYTE *pix;
00259   int i, gifBlockSize;
00260   if ( (fp = fopen(fname, REABIN)) == 0) {
00261     fprintf(stderr, "Can't open %s for reading.\n", fname);
00262     TheEnd();
00263   }
00264 
00265   GifScreenHeader(fp, fout, firstImage);
00266 
00267    /* read until , separator */
00268   do {
00269     switch ( i = Xgetc(fp)) {
00270       case ',':
00271       case '\0':
00272        break;
00273       case '!':
00274        Xgetc(fp); /* the extension code */
00275        for ( i = Xgetc(fp); i > 0; i-- ) Xgetc(fp);
00276        while ( ( i = Xgetc(fp) ) > 0 ) {
00277          for ( i = i ; i > 0; i-- ) Xgetc(fp);
00278        }
00279        break;
00280       default:
00281        fclose(fp);
00282        if ( feof(fp) || i == ';' )
00283        TheEnd1("GifReadHeader: Unexpected End of File\n");
00284        TheEnd1("GifReadHeader: Unknown block type\n");
00285      }
00286    } while(i != ',');
00287 
00288   if(firstImage) {
00289     globscrn.m = gifscrn.m;
00290     globscrn.pixbits = gifscrn.pixbits;
00291     globscrn.bc = gifscrn.bc;
00292     if ( globscrn.m ) {
00293       for (i = gifMask[1+globscrn.pixbits]; i >= 0; i--) {
00294        gifGmap[i].cmap.red   = gifCmap[i].cmap.red;
00295        gifGmap[i].cmap.green = gifCmap[i].cmap.green;
00296        gifGmap[i].cmap.blue  = gifCmap[i].cmap.blue;
00297       }
00298     }
00299     if(loop) GifLoop(fout, loopcount);
00300   }
00301 
00302   ReadImageHeader(fp);
00303 
00304  /*** ACTION for IMAGE */
00305 
00306   if ( ( gifimage.m != 0 && globmap !=0 ) || minimize !=0 ) {
00307     UBYTE translator[256], *p, *po;
00308     int left, right, top, bot, i, j, k, l, hi, wi;
00309     long dsquare, dsquare1;
00310     hi = gifimage.height;
00311     wi = gifimage.width;
00312     if (( pix = (UBYTE *)malloc(wi * hi * sizeof(UBYTE)) ) == NULL )
00313         TheEnd1("No memory for image\n");
00314     if (debugFlag) fprintf(stderr, "  decoding picture no %d\n", count);
00315     GifDecode(fp, pix, gifimage);
00316     gifimage.i = 0;
00317     k = gifMask[1+globscrn.pixbits]; 
00318     l = gifMask[1+gifscrn.pixbits]; 
00319     for (j = 0; j <= l; j++) {
00320       dsquare = 256*256*3;
00321       for (i = 0; i <= k; i++) {
00322        dsquare1 = sq(gifGmap[i].cmap.red, gifCmap[j].cmap.red) +
00323                  sq(gifGmap[i].cmap.green, gifCmap[j].cmap.green) +
00324                  sq(gifGmap[i].cmap.blue,  gifCmap[j].cmap.blue);
00325        if ( dsquare1 < dsquare ) {
00326          dsquare = dsquare1;
00327          translator[j]=i;
00328          if ( dsquare == 0 ) break;
00329        }
00330       }
00331     }
00332     gifimage.m = 0;
00333     gifscrn.pixbits = globscrn.pixbits;
00334     if (debugFlag) fprintf(stderr, "  translating picture no %d\n", count);
00335     for (i = wi * hi -1; i>=0; i--)
00336       pix[i]=translator[pix[i]];
00337     if ( minimize != 0 && pixold != NULL  && hi == gifimageold.height
00338        && wi == gifimageold.width && gifimage.top == gifimageold.top
00339        && gifimage.left == gifimageold.left ) {
00340       gifimageold = gifimage;
00341 /* First test from left to right, top to bottom */
00342       p = pix; po = pixold;
00343       for (i = 0; i < hi; i++ ) {
00344        for (j = 0; j < wi; j++ ) {
00345          if ( *p++ != *po++ ) {
00346            left = j; top=i;
00347            goto done;
00348          }
00349        }
00350       }
00351       if (FALSE) { 
00352 done: /* i.e. a preliminary left and top found */ ;
00353       }
00354       else goto alike;
00355 /* Then test from right to left, bottom to top */
00356       k=hi*wi-1; 
00357       p = &pix[k]; po = &pixold[k];
00358       for (i = hi-1; i >= top; i-- ) {
00359        for (j = wi -1; j >= 0; j-- ) {
00360          if ( *p-- != *po-- ) {
00361            right = j; bot=i;
00362            goto botfound;
00363          }
00364        }
00365       }
00366 botfound:
00367 /* The form of the differing area (not rectangle) may be slanted */
00368       if ( right < left ) {
00369        i = right; right = left; left = i;
00370       }
00371 /* Now test between top and bottom at the left hand side */
00372       for (i = top+1; i <= bot; i++ ) {
00373        k= i * wi;
00374        p = &pix[k]; po = &pixold[k];
00375        for (j = 0; j < left; j++ ) {
00376          if ( *p++ != *po++ ) {
00377            left = j;
00378            break;
00379          }
00380        }
00381       }
00382 /* Finally test between bottom and top at the right hand side */
00383       for (i = bot-1; i >= top; i-- ) {
00384        k= (i+1) * wi-1;
00385        p = &pix[k]; po = &pixold[k];
00386        for (j = wi-1; j > right; j-- ) {
00387          if ( *p-- != *po-- ) {
00388            right = j;
00389            break;
00390          }
00391        }
00392       }
00393       gifimage.left += left;
00394       gifimage.top += top;
00395       gifimage.width = right-left+1;
00396       gifimage.height = bot-top+1;
00397       WriteImageHeader(fout);
00398 /* The rectangle containing diffs is transferred to the mem area of pixold */
00399       po = pixold;
00400       for (i = top; i <= bot; i++ ) {
00401        p = &pix[i * wi+left];
00402        for (j = left; j <= right; j++ ) {
00403          *po++ = *p++;
00404        }
00405       }
00406       GifEncode(fout, pixold, gifscrn.pixbits+1, gifimage.height * gifimage.width);
00407       if (debugFlag)
00408        fprintf(stderr, "  encoded: width= %d, height = %d, left = %d, top = %d\n",
00409           gifimage.width, gifimage.height, gifimage.left, gifimage.top);
00410     }
00411     else {
00412 alike:
00413       WriteImageHeader(fout);
00414       gifimageold = gifimage;
00415       GifEncode(fout, pix, gifscrn.pixbits+1, gifimage.height * gifimage.width);
00416       if (debugFlag) fprintf(stderr, "  picture re-encoded\n");
00417 /*   Undocumented feature:  If two subsequent images are alike, then
00418      send the whole image to the output stream (to keep the timing
00419      between frames, and not confuse the viewer with empty images) */
00420     }
00421     free(pixold);
00422     pixold = pix;
00423     fputc(0, fout);    /* block count of zero */
00424   }
00425   else {
00426     WriteImageHeader(fout);
00427     i = Xgetc(fp); fputc(i, fout); /* the LZW code size */
00428     while ( ( gifBlockSize = Xgetc(fp) ) > 0 ) {
00429       fputc(gifBlockSize, fout);
00430       while ( gifBlockSize-- > 0 ) fputc(Xgetc(fp),fout);
00431     }
00432     if ( gifBlockSize == 0 ) fputc(gifBlockSize, fout);
00433     else TheEnd1("GifPassing: Unexpected End of File\n");
00434   }
00435 
00436   fclose(fp);
00437 }
00438 
00439 
00440 
00441 /*
00442  * read Gif header
00443  */
00444 void GifScreenHeader(FILE *fp, FILE *fout, int firstTime)
00445 {
00446   int temp, i;
00447 
00448   for(i = 0; i < 6; i++) {
00449     temp = Xgetc(fp);
00450     if(i == 4 && temp == '7') temp = '9';
00451     if (firstTime) fputc(temp, fout);
00452   }
00453 
00454   gifscrn.width = GifGetShort(fp);
00455   gifscrn.height = GifGetShort(fp);
00456   temp = Xgetc(fp);
00457   if (firstTime) {
00458     GifPutShort(gifscrn.width, fout);
00459     GifPutShort(gifscrn.height, fout);
00460     fputc(temp, fout);
00461   }
00462   gifscrn.m  =  temp & 0x80;
00463   gifscrn.cres   = (temp & 0x70) >> 4;
00464   gifscrn.pixbits =  temp & 0x07;
00465 
00466   gifscrn.bc  = Xgetc(fp);
00467   if (firstTime) {
00468     if (debugFlag) fprintf(stderr, "First Time ... ");
00469     if(GifBgcolor) gifscrn.bc = GifBgcolor & 0xff;
00470     fputc(gifscrn.bc, fout);
00471   }
00472 
00473   temp = Xgetc(fp);
00474   if (firstTime)  {
00475     fputc(temp, fout);
00476     if ( minimize && gifscrn.bc == 0 ) {
00477       /* Set a pseudo screen filled with the background color.
00478         This is only done for background color index == 0 because
00479         of Netscape and I.E.'s strange handling of backgrounds not
00480         covered by an image.
00481       */
00482       temp = gifscrn.width * gifscrn.height;
00483       if (( pixold = (UBYTE *)malloc(temp * sizeof(UBYTE)) ) == NULL )
00484            TheEnd1("No memory for image\n");
00485       if (debugFlag) fprintf(stderr, "BACKGROUND = %d\n", gifscrn.bc);
00486       while (temp > 0) pixold[--temp] = 0; /* gifscrn.bc; */
00487       gifimageold.left = gifimageold.top = 0;
00488       gifimageold.width = gifscrn.width;
00489       gifimageold.height = gifscrn.height;
00490       gifimageold.pixbits = gifscrn.pixbits;
00491     }
00492   }
00493   imagec = gifPtwo[(1+gifscrn.pixbits)];
00494 
00495   if (debugFlag)
00496     fprintf(stderr, "Screen #%d: %dx%dx%d m=%d cres=%d bkgnd=%d pix=%d\n",
00497       count, gifscrn.width, gifscrn.height, imagec, gifscrn.m, gifscrn.cres,
00498       gifscrn.bc, gifscrn.pixbits);
00499 
00500   if (gifscrn.m) {
00501     for(i = 0; i < imagec; i++) {
00502       gifCmap[i].cmap.red   = temp = Xgetc(fp);
00503       if (firstTime) fputc(temp, fout);
00504       gifCmap[i].cmap.green = temp = Xgetc(fp);
00505       if (firstTime) fputc(temp, fout);
00506       gifCmap[i].cmap.blue  = temp = Xgetc(fp);
00507       if (firstTime) fputc(temp, fout);
00508 
00509     if(firstTime && (global.trans.type==TRANS_RGB && global.trans.valid==0) )
00510       if (global.trans.red == gifCmap[i].cmap.red &&
00511          global.trans.green == gifCmap[i].cmap.green &&
00512          global.trans.blue == gifCmap[i].cmap.blue) {
00513        if(debugFlag > 1) fprintf(stderr, " Transparent match at %d\n", i);
00514        global.trans.map = i;
00515        global.trans.valid = 1;
00516       }
00517       else
00518        if(debugFlag > 1) fprintf(stderr, "No transp. RGB=(%x,%x,%x)\n",
00519         gifCmap[i].cmap.red, gifCmap[i].cmap.green, gifCmap[i].cmap.blue);
00520     }
00521   }
00522 }
00523 
00524 void ReadImageHeader(FILE *fp)
00525 {
00526   int tnum, i, flag;
00527 
00528   gifimage.left  = GifGetShort(fp);
00529   if(global.left) gifimage.left += global.left;
00530 
00531   gifimage.top   = GifGetShort(fp);
00532   if(global.top) gifimage.top += global.top;
00533 
00534   gifimage.width     = GifGetShort(fp);
00535   gifimage.height = GifGetShort(fp);
00536   flag = Xgetc(fp);
00537 
00538   gifimage.i       = flag & 0x40;
00539   gifimage.pixbits = flag & 0x07;
00540   gifimage.m       = flag & 0x80;
00541 
00542   imagex = gifimage.width;
00543   imagey = gifimage.height;
00544   tnum = gifPtwo[(1+gifimage.pixbits)];
00545   if (debugFlag > 1)
00546     fprintf(stderr, "Image: %dx%dx%d (%d,%d) m=%d i=%d pix=%d \n",
00547       imagex, imagey, tnum, gifimage.left, gifimage.top,
00548       gifimage.m, gifimage.i, gifimage.pixbits);
00549 
00550    /* if there is an image cmap then read it */
00551   if (gifimage.m) {
00552     if(debugFlag>1)
00553       fprintf(stderr, "DEBUG:Transferring colormap of %d colors\n",
00554        imagec);
00555   /*
00556    * note below assignment, it may make the subsequent code confusing
00557    */
00558     gifscrn.pixbits = gifimage.pixbits;
00559 
00560     for(i = 0; i < tnum; i++) {
00561       gifCmap[i].cmap.red   = Xgetc(fp);
00562       gifCmap[i].cmap.green = Xgetc(fp);
00563       gifCmap[i].cmap.blue  = Xgetc(fp);
00564     }
00565   }
00566   gifimage.m = 0;
00567   if ( globscrn.m && globscrn.pixbits == gifscrn.pixbits ) {
00568     for (i = gifMask[1+globscrn.pixbits]; i >= 0; i--) {
00569       if (gifGmap[i].cmap.red  != gifCmap[i].cmap.red ||
00570       gifGmap[i].cmap.green != gifCmap[i].cmap.green ||
00571       gifGmap[i].cmap.blue != gifCmap[i].cmap.blue ) {
00572        gifimage.m = 0x80;
00573        break;
00574       }
00575     }
00576   }
00577   else gifimage.m = 0x80;
00578   return;
00579 }
00580 
00581 void WriteImageHeader(FILE *fout)
00582 {
00583   int temp, i, flag;
00584   /* compute a Gif_GCL */
00585 
00586   fputc(0x21, fout);
00587   fputc(0xF9, fout);
00588   fputc(0x04, fout);
00589 
00590   flag = global.disposal <<2;
00591   if(global.time) flag |= 0x80;
00592   if(global.trans.type == TRANS_RGB && global.trans.valid == 0)
00593        gifimage.m = 0x80;
00594 
00595   temp = global.trans.map;
00596   if (gifimage.m != 0 && global.trans.type == TRANS_RGB ) {
00597     temp = 0; /* set a default value, in case nothing found */
00598     for (i = gifMask[1+gifscrn.pixbits]; i >= 0; i--) {
00599       if(global.trans.red == gifCmap[i].cmap.red &&
00600       global.trans.green == gifCmap[i].cmap.green &&
00601       global.trans.blue == gifCmap[i].cmap.blue) {
00602        if(debugFlag > 1) fprintf(stderr, " Transparent match at %d\n", i);
00603        temp = i;
00604        flag |= 0x01;
00605       }
00606     }
00607   }
00608   else if(global.trans.valid) flag |= 0x01;
00609   fputc(flag, fout);
00610 
00611   GifPutShort(global.time, fout); /* the delay speed - 0 is instantaneous */
00612 
00613   fputc(temp, fout); /* the transparency index */
00614   if(debugFlag > 1) {
00615     fprintf(stderr, "GCL: delay %d", global.time);
00616     if(flag && 0x1) fprintf(stderr, " Transparent: %d", temp);
00617     fputc('\n', stderr);
00618   }
00619 
00620   fputc(0, fout);
00621   /* end of Gif_GCL */
00622 
00623   fputc(',', fout); /* image separator */
00624   GifPutShort(gifimage.left  , fout);
00625   GifPutShort(gifimage.top   , fout);
00626   GifPutShort(gifimage.width , fout);
00627   GifPutShort(gifimage.height, fout);
00628   fputc(gifscrn.pixbits | gifimage.i | gifimage.m, fout);
00629 
00630   if ( gifimage.m ) {
00631     for(i = 0; i < imagec; i++) {
00632       fputc(gifCmap[i].cmap.red,   fout);
00633       fputc(gifCmap[i].cmap.green, fout);
00634       fputc(gifCmap[i].cmap.blue,  fout);
00635     }
00636     if(debugFlag) fprintf(stderr, "Local %d color map for picture #%d\n",
00637                         imagec, count);
00638   }
00639 }
00640 
00641 
00642 void GifComment(FILE *fout, char *string)
00643 {
00644   int len;
00645 
00646   if( (len = strlen(string)) > 254 ) fprintf(stderr,
00647                "GifComment: String too long ; dropped\n");
00648   else if ( len > 0 ) {
00649     /* Undocumented feature:
00650        Empty comment string means no comment block in outfile */
00651     fputc(0x21, fout);
00652     fputc(0xFE, fout);
00653     fputc(len, fout);
00654     fputs(string, fout);
00655     fputc(0, fout);
00656   }
00657   return;
00658 }
00659 
00660 /*
00661  * Write a Netscape loop marker.
00662  */
00663 void GifLoop(FILE *fout, unsigned int repeats)
00664 {
00665 
00666   fputc(0x21, fout);
00667   fputc(0xFF, fout);
00668   fputc(0x0B, fout);
00669   fputs("NETSCAPE2.0", fout);
00670 
00671   fputc(0x03, fout);
00672   fputc(0x01, fout);
00673   GifPutShort(repeats, fout); /* repeat count */
00674 
00675   fputc(0x00, fout); /* terminator */
00676 
00677   if(debugFlag) fprintf(stderr, "Wrote loop extension\n");
00678 }
00679 
00680 
00681 void CalcTrans(char *string)
00682 {
00683   if(string[0] != '#') {
00684     global.trans.type = TRANS_MAP;
00685     global.trans.map = atoi(string);
00686     global.trans.valid = 1;
00687   }
00688   else {
00689     /* it's an RGB value */
00690     int r, g, b;
00691     string++;
00692     if (debugFlag > 1) fprintf(stderr, "String is %s\n", string);
00693     if(3 == sscanf(string, "%2x%2x%2x", &r, &g, &b)) {
00694       global.trans.red = r;
00695       global.trans.green = g;
00696       global.trans.blue = b;
00697       global.trans.type = TRANS_RGB;
00698       global.trans.valid = 0;
00699       if(debugFlag > 1)
00700        fprintf(stderr, "Transparent RGB=(%x,%x,%x) = (%d,%d,%d)\n",
00701            r, g, b, r, g, b);
00702     }
00703   }
00704   if(debugFlag > 1)
00705        fprintf(stderr, "DEBUG:CalcTrans is %d\n", global.trans.type);
00706 }
00707 
00708 void SetOffset(char *string)
00709 {
00710   int npar, offX, offY;
00711   char sep;
00712   if ( (npar = sscanf(string, "%d%c%d", &offX, &sep, &offY)) == 3 ) {
00713     /* set the offset */
00714     global.left = offX;
00715     global.top = offY;
00716     if(debugFlag > 1) fprintf(stderr, "Offset set to %d,%d\n",
00717     global.left, global.top);
00718     return;
00719   }
00720   fprintf(stderr, "Offset string: '%s'; fields = %d\n", string, npar);
00721   TheEnd1("Couldn't parse offset values.\n");
00722 }
00723 
00724 void TheEnd()
00725 {
00726  exit(0);
00727 }
00728 
00729 void TheEnd1(char *p)
00730 {
00731  fprintf(stderr, "Image #%d: %s", count, p);
00732  TheEnd();
00733 }
00734 
00735 void Usage()
00736 {
00737   fprintf(stderr, "\nUsage: whirlgif %s\n %s\n %s\n",
00738        "[-o outfile] [-loop [count]] [-time #delay]",
00739        "\t-disp [ none | back | prev | not ]",
00740        "\t[ -i listfile] file1 [ -time #delay] file2 ...");
00741   exit(0);
00742 }
00743 
00744 UBYTE Xgetc(FILE *fin)
00745 {
00746   int i;
00747   if ( ( i = fgetc(fin) ) == EOF ) {
00748     TheEnd1("Unexpected EOF in input file\n"); 
00749   }
00750   return(i & 0xff);
00751 }