-----------------------------------------------------------

This writeup is not really meant to be read on its own, in isolation. By itself it's an unreadable, indegestible mess/mass of source code. It is part of a larger project, the root of which, including all the exposition you'll need, is to be found at: Decoding and Animating GIFs: an implementation.

If you've jumped here FROM the root node, a basic description follows.

This writeup contains the header (.h) and implementaion (.cpp) files for the main class in the project: CGif. CGif is a holder of an array of pointers to the graphic information for each frame of an animated GIF. During animation, the graphic information for each frame is retrieved and displayed to the screen via one or more Win32 BitBlt() operations. (the code that actually performs the animation is in another node, accessible off the root, the link to which is given above.) CGif also contains the logic to decode the lzw compressed image data from the Gif file. This is the readfile() method, the longest in the entire project. Function readfile() decompresses and decodes the data, and stores it in a set of arrays for later use by the animation routine.


/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
///file CGif.h (cpp file contents follow)
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////

#pragma once
#include <deque>
#include "MonitoredFile.h"
using namespace std;


//definition of block layouts to conform with the GIF file spec.

 typedef struct logicalscreendescriptor{
   unsigned short bLogicalScreenWidth;
   unsigned short bLogicalScreenHeight;
   byte bBitFlags;
   byte bBackColorIndex;
   byte bPixelAspectRatio;} LogicalScreenDescriptor;

typedef struct colortableentry{
  byte Red;
  byte Green;
  byte Blue;}ColorTableEntry;

typedef struct applicationextension{
  byte size;
  byte AppIdentifier[8];
  byte AppAuthCode[3];}ApplicationExtension;

typedef struct graphiccontrolextension{
  byte size;
  byte PackedBits;
  unsigned short DelayTime;
  byte TransColorIndex;
  byte term;}GraphicControlExtension;

typedef struct framecontrolblock{
  byte Disposition;
  unsigned short DelayTime;
  bool UserInputExpected;
  bool TransparentColorFlag;
  byte TransparentColorIndex;
  PALETTEENTRY TransColor;}FrameControlBlock;

typedef struct imagedescriptior{
  unsigned short left;
  unsigned short top;
  unsigned short width;
  unsigned short height;
  byte PackedBits;
    byte initsize;}ImageDescriptor;

typedef struct imagedescriptorblock{
  unsigned short left;
  unsigned short top;
  unsigned short width;
  unsigned short height;
  bool localcolortableflag;
  bool interlaceflag;
  bool sortflag;
  int localcolortablesize;
  byte *localcolortable;


  
  HBITMAP bmap;

}ImageDescriptorBlock;

//typedefs for growable arrays of frame data, explained more in the
//actual CGif class declaration, below.
typedef deque<ImageDescriptorBlock *> idbarray;
typedef deque<FrameControlBlock *> fcbarray;

//typedefs for arrays to hold decoded values as per the lzw algorithm.
typedef deque<unsigned short *> codetable;
typedef deque<int>codelengthtable;

 class CGif
{
public:
  CGif(void);
  CGif(CString *sourcefile, CDC *mdc);
  ~CGif(void);
  bool ReadFile(CString * SourceFileName);

  /////////////////////////
  //std::vectors for frame data
  //ImageControlArray and FrameControlArray are
  //dynamically "growable" arrays of pointers to the information for
  //each frame of the animation.  The idbarray contains all the image data
  //in its "bmap" member variable...the fcbarray contains information on 
  //the delay time and transparent color, if any, for the frame.  There
  //will be one array member in each of these arrays for each frame of animation.
  /////////////////////////

  idbarray ImageControlArray;
  fcbarray FrameControlArray;
  fcbarray PerFrameControlArray;
  LOGPALETTE *lpal;
  LogicalScreenDescriptor lsd;
  MonitoredFile *mf;
  unsigned short lastframedelaytime;
  int blitwidth, blitheight;
  bool localcolorsread;
private:
    //following 4 variables are the packed fields of the logical screen descriptor,
    //separated out...
    bool GlobalColorTablePresent;
    byte GlobalColorResolution;
    bool GlobalColorTableSorted;
    int GlobalColorTableSize;
    int outputbytecount;

