/******************************************************************************
 * program:     rasimg library                                                *
 * function:    Library for saving/loading pictures                           *
 * modul:       ras_gif.cc                                                    *
 * licency:     GPL or LGPL                                                   *
 ******************************************************************************/
#include <stdio.h>

#include <stdlib.h>

#include "typedfs.h"
#include "common.h"

#ifdef HAVE_VALUES_H
 #include <values.h>
#else
 #define MAXLONG     0x7FFFFFFF
#endif

#include "image.h"
#include "img_tool.h"
#include "ras_prot.h"
#include "std_str.h"

#ifndef __UNIX__
 #include <io.h>
#endif

#include "imgsupp.h"
#include "struct.h"


// https://tronche.com/computer-graphics/gif/gif89a.html

//--------------------------GIF---------------------------
#ifdef SupportGIF

typedef struct
	{
	char SignatureMain[3];
	char SignatureRelease[3];
	uint16_t Width, Height;
	uint8_t Flag, BackGround, Aspect;
	} TypeGIFHeader;

typedef struct
	{
	uint16_t Left, Top, Width, Height;
	uint8_t Flag;	//Local Color Table Flag 1 Bit; Interlace Flag 1 Bit; Sort Flag 1 Bit; Reserved 2 Bits; Size of Local Color Table 3 Bits
	} TypeGIFImageDescriptor;


#if SupportGIF>=4 || SupportGIF==2

inline long LoadGIFHeader(FILE *f,TypeGIFHeader &SU)
{
#if defined(__PackedStructures__)
return(fread(&SU,1,sizeof(SU),f));
#else
return(loadstruct(f,"a3a3wwbbb",
	 &SU.SignatureMain,&SU.SignatureRelease,
	 &SU.Width,&SU.Height,&SU.Flag,&SU.BackGround,&SU.Aspect));
#endif
}

inline long LoadGIFImageDescriptor(FILE *f,TypeGIFImageDescriptor &SU)
{
#if defined(__PackedStructures__)
return(fread(&SU,1,sizeof(SU),f));
#else
return(loadstruct(f,"wwwwb",
	 &SU.Left,&SU.Top,&SU.Width,&SU.Height,&SU.Flag));
#endif
}

class ABitStream
	{
public: uint8_t *ptr;
	uint8_t *RealBufStart, *BufEnd;
	uint32_t BitBuf;
	uint16_t BufSize;
	uint8_t BitPos;

	long FileLeft;
	FILE *f;

	//uint8_t *BufPhysEnd;
	uint8_t m;
	uint8_t n;

	void InitBitStream(void *p, uint16_t NewBufSize, FILE *file, long fsize);
	uint16_t Read_N(uint8_t BitCount);
	virtual void RdFromFile(void)=0;	// Load bits from GIF file
	};

//This procedure make initialization of the bit stream
void ABitStream::InitBitStream(void *p, uint16_t NewBufSize, FILE *file, long fsize)
{
//----------------------}
  m = 0x01;
  //BufPhysEnd = (uint8_t *)p+BufSize-1;
  n=0;
//---------------------}
  FileLeft = fsize;
  BufSize = NewBufSize & ~(uint16_t)1;
  RealBufStart = (uint8_t *)p;
  ptr = (uint8_t *)p;
  *RealBufStart = 0;
  BufEnd = RealBufStart;
  BitPos = 32;

  f = file;
}

/** This procedure reads n bits from the bit stream. */
uint16_t ABitStream::Read_N(uint8_t BitCount)
{
static const uint16_t bmsk[17]={0x0,0x1,0x3,0x7,0xF,0x1F,0x3F,0x7F,0xFF,
		     0x1FF,0x3FF,0x7FF,0xFFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF};
uint16_t u;

 if(BitPos+BitCount>=32)
 {
   if(BufEnd-ptr==1)	// Last byte in a chunk remains.
   {
     BitBuf = BitBuf >> 8;
     BitPos -= 8;
     BitBuf |= ( (uint32_t)(*(uint8_t*)ptr) << 24 );
     ptr++;
     if(BitPos+BitCount>=32) 
       goto MustReadChunk;	// Byte added is not sufficient enough.
   }
   else
   {
MustReadChunk:
     BitBuf = BitBuf >> 16;
     BitPos -= 16;
     if(ptr>=BufEnd)
     {
       RdFromFile();
     }
#ifdef HI_ENDIAN
     BitBuf |= ( (uint32_t)(*(uint8_t *)ptr) << 16 );
     ptr++;
     BitBuf |= ( (uint32_t)(*(uint8_t *)ptr) << 24 );
     ptr++;
#else
     BitBuf |= ( (uint32_t)(*(uint16_t *)ptr) << 16 );
     ptr+=2;
#endif
   }
 }
 u = BitBuf >> BitPos;
 BitPos += BitCount;
 return(u & bmsk[BitCount]);
}

