1 /*===========================================================================*/
2 /* GifTools - GIF encoder / decoder                                          */
3 /* Copyright (C) 2005 Jarek Tuszynski                                        */
4 /* Distributed under GNU General Public License version 3                    */
5 /*===========================================================================*/
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>   // memset, memcpy
10 #include "GifTools.h"
11 typedef unsigned char uchar;
12 
13 #ifndef USING_R // if not using R language than define following calls:
14   #define print         printf
15   #define Calloc(n, T)  new T[n];
Free(void * p)16   inline void Free(void* p) { delete []p; }
17 #endif
18 
19 //#define STANDALONE_TEST
20 #ifdef STANDALONE_TEST
Error(char * message)21   inline void Error(char *message) { fprintf(stderr, "\nError: %s.\n", message); exit(1); }
22 #endif //STANDALONE_TEST
23 
24 //=======================================================================
25 //  Encoding Algorithm adapted from code by Christoph Hohmann
26 //  found at http://members.aol.com/rf21exe/gif.htm.
27 //  Which was adapted from code by Michael A, Mayer
28 //  found at http://www.danbbs.dk/%7Edino/whirlgif/gifcode.html
29 //  Parts of Decoding Algorithm were adapted from code by David Koblas.
30 //  It had the following notice:
31 //  "Copyright 1990 - 1994, David Koblas. (koblas@netcom.com)
32 //  Permission to use, copy, modify, and distribute this software and its
33 //  documentation for any purpose and without fee is hereby granted, provided
34 //  that the above copyright notice appear in all copies and that both that
35 //  copyright notice and this permission notice appear in supporting
36 //  documentation. This software is provided "as is" without express or
37 //  implied warranty."
38 //=======================================================================
39 
bitGet(int num,int bit)40 inline int bitGet (int  num, int bit) { return ((num &  (1<<bit)) !=0 ); }
41 
42 //==============================================================
43 // bit-packer class
44 //==============================================================
45 
GetDataBlock(FILE * fp,uchar * buffer)46 int GetDataBlock(FILE *fp, uchar *buffer)
47 {
48   int BlockSize = fgetc(fp);
49   if (BlockSize==EOF) return -1;
50   if (BlockSize== 0 ) return  0;
51   if (!fread(buffer, BlockSize, 1, fp)) return -1;
52   return BlockSize;
53 }
54 
55 
56 //=======================================================================
57 // Packs & unpacks a sequence of variable length codes into a buffer. Every
58 // time 255 bytes have been completed, they are written to a binary file as
59 // a data block of 256 bytes (where the first byte is the 'bytecount' of the
60 // rest and therefore equals 255). Any remaining bits are moved to the
61 // buffer start to become part of the following block. After submitting
62 // the last code via submit(), the user must call WriteFlush() to write
63 // a terminal, possibly shorter, data block.
64 //=======================================================================
65 class BitPacker {
66 public:
67 
BitPacker()68   BitPacker()
69   { // Constructor
70     binfile   = NULL;
71     need      = 8;
72     pos       = buffer;
73     *pos      = 0;
74     bytesdone = 0;
75     BlockSize = 255;
76     curbit    = (2+BlockSize)<<3;
77   }
78 
BytesDone()79   int  BytesDone() { return bytesdone; }
GetFile(FILE * bf)80   void GetFile(FILE *bf) { binfile = bf; }
81 
82   //-------------------------------------------------------------------------
83 
SubmitCode(short code,short nBits)84   void SubmitCode(short code, short nBits)
85   //  Packs an incoming 'code' of 'nBits' bits [1,12] to the buffer. As soon
86   //  as 255 bytes are full, they are written to 'binfile' as a data block
87   // end cleared from 'buffer'.
88   {
89     // 'pos' points to a partially empty byte and
90     // 'need' [1..8] tells how many bits will still fit in there.
91     // Since the bytes are filled bottom up (least significant bits first),
92     // the 'need' vacant bits are the most significant ones.
93     if (nBits<0 || nBits>12) Error("BitPacker::SubmitCode");
94     short mask;
95     while (nBits >= need) {
96       mask = (1<<need)-1; // 'mask'= all zeroes followed by 'need' ones
97       *pos += static_cast<uchar>((mask&code) << (8-need)); // the 'need' lowest bits of 'code' fill the current byte at its upper end
98       nBits -= need;  // update the length of 'code'
99       code >>= need;  // remove the written bits from code
100       *(++pos)=0;     // byte is now full, goto next byte & init it
101       need=8;         // current byte can take 8 bits
102     }                 // Now we have nBits < need.
103     // (remainder of) code is written to the nBits rightmost free bits of
104     // the current byte. The current byte can still take 'need' bits, and
105     // we have 'need'>0. The bits will be filled upon future calls.
106     if(nBits>0) {
107       mask  = (1<<nBits)-1;        // 'mask'= all zeroes followed by 'need' ones
108       *pos += static_cast<uchar>((mask&code)<<(8-need));
109       need -= nBits;
110     }
111     // As soon as 255 bytes are full, they are written to 'binfile' as a
112     // data block and removed from 'buffer'.
113     if(pos-buffer >= 255) {         // pos pointing to buffer[255] or beyond
114       fputc(255,binfile);           // write the "bytecount-byte"
115       fwrite(buffer,255,1,binfile); // write buffer[0..254] to file
116       buffer[0] = buffer[255];      // rotate the following bytes, which may still
117       buffer[1] = buffer[256];      // contain data, to the beginning of buffer,
118       pos -= 255;                   // point pos to the position for new input
119       bytesdone += 256;
120     }
121   } // BitPacker::SubmitCode
122 
123   //-------------------------------------------------------------------------
124 
WriteFlush()125   void WriteFlush()
126   //  Writes any data contained in 'buffer' to the file as one data block of
127   //  1<= length<=255.
128   {
129     // if the current byte is partially filled, leave it alone
130     if(need<8) pos++;  // close any partially filled terminal byte
131     int BlockSize = static_cast<int>(pos-buffer);   // # remaining bytes
132     if(BlockSize>0) { // buffer is empty
133       fputc(BlockSize, binfile);
134       fwrite(buffer, BlockSize, 1, binfile);
135       bytesdone += BlockSize+1;
136     }
137   } // BitPacker::WriteFlush
138 
139   //-------------------------------------------------------------------------
140 
GetCode(short nBits)141   short GetCode(short nBits)
142   // Extract nBits [1:32] integer from the buffer.
143   // Read next data block if needed.
144   {
145     short i, j, code, lastbit;
146     // if more bits is needed than we have stored in the buffer
147     lastbit = (2+BlockSize)<<3;     // (BlockSize<<3 == BlockSize*8) - byte to bit conversion
148     while ( (curbit+nBits) >= lastbit ) { // If () should have been enough but used while() just in case
149       buffer[0] = buffer[BlockSize  ];
150       buffer[1] = buffer[BlockSize+1];
151       curbit   -= BlockSize<<3;
152       BlockSize = GetDataBlock(binfile, &buffer[2]);
153       lastbit   = (2+BlockSize)<<3; // (BlockSize<<3 == BlockSize*8) - byte to bit conversion
154       bytesdone += BlockSize+1;     // keep track of number of bytes read
155     }
156     // read next set of nBits from the buffer and store it into ret
157     code = 0;
158     i = curbit;
159     for (j=0; j<nBits; j++, i++)
160       code |= bitGet(buffer[i>>3] , i&7) << j;
161     curbit += nBits; // we read nBits of data from buffer
162     return code;
163   }
164 
165   //-------------------------------------------------------------------------
166 
ReadFlush()167   int ReadFlush()
168   {
169     short count;
170     while ((count = GetDataBlock(binfile, buffer)) > 0);
171     return count;
172   }
173 
174 private:
175   FILE  *binfile;
176   uchar  buffer[260];  // holds the total buffer of 256 + some extra
177   uchar *pos;          // sliding pointer into buffer
178   uchar  need;         // [1..8] tells how many bits will still fit in current byte
179   int bytesdone;       // total number of bytes processed during the object's lifetime
180   int curbit, BlockSize;
181 }; // class bitpacker
182 
183 
184 //==============================================================
185 // Gif-compression de compression functions
186 //==============================================================
187 
188 //===========================================================================
189 // Contains the string-table, generates compression codes and writes them to a
190 // binary file, formatted in data blocks of maximum length 255 with
191 // additional bytecount header.
192 // Encodes the pixel data and writes the "raster data"-section of the GIF
193 // file, consisting of the "code size" byte followed by the counter-headed
194 // data blocks, including the terminating zero block.
195 // bf         must be an opened binary file to which the preceding parts
196 //            of the GIF format have been written
197 // data       is an array of bytes containing one pixel each and sorted
198 //            left to right, top to bottom. The first pixel is in data[0]
199 // nPixel     Number of pixels in the image
200 // nBit       Number of bits per pixel, where 2^nBit is the size of the GIF's
201 //            color tables. Allowed are 1..8. Used to determine 'nbits' and
202 //            the number of root codes. Max(data) HAS to be < 2^nBits
203 // returns:   The total number of bytes that have been written.
204 //-------------------------------------------------------------------------
EncodeLZW(FILE * bf,const uchar * data,int nPixel,short nBits)205 int EncodeLZW(FILE *bf, const uchar *data, int nPixel, short nBits)
206 {
207   BitPacker bp;          // object that does the packing and writing of the compression codes
208   int    iPixel;         // pixel counter
209   uchar  pixel;          // next pixel value to be encoded
210   short  axon[4096], next[4096];  // arrays making up the string-table
211   uchar  pix[4096];      // dito
212   short  freecode;       // next code to be added to the string-table
213   short  i, depth, cc, eoi, up, down, outlet;
214 
215   if (nPixel<0) Error("EncodeLZW: nPixel can not be negative");
216   if (nBits<1 || nBits>8) Error(" EncodeLZW: nBit has to be between 1 and 8");
217 
218   // The following parameters will remain unchanged through the run
219   depth  = (nBits<2 ? 2 : nBits); // number of bits per data item (=pixel). Remains unchanged.
220   cc     = 1<<depth;     // 'cc' or 'clear-code' Signals the clearing of the string-table.
221   eoi    = cc+1;         // 'end-of-information'-code must be the last item of the code stream
222   // initialize pixel reader
223   nBits  = depth+1;      // current length of compression codes in bits [2, 12] (changes during encoding process)
224   iPixel = 0;            // pixel #1 is next to be processed (iPixel will be pixel counter)
225   pixel  = data[iPixel]; // get pixel #1
226   // alocate and initialize memory
227   bp.GetFile(bf);  // object packs the code and renders it to the binary file 'bf'
228   for(i=0; i<cc; i++) pix[i] = static_cast<uchar>(i); // Initialize the string-table's root nodes
229 
230   // Write what the GIF specification calls the "code size". Allowed are [2..8].
231   // This is the number of bits required to represent the pixel values.
232   fputc(depth,bf);             // provide data-depth to the decoder
233   freecode = 4096;             // this will cause string-table flush first time around
234   while(iPixel<nPixel) {       // continue untill all the pixels are processed
235     if(freecode==(1<<nBits))   // if the latest code added to the string-table exceeds 'nbits' bits:
236       nBits++;                 // increase size of compression codes by 1 bit
237     freecode++;                // freecode is only changed in this loop
238     if(freecode>=4096) {       // free code is 2^12 the largest allowed
239       // Flush the string-table  by removing the outlets of all root nodes. Everything is set to initial state.
240       memset(axon, 0, 4096*sizeof(short)); // avoid string-table overflow
241       bp.SubmitCode(cc,nBits); // tell the decoding software to flush its string-table
242       nBits    = depth+1;      // reset nBits
243       freecode = cc+2;         // reset freecode
244     }
245     //  Writes the next code to the codestream and adds one entry to the string-table.
246     outlet=pixel;                 // start with the root node for 'pixel'
247     // Follow the string-table and the data stream to the end of the
248     // longest string that has a code
249     do {
250       up = outlet;
251       iPixel++;                   // advance pixel counter (the only place it is advanced)
252       if(iPixel >= nPixel) break; // end of data stream ? Terminate
253       pixel = data[iPixel];       // get the value of the next pixel
254       // Checks if the chain starting from headnode's axon (axon[up]) contains a node for
255       // 'pixel'. Returns that node's address (=outlet), or 0 if there is no such node.
256       // 0 cannot be the root node 0, since root nodes occur in no chain.
257       outlet = axon[up];
258       while(outlet && pix[outlet]!=pixel) outlet=next[outlet];
259     } while(outlet);
260 
261     // Submit 'up' which is the code of the longest string
262     bp.SubmitCode(up,nBits);
263     if(iPixel >= nPixel) break;  // end of data stream ? Terminate
264 
265     // Extend the string by appending 'pixel':
266     // Create a successor node for 'pixel' whose code is 'freecode'
267     pix [freecode]=pixel;
268     axon[freecode]=next[freecode]=0;
269     // Link it to the end of the chain emanating from axon[up].
270     // Don't link it to the start: it would slow down performance.
271     down=axon[up];
272     if(!down) axon[up]=freecode;
273     else {
274       while(next[down]) down=next[down];
275       next[down]=freecode;
276     }
277   } // while()
278 
279   // Wrap up the file
280   bp.SubmitCode(eoi,nBits); // submit 'eoi' as the last item of the code stream
281   bp.WriteFlush();   // write remaining codes including this 'eoi' to the binary file
282   fputc(0,bf);       // write an empty data block to signal the end of "raster data" section in the file
283   return 2 + bp.BytesDone();
284 } // EncodeLZW
285 
286 
287 //-------------------------------------------------------------------------
288 // Reads the "raster data"-section of the GIF file and decodes the pixel
289 // data.  Most work is done by GifDecomposer class and this function mostly
290 // handles interlace row irdering
291 // bf         must be an opened binary file to which the preceding parts
292 //            of the GIF format have been written
293 // data       is an array of bytes containing one pixel each and sorted
294 //            left to right, top to bottom.
295 // nPixels    Number of pixels in the image
296 // returns:   The total number of bytes that have been written.
297 //-------------------------------------------------------------------------
DecodeLZW(FILE * fp,uchar * data,int nPixel)298 int DecodeLZW(FILE *fp, uchar *data, int nPixel)
299 {
300   BitPacker bp;                 // object that does the packing and writing of the
301   short cc, eoi, freecode, nBits, depth, nStack, code, incode, firstcode, oldcode;
302   short pix[4096], next[4096];
303   uchar stack[4096];
304   int iPixel, ret;
305 
306   freecode=nBits=firstcode=oldcode=0; // unnecesary line used to prevent warnings in gcc
307   depth = fgetc(fp);             // number of bits per data item (=pixel). Remains unchanged.
308   if (depth==EOF) return -1;
309   bp.GetFile(fp);                // object packs the code and renders it to the binary file 'bf'
310   cc    = 1<<depth;              // 'cc' or 'clear-code' Signals the clearing of the string-table.
311   eoi   = cc+1;                  // 'end-of-information'-code must be the last item of the code stream
312 
313   code = cc;                     // this will force the code to flush & initialize the string-table first
314   for(iPixel=0; iPixel<nPixel; ) { // read until all pixels were read
315     if (iPixel) code=bp.GetCode(nBits);
316     if (code == -1)  return 0;   // error
317     if (code == eoi) break;      // 'end-of-information' - last item of the code stream
318     if (code == cc) {            // Flush the string-table. Everything is (re)set to initial state.
319       memset(next, 0, 4096*sizeof(short));
320       memset(pix , 0, 4096*sizeof(short));
321       for (int i=0; i<cc; i++) pix[i] = i;
322       nBits    = depth+1;
323       freecode = cc+2;
324       do { firstcode = bp.GetCode(nBits); } while (firstcode==cc); // keep on flushing until a non cc entry
325       oldcode = firstcode;
326       data[iPixel++] = static_cast<uchar>(firstcode);
327     } else {                     // the regular case
328       nStack = 0;                // (re)initialize the stack
329       incode = code;             // store a copy of the code - it will be needed
330       if (code >= freecode) {
331         stack[nStack++] = static_cast<uchar>(firstcode);
332         code            = oldcode;
333       }
334       while (code >= cc) {       // read string for code from string-table
335         if (nStack>=4096) return 0;   // error
336         stack[nStack++] = static_cast<uchar>(pix[code]);
337         code            = next[code];
338       }
339       firstcode      = pix[code];
340       data[iPixel++] = static_cast<uchar>(pix[code]);
341       while (nStack && iPixel<nPixel)
342         data[iPixel++] = stack[--nStack]; // if there is data on the stack return it
343       if (freecode<4096) {       // free code is smaller than 2^12 the largest allowed
344         next[freecode] = oldcode;// add to string-table
345         pix [freecode] = firstcode;
346         freecode++;
347         if(freecode==(1<<nBits)) // if the latest code added to the string-table exceeds 'nbits' bits:
348           nBits++;               // increase size of compression codes by 1 bit
349       }
350       oldcode = incode;
351     }
352   }
353   if (bp.ReadFlush()==0) ret = 1+bp.BytesDone(); // 'end-of-information' - last item of the code stream
354   else ret = 0;
355   return ret;
356 }
357 
358 
359 //==============================================================
360 // Gif Writer
361 //==============================================================
362 
getint(uchar * buffer)363 inline int getint(uchar *buffer) { return (buffer[1]<<8) | buffer[0]; }
fputw(int w,FILE * fp)364 inline void fputw(int w, FILE *fp)
365 { //  Write out a word to the GIF file
366   fputc(  w     & 0xff, fp );
367   fputc( (w>>8) & 0xff, fp );
368 }
369 
370 //------------------------------------------
371 
372 
imwriteGif(const char * filename,const uchar * data,int nRow,int nCol,int nBand,int nColor,const int * ColorMap,bool interlace,int transparent,int DalayTime,char * comment)373 int imwriteGif(const char* filename, const uchar* data, int nRow, int nCol, int nBand, int nColor,
374                const int *ColorMap,  bool interlace, int transparent, int DalayTime, char* comment)
375 {
376   int B, i, rgb, imMax, filesize=0, Bands, band, n, m;
377   int BitsPerPixel=0, ColorMapSize, Width, Height, nPixel;
378   char fname[256], sig[16], *q;
379   const uchar *p=data;
380 
381   strcpy(fname,filename);
382   i = static_cast<int>(strlen(fname));
383   if (fname[i-4]=='.') strcpy(strrchr(fname,'.'),".gif");
384   Width  = nCol;
385   Height = nRow;
386   Bands  = nBand;
387   nPixel = Width*Height;
388   imMax  = data[0];
389   n = nPixel*nBand;
390   for(i=0; i<n; i++, p++) if(imMax<*p) imMax=*p;
391   nColor=(nColor>256 ? 256 : nColor);     // is a power of two between 2 and 256 compute its exponent BitsPerPixel (between 1 and 8)
392   if (!nColor) nColor = imMax+1;
393   if (imMax>nColor)
394     Error("ImWriteGif: Higher pixel values than size of color table");
395   for(i=1; i<nColor; i*=2) BitsPerPixel++;
396   if (BitsPerPixel==0) BitsPerPixel=1;
397 
398   FILE *fp = fopen(fname,"wb");
399   if (fp==0) return -1;
400 
401   //====================================
402   // GIF Signature and Screen Descriptor
403   //====================================
404   if (transparent>=0 || comment || Bands>1) strcpy(sig,"GIF89a"); else strcpy(sig,"GIF87a");
405   fwrite( sig, 1, 6, fp );           // Write the Magic header
406   fputw( Width , fp );               // Bit 1&2 : Logical Screen Width
407   fputw( Height, fp );               // Bit 3&4 : Logical Screen Height
408   B = 0xf0 | (0x7&(BitsPerPixel-1)); // write BitsPerPixel-1 to the three least significant bits of byte 5
409   fputc( B, fp );                    // Bit 5: global color table (yes), color resolution, sort flag (no) size of global color table
410   fputc( 0, fp );                    // Bit 6: Write out the Background color index
411   fputc( 0, fp );                    // Bit 7: Byte of 0's (no aspect ratio info)
412 
413   //====================================
414   // Global Color Map
415   //====================================
416   ColorMapSize = 1 << BitsPerPixel;
417   if (ColorMap) {
418     for( i=0; i<nColor; i++ ) {      // Write out the Global Colour Map
419       rgb = ColorMap[i];
420       fputc( (rgb >> 16) & 0xff, fp );
421       fputc( (rgb >>  8) & 0xff, fp );
422       fputc(  rgb        & 0xff, fp );
423     }
424   } else { // default gray-scale ramp
425     for( i=0; i<nColor; i++ ) {        // Write out the Global Colour Map
426       rgb = ((i*256)/nColor)  & 0xff;
427       fputc( rgb, fp );
428       fputc( rgb, fp );
429       fputc( rgb, fp );
430     }
431   }
432   for( i=nColor; i<ColorMapSize; i++ ) {  fputc(0,fp); fputc(0,fp); fputc(0,fp);  }
433 
434   //====================================
435   // Extentions (optional)
436   //====================================
437   n = (comment ? static_cast<int>(strlen(comment)) : 0);
438   if (n>0) {
439 	  fputc( 0x21, fp ); // GIF Extention Block introducer
440 	  fputc( 0xfe, fp ); // "Comment Extension"
441     for (q=comment; n>0; n-=255) {
442       m = n<255 ? n : 255;
443       fputc(m, fp );
444       fwrite(q, 1, m, fp );
445       q += m;
446       filesize += m+1;
447     }
448 	  fputc( 0, fp );    // extention Block Terminator
449     filesize += 3;
450 	}
451   if (Bands>1) {
452 	  fputc( 0x21, fp ); // GIF Extention Block introducer
453 	  fputc( 0xff, fp ); // byte 2: 255 (hex 0xFF) Application Extension Label
454     fputc( 11, fp );   // byte 3: 11 (hex (0x0B) Length of Application Block
455     fwrite("NETSCAPE2.0", 1, 11, fp ); // bytes 4 to 14: 11 bis of first sub-block
456     fputc( 3, fp );    // byte 15: 3 (hex 0x03) Length of Data Sub-Block (three bytes of data to follow)
457     fputc( 1, fp );    // byte 16: 1-means next number 2 bytes have iteration counter; 2-means next 4 bytes haveamount of memory needed
458     fputw( 0, fp );    // byte 17&18: 0 to 65535, an unsigned integer. # of iterations the loop should be executed.
459 	  fputc( 0, fp );    // extention Block Terminator
460     filesize += 19;
461 	}
462 
463 
464   filesize += 6 + 7 + 3*ColorMapSize;
465   for (band=0; band<Bands; band++) {
466     if ( transparent >= 0 || Bands>1 ) {
467       fputc( 0x21, fp );                // GIF Extention Block introducer "!"
468       fputc( 0xf9, fp );                // "Graphic Control Extension"
469       fputc( 4, fp );                   // block is of size 4
470       B  = (Bands>1 ? 2 : 0) << 2;      // Disposal Method
471       B |= (0) << 1;                    // User Input flag: is user input needed?
472       B |= (transparent >= 0 ? 1 : 0);  // Transparency flag
473       fputc( B, fp );                   // "transparency color follows" flag
474       fputw( DalayTime, fp );           // delay time in # of hundredths (1/100) of a second delay between frames
475       fputc( static_cast<uchar>(transparent), fp );
476       fputc( 0, fp );                   // extention Block Terminator
477       filesize += 8;
478     }
479 
480     //====================================
481     // Image Descriptor
482     //====================================
483     fputc( 0x2c  , fp );                // Byte 1  : Write an Image Separator ","
484     fputw( 0     , fp );                // Byte 2&3: Write the Image left offset
485     fputw( 0     , fp );                // Byte 4&5: Write the Image top offset
486     fputw( Width , fp );                // Byte 6&7: Write the Image width
487     fputw( Height, fp );                // Byte 8&9: Write the Image height
488     fputc( interlace ? 0x40 : 0x00,fp); // Byte 10 : contains the interlaced flag
489     filesize += 10;
490 
491     //====================================
492     // Raster Data (LZW encrypted)
493     //====================================
494     p = data+band*nPixel;
495     if(interlace) { // rearrange rows to do interlace
496       int i, row=0;
497       uchar* tmp = new uchar[Width*Height];
498       for (i=0; i<Height; i+=8) memcpy(tmp+Width*(row++), p+Width*i, Width);
499       for (i=4; i<Height; i+=8) memcpy(tmp+Width*(row++), p+Width*i, Width);
500       for (i=2; i<Height; i+=4) memcpy(tmp+Width*(row++), p+Width*i, Width);
501       for (i=1; i<Height; i+=2) memcpy(tmp+Width*(row++), p+Width*i, Width);
502       filesize += EncodeLZW(fp, tmp, nPixel, BitsPerPixel);
503       delete []tmp;
504     } else filesize += EncodeLZW(fp, p, nPixel, BitsPerPixel);
505   }
506 
507   fputc(0x3b, fp );                     // Write the GIF file terminator ";"
508   fclose(fp);
509   return filesize+1;
510 }
511 
512 //==============================================================
513 // Gif Reader
514 // Limitations:
515 // - single colormap (global or local)
516 // - all bands will have same dimentions
517 //==============================================================
518 
ReadColorMap(FILE * fp,uchar byte,int ColorMap[256],int skip=0)519 int ReadColorMap(FILE *fp, uchar byte, int ColorMap[256], int skip=0)
520 {
521   int i, nColor, ok=1;
522   uchar buf[16];
523   nColor = 2<<(byte&0x07);
524   if ((byte&0x80)==0x80) {
525     if (skip) {
526       char buffer[3*255];
527       if (!fread(buffer, 3*nColor, 1, fp)) return 0;
528     } else {
529       for (i=0; i<nColor; i++) {
530         if (!fread(buf, 3, 1, fp)) return 0;
531         ColorMap[i] = (buf[0]<<16) | (buf[1]<<8) | buf[2];
532       }
533       while(i<256) ColorMap[i++] = -1;
534     }
535     ok = 2;
536   }
537   return ok;
538 }
539 
540 //------------------------------------------
541 
append(uchar * trg,uchar * src,int nPixel,int nBand)542 uchar* append(uchar *trg, uchar *src, int nPixel, int nBand )
543 {
544   uchar* data = Calloc(nPixel*(nBand+1), uchar);
545   int n = nPixel*nBand*sizeof(uchar);
546   memcpy(data  , trg, n);
547   memcpy(data+n, src, nPixel*sizeof(uchar));
548   Free(trg);
549   Free(src);
550   return data;
551 }
552 
553 //------------------------------------------
554 
imreadGif(const char * filename,int nImage,bool verbose,uchar ** data,int & nRow,int & nCol,int & nBand,int ColorMap[255],int & Transparent,char ** Comment)555 int imreadGif(const char* filename, int nImage, bool verbose,
556               uchar** data, int &nRow, int &nCol, int &nBand,
557               int ColorMap[255], int &Transparent, char** Comment)
558 {
559   bool interlace;
560   uchar buffer[256], *cube=0, *image=0;
561   int Width, Height, i, c, iImage, ret, DelayTime, stats, done, n, m, nColMap=0, filesize=0;
562   char version[7], fname[256], *p, *comment=0;
563 
564   *data=NULL;
565   *Comment=NULL;
566   Width=Height=nRow=nCol=nBand=0;
567   ret=Transparent=-1;
568   strcpy(fname,filename);
569   i = static_cast<int>( strlen(fname));
570   if (fname[i-4]=='.') strcpy(strrchr(fname,'.'),".gif");
571   FILE *fp = fopen(fname,"rb");
572   if (fp==0) return -1;
573 
574   //====================================================
575   // GIF Signature, Screen Descriptor & Global Color Map
576   //====================================================
577   if (!fread(version, 6, 1, fp)) return -2;    // Read Header
578   version[6] = '\0';
579   if ((strcmp(version, "GIF87a") != 0) && (strcmp(version, "GIF89a") != 0)) return -2;
580   if (!fread(buffer, 7, 1, fp)) return -3;     // Read Screen Descriptor
581   if(verbose) print("GIF image header\n");
582   i = ReadColorMap(fp, buffer[4], ColorMap);   // Read Global Colormap
583   if (i==0) return -3;
584   if (i==2) nColMap++;
585   if(verbose) {
586     if(i==2) print("Global colormap with %i colors \n", 2<<(buffer[4]&0x07));
587     else     print("No global colormap provided\n");
588   }
589   filesize += 6 + 7 + 3*256;
590 
591   //====================================================
592   // Raster Data of encoded images and Extention Blocks
593   //====================================================
594   iImage = stats = done = 0;
595   while(!stats && !done) {
596     c = fgetc(fp);
597     switch(c) {
598       case EOF:  stats=3; break;                        // unexpected EOF
599 
600       case 0x3b:                                        // GIF terminator ";"
601         done =1;
602         if(verbose) print("GIF Terminator\n");
603         break;
604 
605       case 0x21:                                        // GIF Extention Block introducer
606         c = fgetc(fp);
607         switch (c) {
608           case EOF : stats=3; break;                    // unexpected EOF
609           case 0xf9:                                    // "Graphic Control Extension"
610             n = GetDataBlock(fp, buffer);               // block is of size 4
611             if (n==4) {                                 // block has to be of size 4
612               DelayTime = getint(buffer+1);
613               if ((buffer[0] & 0x1) != 0) Transparent = buffer[3];
614               if(verbose)
615                 print("Graphic Control Extension (delay=%i transparent=%i)\n",
616                      DelayTime, Transparent);
617             }
618             while (GetDataBlock(fp, buffer) != 0);      // look for block terminator
619             break;
620           case 0xfe:                                    // "Comment Extension"
621             m = (comment ? static_cast<int>(strlen(comment)) : 0);
622             while ((n=GetDataBlock(fp, buffer)) != 0) { // look for block terminator
623                p = Calloc(m+n+1,char);
624                if(m>0) {                                // if there was a previous comment than whey will be concatinated
625                  memcpy(p,comment,m);
626                  free(comment);
627                }
628                comment = p;
629                strncpy(comment+m, (char*) buffer, n);
630                m+=n;
631                comment[m]=0;
632             }
633             if(verbose) print("Comment Extension\n");
634             break;
635           case 0xff:                                    // "Software Specific Extension" most likelly NETSCAPE2.0
636             while (GetDataBlock(fp, buffer) != 0);      // look for block terminator
637             if(verbose) print("Animation Extension\n");
638             break;
639           case 0x01:                                    // "Plain Text Extension"
640             while (GetDataBlock(fp, buffer) != 0);      // look for block terminator
641             if(verbose) print("Plain Text Extension (ignored)\n");
642             break;
643           default:                                      // Any other type of Extension
644             while (GetDataBlock(fp, buffer) != 0);      // look for block terminator
645             if(verbose) print("Unknown Extension %i\n", c);
646             break;
647         }
648         break;
649 
650       case 0x2c:  // Image separator found
651         //====================================
652         // Image Descriptor
653         //====================================
654         if (!fread(buffer, 9, 1, fp)) {stats=3; break;} // unexpected EOF
655         Width     = getint(buffer+4); // Byte 6&7: Read the Image width
656         Height    = getint(buffer+6); // Byte 8&9: Read the Image height
657         interlace = ((buffer[8]&0x40)==0x40);
658         if(verbose) print("Image [%i x %i]: ", Height, Width);
659         if (!nImage && nBand>0 && (nRow!=Height || nCol!=Width)) {stats=5; break;}
660 
661         //=============================================
662         // Local Color Map & Raster Data (LZW encrypted)
663         //=============================================
664         i = ReadColorMap(fp, buffer[8], ColorMap, nColMap*nImage); // Read local Colormap
665         if (i==0) {stats=3; break;} // EOF found during reading local colormap
666         if (i==2) nColMap++;
667         if(image) Free(image);
668         image = Calloc(Height*Width, uchar);
669         ret   = DecodeLZW(fp, image, Height*Width);
670 //        if (ret==0) {stats=4; break;} // syntax error
671         if(interlace) {
672           int i, row=0;
673           uchar* to   = image;
674           uchar* from = new uchar[Width*Height];
675           memcpy(from, to, Width*Height);
676           for (i=0; i<Height; i+=8) memcpy(to+Width*i, from+Width*(row++), Width);
677           for (i=4; i<Height; i+=8) memcpy(to+Width*i, from+Width*(row++), Width);
678           for (i=2; i<Height; i+=4) memcpy(to+Width*i, from+Width*(row++), Width);
679           for (i=1; i<Height; i+=2) memcpy(to+Width*i, from+Width*(row++), Width);
680           delete []from;
681         }
682         if (!nImage) {              // save all bands
683           if (!nBand) cube = image; // first image
684           else cube = append(cube, image, Height*Width, nBand);
685           nBand++;
686         } else {                    // replace each image with new one
687           if(cube) Free(cube);
688           cube  = image;
689           nBand = 1;
690         }
691         image = 0;
692         nRow = Height;
693         nCol = Width;
694         if (ret==0) {stats=4; break;} // DecodeLZW exit without finding file terminator
695         else filesize += ret+10;
696         if (++iImage==nImage) done=1;
697         if(verbose) print("%i bytes \n", ret);
698         if(verbose && i==2) print("Local colormap with %i colors \n", 2<<(buffer[4]&0x07));
699         break;
700 
701       default:
702         if(verbose) print("Unexpected character %c (%i)\n", c, c);
703         stats=4;
704         break;
705     }
706   } // end while
707   if(verbose) print("\n");
708   *Comment = comment;
709   *data = cube;
710   nRow  = Height;
711   nCol  = Width;
712   if (nImage==0 && nColMap>1) stats += 6;
713   if (stats) filesize = -stats; // if no image than save error #
714   return filesize;
715 }
716 
717 
718 //==============================================================
719 // Section below is used in interface with Matrix Library
720 //==============================================================
721 #ifdef MATRIX_INTERFACE
722 
imwriteGif(const bMatrix & im,const char * filename,const iMatrix ColorMap,bool interlace,int transparent,int delayTime,char * comment)723   template <> int imwriteGif<uchar>(const bMatrix &im, const char* filename, const iMatrix ColorMap,
724                                     bool interlace, int transparent, int delayTime, char* comment)
725   {
726     int ret = imwriteGif(filename, im->d(), im->rows(), im->cols(), im->bands(),
727       ColorMap->len(), ColorMap->d(), interlace, transparent, delayTime, comment);
728     if (ret<0) Error("write.gif: cannot open the output GIF file");
729     return ret;
730   }
731 
imreadGif(bMatrix & im,const char * filename,iMatrix & ColorMap,int imageNumber)732   int imreadGif(bMatrix &im, const char* filename, iMatrix &ColorMap, int imageNumber)
733   {
734     int nRow, nCol, nBand, transparent, stats, success, nPixel;
735     char *comment=0;
736     uchar* data=0;
737 
738     ColorMap->init(256);
739     // initialize data
740     nRow=nCol=nBand=transparent=0;
741     comment = NULL;
742     success = imreadGif(filename, imageNumber, false, &data, nRow, nCol,
743               nBand, ColorMap->d(), transparent, &comment);
744     nPixel = nRow*nCol*nBand;
745     if(comment) Free(comment);
746     stats = -success;
747 
748     if (stats>=6)  {
749       print("write.gif: file '", filename,
750         "' contains multiple color-maps. Use 'frame' > 0.");
751       stats = stats-6;
752     }
753     if (nPixel==0) {
754       switch (stats) {
755         case 1: Error("write.gif: cannot open the input GIF file");
756         case 2: Error("write.gif: input file is not a GIF file");
757         case 3: Error("write.gif: unexpected end of input GIF file");
758         case 4: Error("write.gif: syntax error in input GIF file");
759       }
760     } else {
761       switch (stats) { // warnings
762         case 3: print("write.gif: unexpected end of input GIF file: ", filename);
763         case 4: print("write.gif: syntax error in input GIF file: ", filename);
764         case 5: print("write.gif: file '", filename,
765           "' contains multiple images (frames) of uneven length. Use 'imageNumber' > 0." );
766       }
767     }
768     im->moveto(data, nRow*nCol*nBand);
769     im->resize(nBand, nRow, nCol);
770     return success;
771   }
772 
773 #endif
774 
775 
776 //==============================================================
777 // Section below is used in standalone test application
778 //==============================================================
779 #ifdef STANDALONE_TEST
780 
main()781 int main()
782 {
783   bool interlace;
784   int nRow, nCol, nBand, ColorMap[256], transparent, *Data=0, DelayTime, nImage, succes, n;
785   char *Comment=0, str[256];
786   uchar *data=0;
787 
788   interlace = 0;
789   DelayTime = 0;
790   nImage    = 0;
791   succes = imreadGif ("bats.gif", nImage, (bool) 1, &data, nRow, nCol, nBand, ColorMap, transparent, &Comment);
792   printf("Image read = [%i x %i x %i]: %i\n",nRow, nCol, nBand, succes);
793   if (1) {
794     n = nRow*nCol;
795     strcpy(str, "hello world");
796     succes = imwriteGif("tmp.gif", data, nRow, nCol, nBand, 256, ColorMap, interlace, transparent, DelayTime, str);
797     printf("Image written = [%i x %i x %i]: %i\n",nRow, nCol, nBand, succes);
798   }
799   printf("Press any key\n");
800   getchar();
801   return 0;
802 }
803 
804 #endif
805 
806 
807 
808