    //globalcolortable array pointer.

public:
  CPalette *globalPalette;
  CDC  memDC;  
private:
  byte BlockSeparator;
    byte ControlLabel;
    unsigned short retrievedcode;
    unsigned short currentcodesize;
    unsigned short clearcode;
    unsigned short endofdatacode;

public:
  void InitializeColorTable(int);
private:
  int GlobalColorCount;
  int InitialColorCount;
public:
  bool bGifRead;
private:
  void ResetCodeTable(void);
  void CalcRowColumn(ImageDescriptorBlock *idb, int& pixelsperrow,
    int& rowsthispass,int& interlacepass, int actualrow,
    size_t& row, size_t& col);
  void SetFramePalette(BITMAPINFO *lpi,
     ImageDescriptorBlock *idb, int frameColorCount);
  FrameControlBlock * GetFrameControlBlock(fcbarray &PerFrameControlArray);
};




/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////file CGif.cpp
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////

#include "StdAfx.h"
#include <iostream>
#include <deque>
#include <fstream>
#include <string>
#include "gif.h"
#include "GifCodeStripper.h"


codetable CodeTable;
codelengthtable CodeLengthTable;
byte decodeinputbuffer[256];
int framecount = 0;
using namespace std;
CGif::CGif(void)
: retrievedcode(0)
, GlobalColorCount(0)

{
  globalPalette = NULL;
  localcolorsread = false;
}

CGif::CGif(CString *sourcefile, CDC *mDC)
{
  
  globalPalette = NULL;
  BOOL bdbd = this->memDC.CreateCompatibleDC(mDC);
  blitwidth = blitheight = 0;
  //given a filename, open it and commence decodin'.
  ReadFile(sourcefile);
  
  localcolorsread = false;
  
}