class GIFBitStream:public ABitStream
	{
public: virtual void RdFromFile(void);	// Load bits from GIF file
	};

void GIFBitStream::RdFromFile(void)		// Load bits from GIF file
{
uint8_t len;
uint16_t ReadedB;

 ReadedB = 0;

 ptr = RealBufStart;
 if(FileLeft>0)
 {
   ReadedB = fread(&len,1,1,f);
   if(ReadedB!=1 || len==0) 
       {ReadedB=0;}
   else
   {     
     //if(Flag==1) ptr++;
     ReadedB = fread(ptr,1,len,f);
     FileLeft -= ReadedB;
   }

   if(ReadedB==0) 
   {
     FileLeft=0;		//EOF
     *ptr = 0;			// Feed first Word in buffer with zero.
   }
 }

 BufEnd = RealBufStart;
 BufEnd += ReadedB;
// if IOResult<>0 then FileLeft:=-1; {error}
}


void Add2Pixel(Raster2DAbstract *Raster, uint16_t Value, uint16_t &x, uint16_t &y, int &Interlace)
{
 if(!Raster) return;
 Raster->SetValue2D(x,y,Value);
 x++;
 if(x<Raster->GetSize1D()) return;
 x=0;
// if AlineProc!=NULL   AlineProc^.NextLine;

 if(Interlace<0)
	{
	y++;
	return;
	}

 switch(Interlace)
     {
     case 0:y+=8;
	    if(y>=Raster->Size2D)
	      {
	      Interlace++;
	      y=4;
	      }
	    break;
     case 1:y+=8;
	   if(y>=Raster->Size2D)
	      {
	      Interlace++;
	      y=2;
	      }
	   break;
     case 2:y+=4;
	    if(y>=Raster->Size2D)
	      {
	      Interlace++;
	      y=1;
	      }
	    break;
     case 3:y+=2;
	    break;
     }
}   //..Add2Pixel..}


