Subversion Repositories wimsdev

Rev

Rev 3810 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*
  2.  * whirlgif.c
  3.  *
  4.  * Copyright (c) 1997,1998,1999 by Hans Dinsen-Hansen (dino@danbbs.dk)
  5.  * Copyright (c) 1995,1996 by Kevin Kadow (kadokev@msg.net)
  6.  * Based on txtmerge.c
  7.  * Copyright (c) 1990,1991,1992,1993 by Mark Podlipec (podlipec@BayNetworks.com).
  8.  * All rights reserved.
  9.  *
  10.  * This software may be freely copied, modified and redistributed
  11.  * without fee provided that above copyright notices are preserved
  12.  * intact on all copies and modified copies.
  13.  *
  14.  * There is no warranty or other guarantee of fitness of this software.
  15.  * It is provided solely "as is". The author(s) disclaim(s) all
  16.  * responsibility and liability with respect to this software's usage
  17.  * or its effect upon hardware or computer systems.
  18.  *
  19.  * The Graphics Interchange format (c) is the Copyright property of
  20.  * Compuserve Incorporated.  Gif(sm) is a Service Mark property of
  21.  * Compuserve Incorporated.
  22.  *
  23.  */
  24. /*
  25.  * Description:
  26.  *
  27.  * This program reads in a sequence of single-image Gif format files and
  28.  * outputs a single multi-image Gif file, suitable for use as an animation.
  29.  *
  30.  * TODO:
  31.  *
  32.  * More options for dealing with the colormap
  33.  *
  34.  */
  35.  
  36. /*
  37.  * Rev 3.04    21Feb99 Hans Dinsen-Hansen
  38.  * RunLength & Amiga.
  39.  * Rev 3.03    02Feb99 Hans Dinsen-Hansen
  40.  * Published as a patch.  Better error messages.
  41.  * Rev 3.02    01Oct98 Hans Dinsen-Hansen
  42.  * Loop. Verbose -> DEBUG. Further minimizing.
  43.  * Rev 3.01      Oct98 Hans Dinsen-Hansen
  44.  * Never published.  Various experiments with Windows versions.
  45.  * Rev 3.00    29jul98 Hans Dinsen-Hansen
  46.  * Gif-repacking; unification of color map; only output diff.
  47.  * Rev 2.02    09Sep97 Hans Dinsen-Hansen
  48.  * Gif89a input; use global colormap whenever possible; background index
  49.  * Rev 2.01    31Aug96 Kevin Kadow
  50.  * disposal
  51.  * Rev 2.00    05Feb96 Kevin Kadow
  52.  * transparency, gif comments,
  53.  * Rev 1.10    29Jan96 Kevin Kadow
  54.  * first release of whirlgif
  55.  *
  56.  * txtmerge:
  57.  * Rev 1.01    08Jan92 Mark Podlipec
  58.  * use all colormaps, not just 1st.
  59.  * Rev 1.00    23Jul91 Mark Podlipec
  60.  * creation
  61.  *
  62.  *
  63.  */
  64.  
  65. #include "whirlgif.h"
  66.  
  67. /*
  68.  * Set some defaults, these can be changed on the command line
  69.  */
  70. unsigned int loop=DEFAULT_LOOP, loopcount=0,
  71.          useColormap=DEFAULT_USE_COLORMAP, debugFlag=0,
  72.          globmap=0, minimize=0;
  73.  
  74. int imagex = 0, imagey = 0, imagec = 0, GifBgcolor=0, count=0;
  75.  
  76. /* global settings for offset, transparency */
  77.  
  78. Global global;
  79.  
  80. GifColor gifGmap[256], gifCmap[256];
  81. GifScreenHdr globscrn, gifscrn;
  82.  
  83. GifImageHdr gifimage, gifimageold;
  84.  
  85. extern ULONG gifMask[];
  86. extern int picI;
  87. UBYTE *pixold=NULL;
  88. ULONG gifMask[16]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,0,0}, obits;
  89. ULONG gifPtwo[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,0,0};
  90.  
  91. char gifFileName[BIGSTRING];
  92. FILE *ff;
  93.  
  94. long sq(UBYTE i,UBYTE j)
  95. {
  96.   return((i-j)*(i-j));
  97. }
  98.  
  99. int main(int argc, char *argv[])
  100. {
  101.   FILE * infile, *fout;
  102.   char temp[BIGSTRING], *cmt;
  103.   int i;
  104.  
  105.   fprintf(stderr, "whirlgif Rev %2.2f (c) 1997-1999 by %s\n%s\n%s\n",
  106.                 DA_REV,"Hans Dinsen-Hansen",
  107.   "                  (c) 1995-1996 by Kevin Kadow",
  108.   "                  (c) 1990-1993 by Mark Podlipec");
  109.   cmt = temp;
  110.   /* if there is no comment option, let cmt point at the final message */
  111.  
  112.   if (argc < 2) Usage();
  113.  
  114.   /* set global values */
  115.   global.trans.type = TRANS_NONE;
  116.   global.trans.valid = 0;
  117.   global.time = DEFAULT_TIME;
  118.   global.left = 0;
  119.   global.top = 0;
  120.   global.disposal = DEFAULT_DISPOSAL;
  121.  
  122.   fout = stdout;
  123.   i = 1;
  124.   while( i < argc) {
  125.     char *p;
  126.     p = argv[i];
  127.     if (debugFlag > 1) fprintf(stderr, "Option: %s\n", p);
  128.     if ( (p[0] == '-') || (p[0] == '+') ) {
  129.       ++p; /* strip off the - */
  130.       switch(p[0]) {
  131.         case 'v': /* Give lots of information */
  132.           debugFlag++;
  133.           i++;
  134.           fprintf(stderr, "Verbose output; debug level: %d\n", debugFlag);
  135.           break;
  136.         case 'g': /* Use the global colormap throughout */
  137.           globmap++;
  138.           i++;
  139.           if ( debugFlag > 1) fprintf(stderr, "globmap\n");
  140.           break;
  141.         case 'm': /* minimize output */
  142.           minimize++;
  143.           globmap++;
  144.           i++;
  145.           if ( debugFlag > 1) fprintf(stderr, "minimize \n");
  146.           break;
  147.         case 'd': /* disposal setting */
  148.           i++;
  149.           p = argv[i++];
  150.           if(!strcmp("not",  p)) global.disposal = DISP_NOT;
  151.           else if(!strcmp("back", p)) global.disposal = DISP_BACK;
  152.           else if(!strcmp("prev", p)) global.disposal = DISP_PREV;
  153.           else if(!strcmp("none", p)) global.disposal = DISP_NONE;
  154.           else global.disposal = DEFAULT_DISPOSAL;
  155.           if(debugFlag) fprintf(stderr, "Disposal method set to %s = %d\n",
  156.                       p, global.disposal);
  157.           break;
  158.         case 'D': /* Debug setting */
  159.           i++;
  160.           debugFlag = 2;
  161.           fprintf(stderr, "DEBUG: Debug Level %d\n", debugFlag);
  162.           break;
  163.         case 'c': /* set comment pointer */
  164.           i++;
  165.           cmt = argv[i++];
  166.           if(debugFlag) fprintf(stderr, "Comment: '%s'\n", cmt);
  167.           break;
  168.         case 'b': /* set Background color index */
  169.           i++;
  170.           GifBgcolor = atoi(argv[i++]) | 0x100;
  171.           if (debugFlag) fprintf(stderr, "BACKGROUND = %d\n", GifBgcolor & 0xff);
  172.           break;
  173.         case 'l': /* Enable looping */
  174.           loop = TRUE;
  175.           i++;
  176.           if(*argv[i] != '-') {
  177.             /* a loop count was perhaps given */
  178.             loopcount = atoi(argv[i]);
  179.             if(debugFlag) {
  180.               fprintf(stderr, loopcount != 0 ? "Loop %d times\n"
  181.                         : "Loop forever, count = %d\n", loopcount);
  182.             }
  183.             if( (loopcount > 0) | ((loopcount == 0) & (*argv[i] == '0'))) i++;
  184.           }
  185.           else {
  186.             /* default to infinite loop */
  187.             loopcount = 0;
  188.             if(debugFlag) fprintf(stderr, "Looping enabled\n");
  189.           }
  190.           break;
  191.         case 't': /* either time or transparent */
  192.           i++;
  193.           if(!strncmp("time", p, 2)) {
  194.             /* Delay time in 1/100's of a second */
  195.             global.time = atoi(argv[i++]);
  196.           }
  197.           else if(!strncmp("trans", p, 2)) CalcTrans(argv[i++]);
  198.           break;
  199.         case 'o': /* Output file - send output to a given filename */
  200.           i++;
  201.           if(!strncmp("off", p, 2)) SetOffset(argv[i]);
  202.           else if(NULL == (fout = fopen(argv[i], WRIBIN))) {
  203.             /* It must be 'output, so we do that */
  204.             fprintf(stderr, "Cannot open %s for output\n", argv[i]);
  205.             exit(1);
  206.           }
  207.           i++;
  208.           break;
  209.         case 'i': /* input file - file with a list of images */
  210.           i++;
  211.           if(NULL != (infile = fopen(argv[i], REATXT))) {
  212.             while(fgets(gifFileName, BIGSTRING, infile)) {
  213.               strtok(gifFileName, "\n");
  214.               GifReadFile(fout, gifFileName, count++ == 0);
  215.             }
  216.             fclose(infile);
  217.             global.left = global.top = 0;
  218.           }
  219.           else fprintf(stderr, "Cannot read list file %s\n", argv[i]);
  220.           i++;
  221.           break;
  222.         default:
  223.           Usage();
  224.           exit(0);
  225.           break;
  226.       }
  227.       continue;
  228.     }
  229.     /* Not an option, must be the name of an input file */
  230.     GifReadFile(fout, argv[i], count++ == 0);
  231.     global.left = global.top = 0;
  232.     i++;
  233.   }
  234.  /* We're done with all options and file names, finish up */
  235.   if(count >0) {
  236.     sprintf(temp, "whirlgif %2.2f (c) %s\r\n%d %s",
  237.             DA_REV, "dino@danbbs.dk", count,
  238.             count == 1 ? "image" : "images");
  239.     /* Either output above std. mess. or a possible user defined comment */
  240.     GifComment(fout, cmt);
  241.    }
  242.    fputc(';', fout); /* End of Gif file */
  243.  
  244.    fclose(fout);
  245.    fprintf(stderr, "Processed %d files.\n", count);
  246.    exit(0);
  247.    return 0; /* not reached */
  248. }
  249.  
  250.  
  251. /*
  252.  * Read a Gif file.
  253.  */
  254. void GifReadFile(FILE *fout, char *fname, int firstImage)
  255. {
  256.   FILE *fp;
  257.   UBYTE *pix;
  258.   int i, gifBlockSize;
  259.   if ( (fp = fopen(fname, REABIN)) == 0) {
  260.     fprintf(stderr, "Can't open %s for reading.\n", fname);
  261.     TheEnd();
  262.   }
  263.  
  264.   GifScreenHeader(fp, fout, firstImage);
  265.  
  266.    /* read until , separator */
  267.   do {
  268.     switch ( i = Xgetc(fp)) {
  269.       case ',':
  270.       case '\0':
  271.         break;
  272.       case '!':
  273.         Xgetc(fp); /* the extension code */
  274.         for ( i = Xgetc(fp); i > 0; i-- ) Xgetc(fp);
  275.         while ( ( i = Xgetc(fp) ) > 0 ) {
  276.           for ( ; i > 0; i-- ) Xgetc(fp);
  277.         }
  278.         break;
  279.       default:
  280.         fclose(fp);
  281.         if ( feof(fp) || i == ';' )
  282.         TheEnd1("GifReadHeader: Unexpected End of File\n");
  283.         TheEnd1("GifReadHeader: Unknown block type\n");
  284.      }
  285.    } while(i != ',');
  286.  
  287.   if(firstImage) {
  288.     globscrn.m = gifscrn.m;
  289.     globscrn.pixbits = gifscrn.pixbits;
  290.     globscrn.bc = gifscrn.bc;
  291.     if ( globscrn.m ) {
  292.       for (i = gifMask[1+globscrn.pixbits]; i >= 0; i--) {
  293.         gifGmap[i].cmap.red   = gifCmap[i].cmap.red;
  294.         gifGmap[i].cmap.green = gifCmap[i].cmap.green;
  295.         gifGmap[i].cmap.blue  = gifCmap[i].cmap.blue;
  296.       }
  297.     }
  298.     if(loop) GifLoop(fout, loopcount);
  299.   }
  300.  
  301.   ReadImageHeader(fp);
  302.  
  303.  /*** ACTION for IMAGE */
  304.  
  305.   if ( ( gifimage.m != 0 && globmap !=0 ) || minimize !=0 ) {
  306.     UBYTE translator[256], *p, *po;
  307.     int left, right, top, bot, i, j, k, l, hi, wi;
  308.     long dsquare, dsquare1;
  309.     right = 0; /* -Wall */
  310.     bot = 0; /* -Wall */
  311.     hi = gifimage.height;
  312.     wi = gifimage.width;
  313.     if (( pix = (UBYTE *)malloc(wi * hi * sizeof(UBYTE)) ) == NULL )
  314.          TheEnd1("No memory for image\n");
  315.     if (debugFlag) fprintf(stderr, "  decoding picture no %d\n", count);
  316.     GifDecode(fp, pix, gifimage);
  317.     gifimage.i = 0;
  318.     k = gifMask[1+globscrn.pixbits];
  319.     l = gifMask[1+gifscrn.pixbits];
  320.     for (j = 0; j <= l; j++) {
  321.       dsquare = 256*256*3;
  322.       for (i = 0; i <= k; i++) {
  323.         dsquare1 = sq(gifGmap[i].cmap.red, gifCmap[j].cmap.red) +
  324.                    sq(gifGmap[i].cmap.green, gifCmap[j].cmap.green) +
  325.                    sq(gifGmap[i].cmap.blue,  gifCmap[j].cmap.blue);
  326.         if ( dsquare1 < dsquare ) {
  327.           dsquare = dsquare1;
  328.           translator[j]=i;
  329.           if ( dsquare == 0 ) break;
  330.         }
  331.       }
  332.     }
  333.     gifimage.m = 0;
  334.     gifscrn.pixbits = globscrn.pixbits;
  335.     if (debugFlag) fprintf(stderr, "  translating picture no %d\n", count);
  336.     for (i = wi * hi -1; i>=0; i--)
  337.       pix[i]=translator[pix[i]];
  338.     if ( minimize != 0 && pixold != NULL  && hi == gifimageold.height
  339.         && wi == gifimageold.width && gifimage.top == gifimageold.top
  340.         && gifimage.left == gifimageold.left ) {
  341.       gifimageold = gifimage;
  342. /* First test from left to right, top to bottom */
  343.       p = pix; po = pixold;
  344.       for (i = 0; i < hi; i++ ) {
  345.         for (j = 0; j < wi; j++ ) {
  346.           if ( *p++ != *po++ ) {
  347.             left = j; top=i;
  348.             goto done;
  349.           }
  350.         }
  351.       }
  352.       if (FALSE) {
  353. done: /* i.e. a preliminary left and top found */ ;
  354.       }
  355.       else goto alike;
  356. /* Then test from right to left, bottom to top */
  357.       k=hi*wi-1;
  358.       p = &pix[k]; po = &pixold[k];
  359.       for (i = hi-1; i >= top; i-- ) {
  360.         for (j = wi -1; j >= 0; j-- ) {
  361.           if ( *p-- != *po-- ) {
  362.             right = j; bot=i;
  363.             goto botfound;
  364.           }
  365.         }
  366.       }
  367. botfound:
  368. /* The form of the differing area (not rectangle) may be slanted */
  369.       if ( right < left ) {
  370.         i = right; right = left; left = i;
  371.       }
  372. /* Now test between top and bottom at the left hand side */
  373.       for (i = top+1; i <= bot; i++ ) {
  374.         k= i * wi;
  375.         p = &pix[k]; po = &pixold[k];
  376.         for (j = 0; j < left; j++ ) {
  377.           if ( *p++ != *po++ ) {
  378.             left = j;
  379.             break;
  380.           }
  381.         }
  382.       }
  383. /* Finally test between bottom and top at the right hand side */
  384.       for (i = bot-1; i >= top; i-- ) {
  385.         k= (i+1) * wi-1;
  386.         p = &pix[k]; po = &pixold[k];
  387.         for (j = wi-1; j > right; j-- ) {
  388.           if ( *p-- != *po-- ) {
  389.             right = j;
  390.             break;
  391.           }
  392.         }
  393.       }
  394.       gifimage.left += left;
  395.       gifimage.top += top;
  396.       gifimage.width = right-left+1;
  397.       gifimage.height = bot-top+1;
  398.       WriteImageHeader(fout);
  399. /* The rectangle containing diffs is transferred to the mem area of pixold */
  400.       po = pixold;
  401.       for (i = top; i <= bot; i++ ) {
  402.         p = &pix[i * wi+left];
  403.         for (j = left; j <= right; j++ ) {
  404.           *po++ = *p++;
  405.         }
  406.       }
  407.       GifEncode(fout, pixold, gifscrn.pixbits+1, gifimage.height * gifimage.width);
  408.       if (debugFlag)
  409.         fprintf(stderr, "  encoded: width= %d, height = %d, left = %d, top = %d\n",
  410.            gifimage.width, gifimage.height, gifimage.left, gifimage.top);
  411.     }
  412.     else {
  413. alike:
  414.       WriteImageHeader(fout);
  415.       gifimageold = gifimage;
  416.       GifEncode(fout, pix, gifscrn.pixbits+1, gifimage.height * gifimage.width);
  417.       if (debugFlag) fprintf(stderr, "  picture re-encoded\n");
  418. /*   Undocumented feature:  If two subsequent images are alike, then
  419.      send the whole image to the output stream (to keep the timing
  420.      between frames, and not confuse the viewer with empty images) */
  421.     }
  422.     free(pixold);
  423.     pixold = pix;
  424.     fputc(0, fout);    /* block count of zero */
  425.   }
  426.   else {
  427.     WriteImageHeader(fout);
  428.     i = Xgetc(fp); fputc(i, fout); /* the LZW code size */
  429.     while ( ( gifBlockSize = Xgetc(fp) ) > 0 ) {
  430.       fputc(gifBlockSize, fout);
  431.       while ( gifBlockSize-- > 0 ) fputc(Xgetc(fp),fout);
  432.     }
  433.     if ( gifBlockSize == 0 ) fputc(gifBlockSize, fout);
  434.     else TheEnd1("GifPassing: Unexpected End of File\n");
  435.   }
  436.  
  437.   fclose(fp);
  438. }
  439.  
  440.  
  441.  
  442. /*
  443.  * read Gif header
  444.  */
  445. void GifScreenHeader(FILE *fp, FILE *fout, int firstTime)
  446. {
  447.   int temp, i;
  448.  
  449.   for(i = 0; i < 6; i++) {
  450.     temp = Xgetc(fp);
  451.     if(i == 4 && temp == '7') temp = '9';
  452.     if (firstTime) fputc(temp, fout);
  453.   }
  454.  
  455.   gifscrn.width = GifGetShort(fp);
  456.   gifscrn.height = GifGetShort(fp);
  457.   temp = Xgetc(fp);
  458.   if (firstTime) {
  459.     GifPutShort(gifscrn.width, fout);
  460.     GifPutShort(gifscrn.height, fout);
  461.     fputc(temp, fout);
  462.   }
  463.   gifscrn.m  =  temp & 0x80;
  464.   gifscrn.cres   = (temp & 0x70) >> 4;
  465.   gifscrn.pixbits =  temp & 0x07;
  466.  
  467.   gifscrn.bc  = Xgetc(fp);
  468.   if (firstTime) {
  469.     if (debugFlag) fprintf(stderr, "First Time ... ");
  470.     if(GifBgcolor) gifscrn.bc = GifBgcolor & 0xff;
  471.     fputc(gifscrn.bc, fout);
  472.   }
  473.  
  474.   temp = Xgetc(fp);
  475.   if (firstTime)  {
  476.     fputc(temp, fout);
  477.     if ( minimize && gifscrn.bc == 0 ) {
  478.       /* Set a pseudo screen filled with the background color.
  479.          This is only done for background color index == 0 because
  480.          of Netscape and I.E.'s strange handling of backgrounds not
  481.          covered by an image.
  482.       */
  483.       temp = gifscrn.width * gifscrn.height;
  484.       if (( pixold = (UBYTE *)malloc(temp * sizeof(UBYTE)) ) == NULL )
  485.             TheEnd1("No memory for image\n");
  486.       if (debugFlag) fprintf(stderr, "BACKGROUND = %d\n", gifscrn.bc);
  487.       while (temp > 0) pixold[--temp] = 0; /* gifscrn.bc; */
  488.       gifimageold.left = gifimageold.top = 0;
  489.       gifimageold.width = gifscrn.width;
  490.       gifimageold.height = gifscrn.height;
  491.       gifimageold.pixbits = gifscrn.pixbits;
  492.     }
  493.   }
  494.   imagec = gifPtwo[(1+gifscrn.pixbits)];
  495.  
  496.   if (debugFlag)
  497.     fprintf(stderr, "Screen #%d: %dx%dx%d m=%d cres=%d bkgnd=%d pix=%d\n",
  498.       count, gifscrn.width, gifscrn.height, imagec, gifscrn.m, gifscrn.cres,
  499.       gifscrn.bc, gifscrn.pixbits);
  500.  
  501.   if (gifscrn.m) {
  502.     for(i = 0; i < imagec; i++) {
  503.       gifCmap[i].cmap.red   = temp = Xgetc(fp);
  504.       if (firstTime) fputc(temp, fout);
  505.       gifCmap[i].cmap.green = temp = Xgetc(fp);
  506.       if (firstTime) fputc(temp, fout);
  507.       gifCmap[i].cmap.blue  = temp = Xgetc(fp);
  508.       if (firstTime) fputc(temp, fout);
  509.  
  510.       if(firstTime && (global.trans.type==TRANS_RGB && global.trans.valid==0)) {
  511.         if (global.trans.red == gifCmap[i].cmap.red &&
  512.             global.trans.green == gifCmap[i].cmap.green &&
  513.             global.trans.blue == gifCmap[i].cmap.blue) {
  514.           if(debugFlag > 1) fprintf(stderr, " Transparent match at %d\n", i);
  515.           global.trans.map = i;
  516.           global.trans.valid = 1;
  517.         }
  518.         else
  519.           if(debugFlag > 1) fprintf(stderr, "No transp. RGB=(%x,%x,%x)\n",
  520.             gifCmap[i].cmap.red, gifCmap[i].cmap.green, gifCmap[i].cmap.blue);
  521.       }
  522.     }
  523.   }
  524. }
  525.  
  526. void ReadImageHeader(FILE *fp)
  527. {
  528.   int tnum, i, flag;
  529.  
  530.   gifimage.left  = GifGetShort(fp);
  531.   if(global.left) gifimage.left += global.left;
  532.  
  533.   gifimage.top   = GifGetShort(fp);
  534.   if(global.top) gifimage.top += global.top;
  535.  
  536.   gifimage.width     = GifGetShort(fp);
  537.   gifimage.height = GifGetShort(fp);
  538.   flag = Xgetc(fp);
  539.  
  540.   gifimage.i       = flag & 0x40;
  541.   gifimage.pixbits = flag & 0x07;
  542.   gifimage.m       = flag & 0x80;
  543.  
  544.   imagex = gifimage.width;
  545.   imagey = gifimage.height;
  546.   tnum = gifPtwo[(1+gifimage.pixbits)];
  547.   if (debugFlag > 1)
  548.     fprintf(stderr, "Image: %dx%dx%d (%d,%d) m=%d i=%d pix=%d \n",
  549.       imagex, imagey, tnum, gifimage.left, gifimage.top,
  550.       gifimage.m, gifimage.i, gifimage.pixbits);
  551.  
  552.    /* if there is an image cmap then read it */
  553.   if (gifimage.m) {
  554.     if(debugFlag>1)
  555.       fprintf(stderr, "DEBUG:Transferring colormap of %d colors\n",
  556.         imagec);
  557.   /*
  558.    * note below assignment, it may make the subsequent code confusing
  559.    */
  560.     gifscrn.pixbits = gifimage.pixbits;
  561.  
  562.     for(i = 0; i < tnum; i++) {
  563.       gifCmap[i].cmap.red   = Xgetc(fp);
  564.       gifCmap[i].cmap.green = Xgetc(fp);
  565.       gifCmap[i].cmap.blue  = Xgetc(fp);
  566.     }
  567.   }
  568.   gifimage.m = 0;
  569.   if ( globscrn.m && globscrn.pixbits == gifscrn.pixbits ) {
  570.     for (i = gifMask[1+globscrn.pixbits]; i >= 0; i--) {
  571.       if (gifGmap[i].cmap.red  != gifCmap[i].cmap.red ||
  572.       gifGmap[i].cmap.green != gifCmap[i].cmap.green ||
  573.       gifGmap[i].cmap.blue != gifCmap[i].cmap.blue ) {
  574.         gifimage.m = 0x80;
  575.         break;
  576.       }
  577.     }
  578.   }
  579.   else gifimage.m = 0x80;
  580.   return;
  581. }
  582.  
  583. void WriteImageHeader(FILE *fout)
  584. {
  585.   int temp, i, flag;
  586.   /* compute a Gif_GCL */
  587.  
  588.   fputc(0x21, fout);
  589.   fputc(0xF9, fout);
  590.   fputc(0x04, fout);
  591.  
  592.   flag = global.disposal <<2;
  593.   if(global.time) flag |= 0x80;
  594.   if(global.trans.type == TRANS_RGB && global.trans.valid == 0)
  595.         gifimage.m = 0x80;
  596.  
  597.   temp = global.trans.map;
  598.   if (gifimage.m != 0 && global.trans.type == TRANS_RGB ) {
  599.     temp = 0; /* set a default value, in case nothing found */
  600.     for (i = gifMask[1+gifscrn.pixbits]; i >= 0; i--) {
  601.       if(global.trans.red == gifCmap[i].cmap.red &&
  602.       global.trans.green == gifCmap[i].cmap.green &&
  603.       global.trans.blue == gifCmap[i].cmap.blue) {
  604.         if(debugFlag > 1) fprintf(stderr, " Transparent match at %d\n", i);
  605.         temp = i;
  606.         flag |= 0x01;
  607.       }
  608.     }
  609.   }
  610.   else if(global.trans.valid) flag |= 0x01;
  611.   fputc(flag, fout);
  612.  
  613.   GifPutShort(global.time, fout); /* the delay speed - 0 is instantaneous */
  614.  
  615.   fputc(temp, fout); /* the transparency index */
  616.   if(debugFlag > 1) {
  617.     fprintf(stderr, "GCL: delay %d", global.time);
  618.     if(flag && 0x1) fprintf(stderr, " Transparent: %d", temp);
  619.     fputc('\n', stderr);
  620.   }
  621.  
  622.   fputc(0, fout);
  623.   /* end of Gif_GCL */
  624.  
  625.   fputc(',', fout); /* image separator */
  626.   GifPutShort(gifimage.left  , fout);
  627.   GifPutShort(gifimage.top   , fout);
  628.   GifPutShort(gifimage.width , fout);
  629.   GifPutShort(gifimage.height, fout);
  630.   fputc(gifscrn.pixbits | gifimage.i | gifimage.m, fout);
  631.  
  632.   if ( gifimage.m ) {
  633.     for(i = 0; i < imagec; i++) {
  634.       fputc(gifCmap[i].cmap.red,   fout);
  635.       fputc(gifCmap[i].cmap.green, fout);
  636.       fputc(gifCmap[i].cmap.blue,  fout);
  637.     }
  638.     if(debugFlag) fprintf(stderr, "Local %d color map for picture #%d\n",
  639.                            imagec, count);
  640.   }
  641. }
  642.  
  643.  
  644. void GifComment(FILE *fout, char *string)
  645. {
  646.   int len;
  647.  
  648.   if( (len = strlen(string)) > 254 ) fprintf(stderr,
  649.                  "GifComment: String too long ; dropped\n");
  650.   else if ( len > 0 ) {
  651.     /* Undocumented feature:
  652.        Empty comment string means no comment block in outfile */
  653.     fputc(0x21, fout);
  654.     fputc(0xFE, fout);
  655.     fputc(len, fout);
  656.     fputs(string, fout);
  657.     fputc(0, fout);
  658.   }
  659.   return;
  660. }
  661.  
  662. /*
  663.  * Write a Netscape loop marker.
  664.  */
  665. void GifLoop(FILE *fout, unsigned int repeats)
  666. {
  667.  
  668.   fputc(0x21, fout);
  669.   fputc(0xFF, fout);
  670.   fputc(0x0B, fout);
  671.   fputs("NETSCAPE2.0", fout);
  672.  
  673.   fputc(0x03, fout);
  674.   fputc(0x01, fout);
  675.   GifPutShort(repeats, fout); /* repeat count */
  676.  
  677.   fputc(0x00, fout); /* terminator */
  678.  
  679.   if(debugFlag) fprintf(stderr, "Wrote loop extension\n");
  680. }
  681.  
  682.  
  683. void CalcTrans(char *string)
  684. {
  685.   if(string[0] != '#') {
  686.     global.trans.type = TRANS_MAP;
  687.     global.trans.map = atoi(string);
  688.     global.trans.valid = 1;
  689.   }
  690.   else {
  691.     /* it's an RGB value */
  692.     int r, g, b;
  693.     string++;
  694.     if (debugFlag > 1) fprintf(stderr, "String is %s\n", string);
  695.     if(3 == sscanf(string, "%2x%2x%2x", &r, &g, &b)) {
  696.       global.trans.red = r;
  697.       global.trans.green = g;
  698.       global.trans.blue = b;
  699.       global.trans.type = TRANS_RGB;
  700.       global.trans.valid = 0;
  701.       if(debugFlag > 1)
  702.         fprintf(stderr, "Transparent RGB=(%x,%x,%x) = (%d,%d,%d)\n",
  703.             r, g, b, r, g, b);
  704.     }
  705.   }
  706.   if(debugFlag > 1)
  707.        fprintf(stderr, "DEBUG:CalcTrans is %d\n", global.trans.type);
  708. }
  709.  
  710. void SetOffset(char *string)
  711. {
  712.   int npar, offX, offY;
  713.   char sep;
  714.   if ( (npar = sscanf(string, "%d%c%d", &offX, &sep, &offY)) == 3 ) {
  715.     /* set the offset */
  716.     global.left = offX;
  717.     global.top = offY;
  718.     if(debugFlag > 1) fprintf(stderr, "Offset set to %d,%d\n",
  719.     global.left, global.top);
  720.     return;
  721.   }
  722.   fprintf(stderr, "Offset string: '%s'; fields = %d\n", string, npar);
  723.   TheEnd1("Couldn't parse offset values.\n");
  724. }
  725.  
  726. void TheEnd()
  727. {
  728.  exit(0);
  729. }
  730.  
  731. void TheEnd1(char *p)
  732. {
  733.  fprintf(stderr, "Image #%d: %s", count, p);
  734.  TheEnd();
  735. }
  736.  
  737. void Usage()
  738. {
  739.   fprintf(stderr, "\nUsage: whirlgif %s\n %s\n %s\n",
  740.         "[-o outfile] [-loop [count]] [-time #delay]",
  741.         "\t-disp [ none | back | prev | not ]",
  742.         "\t[ -i listfile] file1 [ -time #delay] file2 ...");
  743.   exit(0);
  744. }
  745.  
  746. UBYTE Xgetc(FILE *fin)
  747. {
  748.   int i;
  749.   if ( ( i = fgetc(fin) ) == EOF ) {
  750.     TheEnd1("Unexpected EOF in input file\n");
  751.   }
  752.   return(i & 0xff);
  753. }
  754.