CGif::~CGif(void)
{
//in the destructor we must deallocate all the
//structures we've dynamically allocated off the heap.
//Which, as you can see, is rather a lot of stuff.
//failure to delete and free all this stuff would 
//cause memory leaks.  Memory leaks are bad, m'kay?

  if(globalPalette)
    delete(globalPalette);
  int fcasize = (int)FrameControlArray.size();
  size_t jj = ImageControlArray.size() ;
  for (size_t i = jj; i > 0 ; i--)
  {
    DeleteObject(ImageControlArray[i-1]->bmap);
    if (FrameControlArray[i-1]->TransparentColorFlag)
    {
      if(ImageControlArray[i-1]->localcolortableflag)
      {
        delete(ImageControlArray[i-1]->localcolortable);
      }
    }
    delete(ImageControlArray[i-1]);

    ImageControlArray.pop_back();

  }

  for(size_t as =fcasize; as > 0; as--)
  {
    delete(FrameControlArray[as - 1]);
    FrameControlArray.pop_back();
  }


  jj = CodeTable.size();
  for (size_t i = jj; i > 0; i--)
  {
    unsigned short *s = CodeTable[i - 1];
    size_t cl = CodeLengthTable[i - 1];

    delete [] s;

    CodeTable.pop_back();
    CodeLengthTable.pop_back();
  }
    
  jj = PerFrameControlArray.size();
  if(jj)
  {
    for(size_t i = jj; i > 0; i--)
    {
      delete(PerFrameControlArray[0]);
      PerFrameControlArray.pop_front();
    }
  }

  if (globalPalette)
  {
    DeleteObject(HPALETTE(globalPalette));  
    delete(lpal);
  }
    
}
///////////
//ReadFile is the routine that opens the gif file
//and initiates the decode operation.
//Don't even think of trying to follow along until
//you've downloaded, printed, and familiarized yourself
//with the GIF 89a filespec.
////////
bool CGif::ReadFile(CString * SourceFileName)
{
  bool bGoodRead = false;
  char fb[7];
  
  FILE *fin = fopen(SourceFileName->GetBuffer(0),"rb");
  if (fin)
  {
    lastframedelaytime = 0;
    mf = new MonitoredFile();
    mf->fin = fin;
    mf->MRead(fb,1,6); 
    //first six chars should always say "Gif89a".  I don't
    //check this, and I probably should be punished for it.
    mf->MRead(&lsd,1,7);
    blitwidth = lsd.bLogicalScreenWidth; 
    blitheight = lsd.bLogicalScreenHeight;
    if(lsd.bBitFlags & 0x80)
      GlobalColorTablePresent = true;
    else
      GlobalColorTablePresent = false;

    GlobalColorResolution = ((lsd.bBitFlags & 0x70)>>4) + 1;

    if (lsd.bBitFlags & 0x08)
      GlobalColorTableSorted = true;
    else
      GlobalColorTableSorted = false;
  
    GlobalColorTableSize =  2 << ((lsd.bBitFlags & 0x07));
    if(GlobalColorTablePresent)
    {
      GlobalColorCount = GlobalColorTableSize;
      size_t bc = sizeof(LOGPALETTE)+ (GlobalColorCount * sizeof(PALETTEENTRY));
      lpal = (LOGPALETTE *) new byte[bc];
      for(int jj = 0; jj < GlobalColorCount; jj++)
      {
        mf->MRead(&lpal->palPalEntry[jj].peRed,1,1);
        mf->MRead(&lpal->palPalEntry[jj].peGreen,1,1);
        mf->MRead(&lpal->palPalEntry[jj].peBlue,1,1); 
        lpal->palPalEntry[jj].peFlags = 0;
      }

      lpal->palVersion = 0;
      lpal->palNumEntries = InitialColorCount = GlobalColorCount;
      //lpal is a Win32 logical palette structure...
      //from this we'll create an
      //MFC object that "wraps" the global palette.
      CPalette * cpal = new CPalette();
      cpal->CreatePalette(lpal);
      globalPalette = cpal;
      
    }
    //The main file processing loop.  Reads and parses the 
    //different types of block structures defined by the 
    //GIF filespec.
    while (!feof(fin))
    {
      mf->MRead(&BlockSeparator,1,1); 
      //the block separator cases that follow correspond
      //with those defined in the gif file spec.
      switch(BlockSeparator)
      {
      case 0x21: //extension
        mf->MRead(&ControlLabel,1,1);
        switch(ControlLabel)
        {  
        case 0xff: //application extension
          {
            ApplicationExtension ae;
            mf->MRead(&ae,1,sizeof(ae)); 
            int i = 0;
            char datac;
            mf->MRead(&datac,1,1); 
            while (datac != 0x00)
            {
              mf->MRead(decodeinputbuffer, datac, 1);
              mf->MRead(&datac,1,1);
            }
            break;
          }
        
        case 0xf9: //Graphic Control Extension
          {
            GraphicControlExtension ge;
            mf->MRead(&ge,1,sizeof(ge));
            FrameControlBlock *f = new FrameControlBlock;
            f->Disposition = (ge.PackedBits & 0x1c) >> 2;
            f->DelayTime = ge.DelayTime;

            if((ge.PackedBits & 0x02) >>1)
            f->UserInputExpected = true;
            f->TransparentColorFlag = ge.PackedBits & 0x01;
            f->TransparentColorIndex = ge.TransColorIndex;
            
            PerFrameControlArray.push_back(f);
            break;
          }
        case 0xfe: //Comment extension
          {
            byte CommentBuffer[255];
            byte blksize;
            mf->MRead(&blksize,1,1); 
            do
            {
              mf->MRead(CommentBuffer,blksize,1); 
              if(CommentBuffer[blksize - 1] == 0)
                blksize = 0;
              else
                mf->MRead(&blksize,1,1); 
            }while (blksize > 0 );
            break;
          }
        
        }

      break;
      case 0x2c: //image descriptor
        {
          //These two variables represent
          //Windows GDI bitmap structures.
          //We initialize these with info we read from 
          //the filestream.  A BITMAPINFO structure is
          //a BITMAPINFOHEADER structure followed by the
          //rgb values of the palette for that bitmap.
            LPBITMAPINFOHEADER lpInfo;
          BITMAPINFO *lpi;

          framecount++;

          //with each bitmap/frame of animation we will store
          //a frame control block that contains info on the
          //transparent color, if present, and the delay time
          //for the frame.  I have found examples of GIF files
          //that contain MULTIPLE frame control blocks per frame.
          //If this is the case, our array PerFrameControlArray
          //will have a size > 1.  Function GetFrameControlBlock()
          //decides which of these to use.
          FrameControlBlock *f;
          f = GetFrameControlBlock(PerFrameControlArray);

          FrameControlArray.push_back(f);
          

          ImageDescriptor id;
          int initialcodecount = 0;
          int currentcodecount = 0;
          int outputbytepointer = 0;
          outputbytecount = 0;
          int frameColorCount = 0;
          mf->MRead(&id,1,sizeof(id) - 1); 
          ImageDescriptorBlock *idb = new ImageDescriptorBlock;
          idb->interlaceflag = idb->localcolortableflag = idb->sortflag = false;
          idb->left = id.left;
          idb->top = id.top;
          idb->width = id.width;
          idb->height = id.height;
          //the dimensions of the area to be "blitted" are either 
          //the logical screen descriptor dimensions or
          //the dimensions of the largest frame within the animation.
          blitwidth = (idb->width > blitwidth?idb->width:blitwidth);
          blitheight = (idb->height > blitheight?idb->height:blitheight);
          //break out the local color table value, the interlace flag, and
          //sorted flag from the packed bits field.
          if (id.PackedBits >>7)
            idb->localcolortableflag = true;
          if ((id.PackedBits & 0x40) >>6)
            idb->interlaceflag = true;
          if ((id.PackedBits & 0x20) >>5)
            idb->sortflag = true;
          idb->localcolortablesize = 0;
          idb->localcolortable = NULL;
          initialcodecount = GlobalColorCount;
          frameColorCount = GlobalColorCount;
          if (idb->localcolortableflag)
          {
            //local color table read here.
            frameColorCount = idb->localcolortablesize = 
              (2 <<((id.PackedBits & 0x07)));
            idb->localcolortable = new byte[idb->localcolortablesize * 3];
            mf->MRead(idb->localcolortable,idb->localcolortablesize * 3,1); 
            initialcodecount = idb->localcolortablesize;
            idb->localcolortablesize *= 3;
            localcolorsread = true;

          }
            
          lpInfo = (LPBITMAPINFOHEADER)new byte[sizeof(BITMAPINFOHEADER) 
            + sizeof(RGBQUAD) * frameColorCount];
          lpInfo->biBitCount = 8;
          lpInfo->biHeight = idb->height;
          lpInfo->biWidth = idb->width;
          lpInfo->biPlanes = 1;
          lpInfo->biSize = sizeof(BITMAPINFOHEADER);
          lpInfo->biClrUsed = frameColorCount;
          lpInfo->biClrImportant = 0;
          lpInfo->biSizeImage = 0; 
          lpInfo->biCompression = BI_RGB;
          lpi = (BITMAPINFO *)lpInfo;

          //set the palette rgb values for this frame.
          //If local color table values were read, use them.
          //Otherwise, use the global color table values.
          SetFramePalette(lpi,idb,frameColorCount);
                  
          size_t sz = FrameControlArray.size() - 1;
        
          if (FrameControlArray[sz]->TransparentColorFlag)
          {
            
            FrameControlArray[sz]->TransColor.peRed = 
              lpi->bmiColors[FrameControlArray[sz]->TransparentColorIndex].rgbRed;
            FrameControlArray[sz]->TransColor.peGreen = 
              lpi->bmiColors[FrameControlArray[sz]->TransparentColorIndex].rgbGreen;
            FrameControlArray[sz]->TransColor.peBlue = 
              lpi->bmiColors[FrameControlArray[sz]->TransparentColorIndex].rgbBlue;
          }

            InitializeColorTable(initialcodecount);
            currentcodecount = initialcodecount;
        
          //the width of the bitmap must be divisible by 4.  This
          //seems to be a restriction of the Windows GDI, not
            //the GIF filespec.
          size_t row_width = idb->width;
          size_t leftover = row_width %4;
          if (leftover)
            row_width += 4 - leftover;

          //create a device independent bitmap structure.
          //when the CreateDIBSection call returns, the pointer
          //ppvBits will point to the actual bitmap bits.  The
          //values we decode from the input stream can be stored
          //directly here to create the graphic.
          void *ppvBits = 0;
          HBITMAP hDDBitmap = 
            CreateDIBSection(this->memDC.m_hDC,lpi,DIB_RGB_COLORS,&ppvBits,0,0);
          
          //set up a GifCodeStripper object to retrieve
          //codes on demand from the file stream.
          GifCodeStripper gc;
          gc.mf = mf;
          mf->MRead(&id.initsize,1,1);
          gc.currentCodeSize = gc.initialCodeSize = id.initsize + 1;
          gc.bytesLeftThisSubBlock = 0;
          gc.currentSubBlockByteIndex = 0;
          gc.bitsLeftInThisByte = 0;
          gc.bitsLeftToGetForCode = gc.currentCodeSize;
          gc.clearcode = 2 << (id.initsize - 1);
          gc.endofdatacode = gc.clearcode + 1; 
          gc.totalBitsOffset = 0;
          gc.blockcount = 0;

          //first couple of values in the code table 
          //must be the clear code, and the clear code + 1,
          //according to the file spec.
          unsigned short *c = new unsigned short;
          *c = (byte)gc.clearcode;
          CodeTable.push_back(c);
          CodeLengthTable.push_back(1);
          unsigned short *cc = new unsigned short;
          *cc = gc.clearcode + 1;
          CodeTable.push_back(cc);
          CodeLengthTable.push_back(1);

          unsigned short code = 0;
          unsigned short oldcode = 0;
          code = gc.GetNextCode();
          ASSERT(code == gc.clearcode); //1st code should always be clear...
          code = gc.GetNextCode();
          //1st real data code should always be an atom...
          ASSERT(code < CodeTable.size());
          unsigned short *retrievedcode = CodeTable[code];
          
          //pbb now represents the decoded bits of the raster image, 
          //each number being an index into the palette.
          byte *pbb = (byte *)ppvBits;
          size_t dibrow = idb->height - (outputbytecount/idb->width) - 1;
          size_t dibcol = outputbytecount%idb->width;
          pbb[(dibrow * row_width) + dibcol] = (byte)*retrievedcode;

          outputbytecount++;
          oldcode = code;
          int pixelsthisrow, interlacepass, rowsthispass;
          pixelsthisrow = interlacepass = 1;
          rowsthispass = 0;

          //////////////////////////
          ///off to the races, now.  
          ///loop to read compressed 
          ///image bits for the frame.
          /////////////////////////
          code = gc.GetNextCode();
          while ((code != gc.endofdatacode) 
            && (outputbytecount < idb->width * idb->height))
          {
            
            if(code == gc.clearcode)
            {
              //reset all the internal variables and start
              //decoding "fresh".
              ResetCodeTable();
              InitializeColorTable(initialcodecount);
              gc.currentCodeSize = gc.initialCodeSize;
              gc.bitsLeftToGetForCode = gc.currentCodeSize;
              unsigned short *c = new unsigned short;
              *c = (unsigned short)gc.clearcode;
              CodeTable.push_back(c);
              CodeLengthTable.push_back(1);
              unsigned short *cc = new unsigned short;
              *cc = gc.clearcode + 1;
              CodeTable.push_back(cc);
              CodeLengthTable.push_back(1);

              code = gc.GetNextCode();
              if(code > CodeTable.size())
                code = 0;
              //1st real data code should always be an atom...
              ASSERT(code < CodeTable.size());
              unsigned short *retrievedcode = CodeTable[code];
              
              byte *pbb = (byte *)ppvBits;
              size_t dibrow = idb->height - (outputbytecount/idb->width) - 1;
              size_t dibcol = outputbytecount%idb->width;
              pbb[(dibrow * row_width) + dibcol] = (byte)*retrievedcode;
              outputbytecount++;
              oldcode = code;
            }else
            {
              if (code < CodeTable.size())
              {
                //The code exists in the codetable,
                //therefore, output translated string to pixel buffer...
                int oldcodelength = CodeLengthTable[oldcode];
                unsigned short *newcodetrans = CodeTable[code]  ;  
                int codelength = CodeLengthTable[code];
                //put the decoded bits in the pixel buffer.  
                //for an interlaced gif, this is a bit involved,
                //because we don't build the image from top to bottom, but
                            //but must calculate which actual row of the image we are
                //building according to the interlace algorithm detailed
                //in the GIF file spec.
                //for a NON interlaced gif, though, it's quite simple.
                for (int j = 0; j < codelength;j++)
                {  
              
                  unsigned short color = newcodetrans[j];
                  int actualrow = 0;

                  CalcRowColumn(idb,
                        pixelsthisrow,rowsthispass,
                        interlacepass,actualrow,dibrow,dibcol);
                  pbb[(dibrow * row_width) + dibcol] = (byte)color;
                  if(idb->interlaceflag)
                    pixelsthisrow++;

                  outputbytecount++;
                }
                
                unsigned short *prefix = new unsigned short[oldcodelength + 1]; 
                unsigned short *oldcodeptr = CodeTable[oldcode];
                for (j = 0; j < oldcodelength; j++)
                  prefix[j] = oldcodeptr[j];
                prefix[oldcodelength] = newcodetrans[0];
                CodeTable.push_back(prefix);
                CodeLengthTable.push_back(oldcodelength + 1);
                //if we just added CodeTable entry 2^gc.currentCodeSize -1,
                //we need to add 1 to currentcodesize.
                if ((CodeTable.size() > (size_t)(2 <<(gc.currentCodeSize-1))-1)
                  && gc.currentCodeSize < 12)
                  gc.currentCodeSize++;
                //ASSERT(gc.currentCodeSize <= 12);
                oldcode = code;
              }else
              {
                //code does not yet exist in codetable, so infer it,
                //as per the lzw algorithm...
                //ASSERT(code == CodeTable.size());
                unsigned short *prefix = CodeTable[oldcode];
                int oldcodelength = CodeLengthTable[oldcode];
                unsigned short K = prefix[0];
                unsigned short *newtrans = new unsigned short[oldcodelength+1];
                for (int j = 0; j < oldcodelength; j++)
                  newtrans[j] = prefix[j];
                newtrans[oldcodelength] = K;
                for (j = 0; j < oldcodelength+1; j++)
                {
                  unsigned short color = newtrans[j];
                  int actualrow = 0;
                  CalcRowColumn(idb,
                        pixelsthisrow,rowsthispass,
                        interlacepass,actualrow,dibrow,dibcol);
                  pbb[(dibrow * row_width) + dibcol] = (byte)color;
                  if(idb->interlaceflag)
                    pixelsthisrow++;
                  outputbytecount++;
                }
                
                CodeTable.push_back(newtrans);  
                CodeLengthTable.push_back(oldcodelength+1);
                //if we just added CodeTable entry 2^gc.currentCodeSize -1,
                //we need to add 1 to currentcodesize.
                if ((CodeTable.size() > (size_t)(2 <<(gc.currentCodeSize-1))-1)
                  && gc.currentCodeSize < 12)
                  gc.currentCodeSize++;

                oldcode = code;
              }
            }
            code = gc.GetNextCode();
            
          }
          bGifRead = true;
          idb->bmap = hDDBitmap;
       

          ////////////////
          //the following line is the point of the
          //whole decoding excersize!  the idb 
          //pointer points to the image data for a frame
          //of animation.  This information will be 
          //used later in one or more bitblit operations
          //to produce the animation effect.
          /////////////////
          this->ImageControlArray.push_back(idb);
          delete(lpi);
          
          ResetCodeTable();
          break;
        }

      }
    }
    if(mf)
      delete(mf);
    fclose(fin);

  }
  
  
  //delete(
  return bGoodRead;
}