Image LoadPictureGIF(const char *Name)
{
uint8_t *OutCode=NULL;
uint16_t *Prefix=NULL,*Suffix=NULL;
uint16_t Nacteno;
FILE *f;
Image Img,*CurrImg;
uint16_t InCode,OldCode,Code,ClearCode,OutCount,FirstFree,FreeCode,EOFCode,MaxCode;
uint8_t FinChar,BitMask,CodeSize,InitCodeSize;
uint16_t X,Y;
int Interlace;
TypeGIFHeader GIFHeader;
TypeGIFImageDescriptor GIFImageDescriptor;
GIFBitStream bit;
void *CompBuffer;
Raster2DAbstract *Raster=NULL;
APalette *Palette=NULL,*LocalPalette=NULL;
const int GIFBufSize=256;
int I;

  CompBuffer=NULL;
  CurrImg=&Img;

  if((f=fopen(Name,"rb"))==NULL) return(NULL);

  if((X=LoadGIFHeader(f,GIFHeader))<13) goto FINISH;
  if(strncmp(GIFHeader.SignatureMain,"GIF",3)) goto FINISH;

  if(GIFHeader.Width==0||GIFHeader.Height==0) goto FINISH;
// if AlineProc!=NULL AlineProc^.InitPassing(Obr.y,'Loading GIF');

//  seek(soubor,Sizeof(GIFHeader));

  if((GIFHeader.Flag&0x80) > 0)  	/*Global Color Map*/
  {
	X = (unsigned)2 << (GIFHeader.Flag & 7);
	Palette = BuildPalette(X,8);
	if(Palette==NULL) goto FINISH;
	if(Palette->Data1D==NULL) goto FINISH;
	X *= 3;
	Nacteno = fread(Palette->Data1D,1,X,f);
	if(Nacteno<X) goto FINISH;
  }
  /*?-Global Color Map*/

  if((OutCode=(uint8_t *)malloc(4096))==NULL) goto FINISH;
  if((CompBuffer=(uint8_t *)malloc(GIFBufSize))==NULL) goto FINISH;
  if((Prefix=(uint16_t *)calloc(4096,sizeof(uint16_t)))==NULL) goto FINISH;
  if((Suffix=(uint16_t *)calloc(4096,sizeof(uint16_t)))==NULL) goto FINISH;

  while(!feof(f))
  {
    I = fgetc(f);
    //fpos_t pos;fgetpos(f,&pos);printf("%lX,%X\n",pos,I);
    switch(I)
       {
       case EOF:goto FINISH;	//Next Byte

       case 0x21:				// '!' GIF Extended Blok
	  if(fread(OutCode,1,2,f)!=2) goto FINISH;
	   //      WriteLn('Extension block type: ',Outcode^[0]);}
	  CodeSize = OutCode[1];

#ifndef NO_EXTENSIONS
	  if(OutCode[0]==0xFE && CodeSize>0)		// Read comment extension
          {
            PropertyItem *Prop = new PropertyItem("comment");
            if(Prop)
            {
	      while(CodeSize>0)
              {
	        Nacteno = fread(OutCode,1,CodeSize+1,f);
	        if(Nacteno!=CodeSize+1) {delete(Prop);goto FINISH;}
                Prop->Append(OutCode,CodeSize);
                CodeSize = OutCode[CodeSize];
              } while(CodeSize>0);
	      break;
              if(Prop->Data && Prop->DataSize>0)
		  Img.Properties.AddProp(Prop);
              else
                  delete(Prop);
            }
	  }
#endif
			// dummy read blob
	  while(CodeSize>0)
	  {
	     Nacteno = fread(OutCode,1,CodeSize+1,f);
	     if(Nacteno!=CodeSize+1) goto FINISH;
	     CodeSize = OutCode[CodeSize];
	  }
	  break;

       case 0x2C:
	  if(LoadGIFImageDescriptor(f,GIFImageDescriptor)!=9) goto FINISH;
	  Interlace = -1;
	  if((GIFImageDescriptor.Flag & 64)>0) Interlace=0;
	  /*?-Image Descriptor*/
	  //fprintf(F,"New raster W=%u, H=%u\n", GIFImageDescriptor.Width,GIFImageDescriptor.Height);

	  Raster = CreateRaster2D(GIFImageDescriptor.Width,GIFImageDescriptor.Height,
		NearAvailPlanes((((GIFImageDescriptor.Flag & 0x80)?GIFImageDescriptor.Flag:GIFHeader.Flag) & 7) + 1));
	  if(Raster==NULL) goto FINISH;

	  if(GIFImageDescriptor.Flag & 0x80)	/*Local Color Map*/
		 {
		 X = (unsigned)2 << (GIFImageDescriptor.Flag & 7);
		 LocalPalette=BuildPalette(X,8);
		 if(LocalPalette==NULL) goto FINISH;
		 if(LocalPalette->Data1D==NULL) goto FINISH;
		 X*=3;
		 Nacteno = fread(LocalPalette->Data1D,1,X,f);
		 if(Nacteno<X) goto FINISH;
		 }

	  X = Y = 0;
	  CodeSize = fgetc(f);
	  ClearCode = 1 << CodeSize;
	  if(CodeSize==0) goto FINISH;			//GIF error

	  EOFCode = ClearCode+1;
	  FirstFree = ClearCode+2;
	  FreeCode = FirstFree;

	  CodeSize++;
	  InitCodeSize = CodeSize;

	  MaxCode = 1 << CodeSize;

	  BitMask = GIFImageDescriptor.Flag & 7;  // Prefer bit depth in descriptor
	  if(BitMask==0) BitMask = GIFHeader.Flag & 7;	  // get it from header if not available
	  BitMask = (2<<BitMask) - 1;

	  OutCount=0;

	  bit.InitBitStream(CompBuffer,GIFBufSize,f,MAXLONG);

	  do {				// Decompress GIF raster
	     if(bit.FileLeft<0)
		 {			// Load error
		 goto FINISH;
		 }
	     Code = bit.Read_N(CodeSize);
	     //if(CodeSize==9) fprintf(F,"%u(%u) X=%u; Y=%u\n", Code, CodeSize, X, Y);

	     if(Code!=EOFCode)
		{
		if(Code==ClearCode)
		   {
		   CodeSize = InitCodeSize;
		   MaxCode = 1 << CodeSize;
		   FreeCode = FirstFree;

		   Code = bit.Read_N(CodeSize);
	           //fprintf(F,"%u(%u) X=%u; Y=%u\n", Code, CodeSize, X, Y);

		   //CurCode = Code;
		   OldCode = Code;
		   FinChar = Code & BitMask;
		   Add2Pixel (Raster,FinChar,X,Y,Interlace);
		   }
	       else
		   {
		   uint16_t CurCode = Code;
		   InCode = Code;
		   if(Code >= FreeCode)
			{
			 CurCode = OldCode;
			 OutCode[OutCount] = FinChar;
			 OutCount++;
			 if(Code>FreeCode)
			     {
			     //fprintf(F,"Error Code %u > FreeCode %u\n", Code, FreeCode);
			     break;		//Decompression Error
			     }
			 }
		   while(CurCode>=ClearCode)    //first vocambulary item
		        {
			OutCode[OutCount]=Suffix[CurCode];
			OutCount++;
			CurCode=Prefix[CurCode];
		        }
		   FinChar=CurCode & BitMask;
		   OutCode[OutCount]=FinChar;
		   OutCount++;
		   for(I=OutCount-1;I>=0;I--)
			       Add2Pixel(Raster,OutCode[I],X,Y,Interlace);
		   OutCount=0;
		   Prefix[FreeCode]=OldCode;
		   Suffix[FreeCode]=FinChar;
		   OldCode=InCode;
		   FreeCode++;
		   if(FreeCode>=MaxCode)
			{
			 if(CodeSize<12)
			    {
			    CodeSize++;
			    MaxCode=MaxCode*2;
			    }
			 else FreeCode=FirstFree;
			 }
		    }
		  }
	   } while(Code!=EOFCode);

	if(Img.Raster!=NULL)
		{
		CurrImg->Next=new Image;
		CurrImg=CurrImg->Next;
		}
	CurrImg->x = GIFImageDescriptor.Left/5.0f;
//	CurrImg->y = (GIFHeader.Height-GIFImageDescriptor.Top-GIFImageDescriptor.Height)/5.0f;
	CurrImg->y = (GIFImageDescriptor.Height)/5.0f;
	CurrImg->dx = GIFImageDescriptor.Width/5.0f;
	CurrImg->dy = GIFImageDescriptor.Height/5.0f;
	if(Raster!=NULL) {CurrImg->AttachRaster(Raster);Raster=NULL;}
	if(LocalPalette) {CurrImg->AttachPalette(LocalPalette);LocalPalette=NULL;}
		    else if(Palette!=NULL){CurrImg->AttachPalette(Palette);}

	I=fgetc(f);
	if(I==EOF) goto FINISH;
	if(I!=0) ungetc(I,f);
	break;

     default:goto FINISH;
     }
 }

FINISH:
 //fclose(F);
 if(f) fclose(f);

 if(CompBuffer!=NULL) free(CompBuffer);
 if(OutCode!=NULL) free(OutCode);
 if(Prefix!=NULL) free(Prefix);
 if(Suffix!=NULL) free(Suffix);

 if(Raster!=NULL && Raster->UsageCount==0) delete Raster;
 if(Palette!=NULL && Palette->UsageCount==0) delete Palette;
 if(LocalPalette!=NULL && LocalPalette->UsageCount==0) delete LocalPalette;
 return(Img);
} //LoadGIF

