-----------------------------------------------------------
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 and implementation files for classes "MonitoredFile" and "GifCodeStripper".
Class GifCodeStripper exposes a method, GetNextCode(), which returns code values from the filestream to support decompression according to the lzw algorithm. It accommodates sublocks of various length, becuase each sub-block is preceded by a 1-byte length value. It keeps track of the variable code sizes that can be used by the algorithm, for example, code values can be from 4 to 12 bits in length, so the GifCodeStripper must at times retrieve code values that cross byte boundaries. It does this by a combination of bitwise shift and OR operations.
Class MonitoredFile is purely a debugging support tool. It exposes one method, MRead, which wraps a call to the stream IO function fread. MonitoredFile also maintains a value, fileoffset, which represents the offset from the beginning of the file, in bytes, which has been read. You can use this by setting a breakpoint at some point in the decode routine, CGif::Readfile(). Variable MonitoredFile::fileoffset will indicate the byte offset that has most recently been read. You can then open the GIF file in a Hex Editor and go right to that spot to debug and analyze the decoding routine.
You can also do away with class MonitoredRead entirely by replacing all calls to routine MRead() with a call to fread(), instead.
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////file MonitoredFile.h
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
#pragma once
#include "StdAfx.h"
class MonitoredFile
{
public:
MonitoredFile(void);
~MonitoredFile(void);
FILE *fin;
size_t fileoffset;
size_t MRead(void *buffer, size_t size, size_t count);
};
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////file MonitoredFile.cpp
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "monitoredfile.h"
MonitoredFile::MonitoredFile(void)
{
fileoffset = 0;
}
MonitoredFile::~MonitoredFile(void)
{
}
size_t MonitoredFile::MRead(void *buffer, size_t size, size_t count)
{
size_t retval = 0;
ASSERT(fin != NULL);
retval = fread(buffer, size, count, fin);
fileoffset += size * count;
return(retval);
}
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////file GifCodeStripper.h
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
#pragma once
#include "MonitoredFile.h"
class GifCodeStripper
{
public:
GifCodeStripper(void);
~GifCodeStripper(void);
byte CodeBuf[257];
int initialCodeSize;
int currentCodeSize;
unsigned short clearcode;
unsigned short endofdatacode;
int bitsLeftInThisByte;
int bitsLeftToGetForCode;
size_t bytesLeftThisSubBlock;
int currentSubBlockByteIndex;
int inputbytesread;
MonitoredFile *mf;
//private:
public:
int blockcount;
int totalBitsOffset;
int CodeCount;
int GetNextCode(void);
};
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////file GifCodeStripper.cpp
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "gifcodestripper.h"
//the following constants are used to mask off
//the bits we need from a 16-bit value from the
//input stream.
unsigned short MaskFromRight[] =
{
0x01, // 00000001
0x03, // 00000011
0x07, // 00000111
0x0f, // 00001111
0x1f, // 00011111
0x3f, // 00111111
0x7f, // 01111111
0xff, // 11111111
0x1ff,
0x3ff,
0x7ff,
0xfff // 111111111111 12 bits, the max, mate.
};
//need constructor with params to set clearcode, cdurrentCodeSize, etc...
GifCodeStripper::GifCodeStripper(void)
: inputbytesread(0)
{
CodeCount = 0;
blockcount = 0;
}
GifCodeStripper::~GifCodeStripper(void)
{
}
int GifCodeStripper::GetNextCode(void)
{
byte subblockdatalen;
int CodeToReturn = 0;
//on entry to this function, bitsLeftToGetForCode
//always equals currentCodeSize.
bitsLeftToGetForCode = currentCodeSize;
//grab codes, currentCodeSize bits at a time from
//the buffer. By the definition of LZW encoding,
//currentCodeSize will be in range 4 to 12.
while (bitsLeftToGetForCode > 0)
{
if (bitsLeftInThisByte == 0)
{
//if we need to read another subblock, do so...
if(bytesLeftThisSubBlock == 0)
{
mf->MRead(&subblockdatalen,1,1);
size_t ofs = mf->fileoffset;
if (subblockdatalen == 0)
return endofdatacode;
size_t bytesread = mf->MRead(CodeBuf,subblockdatalen,1);
if (bytesread == 0)
return endofdatacode;
bytesLeftThisSubBlock = --subblockdatalen;
currentSubBlockByteIndex = 0;
bitsLeftInThisByte = 8;
totalBitsOffset++;
blockcount++;
}
else
{
//advance the currentSubBlockByteIndex to
//point to the next byte, and set
//the bitsLeftInThisByte indicator to the full 8...
currentSubBlockByteIndex++;
inputbytesread++;
bitsLeftInThisByte = 8;
bytesLeftThisSubBlock--;
totalBitsOffset++;
}
}else
{//still bits left in the current byte
if (bitsLeftInThisByte >= bitsLeftToGetForCode)
{
//more bits left than needed (or, exactly the number needed).
//Get what we need.
byte bb = CodeBuf[currentSubBlockByteIndex];
//shift the bits so that the ones we need
//are the only ones left...
bb = bb >> (8 - bitsLeftInThisByte);
//...bb should now hold only the bits we need.
//stick 'em on the left of the CodeToReturn
CodeToReturn = CodeToReturn |((bb &
MaskFromRight[bitsLeftToGetForCode-1])
<< (this->currentCodeSize -
bitsLeftToGetForCode)) ;
bitsLeftInThisByte -= bitsLeftToGetForCode;
CodeToReturn = CodeToReturn & MaskFromRight[this->currentCodeSize - 1];
bitsLeftToGetForCode = 0;
}else
{
//fewer bits than needed. stick the ones available in this byte onto
//the left of the ones we already got.
int bb = CodeBuf[currentSubBlockByteIndex];
size_t bitsWeGot = currentCodeSize - bitsLeftToGetForCode;
bb = bb >> ( 8 - bitsLeftInThisByte);
bb = bb << bitsWeGot;
//pick off the number of available bits this byte from the right,
//and stick them on the left of CodeToReturn...
//and adjust indexes, counters, pointers, as needed...
CodeToReturn = CodeToReturn | bb;
bitsLeftToGetForCode -= bitsLeftInThisByte;
currentSubBlockByteIndex++;
inputbytesread++;
if (bytesLeftThisSubBlock)
{
bytesLeftThisSubBlock--;
bitsLeftInThisByte = 8;
}else
bitsLeftInThisByte = 0;
}
}
}
CodeCount++;
return CodeToReturn;
}