void CGif::InitializeColorTable(int count)
{
  for (int i = 0; i < count; i++)
  {
    unsigned short *color = new unsigned short;
    *color = i;
    CodeTable.push_back(color);
    CodeLengthTable.push_back(1);
  }
}
//remove all the old values from the code table and codelength table.
//since the code table entries are arrays that were allocated from the 
//heap, we must delete them to recover the space.
void CGif::ResetCodeTable(void)
{
  size_t iOldSize = CodeTable.size();

  for (size_t icp = iOldSize; icp > 0; icp--)
  {

    unsigned short *s = CodeTable[icp - 1];
    size_t cl = CodeLengthTable[icp - 1];
    delete [] s;
    CodeTable.pop_back();
    CodeLengthTable.pop_back();

  }

}


//calculate the row and column where a decoded palette value should
//be stored in the raster data array of a bitmap.  For non-interlaced
//images, this is trivially simple, but for interlaced images, is
//a bit more involved, and follows the interlace algorithm detailed
//in the GIF filespec.
void CGif::CalcRowColumn(ImageDescriptorBlock *idb, int &pixelsthisrow,
    int &rowsthispass,int& interlacepass, int actualrow,
    size_t& row, size_t& col)
{
  if (idb->interlaceflag)
  {
    //calculate interlace row and column
    if (pixelsthisrow >= idb->width)
    {
      pixelsthisrow = 0;
      rowsthispass++;
    }
    do
    {
      if (interlacepass == 1)
        actualrow = rowsthispass * 8;
      if (interlacepass == 2)
        actualrow = (rowsthispass * 8) + 4;
      if (interlacepass == 3)
        actualrow = (rowsthispass * 4) + 2;
      if (interlacepass == 4) 
        actualrow = (rowsthispass * 2) + 1;
      if(actualrow >= idb->height)
      {
        interlacepass++;
        rowsthispass = 0;
      }
    } while (actualrow >= idb->height);
    row = idb->height - actualrow - 1;
    col = pixelsthisrow;
  }else
  {
    //row & column calculation for non-interlaced image.
    row = idb->height - (outputbytecount/idb->width) - 1;
    if(row == 0xffffffff || row == 0xfffffffe)
      row = 0;
    col = outputbytecount%idb->width;
  }
}