#endif


#if SupportGIF>=3


/** Calculates number of bits needed to store numbers between 0 and n - 1
 * @param[in]	n	Number of numbers to store (0 to n - 1)
 * @return	Number of bits needed */
static int BitsNeeded(uint16_t n)
{
int ret = 1;

  if(!n--)
      return 0;

  while(n >>= 1)
      ++ret;

  return ret;
}


static int StoreGIFHeader(FILE *f, const Image &Img, APalette *GlobPalette=NULL, bool Ver89=false)
{
/* typedef struct {
    Word LocalScreenWidth,	// The width of the image - the first byte is the least significant: "01 00" would be 1 as "00 01" would be 256
         LocalScreenHeight;	// The height of the image - the first byte is the least significant again.
		//This byte contains some options in its bits (most to least significant from left to right starting with index 0 - so 0 means 128 and 7 means 1): 
    Byte GlobalColorTableSize : 3, // The size of the global color table. The number of colors in the table is calculated with 2^(N+1) - so 001 would stand for 4 colors
         SortFlag             : 1, // If set to 1, the global color table is sorted by ascending occurrence in the image data.
         ColorResolution      : 3, // The number of bits used to describe a pixel.
         GlobalColorTableFlag : 1; // If set to 1, a global color table is used.
    Byte BackgroundColorIndex;	// The index of the background color - irrelevant if no global color table is used
    Byte PixelAspectRatio;	// Aspect Ratio - this is ignored nowadays.
} ScreenDescriptor; */
  int NumColors;
  if(GlobPalette)
    NumColors = GlobPalette->Size1D;
  else
  {
    NumColors = Img.Raster->GetPlanes();
    if(NumColors<0) return ErrUnsupportedResolution;
    NumColors = 1 << NumColors;
  }

  if((fwrite(Ver89?"GIF89a":"GIF87a", 6, 1, f)) != 1) return ErrWriteFile;
  WrWORD_LoEnd(Img.Raster->Size1D,f);
  WrWORD_LoEnd(Img.Raster->Size2D,f);
 
  uint16_t GlobalColorTableSize;
  //uint8_t GlobalColorTableFlag;
  if(NumColors>0)
  {
    NumColors = 1 << BitsNeeded(NumColors);
    GlobalColorTableSize = BitsNeeded(NumColors) - 1;
    //GlobalColorTableFlag = 1;
  }
  else
  {
    GlobalColorTableSize = 0;
    //GlobalColorTableFlag = 0;
  }

  uint8_t Flag =  (Img.Raster->GetPlanes() << 4) |
		// Sort Flag = 0
	      (GlobalColorTableSize & 0x7);
  if(GlobPalette != NULL)
  {
    Flag |= 0x80;	// If set to 1, a global color table is used.
  }
  fputc(Flag, f);	// GCT
  fputc(0, f);		// Background color: index
  fputc(0, f);		// Default pixel aspect ratio.

  if((Flag & 0x80)>0)  	/*Global Color Map*/
  {
    GlobalColorTableSize = (unsigned)2 << (Flag & 7);
    if(GlobPalette->Size1D >= GlobalColorTableSize)
        fwrite(GlobPalette->Data1D,3,GlobalColorTableSize,f);
    else
    {
      fwrite(GlobPalette->Data1D,3,GlobPalette->Size1D,f);
      for(int i=GlobPalette->Size1D; i<GlobalColorTableSize; i++) // feed rest of palette with zeros.
      {
        fputc(0, f);
        fputc(0, f);
        fputc(0, f);
      }
    }
  }

return 0;
}


