Subversion Repositories wimsdev

Rev

Rev 8080 | 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,flag;
  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.   flag=feof(fp);
  281.         fclose(fp);
  282.         if ( flag || i == ';' )
  283.         TheEnd1("GifReadHeader: Unexpected End of File\n");
  284.         TheEnd1("GifReadHeader: Unknown block type\n");
  285.      }
  286.    } while(i != ',');
  287.  
  288.   if(firstImage) {
  289.     globscrn.m = gifscrn.m;
  290.     globscrn.pixbits = gifscrn.pixbits;
  291.     globscrn.bc = gifscrn.bc;
  292.     if ( globscrn.m ) {
  293.       for (i = gifMask[1+globscrn.pixbits]; i >= 0; i--) {
  294.         gifGmap[i].cmap.red   = gifCmap[i].cmap.red;
  295.         gifGmap[i].cmap.green = gifCmap[i].cmap.green;
  296.         gifGmap[i].cmap.blue  = gifCmap[i].cmap.blue;
  297.       }
  298.     }
  299.     if(loop) GifLoop(fout, loopcount);
  300.   }
  301.  
  302.   ReadImageHeader(fp);
  303.  
  304.  /*** ACTION for IMAGE */
  305.  
  306.   if ( ( gifimage.m != 0 && globmap !=0 ) || minimize !=0 ) {
  307.     UBYTE translator[256], *p, *po;
  308.     int left, right, top, bot, i, j, k, l, hi, wi;
  309.     long dsquare, dsquare1;
  310.     right = 0; /* -Wall */
  311.     bot = 0; /* -Wall */
  312.     hi = gifimage.height;
  313.     wi = gifimage.width;
  314.     if (( pix = (UBYTE *)malloc(wi * hi * sizeof(UBYTE)) ) == NULL )
  315.          TheEnd1("No memory for image\n");
  316.     if (debugFlag) fprintf(stderr, "  decoding picture no %d\n", count);
  317.     GifDecode(fp, pix, gifimage);
  318.     gifimage.i = 0;
  319.     k = gifMask[1+globscrn.pixbits];
  320.     l = gifMask[1+gifscrn.pixbits];
  321.     for (j = 0; j <= l; j++) {
  322.       dsquare = 256*256*3;
  323.       for (i = 0; i <= k; i++) {
  324.         dsquare1 = sq(gifGmap[i].cmap.red, gifCmap[j].cmap.red) +
  325.                    sq(gifGmap[i].cmap.green, gifCmap[j].cmap.green) +
  326.                    sq(gifGmap[i].cmap.blue,  gifCmap[j].cmap.blue);
  327.         if ( dsquare1 < dsquare ) {
  328.           dsquare = dsquare1;
  329.           translator[j]=i;
  330.           if ( dsquare == 0 ) break;
  331.         }
  332.       }
  333.     }
  334.     gifimage.m = 0;
  335.     gifscrn.pixbits = globscrn.pixbits;
  336.     if (debugFlag) fprintf(stderr, "  translating picture no %d\n", count);
  337.     for (i = wi * hi -1; i>=0; i--)
  338.       pix[i]=translator[pix[i]];
  339.     if ( minimize != 0 && pixold != NULL  && hi == gifimageold.height
  340.         && wi == gifimageold.width && gifimage.top == gifimageold.top
  341.         && gifimage.left == gifimageold.left ) {
  342.       gifimageold = gifimage;
  343. /* First test from left to right, top to bottom */
  344.       p = pix; po = pixold;
  345.       for (i = 0; i < hi; i++ ) {
  346.         for (j = 0; j < wi; j++ ) {
  347.           if ( *p++ != *po++ ) {
  348.             left = j; top=i;
  349.             goto done;
  350.           }
  351.         }
  352.       }
  353.       if (FALSE) {
  354. done: /* i.e. a preliminary left and top found */ ;
  355.       }
  356.       else goto alike;
  357. /* Then test from right to left, bottom to top */
  358.       k=hi*wi-1;
  359.       p = &pix[k]; po = &pixold[k];
  360.       for (i = hi-1; i >= top; i-- ) {
  361.         for (j = wi -1; j >= 0; j-- ) {
  362.           if ( *p-- != *po-- ) {
  363.             right = j; bot=i;
  364.             goto botfound;
  365.           }
  366.         }
  367.       }
  368. botfound:
  369. /* The form of the differing area (not rectangle) may be slanted */
  370.       if ( right < left ) {
  371.         i = right; right = left; left = i;
  372.       }
  373. /* Now test between top and bottom at the left hand side */
  374.       for (i = top+1; i <= bot; i++ ) {
  375.         k= i * wi;
  376.         p = &pix[k]; po = &pixold[k];
  377.         for (j = 0; j < left; j++ ) {
  378.           if ( *p++ != *po++ ) {
  379.             left = j;
  380.             break;
  381.           }
  382.         }
  383.       }
  384. /* Finally test between bottom and top at the right hand side */
  385.       for (i = bot-1; i >= top; i-- ) {
  386.         k= (i+1) * wi-1;
  387.         p = &pix[k]; po = &pixold[k];
  388.         for (j = wi-1; j > right; j-- ) {
  389.           if ( *p-- != *po-- ) {
  390.             right = j;
  391.             break;
  392.           }
  393.         }
  394.       }
  395.       gifimage.left += left;
  396.       gifimage.top += top;
  397.       gifimage.width = right-left+1;
  398.       gifimage.height = bot-top+1;
  399.       WriteImageHeader(fout);
  400. /* The rectangle containing diffs is transferred to the mem area of pixold */
  401.       po = pixold;
  402.       for (i = top; i <= bot; i++ ) {
  403.         p = &pix[i * wi+left];
  404.         for (j = left; j <= right; j++ ) {
  405.           *po++ = *p++;
  406.         }
  407.       }
  408.       GifEncode(fout, pixold, gifscrn.pixbits+1, gifimage.height * gifimage.width);
  409.       if (debugFlag)
  410.         fprintf(stderr, "  encoded: width= %d, height = %d, left = %d, top = %d\n",
  411.            gifimage.width, gifimage.height, gifimage.left, gifimage.top);
  412.     }
  413.     else {
  414. alike:
  415.       WriteImageHeader(fout);
  416.       gifimageold = gifimage;
  417.       GifEncode(fout, pix, gifscrn.pixbits+1, gifimage.height * gifimage.width);
  418.       if (debugFlag) fprintf(stderr, "  picture re-encoded\n");
  419. /*   Undocumented feature:  If two subsequent images are alike, then
  420.      send the whole image to the output stream (to keep the timing
  421.      between frames, and not confuse the viewer with empty images) */
  422.     }
  423.     free(pixold);
  424.     pixold = pix;
  425.     fputc(0, fout);    /* block count of zero */
  426.   }
  427.   else {
  428.     WriteImageHeader(fout);
  429.     i = Xgetc(fp); fputc(i, fout); /* the LZW code size */
  430.     while ( ( gifBlockSize = Xgetc(fp) ) > 0 ) {
  431.       fputc(gifBlockSize, fout);
  432.       while ( gifBlockSize-- > 0 ) fputc(Xgetc(fp),fout);
  433.     }
  434.     if ( gifBlockSize == 0 ) fputc(gifBlockSize, fout);
  435.     else TheEnd1("GifPassing: Unexpected End of File\n");
  436.   }
  437.  
  438.   fclose(fp);
  439. }
  440.  
  441.  
  442.  
  443. /*
  444.  * read Gif header
  445.  */
  446. void GifScreenHeader(FILE *fp, FILE *fout, int firstTime)
  447. {
  448.   int temp, i;
  449.  
  450.   for(i = 0; i < 6; i++) {
  451.     temp = Xgetc(fp);
  452.     if(i == 4 && temp == '7') temp = '9';
  453.     if (firstTime) fputc(temp, fout);
  454.   }
  455.  
  456.   gifscrn.width = GifGetShort(fp);
  457.   gifscrn.height = GifGetShort(fp);
  458.   temp = Xgetc(fp);
  459.   if (firstTime) {
  460.     GifPutShort(gifscrn.width, fout);
  461.     GifPutShort(gifscrn.height, fout);
  462.     fputc(temp, fout);
  463.   }
  464.   gifscrn.m  =  temp & 0x80;
  465.   gifscrn.cres   = (temp & 0x70) >> 4;
  466.   gifscrn.pixbits =  temp & 0x07;
  467.  
  468.   gifscrn.bc  = Xgetc(fp);
  469.   if (firstTime) {
  470.     if (debugFlag) fprintf(stderr, "First Time ... ");
  471.     if(GifBgcolor) gifscrn.bc = GifBgcolor & 0xff;
  472.     fputc(gifscrn.bc, fout);
  473.   }
  474.  
  475.   temp = Xgetc(fp);
  476.   if (firstTime)  {
  477.     fputc(temp, fout);
  478.     if ( minimize && gifscrn.bc == 0 ) {
  479.       /* Set a pseudo screen filled with the background color.
  480.          This is only done for background color index == 0 because
  481.          of Netscape and I.E.'s strange handling of backgrounds not
  482.          covered by an image.
  483.       */
  484.       temp = gifscrn.width * gifscrn.height;
  485.       if (( pixold = (UBYTE *)malloc(temp * sizeof(UBYTE)) ) == NULL )
  486.             TheEnd1("No memory for image\n");
  487.       if (debugFlag) fprintf(stderr, "BACKGROUND = %d\n", gifscrn.bc);
  488.       while (temp > 0) pixold[--temp] = 0; /* gifscrn.bc; */
  489.       gifimageold.left = gifimageold.top = 0;
  490.       gifimageold.width = gifscrn.width;
  491.       gifimageold.height = gifscrn.height;
  492.       gifimageold.pixbits = gifscrn.pixbits;
  493.     }
  494.   }
  495.   imagec = gifPtwo[(1+gifscrn.pixbits)];
  496.  
  497.   if (debugFlag)
  498.     fprintf(stderr, "Screen #%d: %dx%dx%d m=%d cres=%d bkgnd=%d pix=%d\n",
  499.       count, gifscrn.width, gifscrn.height, imagec, gifscrn.m, gifscrn.cres,
  500.       gifscrn.bc, gifscrn.pixbits);
  501.  
  502.   if (gifscrn.m) {
  503.     for(i = 0; i < imagec; i++) {
  504.       gifCmap[i].cmap.red   = temp = Xgetc(fp);
  505.       if (firstTime) fputc(temp, fout);
  506.       gifCmap[i].cmap.green = temp = Xgetc(fp);
  507.       if (firstTime) fputc(temp, fout);
  508.       gifCmap[i].cmap.blue  = temp = Xgetc(fp);
  509.       if (firstTime) fputc(temp, fout);
  510.  
  511.       if(firstTime && (global.trans.type==TRANS_RGB && global.trans.valid==0)) {
  512.         if (global.trans.red == gifCmap[i].cmap.red &&
  513.             global.trans.green == gifCmap[i].cmap.green &&
  514.             global.trans.blue == gifCmap[i].cmap.blue) {
  515.           if(debugFlag > 1) fprintf(stderr, " Transparent match at %d\n", i);
  516.           global.trans.map = i;
  517.           global.trans.valid = 1;
  518.         }
  519.         else
  520.           if(debugFlag > 1) fprintf(stderr, "No transp. RGB=(%x,%x,%x)\n",
  521.             gifCmap[i].cmap.red, gifCmap[i].cmap.green, gifCmap[i].cmap.blue);
  522.       }
  523.     }
  524.   }
  525. }
  526.  
  527. void ReadImageHeader(FILE *fp)
  528. {
  529.   int tnum, i, flag;
  530.  
  531.   gifimage.left  = GifGetShort(fp);
  532.   if(global.left) gifimage.left += global.left;
  533.  
  534.   gifimage.top   = GifGetShort(fp);
  535.   if(global.top) gifimage.top += global.top;
  536.  
  537.   gifimage.width     = GifGetShort(fp);
  538.   gifimage.height = GifGetShort(fp);
  539.   flag = Xgetc(fp);
  540.  
  541.   gifimage.i       = flag & 0x40;
  542.   gifimage.pixbits = flag & 0x07;
  543.   gifimage.m       = flag & 0x80;
  544.  
  545.   imagex = gifimage.width;
  546.   imagey = gifimage.height;
  547.   tnum = gifPtwo[(1+gifimage.pixbits)];
  548.   if (debugFlag > 1)
  549.     fprintf(stderr, "Image: %dx%dx%d (%d,%d) m=%d i=%d pix=%d \n",
  550.       imagex, imagey, tnum, gifimage.left, gifimage.top,
  551.       gifimage.m, gifimage.i, gifimage.pixbits);
  552.  
  553.    /* if there is an image cmap then read it */
  554.   if (gifimage.m) {
  555.     if(debugFlag>1)
  556.       fprintf(stderr, "DEBUG:Transferring colormap of %d colors\n",
  557.         imagec);
  558.   /*
  559.    * note below assignment, it may make the subsequent code confusing
  560.    */
  561.     gifscrn.pixbits = gifimage.pixbits;
  562.  
  563.     for(i = 0; i < tnum; i++) {
  564.       gifCmap[i].cmap.red   = Xgetc(fp);
  565.       gifCmap[i].cmap.green = Xgetc(fp);
  566.       gifCmap[i].cmap.blue  = Xgetc(fp);
  567.     }
  568.   }
  569.   gifimage.m = 0;
  570.   if ( globscrn.m && globscrn.pixbits == gifscrn.pixbits ) {
  571.     for (i = gifMask[1+globscrn.pixbits]; i >= 0; i--) {
  572.       if (gifGmap[i].cmap.red  != gifCmap[i].cmap.red ||
  573.       gifGmap[i].cmap.green != gifCmap[i].cmap.green ||
  574.       gifGmap[i].cmap.blue != gifCmap[i].cmap.blue ) {
  575.         gifimage.m = 0x80;
  576.         break;
  577.       }
  578.     }
  579.   }
  580.   else gifimage.m = 0x80;
  581.   return;
  582. }
  583.  
  584. void WriteImageHeader(FILE *fout)
  585. {
  586.   int temp, i, flag;
  587.   /* compute a Gif_GCL */
  588.  
  589.   fputc(0x21, fout);
  590.   fputc(0xF9, fout);
  591.   fputc(0x04, fout);
  592.  
  593.   flag = global.disposal <<2;
  594.   if(global.time) flag |= 0x80;
  595.   if(global.trans.type == TRANS_RGB && global.trans.valid == 0)
  596.         gifimage.m = 0x80;
  597.  
  598.   temp = global.trans.map;
  599.   if (gifimage.m != 0 && global.trans.type == TRANS_RGB ) {
  600.     temp = 0; /* set a default value, in case nothing found */
  601.     for (i = gifMask[1+gifscrn.pixbits]; i >= 0; i--) {
  602.       if(global.trans.red == gifCmap[i].cmap.red &&
  603.       global.trans.green == gifCmap[i].cmap.green &&
  604.       global.trans.blue == gifCmap[i].cmap.blue) {
  605.         if(debugFlag > 1) fprintf(stderr, " Transparent match at %d\n", i);
  606.         temp = i;
  607.         flag |= 0x01;
  608.       }
  609.     }
  610.   }
  611.   else if(global.trans.valid) flag |= 0x01;
  612.   fputc(flag, fout);
  613.  
  614.   GifPutShort(global.time, fout); /* the delay speed - 0 is instantaneous */
  615.  
  616.   fputc(temp, fout); /* the transparency index */
  617.   if(debugFlag > 1) {
  618.     fprintf(stderr, "GCL: delay %d", global.time);
  619.     if(flag && 0x1) fprintf(stderr, " Transparent: %d", temp);
  620.     fputc('\n', stderr);
  621.   }
  622.  
  623.   fputc(0, fout);
  624.   /* end of Gif_GCL */
  625.  
  626.   fputc(',', fout); /* image separator */
  627.   GifPutShort(gifimage.left  , fout);
  628.   GifPutShort(gifimage.top   , fout);
  629.   GifPutShort(gifimage.width , fout);
  630.   GifPutShort(gifimage.height, fout);
  631.   fputc(gifscrn.pixbits | gifimage.i | gifimage.m, fout);
  632.  
  633.   if ( gifimage.m ) {
  634.     for(i = 0; i < imagec; i++) {
  635.       fputc(gifCmap[i].cmap.red,   fout);
  636.       fputc(gifCmap[i].cmap.green, fout);
  637.       fputc(gifCmap[i].cmap.blue,  fout);
  638.     }
  639.     if(debugFlag) fprintf(stderr, "Local %d color map for picture #%d\n",
  640.                            imagec, count);
  641.   }
  642. }
  643.  
  644.  
  645. void GifComment(FILE *fout, char *string)
  646. {
  647.   int len;
  648.  
  649.   if( (len = strlen(string)) > 254 ) fprintf(stderr,
  650.                  "GifComment: String too long ; dropped\n");
  651.   else if ( len > 0 ) {
  652.     /* Undocumented feature:
  653.        Empty comment string means no comment block in outfile */
  654.     fputc(0x21, fout);
  655.     fputc(0xFE, fout);
  656.     fputc(len, fout);
  657.     fputs(string, fout);
  658.     fputc(0, fout);
  659.   }
  660.   return;
  661. }
  662.  
  663. /*
  664.  * Write a Netscape loop marker.
  665.  */
  666. void GifLoop(FILE *fout, unsigned int repeats)
  667. {
  668.  
  669.   fputc(0x21, fout);
  670.   fputc(0xFF, fout);
  671.   fputc(0x0B, fout);
  672.   fputs("NETSCAPE2.0", fout);
  673.  
  674.   fputc(0x03, fout);
  675.   fputc(0x01, fout);
  676.   GifPutShort(repeats, fout); /* repeat count */
  677.  
  678.   fputc(0x00, fout); /* terminator */
  679.  
  680.   if(debugFlag) fprintf(stderr, "Wrote loop extension\n");
  681. }
  682.  
  683.  
  684. void CalcTrans(char *string)
  685. {
  686.   if(string[0] != '#') {
  687.     global.trans.type = TRANS_MAP;
  688.     global.trans.map = atoi(string);
  689.     global.trans.valid = 1;
  690.   }
  691.   else {
  692.     /* it's an RGB value */
  693.     int r, g, b;
  694.     string++;
  695.     if (debugFlag > 1) fprintf(stderr, "String is %s\n", string);
  696.     if(3 == sscanf(string, "%2x%2x%2x", &r, &g, &b)) {
  697.       global.trans.red = r;
  698.       global.trans.green = g;
  699.       global.trans.blue = b;
  700.       global.trans.type = TRANS_RGB;
  701.       global.trans.valid = 0;
  702.       if(debugFlag > 1)
  703.         fprintf(stderr, "Transparent RGB=(%x,%x,%x) = (%d,%d,%d)\n",
  704.             r, g, b, r, g, b);
  705.     }
  706.   }
  707.   if(debugFlag > 1)
  708.        fprintf(stderr, "DEBUG:CalcTrans is %d\n", global.trans.type);
  709. }
  710.  
  711. void SetOffset(char *string)
  712. {
  713.   int npar, offX, offY;
  714.   char sep;
  715.   if ( (npar = sscanf(string, "%d%c%d", &offX, &sep, &offY)) == 3 ) {
  716.     /* set the offset */
  717.     global.left = offX;
  718.     global.top = offY;
  719.     if(debugFlag > 1) fprintf(stderr, "Offset set to %d,%d\n",
  720.     global.left, global.top);
  721.     return;
  722.   }
  723.   fprintf(stderr, "Offset string: '%s'; fields = %d\n", string, npar);
  724.   TheEnd1("Couldn't parse offset values.\n");
  725. }
  726.  
  727. void TheEnd()
  728. {
  729.  exit(0);
  730. }
  731.  
  732. void TheEnd1(char *p)
  733. {
  734.  fprintf(stderr, "Image #%d: %s", count, p);
  735.  TheEnd();
  736. }
  737.  
  738. void Usage()
  739. {
  740.   fprintf(stderr, "\nUsage: whirlgif %s\n %s\n %s\n",
  741.         "[-o outfile] [-loop [count]] [-time #delay]",
  742.         "\t-disp [ none | back | prev | not ]",
  743.         "\t[ -i listfile] file1 [ -time #delay] file2 ...");
  744.   exit(0);
  745. }
  746.  
  747. UBYTE Xgetc(FILE *fin)
  748. {
  749.   int i;
  750.   if ( ( i = fgetc(fin) ) == EOF ) {
  751.     TheEnd1("Unexpected EOF in input file\n");
  752.   }
  753.   return(i & 0xff);
  754. }
  755.