///Set the palette rgb values for a frame of animation.
///...if local color table info was read, use that, otherwise, use
// the global color table values.  
void CGif::SetFramePalette(BITMAPINFO *lpi, 
       ImageDescriptorBlock *idb, int frameColorCount)
{
  if(idb->localcolortableflag)
  {
    
    size_t oldcolorcount = GlobalColorCount;
    GlobalColorCount = frameColorCount;
    size_t bc = sizeof(LOGPALETTE)+ (GlobalColorCount * sizeof(PALETTEENTRY));
    LOGPALETTE* newlpal = (LOGPALETTE *) new byte[bc];
    
    for (int ijk = 0; ijk < frameColorCount; ijk++)
    {
      lpi->bmiColors[ijk].rgbReserved = 0;
      byte *lt = (byte *)idb->localcolortable;
      lpi->bmiColors[ijk].rgbRed = lt[ijk * 3];
      lpi->bmiColors[ijk].rgbGreen = lt[(ijk * 3) + 1];
      lpi->bmiColors[ijk].rgbBlue = lt[(ijk * 3) + 2];
      //the following is a regrettable hack, but 
      //necessary since I found one gif file that had
      //one frame with a local color table, which was larger 
      //than the global color table, and which had subsequent
      //frames that, though they used the global color table, 
      //seemed to assume that the palette indices of the larger
      //local color table were still available, i.e., there were
      //palette indices in the image data that were higher than the
      //highest global color table index.  Yeah, it was
      //confusing to me, too.
      if(ijk < (int)oldcolorcount)
      {
        newlpal->palPalEntry[ijk].peRed = lpal->palPalEntry[ijk].peRed;
        newlpal->palPalEntry[ijk].peGreen = lpal->palPalEntry[ijk].peGreen;
        newlpal->palPalEntry[ijk].peBlue = lpal->palPalEntry[ijk].peBlue;
        newlpal->palPalEntry[ijk].peFlags = 0;
      }else
      {
        newlpal->palPalEntry[ijk].peRed = lt[ijk * 3];
        newlpal->palPalEntry[ijk].peGreen = lt[(ijk * 3) + 1];
        newlpal->palPalEntry[ijk].peBlue = lt[(ijk * 3) + 2];
        newlpal->palPalEntry[ijk].peFlags = 0;
      }

    }
    delete(lpal);
    lpal = newlpal;

  }
  else
  {
    for (int ijk = 0; ijk < frameColorCount; ijk++)
    {
      lpi->bmiColors[ijk].rgbBlue = lpal->palPalEntry[ijk].peBlue;
      lpi->bmiColors[ijk].rgbGreen = lpal->palPalEntry[ijk].peGreen;
      lpi->bmiColors[ijk].rgbRed = lpal->palPalEntry[ijk].peRed;
      lpi->bmiColors[ijk].rgbReserved = 0;
      
    }
  }
  int tci = 0;
  if(FrameControlArray.size())
  {
  }
  //TransparentBlt apparently doesn't like a transparent color of black (0,0,0).
  //So, if the transparent color IS black, make it something else.
  if(FrameControlArray[FrameControlArray.size() - 1]->TransparentColorFlag)
  {
    tci = FrameControlArray[FrameControlArray.size() - 1]->TransparentColorIndex;

    if(lpi->bmiColors[tci].rgbBlue == 0 
      && lpi->bmiColors[tci].rgbGreen == 0 
      && lpi->bmiColors[tci].rgbRed == 0)
    {
      //need a function to check that the color we're about to 
      //pick for the new transparent color is not a duplicate
      //of some other color in the palette.  
      //for now, just iterate on the red color component, until
      //we find a color that is not duplicated elsewhere
      //in the palette...
      int ir = 1;
      int ig = 0;
      int ib = 0;
      bool goodcolor = false;
      while (ir < 255 && !goodcolor)
      {
        goodcolor = true;
        for (int ic = 0; ic < frameColorCount; ic++)
        {

          if (lpi->bmiColors[ic].rgbBlue == ib && 
            lpi->bmiColors[ic].rgbGreen == ig &&
            lpi->bmiColors[ic].rgbRed == ir)
          {
            goodcolor = false;
            
          }
        }
        if(!goodcolor)
          ir++;
      }
      lpi->bmiColors[tci].rgbRed = lpal->palPalEntry[tci].peRed = ir;
      lpi->bmiColors[tci].rgbGreen = lpi->bmiColors[tci].rgbGreen = ig;
      lpi->bmiColors[tci].rgbBlue = lpal->palPalEntry[tci].peBlue = ib;
      
    }
  }


}