#define GIF_HASH(index, lastbyte) (((lastbyte << 8) ^ index) % HASHSIZE)

class WbitStream
{
protected:
  enum GIF_Code {
    GIF_OK,
    GIF_ERRCREATE,
    GIF_ERRWRITE,
    GIF_OUTMEM
  };

public:
   WbitStream(FILE *new_f, Raster2DAbstract *NewRas): OutFile(new_f), Ras(NewRas) 
   {StrChr=NULL; StrNxt=NULL; StrHsh=NULL; X=Y=0; /*DebugF=NULL;*/}
   ~WbitStream() {FreeStrtab(); /*if(DebugF) fclose(DebugF);*/}

   FILE *OutFile;
   Raster2DAbstract *Ras;
   int codesize;
   uint8_t Buffer[256];		/**< There must be one to much !!! */
   int  Index;			/**< Current byte in buffer */
   int BitsLeft;		/**< Bits left to fill in current byte. These are right-justified */
   uint8_t *StrChr;
   uint16_t *StrNxt,
        *StrHsh,
        NumStrings;

   int LZW_Compress(void);
   void InitBitFile(void);
   void FreeStrtab(void);
   int AllocStrtab(void);
   void ClearStrtab(int codesize);
   uint16_t AddCharString(uint16_t index, uint8_t b);
   int WriteBits(int bits, int numbits);
   uint16_t FindCharString(uint16_t index, uint8_t b);
   int ResetOutBitFile(void);

   uint16_t X, Y;
   int inputbyte(void);

   //FILE *DebugF;

   inline int WriteByte(uint8_t b)
   {
     if (putc(b, OutFile) == EOF)
        return GIF_ERRWRITE;

     return GIF_OK;
   }

    /** Output bytes to the current OutFile.
     * @param[in]  buf	Pointer to buffer to write.
     * @param[in]  len	Number of bytes to write.
     * @return    GIF_OK       - OK
     *            GIF_ERRWRITE - Error writing to the file. */
    inline int Write(const void *buf, unsigned len)
    {
      if(fwrite(buf, sizeof(uint8_t), len, OutFile) < len)
        return GIF_ERRWRITE;
      return GIF_OK;
    }

   static const uint16_t MAXBITS = 12;
   static const uint16_t MAXSTR = (1 << MAXBITS);

   static const uint16_t HASHSIZE = 9973;
   static const uint16_t HASHSTEP = 2039;

   static const uint16_t HASH_FREE = 0xFFFF;
   static const uint16_t RES_CODES = 2;
   static const uint16_t NEXT_FIRST = 0xFFFF;
};


int WbitStream::inputbyte(void)
{
  if(X>=Ras->Size1D)
  {
    X = 0;
    Y++;
    if(Y>=Ras->Size2D) return -1;
  }
  return Ras->GetValue2D(X++,Y);
}


/* Initiate for using a bitfile. All output is sent to
 * the current OutFile using the I/O-routines above. */
void WbitStream::InitBitFile(void)
{
  Buffer[Index=0] = 0;
  BitsLeft = 8;
}


/** Allocate arrays used in string table routines
 * @return	GIF_OK     - OK
 *              GIF_OUTMEM - Out of memory */
