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. void main(argc, argv)
  100.  int            argc;
  101.  char           *argv[];
  102. {
  103.   FILE * infile, *fout;
  104.   char temp[BIGSTRING], *cmt;
  105.   int i;
  106.  
  107.   fprintf(stderr, "whirlgif Rev %2.2f (c) 1997-1999 by %s\n%s\n%s\n",
  108.                 DA_REV,"Hans Dinsen-Hansen",
  109.   "                  (c) 1995-1996 by Kevin Kadow",
  110.   "                  (c) 1990-1993 by Mark Podlipec");
  111.   cmt = temp;
  112.   /* if there is no comment option, let cmt point at the final message */
  113.  
  114.   if (argc < 2) Usage();
  115.  
  116.   /* set global values */
  117.   global.trans.type = TRANS_NONE;
  118.   global.trans.valid = 0;
  119.   global.time = DEFAULT_TIME;
  120.   global.left = 0;
  121.   global.top = 0;
  122.   global.disposal = DEFAULT_DISPOSAL;
  123.  
  124.   fout = stdout;
  125.   i = 1;
  126.   while( i < argc) {
  127.     char *p;
  128.     p = argv[i];
  129.     if (debugFlag > 1) fprintf(stderr, "Option: %s\n", p);
  130.     if ( (p[0] == '-') || (p[0] == '+') ) {
  131.       ++p; /* strip off the - */
  132.       switch(p[0]) {
  133.         case 'v': /* Give lots of information */
  134.           debugFlag++;
  135.           i++;
  136.           fprintf(stderr, "Verbose output; debug level: %d\n", debugFlag);
  137.           break;
  138.         case 'g': /* Use the global colormap throughout */
  139.           globmap++;
  140.           i++;
  141.           if ( debugFlag > 1) fprintf(stderr, "globmap\n");
  142.           break;
  143.         case 'm': /* minimize output */
  144.           minimize++;
  145.           globmap++;
  146.           i++;
  147.           if ( debugFlag > 1) fprintf(stderr, "minimize \n");
  148.           break;
  149.         case 'd': /* disposal setting */
  150.           i++;
  151.           p = argv[i++];
  152.           if(!strcmp("not",  p)) global.disposal = DISP_NOT;
  153.           else if(!strcmp("back", p)) global.disposal = DISP_BACK;
  154.           else if(!strcmp("prev", p)) global.disposal = DISP_PREV;
  155.           else if(!strcmp("none", p)) global.disposal = DISP_NONE;
  156.           else global.disposal = DEFAULT_DISPOSAL;
  157.           if(debugFlag) fprintf(stderr, "Disposal method set to %s = %d\n",
  158.                       p, global.disposal);
  159.           break;
  160.         case 'D': /* Debug setting */
  161.           i++;
  162.           debugFlag = 2;
  163.           fprintf(stderr, "DEBUG: Debug Level %d\n", debugFlag);
  164.           break;
  165.         case 'c': /* set comment pointer */
  166.           i++;
  167.           cmt = argv[i++];
  168.           if(debugFlag) fprintf(stderr, "Comment: '%s'\n", cmt);
  169.           break;
  170.         case 'b': /* set Background color index */
  171.           i++;
  172.           GifBgcolor = atoi(argv[i++]) | 0x100;
  173.           if (debugFlag) fprintf(stderr, "BACKGROUND = %d\n", GifBgcolor & 0xff);
  174.           break;
  175.         case 'l': /* Enable looping */
  176.           loop = TRUE;
  177.           i++;
  178.           if(*argv[i] != '-') {
  179.             /* a loop count was perhaps given */
  180.             loopcount = atoi(argv[i]);
  181.             if(debugFlag) {
  182.               fprintf(stderr, loopcount != 0 ? "Loop %d times\n"
  183.                         : "Loop forever, count = %d\n", loopcount);
  184.             }
  185.             if( (loopcount > 0) | ((loopcount == 0) & (*argv[i] == '0'))) i++;
  186.           }
  187.           else {
  188.             /* default to infinite loop */
  189.             loopcount = 0;
  190.             if(debugFlag) fprintf(stderr, "Looping enabled\n");
  191.           }
  192.           break;
  193.         case 't': /* either time or transparent */
  194.           i++;
  195.           if(!strncmp("time", p, 2)) {
  196.             /* Delay time in 1/100's of a second */
  197.             global.time = atoi(argv[i++]);
  198.           }
  199.           else if(!strncmp("trans", p, 2)) CalcTrans(argv[i++]);
  200.           break;
  201.         case 'o': /* Output file - send output to a given filename */
  202.           i++;
  203.           if(!strncmp("off", p, 2)) SetOffset(argv[i]);
  204.           else if(NULL == (fout = fopen(argv[i], WRIBIN))) {
  205.             /* It must be 'output, so we do that */
  206.             fprintf(stderr, "Cannot open %s for output\n", argv[i]);
  207.             exit(1);
  208.           }
  209.           i++;
  210.           break;
  211.         case 'i': /* input file - file with a list of images */
  212.           i++;
  213.           if(NULL != (infile = fopen(argv[i], REATXT))) {
  214.             while(fgets(gifFileName, BIGSTRING, infile)) {
  215.               strtok(gifFileName, "\n");
  216.               GifReadFile(fout, gifFileName, count++ == 0);
  217.             }
  218.             fclose(infile);
  219.             global.left = global.top = 0;
  220.           }
  221.           else fprintf(stderr, "Cannot read list file %s\n", argv[i]);
  222.           i++;
  223.           break;
  224.         default:
  225.           Usage();
  226.           exit(0);
  227.           break;
  228.       }
  229.       continue;
  230.     }
  231.     /* Not an option, must be the name of an input file */
  232.     GifReadFile(fout, argv[i], count++ == 0);
  233.     global.left = global.top = 0;
  234.     i++;
  235.   }
  236.  /* We're done with all options and file names, finish up */
  237.   if(count >0) {
  238.     sprintf(temp, "whirlgif %2.2f (c) %s\r\n%d %s",
  239.             DA_REV, "dino@danbbs.dk", count,
  240.             count == 1 ? "image" : "images");
  241.     /* Either output above std. mess. or a possible user defined comment */
  242.     GifComment(fout, cmt);
  243.    }
  244.    fputc(';', fout); /* End of Gif file */
  245.  
  246.    fclose(fout);
  247.    fprintf(stderr, "Processed %d files.\n", count);
  248.    exit(0);
  249. }
  250.  
  251.  
  252. /*
  253.  * Read a Gif file.
  254.  */
  255. void GifReadFile(FILE *fout, char *fname, int firstImage)
  256. {
  257.   FILE *fp;
  258.   UBYTE *pix;
  259.   int i, gifBlockSize;
  260.   if ( (fp = fopen(fname, REABIN)) == 0) {
  261.     fprintf(stderr, "Can't open %s for reading.\n", fname);
  262.     TheEnd();
  263.   }
  264.  
  265.   GifScreenHeader(fp, fout, firstImage);
  266.  
  267.    /* read until , separator */
  268.   do {
  269.     switch ( i = Xgetc(fp)) {
  270.       case ',':
  271.       case '\0':
  272.         break;
  273.       case '!':
  274.         Xgetc(fp); /* the extension code */
  275.         for ( i = Xgetc(fp); i > 0; i-- ) Xgetc(fp);
  276.         while ( ( i = Xgetc(fp) ) > 0 ) {
  277.           for ( i = i ; i > 0; i-- ) Xgetc(fp);
  278.         }
  279.         break;
  280.       default:
  281.         fclose(fp);
  282.         if ( feof(fp) || 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.     hi = gifimage.height;
  311.     wi = gifimage.width;
  312.     if (( pix = (UBYTE *)malloc(wi * hi * sizeof(UBYTE)) ) == NULL )
  313.          TheEnd1("No memory for image\n");
  314.     if (debugFlag) fprintf(stderr, "  decoding picture no %d\n", count);
  315.     GifDecode(fp, pix, gifimage);
  316.     gifimage.i = 0;
  317.     k = gifMask[1+globscrn.pixbits];
  318.     l = gifMask[1+gifscrn.pixbits];
  319.     for (j = 0; j <= l; j++) {
  320.       dsquare = 256*256*3;
  321.       for (i = 0; i <= k; i++) {
  322.         dsquare1 = sq(gifGmap[i].cmap.red, gifCmap[j].cmap.red) +
  323.                    sq(gifGmap[i].cmap.green, gifCmap[j].cmap.green) +
  324.                    sq(gifGmap[i].cmap.blue,  gifCmap[j].cmap.blue);
  325.         if ( dsquare1 < dsquare ) {
  326.           dsquare = dsquare1;
  327.           translator[j]=i;
  328.           if ( dsquare == 0 ) break;
  329.         }
  330.       }
  331.     }
  332.     gifimage.m = 0;
  333.     gifscrn.pixbits = globscrn.pixbits;
  334.     if (debugFlag) fprintf(stderr, "  translating picture no %d\n", count);
  335.     for (i = wi * hi -1; i>=0; i--)
  336.       pix[i]=translator[pix[i]];
  337.     if ( minimize != 0 && pixold != NULL  && hi == gifimageold.height
  338.         && wi == gifimageold.width && gifimage.top == gifimageold.top
  339.         && gifimage.left == gifimageold.left ) {
  340.       gifimageold = gifimage;
  341. /* First test from left to right, top to bottom */
  342.       p = pix; po = pixold;
  343.       for (i = 0; i < hi; i++ ) {
  344.         for (j = 0; j < wi; j++ ) {
  345.           if ( *p++ != *po++ ) {
  346.             left = j; top=i;
  347.             goto done;
  348.           }
  349.         }
  350.       }
  351.       if (FALSE) {
  352. done: /* i.e. a preliminary left and top found */ ;
  353.       }
  354.       else goto alike;
  355. /* Then test from right to left, bottom to top */
  356.       k=hi*wi-1;
  357.       p = &pix[k]; po = &pixold[k];
  358.       for (i = hi-1; i >= top; i-- ) {
  359.         for (j = wi -1; j >= 0; j-- ) {
  360.           if ( *p-- != *po-- ) {
  361.             right = j; bot=i;
  362.             goto botfound;
  363.           }
  364.         }
  365.       }
  366. botfound:
  367. /* The form of the differing area (not rectangle) may be slanted */
  368.       if ( right < left ) {
  369.         i = right; right = left; left = i;
  370.       }
  371. /* Now test between top and bottom at the left hand side */
  372.       for (i = top+1; i <= bot; i++ ) {
  373.         k= i * wi;
  374.         p = &pix[k]; po = &pixold[k];
  375.         for (j = 0; j < left; j++ ) {
  376.           if ( *p++ != *po++ ) {
  377.             left = j;
  378.             break;
  379.           }
  380.         }
  381.       }
  382. /* Finally test between bottom and top at the right hand side */
  383.       for (i = bot-1; i >= top; i-- ) {
  384.         k= (i+1) * wi-1;
  385.         p = &pix[k]; po = &pixold[k];
  386.         for (j = wi-1; j > right; j-- ) {
  387.           if ( *p-- != *po-- ) {
  388.             right = j;
  389.             break;
  390.           }
  391.         }
  392.       }
  393.       gifimage.left += left;
  394.       gifimage.top += top;
  395.       gifimage.width = right-left+1;
  396.       gifimage.height = bot-top+1;
  397.       WriteImageHeader(fout);
  398. /* The rectangle containing diffs is transferred to the mem area of pixold */
  399.       po = pixold;
  400.       for (i = top; i <= bot; i++ ) {
  401.         p = &pix[i * wi+left];
  402.         for (j = left; j <= right; j++ ) {
  403.           *po++ = *p++;
  404.         }
  405.       }
  406.       GifEncode(fout, pixold, gifscrn.pixbits+1, gifimage.height * gifimage.width);
  407.       if (debugFlag)
  408.         fprintf(stderr, "  encoded: width= %d, height = %d, left = %d, top = %d\n",
  409.            gifimage.width, gifimage.height, gifimage.left, gifimage.top);
  410.     }
  411.     else {
  412. alike:
  413.       WriteImageHeader(fout);
  414.       gifimageold = gifimage;
  415.       GifEncode(fout, pix, gifscrn.pixbits+1, gifimage.height * gifimage.width);
  416.       if (debugFlag) fprintf(stderr, "  picture re-encoded\n");
  417. /*   Undocumented feature:  If two subsequent images are alike, then
  418.      send the whole image to the output stream (to keep the timing
  419.      between frames, and not confuse the viewer with empty images) */
  420.     }
  421.     free(pixold);
  422.     pixold = pix;
  423.     fputc(0, fout);    /* block count of zero */
  424.   }
  425.   else {
  426.     WriteImageHeader(fout);
  427.     i = Xgetc(fp); fputc(i, fout); /* the LZW code size */
  428.     while ( ( gifBlockSize = Xgetc(fp) ) > 0 ) {
  429.       fputc(gifBlockSize, fout);
  430.       while ( gifBlockSize-- > 0 ) fputc(Xgetc(fp),fout);
  431.     }
  432.     if ( gifBlockSize == 0 ) fputc(gifBlockSize, fout);
  433.     else TheEnd1("GifPassing: Unexpected End of File\n");
  434.   }
  435.  
  436.   fclose(fp);
  437. }
  438.  
  439.  
  440.  
  441. /*
  442.  * read Gif header
  443.  */
  444. void GifScreenHeader(FILE *fp, FILE *fout, int firstTime)
  445. {
  446.   int temp, i;
  447.  
  448.   for(i = 0; i < 6; i++) {
  449.     temp = Xgetc(fp);
  450.     if(i == 4 && temp == '7') temp = '9';
  451.     if (firstTime) fputc(temp, fout);
  452.   }
  453.  
  454.   gifscrn.width = GifGetShort(fp);
  455.   gifscrn.height = GifGetShort(fp);
  456.   temp = Xgetc(fp);
  457.   if (firstTime) {
  458.     GifPutShort(gifscrn.width, fout);
  459.     GifPutShort(gifscrn.height, fout);
  460.     fputc(temp, fout);
  461.   }
  462.   gifscrn.m  =  temp & 0x80;
  463.   gifscrn.cres   = (temp & 0x70) >> 4;
  464.   gifscrn.pixbits =  temp & 0x07;
  465.  
  466.   gifscrn.bc  = Xgetc(fp);
  467.   if (firstTime) {
  468.     if (debugFlag) fprintf(stderr, "First Time ... ");
  469.     if(GifBgcolor) gifscrn.bc = GifBgcolor & 0xff;
  470.     fputc(gifscrn.bc, fout);
  471.   }
  472.  
  473.   temp = Xgetc(fp);
  474.   if (firstTime)  {
  475.     fputc(temp, fout);
  476.     if ( minimize && gifscrn.bc == 0 ) {
  477.       /* Set a pseudo screen filled with the background color.
  478.          This is only done for background color index == 0 because
  479.          of Netscape and I.E.'s strange handling of backgrounds not
  480.          covered by an image.
  481.       */
  482.       temp = gifscrn.width * gifscrn.height;
  483.       if (( pixold = (UBYTE *)malloc(temp * sizeof(UBYTE)) ) == NULL )
  484.             TheEnd1("No memory for image\n");
  485.       if (debugFlag) fprintf(stderr, "BACKGROUND = %d\n", gifscrn.bc);
  486.       while (temp > 0) pixold[--temp] = 0; /* gifscrn.bc; */
  487.       gifimageold.left = gifimageold.top = 0;
  488.       gifimageold.width = gifscrn.width;
  489.       gifimageold.height = gifscrn.height;
  490.       gifimageold.pixbits = gifscrn.pixbits;
  491.     }
  492.   }
  493.   imagec = gifPtwo[(1+gifscrn.pixbits)];
  494.  
  495.   if (debugFlag)
  496.     fprintf(stderr, "Screen #%d: %dx%dx%d m=%d cres=%d bkgnd=%d pix=%d\n",
  497.       count, gifscrn.width, gifscrn.height, imagec, gifscrn.m, gifscrn.cres,
  498.       gifscrn.bc, gifscrn.pixbits);
  499.  
  500.   if (gifscrn.m) {
  501.     for(i = 0; i < imagec; i++) {
  502.       gifCmap[i].cmap.red   = temp = Xgetc(fp);
  503.       if (firstTime) fputc(temp, fout);
  504.       gifCmap[i].cmap.green = temp = Xgetc(fp);
  505.       if (firstTime) fputc(temp, fout);
  506.       gifCmap[i].cmap.blue  = temp = Xgetc(fp);
  507.       if (firstTime) fputc(temp, fout);
  508.  
  509.     if(firstTime && (global.trans.type==TRANS_RGB && global.trans.valid==0) )
  510.       if (global.trans.red == gifCmap[i].cmap.red &&
  511.           global.trans.green == gifCmap[i].cmap.green &&
  512.           global.trans.blue == gifCmap[i].cmap.blue) {
  513.         if(debugFlag > 1) fprintf(stderr, " Transparent match at %d\n", i);
  514.         global.trans.map = i;
  515.         global.trans.valid = 1;
  516.       }
  517.       else
  518.         if(debugFlag > 1) fprintf(stderr, "No transp. RGB=(%x,%x,%x)\n",
  519.          gifCmap[i].cmap.red, gifCmap[i].cmap.green, gifCmap[i].cmap.blue);
  520.     }
  521.   }
  522. }
  523.  
  524. void ReadImageHeader(FILE *fp)
  525. {
  526.   int tnum, i, flag;
  527.  
  528.   gifimage.left  = GifGetShort(fp);
  529.   if(global.left) gifimage.left += global.left;
  530.  
  531.   gifimage.top   = GifGetShort(fp);
  532.   if(global.top) gifimage.top += global.top;
  533.  
  534.   gifimage.width     = GifGetShort(fp);
  535.   gifimage.height = GifGetShort(fp);
  536.   flag = Xgetc(fp);
  537.  
  538.   gifimage.i       = flag & 0x40;
  539.   gifimage.pixbits = flag & 0x07;
  540.   gifimage.m       = flag & 0x80;
  541.  
  542.   imagex = gifimage.width;
  543.   imagey = gifimage.height;
  544.   tnum = gifPtwo[(1+gifimage.pixbits)];
  545.   if (debugFlag > 1)
  546.     fprintf(stderr, "Image: %dx%dx%d (%d,%d) m=%d i=%d pix=%d \n",
  547.       imagex, imagey, tnum, gifimage.left, gifimage.top,
  548.       gifimage.m, gifimage.i, gifimage.pixbits);
  549.  
  550.    /* if there is an image cmap then read it */
  551.   if (gifimage.m) {
  552.     if(debugFlag>1)
  553.       fprintf(stderr, "DEBUG:Transferring colormap of %d colors\n",
  554.         imagec);
  555.   /*
  556.    * note below assignment, it may make the subsequent code confusing
  557.    */
  558.     gifscrn.pixbits = gifimage.pixbits;
  559.  
  560.     for(i = 0; i < tnum; i++) {
  561.       gifCmap[i].cmap.red   = Xgetc(fp);
  562.       gifCmap[i].cmap.green = Xgetc(fp);
  563.       gifCmap[i].cmap.blue  = Xgetc(fp);
  564.     }
  565.   }
  566.   gifimage.m = 0;
  567.   if ( globscrn.m && globscrn.pixbits == gifscrn.pixbits ) {
  568.     for (i = gifMask[1+globscrn.pixbits]; i >= 0; i--) {
  569.       if (gifGmap[i].cmap.red  != gifCmap[i].cmap.red ||
  570.       gifGmap[i].cmap.green != gifCmap[i].cmap.green ||
  571.       gifGmap[i].cmap.blue != gifCmap[i].cmap.blue ) {
  572.         gifimage.m = 0x80;
  573.         break;
  574.       }
  575.     }
  576.   }
  577.   else gifimage.m = 0x80;
  578.   return;
  579. }
  580.  
  581. void WriteImageHeader(FILE *fout)
  582. {
  583.   int temp, i, flag;
  584.   /* compute a Gif_GCL */
  585.  
  586.   fputc(0x21, fout);
  587.   fputc(0xF9, fout);
  588.   fputc(0x04, fout);
  589.  
  590.   flag = global.disposal <<2;
  591.   if(global.time) flag |= 0x80;
  592.   if(global.trans.type == TRANS_RGB && global.trans.valid == 0)
  593.         gifimage.m = 0x80;
  594.  
  595.   temp = global.trans.map;
  596.   if (gifimage.m != 0 && global.trans.type == TRANS_RGB ) {
  597.     temp = 0; /* set a default value, in case nothing found */
  598.     for (i = gifMask[1+gifscrn.pixbits]; i >= 0; i--) {
  599.       if(global.trans.red == gifCmap[i].cmap.red &&
  600.       global.trans.green == gifCmap[i].cmap.green &&
  601.       global.trans.blue == gifCmap[i].cmap.blue) {
  602.         if(debugFlag > 1) fprintf(stderr, " Transparent match at %d\n", i);
  603.         temp = i;
  604.         flag |= 0x01;
  605.       }
  606.     }
  607.   }
  608.   else if(global.trans.valid) flag |= 0x01;
  609.   fputc(flag, fout);
  610.  
  611.   GifPutShort(global.time, fout); /* the delay speed - 0 is instantaneous */
  612.  
  613.   fputc(temp, fout); /* the transparency index */
  614.   if(debugFlag > 1) {
  615.     fprintf(stderr, "GCL: delay %d", global.time);
  616.     if(flag && 0x1) fprintf(stderr, " Transparent: %d", temp);
  617.     fputc('\n', stderr);
  618.   }
  619.  
  620.   fputc(0, fout);
  621.   /* end of Gif_GCL */
  622.  
  623.   fputc(',', fout); /* image separator */
  624.   GifPutShort(gifimage.left  , fout);
  625.   GifPutShort(gifimage.top   , fout);
  626.   GifPutShort(gifimage.width , fout);
  627.   GifPutShort(gifimage.height, fout);
  628.   fputc(gifscrn.pixbits | gifimage.i | gifimage.m, fout);
  629.  
  630.   if ( gifimage.m ) {
  631.     for(i = 0; i < imagec; i++) {
  632.       fputc(gifCmap[i].cmap.red,   fout);
  633.       fputc(gifCmap[i].cmap.green, fout);
  634.       fputc(gifCmap[i].cmap.blue,  fout);
  635.     }
  636.     if(debugFlag) fprintf(stderr, "Local %d color map for picture #%d\n",
  637.                            imagec, count);
  638.   }
  639. }
  640.  
  641.  
  642. void GifComment(FILE *fout, char *string)
  643. {
  644.   int len;
  645.  
  646.   if( (len = strlen(string)) > 254 ) fprintf(stderr,
  647.                  "GifComment: String too long ; dropped\n");
  648.   else if ( len > 0 ) {
  649.     /* Undocumented feature:
  650.        Empty comment string means no comment block in outfile */
  651.     fputc(0x21, fout);
  652.     fputc(0xFE, fout);
  653.     fputc(len, fout);
  654.     fputs(string, fout);
  655.     fputc(0, fout);
  656.   }
  657.   return;
  658. }
  659.  
  660. /*
  661.  * Write a Netscape loop marker.
  662.  */
  663. void GifLoop(FILE *fout, unsigned int repeats)
  664. {
  665.  
  666.   fputc(0x21, fout);
  667.   fputc(0xFF, fout);
  668.   fputc(0x0B, fout);
  669.   fputs("NETSCAPE2.0", fout);
  670.  
  671.   fputc(0x03, fout);
  672.   fputc(0x01, fout);
  673.   GifPutShort(repeats, fout); /* repeat count */
  674.  
  675.   fputc(0x00, fout); /* terminator */
  676.  
  677.   if(debugFlag) fprintf(stderr, "Wrote loop extension\n");
  678. }
  679.  
  680.  
  681. void CalcTrans(char *string)
  682. {
  683.   if(string[0] != '#') {
  684.     global.trans.type = TRANS_MAP;
  685.     global.trans.map = atoi(string);
  686.     global.trans.valid = 1;
  687.   }
  688.   else {
  689.     /* it's an RGB value */
  690.     int r, g, b;
  691.     string++;
  692.     if (debugFlag > 1) fprintf(stderr, "String is %s\n", string);
  693.     if(3 == sscanf(string, "%2x%2x%2x", &r, &g, &b)) {
  694.       global.trans.red = r;
  695.       global.trans.green = g;
  696.       global.trans.blue = b;
  697.       global.trans.type = TRANS_RGB;
  698.       global.trans.valid = 0;
  699.       if(debugFlag > 1)
  700.         fprintf(stderr, "Transparent RGB=(%x,%x,%x) = (%d,%d,%d)\n",
  701.             r, g, b, r, g, b);
  702.     }
  703.   }
  704.   if(debugFlag > 1)
  705.        fprintf(stderr, "DEBUG:CalcTrans is %d\n", global.trans.type);
  706. }
  707.  
  708. void SetOffset(char *string)
  709. {
  710.   int npar, offX, offY;
  711.   char sep;
  712.   if ( (npar = sscanf(string, "%d%c%d", &offX, &sep, &offY)) == 3 ) {
  713.     /* set the offset */
  714.     global.left = offX;
  715.     global.top = offY;
  716.     if(debugFlag > 1) fprintf(stderr, "Offset set to %d,%d\n",
  717.     global.left, global.top);
  718.     return;
  719.   }
  720.   fprintf(stderr, "Offset string: '%s'; fields = %d\n", string, npar);
  721.   TheEnd1("Couldn't parse offset values.\n");
  722. }
  723.  
  724. void TheEnd()
  725. {
  726.  exit(0);
  727. }
  728.  
  729. void TheEnd1(char *p)
  730. {
  731.  fprintf(stderr, "Image #%d: %s", count, p);
  732.  TheEnd();
  733. }
  734.  
  735. void Usage()
  736. {
  737.   fprintf(stderr, "\nUsage: whirlgif %s\n %s\n %s\n",
  738.         "[-o outfile] [-loop [count]] [-time #delay]",
  739.         "\t-disp [ none | back | prev | not ]",
  740.         "\t[ -i listfile] file1 [ -time #delay] file2 ...");
  741.   exit(0);
  742. }
  743.  
  744. UBYTE Xgetc(FILE *fin)
  745. {
  746.   int i;
  747.   if ( ( i = fgetc(fin) ) == EOF ) {
  748.     TheEnd1("Unexpected EOF in input file\n");
  749.   }
  750.   return(i & 0xff);
  751. }
  752.