FrameControlBlock * CGif::GetFrameControlBlock(fcbarray &PerFrameControlArray)
{
  FrameControlBlock *f;

  //if there are multiple frame control blocks per frame, pick the 
  //first one with the non-zero delay.  This may not be right,
  //but seems to work with the set of test files I'm using.
  if(PerFrameControlArray.size() == 1)
  {
    f = PerFrameControlArray[0];
    PerFrameControlArray.pop_back();
  }else
  {
    f = NULL;
    bool bfcbchosen = false;
    int pfs = (int)PerFrameControlArray.size();
    for (int ij = 0; ij < pfs; ij++)
    {
      if(PerFrameControlArray[0]->DelayTime > 0  && !bfcbchosen)
      {
        f = PerFrameControlArray[0];
        bfcbchosen = true;
        
      }else
      {
        delete(PerFrameControlArray[0]);
      }
      PerFrameControlArray.pop_front();
      
    }
  }
  if(!f)
  {
    f = new FrameControlBlock();
    f->Disposition = 0;
    f->TransparentColorFlag = false;
    f->DelayTime = 0;
  }
  if(lastframedelaytime == 0)
    lastframedelaytime = 10;
  if(f->DelayTime > 1)
    lastframedelaytime = f->DelayTime;
  else
    f->DelayTime = lastframedelaytime;
  
  return(f);
}

Y'know, if you log in, you can write something here, or contact authors directly on the site. Create a New User if you don't already have an account.