int WbitStream::AllocStrtab(void)
{
    /* Just in case . . . */
  FreeStrtab();

  if((StrChr = (uint8_t *)malloc(MAXSTR * sizeof(uint8_t))) == 0) {
        FreeStrtab();
        return -1;
  }

  if((StrNxt = (uint16_t*) malloc(MAXSTR * sizeof(uint16_t))) == 0) {
        FreeStrtab();
        return -1;
  }

  if ((StrHsh = (uint16_t*) malloc(HASHSIZE * sizeof(uint16_t))) == 0) {
        FreeStrtab();
        return -1;
  }

  return 0;
}


/** Free arrays used in string table routines */
void WbitStream::FreeStrtab(void)
{
  if(StrHsh) {
        free(StrHsh);
        StrHsh = NULL;
  }

  if(StrNxt) {
        free(StrNxt);
        StrNxt = NULL;
  }

  if(StrChr) {
        free(StrChr);
        StrChr = NULL;
  }
}


/** Add a string consisting of the string of index plus the byte b.
 *                  If a string of length 1 is wanted, the index should
 *                  be 0xFFFF.
 *
 * @param[in] index  Index to first part of string, or 0xFFFF is only 1 byte is wanted
 * @param[in] b     Last byte in new string
 * @return Index to new string, or 0xFFFF if no more room */
uint16_t WbitStream::AddCharString(uint16_t index, uint8_t b)
{
  uint16_t hshidx;


    /*
     *  Check if there is more room
     */
  if (NumStrings >= MAXSTR)
        return 0xFFFF;

    /*
     *  Search the string table until a free position is found
     */
  hshidx = GIF_HASH(index, b);
  while (StrHsh[hshidx] != 0xFFFF)
      hshidx = (hshidx + HASHSTEP) % HASHSIZE;

    /*
     *  Insert new string
     */
  StrHsh[hshidx] = NumStrings;
  StrChr[NumStrings] = b;
  StrNxt[NumStrings] = (index != 0xFFFF) ? index : NEXT_FIRST;

  return NumStrings++;
}


/** Mark the entire table as free, enter the 2**codesize
 *                  one-byte strings, and reserve the RES_CODES reserved codes.
 * @param[in] codesize	Number of bits to encode one pixel */
void WbitStream::ClearStrtab(int codesize)
{
  int q, w;
  uint16_t *wp;


    /*
     *  No strings currently in the table
     */
    NumStrings = 0;

    /*
     *  Mark entire hashtable as free
     */
    wp = StrHsh;
    for (q = 0; q < HASHSIZE; q++)
        *wp++ = HASH_FREE;

    /*
     *  Insert 2**codesize one-character strings, and reserved codes
     */
    w = (1 << codesize) + RES_CODES;
    for (q = 0; q < w; q++)
        AddCharString(0xFFFF, q);
}


/** Find index of string consisting of the string of index
 *                  plus the byte b.
 *                  If a string of length 1 is wanted, the index should
 *                  be 0xFFFF.
 *
 *  PARAMETERS:     index - Index to first part of string, or 0xFFFF is
 *                          only 1 byte is wanted
 *                  b     - Last byte in string
 *  RETURNS:        Index to string, or 0xFFFF if not found */
uint16_t WbitStream::FindCharString(uint16_t index, uint8_t b)
{
    uint16_t hshidx, nxtidx;


    /*
     *  Check if index is 0xFFFF. In that case we need only
     *  return b, since all one-character strings has their
     *  bytevalue as their index
     */
    if (index == 0xFFFF)
        return b;

    /*
     *  Search the string table until the string is found, or
     *  we find HASH_FREE. In that case the string does not
     *  exist.
     */
    hshidx = GIF_HASH(index, b);
    while ((nxtidx = StrHsh[hshidx]) != 0xFFFF) {
        if (StrNxt[nxtidx] == index && StrChr[nxtidx] == b)
            return nxtidx;
        hshidx = (hshidx + HASHSTEP) % HASHSIZE;
    }

    /*
     *  No match is found
     */
    return 0xFFFF;
}


int WbitStream::LZW_Compress(void)
{
  int c;
  uint16_t index;
  int  clearcode, endofinfo, numbits, limit, errcode;
  uint16_t prefix = 0xFFFF;

	/* Set up the given outfile */
  InitBitFile();

	/* Set up variables and tables */
  clearcode = 1 << codesize;
  endofinfo = clearcode + 1;

  numbits = codesize + 1;
  limit = (1 << numbits) - 1;

  if((errcode = AllocStrtab()) != GIF_OK)
        return errcode;
  ClearStrtab(codesize);

	/* First send a code telling the unpacker to clear the stringtable. */
  WriteBits(clearcode, numbits);

    /* Pack image */
  while ((c = inputbyte()) != -1) {
        /*
         *  Now perform the packing.
         *  Check if the prefix + the new character is a string that
         *  exists in the table
         */
        if ((index = FindCharString(prefix, c)) != 0xFFFF) {
            /*
             *  The string exists in the table.
             *  Make this string the new prefix.
             */
            prefix = index;

        } else {
            /*
             *  The string does not exist in the table.
             *  First write code of the old prefix to the file.
             */
            WriteBits(prefix, numbits);

            /*
             *  Add the new string (the prefix + the new character)
             *  to the stringtable.
             */
            if (AddCharString(prefix, c) > limit) {
                if (++numbits > 12) {
                    WriteBits(clearcode, numbits - 1);
                    ClearStrtab(codesize);
                    numbits = codesize + 1;
                }
                limit = (1 << numbits) - 1;
            }

            /*
             *  Set prefix to a string containing only the character
             *  read. Since all possible one-character strings exists
             *  int the table, there's no need to check if it is found.
             */
            prefix = c;
        }
    }

    /*
     *  End of info is reached. Write last prefix.
     */
    if (prefix != 0xFFFF)
        WriteBits(prefix, numbits);

    /*
     *  Write end of info -mark.
     */
    WriteBits(endofinfo, numbits);

	/* Flush the buffer */
    ResetOutBitFile();

	/* Tidy up */
    FreeStrtab();

    return GIF_OK;
}


/** Tidy up after using a bitfile.
 *  RETURNS:        0 - OK, -1 - error */
int WbitStream::ResetOutBitFile(void)
{
    uint8_t numbytes;


    /*
     *  Find out how much is in the buffer
     */
    numbytes = Index + (BitsLeft == 8 ? 0 : 1);

    /*
     *  Write whatever is in the buffer to the file
     */
    if(numbytes) {
        if (WriteByte(numbytes) != GIF_OK)
            return -1;

    if(Write(Buffer, numbytes) != GIF_OK)
            return -1;

        Buffer[Index = 0] = 0;
        BitsLeft = 8;
    }

    return 0;
}


/** Put the given number of bits to the outfile.
 *  PARAMETERS:     bits    - bits to write from (right justified)
 *                  numbits - number of bits to write
 *  RETURNS:        bits written, or -1 on error. */
int WbitStream::WriteBits(int bits, int numbits)
{
  int  bitswritten = 0;
  uint8_t numbytes = 255;

  //if(DebugF) fprintf(DebugF,"%u(%u) X=%u; Y=%u\n", bits, numbits, X, Y);

  do {
        /*
         *  If the buffer is full, write it.
         */
      if ((Index == 254 && !BitsLeft) || Index > 254) {
            if (WriteByte(numbytes) != GIF_OK)
                return -1;

            if (Write(Buffer, numbytes) != GIF_OK)
                return -1;

            Buffer[Index = 0] = 0;
            BitsLeft = 8;
        }

		/* Now take care of the two specialcases */
        if (numbits <= BitsLeft) {
            Buffer[Index] |= (bits & ((1 << numbits) - 1)) << (8 - BitsLeft);
            bitswritten += numbits;
            BitsLeft -= numbits;
            numbits = 0;
        } else {
            Buffer[Index] |= (bits & ((1 << BitsLeft) - 1)) << (8 - BitsLeft);
            bitswritten += BitsLeft;
            bits >>= BitsLeft;
            numbits -= BitsLeft;

            Buffer[++Index] = 0;
            BitsLeft = 8;
        }
    } while(numbits);

    return bitswritten;
}


inline long SaveGIFImageDescriptor(FILE *f, const TypeGIFImageDescriptor &SU)
{
#if defined(__PackedStructures__)
return(fwrite(&SU,1,sizeof(SU),f));
#else
return(savestruct(f,"wwwwb",
	 SU.Left,SU.Top,SU.Width,SU.Height,SU.Flag));
#endif
}


int SavePictureGIF(const char *Name, const Image &Img)
{
FILE *f;
TypeGIFImageDescriptor ID;
const Image *CurrentImage;
APalette *GlobPalette = NULL;
bool NeedLocalPalette = false;

  if(Img.Raster==NULL) return ErrEmptyRaster;
  if(Img.Raster->GetPlanes()>8) return(-11);

  if((f=fopen(Name,"wb"))==NULL) return(-1);

  if(Img.Next==NULL)
  {
    if(Img.Palette!=NULL)
    {
      if(Img.Palette->GetPlanes()!=24 || Img.Palette->Channels()!=3)	// Need 3channels RGB 8 bit.
      {
        GlobPalette = BuildPalette(1<<Img.Raster->GetPlanes(), 8);
        //Img.Palette->Set(*GlobPalette);  //does not work.
        for(int i=0; i<GlobPalette->Size1D; i++)
        {
          RGBQuad RGB;
          Img.Palette->Get(i,&RGB);
          GlobPalette->Set(i,&RGB);
        }
      }
      else
      {
        GlobPalette = Img.Palette;
        GlobPalette->UsageCount++;
      }
    }
    else
    {
      GlobPalette = BuildPalette(1<<Img.Raster->GetPlanes(), 8);
      if(GlobPalette)
      {
        GlobPalette->UsageCount++;
        FillGray(GlobPalette);
      }
    }
  }
  else
    NeedLocalPalette = true;

  const PropertyItem *PropComment;
#ifndef NO_EXTENSIONS
  CurrentImage = &Img;
  do
  {
    unsigned ix=0;
    PropComment = Img.Properties.Find("comment",ix);
    CurrentImage = CurrentImage->Next;
  } while(CurrentImage!=NULL && PropComment==NULL);
#else
  PropComment = NULL;
#endif

	/* Write GIF signature */
  int ret = StoreGIFHeader(f, Img, GlobPalette, PropComment!=NULL);

  if(GlobPalette)
  {
    GlobPalette->UsageCount--;
    if(GlobPalette->UsageCount<=0)
    {
      delete GlobPalette;
    }
    GlobPalette = NULL;
  }

  if(ret < 0)
  {
    fclose(f);
    return ret;
  }

  CurrentImage = &Img;
  do
  {
#ifndef NO_EXTENSIONS
    {
      unsigned count=0;
      PropComment = Img.Properties.Find("comment",count);
      if(PropComment!=NULL && PropComment->Data!=NULL)
      {
        unsigned char Blob[2] = {0x21, 0xFE};
        fwrite(Blob,1,2,f);
        count = PropComment->DataSize;
        uint8_t *Offs = (uint8_t*)PropComment->Data;
        do
        {
          Blob[0] = (count>255) ? 255 : count;
          fwrite(Blob,1,1,f);
          fwrite(Offs,1,Blob[0],f);
	  count -= Blob[0];
          Offs += Blob[0];
        } while(Blob[0]!=0);
      }
    }
#endif

    fputc(',',f);	// Separator
    ID.Left = 0;
    ID.Top = 0;
    ID.Width = CurrentImage->Raster->Size1D;
    ID.Height = CurrentImage->Raster->Size2D;
    ID.Flag = 0;
    if(NeedLocalPalette)
    {
      uint16_t NumColors = 1 << Img.Raster->GetPlanes();
      GlobPalette = Img.Palette;
      if(GlobPalette!=NULL)
      {
        GlobPalette->UsageCount++;
      }
      else
      {
        GlobPalette = BuildPalette(NumColors, 8);
        if(GlobPalette)
        {
          GlobPalette->UsageCount++;
          FillGray(GlobPalette);
        }
      }
      uint16_t LocalColorTableSize;
      if(NumColors>0)
      {
        NumColors = 1 << BitsNeeded(NumColors);
        LocalColorTableSize = BitsNeeded(NumColors) - 1;
      }
      else
        LocalColorTableSize = 0;
      if(GlobPalette)
          ID.Flag = 0x80 | (LocalColorTableSize & 0x7);
    }
    SaveGIFImageDescriptor(f,ID);
    if((ID.Flag & 0x80)>0)			/*Local Color Map*/
    {
      uint16_t LocalColorTableSize = (unsigned)2 << (ID.Flag & 7);
      if(GlobPalette->Size1D >= LocalColorTableSize)
        fwrite(GlobPalette->Data1D,3,LocalColorTableSize,f);
      else
      {
        fwrite(GlobPalette->Data1D,3,GlobPalette->Size1D,f);
        for(int i=GlobPalette->Size1D; i<LocalColorTableSize; i++) // feed rest of palette with zeros.
        {
          fputc(0, f);
          fputc(0, f);
          fputc(0, f);
        }
      }
      if(GlobPalette->UsageCount-- <= 1) delete GlobPalette;
      GlobPalette = NULL;
    }

    WbitStream GIF_Writer(f,CurrentImage->Raster);
    GIF_Writer.codesize = CurrentImage->Raster->GetPlanes();
    if(GIF_Writer.codesize == 1) ++GIF_Writer.codesize;
    fputc(GIF_Writer.codesize,f);

    GIF_Writer.LZW_Compress();

	/* Write terminating 0-byte */
    fputc(0,f);

    CurrentImage = CurrentImage->Next;
  } while(CurrentImage != NULL);


	/* Initiate and write ending image descriptor */
  fputc(';',f);
  memset(&ID, 0, sizeof(ID));
  SaveGIFImageDescriptor(f,ID);

  fclose(f);
return 0;
}

#endif


#endif
//-------------------End of GIF routines------------------
