840 lines
20 KiB
C++
840 lines
20 KiB
C++
// Copyright (C) Microsoft Corporation 1990-1997, All Rights reserved.
|
|
|
|
#include "header.h"
|
|
#include "cpaldc.h"
|
|
#include "gif.h"
|
|
#include "hha_strtable.h"
|
|
#include "hhctrl.h"
|
|
|
|
#define GIF_HDR_SIZE 6
|
|
|
|
#define MAX_BUFFER_SIZE (2L * MAXSIZE)
|
|
#define END_INFO (plzData->ClearCode+1)
|
|
#define FIRST_ENTRY (plzData->ClearCode+2)
|
|
#define HASH_SIZE 5003
|
|
|
|
STATIC int GetImagePalette(IFFPTR piff, LPBYTE pal);
|
|
STATIC FSERR GetInit(PCSTR pszFileName, IFFPTR piff, UINT GetFlag);
|
|
STATIC void GIFFreeMemory(IFFPTR piff);
|
|
STATIC void SetUserParams(IFFPTR);
|
|
STATIC int IFF_PackLine(PBYTE ToBuff, const BYTE* FromBuff, int Pixels, int Bits);
|
|
STATIC LZDATA* LZInitialize(int CharSize);
|
|
STATIC void LZCleanUp(LZDATA*);
|
|
STATIC int LZExpand(LZDATA*, PBYTE, PBYTE, unsigned, unsigned);
|
|
STATIC void BitmapHeaderFromIff(IFFPTR piff, LPBITMAPINFOHEADER pbih);
|
|
STATIC IFFPTR GifOpen(PCSTR pszFileName);
|
|
STATIC void GifClose(IFFPTR piff);
|
|
STATIC int GifGetLine(int NumLines, PBYTE Buffer, IFFPTR piff);
|
|
|
|
INLINE void InvertLine(LPBYTE Buffer, int DimX);
|
|
INLINE int ColorsFromBitDepth(int bitsperpixel);
|
|
INLINE int ScanLineWidth(int width, int bitcount);
|
|
|
|
static const int InterlaceMultiplier[] = { 8, 8, 4, 2 };
|
|
static const int InterlaceOffset[] = { 0, 4, 2, 1 };
|
|
|
|
void BitmapHeaderFromIff(IFFPTR piff, LPBITMAPINFOHEADER pbih)
|
|
{
|
|
pbih->biSize = sizeof(BITMAPINFOHEADER);
|
|
pbih->biWidth = piff->DimX;
|
|
pbih->biHeight = piff->DimY;
|
|
pbih->biPlanes = 1;
|
|
pbih->biCompression = 0;
|
|
pbih->biXPelsPerMeter= 0;
|
|
pbih->biYPelsPerMeter= 0;
|
|
pbih->biClrImportant = 0;
|
|
|
|
switch (piff->Class) {
|
|
case IFFCL_BILEVEL:
|
|
pbih->biBitCount = 1;
|
|
pbih->biClrUsed = 2;
|
|
break;
|
|
|
|
case IFFCL_PALETTE:
|
|
if (piff->Bits <= 4) {
|
|
pbih->biBitCount = 4;
|
|
pbih->biClrUsed = 16;
|
|
}
|
|
else {
|
|
pbih->biBitCount = 8;
|
|
pbih->biClrUsed = 256;
|
|
}
|
|
break;
|
|
|
|
case IFFCL_GRAY:
|
|
pbih->biBitCount = 8;
|
|
pbih->biClrUsed = 256;
|
|
break;
|
|
}
|
|
|
|
pbih->biSizeImage = (pbih->biWidth * pbih->biBitCount + 31) / 32;
|
|
pbih->biSizeImage *= 4 * pbih->biHeight;
|
|
}
|
|
|
|
BOOL LoadGif(PCSTR pszFile, HBITMAP* phbmp, HPALETTE* phpal, CHtmlHelpControl* phhctrl)
|
|
{
|
|
IFFPTR piff = GifOpen(pszFile);
|
|
if (!piff)
|
|
return FALSE;
|
|
|
|
CMem memBmi(sizeof(BITMAPINFOHEADER) +
|
|
sizeof(RGBQUAD) * (piff->Bits > 8 ? 0 : 256));
|
|
|
|
PBITMAPINFO pbmi = (PBITMAPINFO) memBmi.pb;
|
|
PBITMAPINFOHEADER pbih = (PBITMAPINFOHEADER) pbmi;
|
|
|
|
BitmapHeaderFromIff(piff, pbih);
|
|
|
|
if (pbih->biBitCount)
|
|
pbih->biBitCount = 8; // filter always returns 256-color image
|
|
|
|
ASSERT(pbih->biBitCount);
|
|
|
|
// Read in the palette
|
|
|
|
if (pbih->biBitCount && phpal) {
|
|
CMem memPal(768);
|
|
int cColors = ColorsFromBitDepth(pbih->biBitCount);
|
|
if (piff->Class == IFFCL_PALETTE || piff->Class == IFFCL_BILEVEL) {
|
|
GetImagePalette(piff, memPal.pb);
|
|
for (int i = 0; i < cColors; i++) {
|
|
pbmi->bmiColors[i].rgbRed = memPal.pb[i * 3];
|
|
pbmi->bmiColors[i].rgbGreen = memPal.pb[i * 3 + 1];
|
|
pbmi->bmiColors[i].rgbBlue = memPal.pb[i * 3 + 2];
|
|
pbmi->bmiColors[i].rgbReserved = 0;
|
|
}
|
|
}
|
|
else {
|
|
for (int i = 0; i < cColors; i++) {
|
|
pbmi->bmiColors[i].rgbRed =
|
|
pbmi->bmiColors[i].rgbGreen =
|
|
pbmi->bmiColors[i].rgbBlue = (BYTE) i;
|
|
pbmi->bmiColors[i].rgbReserved = 0;
|
|
}
|
|
}
|
|
|
|
*phpal = CreateBIPalette(pbih);
|
|
}
|
|
|
|
CPalDC dc;
|
|
|
|
if (phpal && *phpal)
|
|
dc.SelectPal(*phpal);
|
|
|
|
int cbLineWidth = ScanLineWidth(pbih->biWidth, pbih->biBitCount);
|
|
|
|
PBYTE pBits;
|
|
*phbmp = CreateDIBSection(dc.m_hdc, pbmi, DIB_RGB_COLORS,
|
|
(void**) &pBits, NULL, 0);
|
|
if (!*phbmp)
|
|
goto ErrorReturn;
|
|
|
|
if (piff->Sequence == IFFSEQ_INTERLACED) {
|
|
int InterPass = 0;
|
|
int InterLine = InterlaceOffset[InterPass];
|
|
for (int i = 0; i < pbih->biHeight; i++) {
|
|
int TmpLine = InterlaceMultiplier[InterPass] * InterLine + InterlaceOffset[InterPass];
|
|
|
|
// Small images will skip one or more passes
|
|
|
|
while (TmpLine >= pbih->biHeight) {
|
|
InterPass++;
|
|
InterLine = 0;
|
|
TmpLine = InterlaceOffset[InterPass];
|
|
if (TmpLine >= pbih->biHeight)
|
|
TmpLine = pbih->biHeight - 1;
|
|
}
|
|
PBYTE pbBits = pBits +
|
|
((pbih->biHeight - 1) - TmpLine) * cbLineWidth;
|
|
ASSERT(pbBits <= (pBits + ((pbih->biHeight - 1) * cbLineWidth)));
|
|
if (GifGetLine(1, pbBits, piff) == IFFERR_NONE) {
|
|
InterLine++;
|
|
}
|
|
else {
|
|
goto ErrorReturn;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (int i = 0; i < pbih->biHeight; i++) {
|
|
PBYTE pbBits = pBits +
|
|
((pbih->biHeight - 1) - i) * cbLineWidth;
|
|
int result = GifGetLine(1, pbBits, piff);
|
|
if (result != IFFERR_NONE) {
|
|
goto ErrorReturn;
|
|
}
|
|
}
|
|
}
|
|
|
|
GifClose(piff);
|
|
return TRUE;
|
|
|
|
ErrorReturn:
|
|
if (phhctrl)
|
|
phhctrl->AuthorMsg(IDSHHA_GIF_CORRUPT, pszFile);
|
|
else
|
|
doAuthorMsg(IDSHHA_GIF_CORRUPT, pszFile);
|
|
GifClose(piff);
|
|
if (phpal && *phpal) {
|
|
dc.SelectPal(NULL);
|
|
DeleteObject(*phpal);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC IFFPTR GifOpen(PCSTR pszFileName)
|
|
{
|
|
IFFPTR piff = (IFFPTR) lcCalloc(sizeof(IFF_FID));
|
|
|
|
UINT GetFlag = 0;
|
|
|
|
piff->Error = IFFERR_NONE;
|
|
piff->PackMode = IFFPM_NORMALIZED;
|
|
piff->linelen = -1;
|
|
|
|
if ((piff->Error = GetInit(pszFileName, piff, GetFlag)) != FSERR_NONE) {
|
|
if (piff->prstream) {
|
|
delete piff->prstream;
|
|
}
|
|
GIFFreeMemory(piff);
|
|
lcFree(piff);
|
|
return NULL;
|
|
}
|
|
SetUserParams(piff);
|
|
|
|
switch (piff->Class) {
|
|
case IFFCL_BILEVEL:
|
|
piff->LineBytes = (piff->DimX + 7) / 8;
|
|
piff->LineOffset = piff->DimX;
|
|
break;
|
|
|
|
case IFFCL_RGB:
|
|
piff->LineBytes = piff->DimX * 3;
|
|
piff->LineOffset = piff->LineBytes;
|
|
break;
|
|
|
|
default:
|
|
piff->LineBytes = piff->DimX;
|
|
piff->LineOffset = piff->LineBytes;
|
|
break;
|
|
}
|
|
|
|
return piff;
|
|
}
|
|
|
|
STATIC void GifClose(IFFPTR piff)
|
|
{
|
|
if (piff->prstream)
|
|
delete piff->prstream;
|
|
|
|
GIFFreeMemory(piff);
|
|
lcFree(piff);
|
|
}
|
|
|
|
STATIC int GifGetLine(int NumLines, PBYTE Buffer, IFFPTR piff)
|
|
{
|
|
int Count, RetCnt;
|
|
int UserWidth;
|
|
LPBYTE ptr;
|
|
|
|
if (piff == NULL)
|
|
return (IFFERR_PARAMETER);
|
|
|
|
if (piff->PackMode == IFFPM_PACKED)
|
|
UserWidth = piff->BytesPerLine;
|
|
else
|
|
UserWidth = piff->DimX;
|
|
|
|
do {
|
|
// if there are uncompressed lines in the buffer, just copy them
|
|
|
|
if (piff->StripLines > 0) {
|
|
ptr = piff->DecompBuffer + ((piff->ActualLinesPerStrip - piff->StripLines) * piff->BytesPerLine);
|
|
if (piff->PackMode == IFFPM_PACKED) {
|
|
IFF_PackLine(Buffer, ptr, piff->DimX, piff->Bits);
|
|
if (piff->BlackOnWhite)
|
|
InvertLine(Buffer, piff->DimX);
|
|
}
|
|
else
|
|
memcpy(Buffer, ptr, piff->DimX);
|
|
|
|
piff->StripLines--;
|
|
NumLines--;
|
|
piff->curline++;
|
|
Buffer += UserWidth;
|
|
}
|
|
else {
|
|
|
|
// decompress a strip, first get enough compressed data
|
|
|
|
if (!piff->ReadItAll) {
|
|
|
|
// copy already read lines to beginning of comp buffer
|
|
|
|
if (piff->BytesInRWBuffer)
|
|
memcpy(piff->RWBuffer, piff->rw_ptr, piff->BytesInRWBuffer);
|
|
piff->rw_ptr = piff->RWBuffer + piff->BytesInRWBuffer;
|
|
|
|
// read compressed blocks until comp buffer is full
|
|
|
|
while (piff->BytesInRWBuffer < (piff->CompBufferSize - 256)) {
|
|
BYTE BlockSize = piff->prstream->cget();
|
|
if (piff->prstream->m_fEndOfFile) {
|
|
piff->Error = IFFERR_IO_READ;
|
|
return piff->Error;
|
|
}
|
|
if (BlockSize == 0) {
|
|
piff->ReadItAll = 1;
|
|
break;
|
|
}
|
|
|
|
Count = BlockSize;
|
|
if (!piff->prstream->doRead(piff->rw_ptr, BlockSize)) {
|
|
piff->Error = IFFERR_IO_READ;
|
|
return piff->Error;
|
|
}
|
|
|
|
piff->BytesInRWBuffer += Count;
|
|
piff->rw_ptr += Count;
|
|
}
|
|
|
|
piff->rw_ptr = piff->RWBuffer;
|
|
}
|
|
|
|
// copy back already decompressed bytes to beginning of decomp buffer
|
|
|
|
if (piff->BytesLeft)
|
|
memcpy(piff->DecompBuffer, piff->dcmp_ptr, piff->BytesLeft);
|
|
piff->dcmp_ptr = piff->DecompBuffer + piff->BytesLeft;
|
|
|
|
// decompress to fill the decomp buffer
|
|
|
|
Count = piff->DecompBufferSize;
|
|
ASSERT(piff->plzData);
|
|
RetCnt = LZExpand(piff->plzData, piff->dcmp_ptr, piff->rw_ptr, Count, piff->BytesInRWBuffer);
|
|
if (RetCnt <= 0)
|
|
return IFFERR_IMAGE;
|
|
piff->BytesInRWBuffer -= RetCnt;
|
|
piff->rw_ptr += RetCnt;
|
|
piff->StripLines = Count / piff->BytesPerLine;
|
|
piff->BytesLeft = Count % piff->BytesPerLine;
|
|
piff->ActualLinesPerStrip = piff->StripLines;
|
|
piff->dcmp_ptr = piff->DecompBuffer + piff->StripLines * piff->BytesPerLine;
|
|
}
|
|
} while (NumLines);
|
|
|
|
return IFFERR_NONE;
|
|
}
|
|
|
|
STATIC void SetUserParams(IFFPTR piff)
|
|
{
|
|
int i;
|
|
BOOL fIsPalette;
|
|
|
|
piff->BlackOnWhite = FALSE;
|
|
fIsPalette = TRUE;
|
|
if (piff->Bits == 1) {
|
|
if (piff->Palette == NULL)
|
|
fIsPalette = FALSE;
|
|
else {
|
|
if (piff->Palette[0] == piff->Palette[1] &&
|
|
piff->Palette[1] == piff->Palette[2] &&
|
|
piff->Palette[0] == 0 &&
|
|
piff->Palette[3] == piff->Palette[4] &&
|
|
piff->Palette[4] == piff->Palette[5] &&
|
|
piff->Palette[3] == 255)
|
|
fIsPalette = FALSE;
|
|
if (piff->Palette[0] == piff->Palette[1] &&
|
|
piff->Palette[1] == piff->Palette[2] &&
|
|
piff->Palette[0] == 255 &&
|
|
piff->Palette[3] == piff->Palette[4] &&
|
|
piff->Palette[4] == piff->Palette[5] &&
|
|
piff->Palette[3] == 0)
|
|
{
|
|
piff->BlackOnWhite = TRUE;
|
|
fIsPalette = FALSE;
|
|
}
|
|
}
|
|
if (!fIsPalette)
|
|
piff->Class = IFFCL_BILEVEL;
|
|
}
|
|
if (fIsPalette) {
|
|
for (i = 0; i < piff->PaletteSize / 3; i++)
|
|
if (piff->Palette[i * 3] != piff->Palette[i * 3 + 1] ||
|
|
piff->Palette[i * 3 + 1] != piff->Palette[i * 3 + 2] ||
|
|
piff->Palette[i * 3] != (BYTE)i)
|
|
break;
|
|
|
|
if (i == piff->PaletteSize / 3) {
|
|
for (i = 0; i < piff->PaletteSize / 3; i++)
|
|
piff->Palette[i] = piff->Palette[i * 3];
|
|
piff->PaletteSize /= 3;
|
|
piff->Class = IFFCL_GRAY;
|
|
}
|
|
else
|
|
piff->Class = IFFCL_PALETTE;
|
|
}
|
|
}
|
|
|
|
STATIC int GetImagePalette(IFFPTR piff, LPBYTE Pal)
|
|
{
|
|
if (piff->PaletteSize > 0)
|
|
memcpy (Pal, piff->Palette, (unsigned) piff->PaletteSize);
|
|
else
|
|
return IFFERR_NOTAVAILABLE;
|
|
|
|
return IFFERR_NONE;
|
|
}
|
|
|
|
STATIC FSERR GetInit(PCSTR pszFileName, IFFPTR piff, UINT GetFlag)
|
|
{
|
|
BYTE Buffer[13];
|
|
|
|
piff->prstream = new CStream(pszFileName);
|
|
if (!piff->prstream->fInitialized) {
|
|
GIFFreeMemory(piff);
|
|
return FSERR_CANT_OPEN;
|
|
}
|
|
|
|
if (!piff->prstream->doRead(Buffer, GIF_HDR_SIZE)) {
|
|
GIFFreeMemory(piff);
|
|
piff->Error = IFFERR_IO_READ;
|
|
return FSERR_TRUNCATED;
|
|
}
|
|
|
|
if (((Buffer[0] != 'G') || (Buffer[1] != 'I') || (Buffer[2] != 'F') ||
|
|
(Buffer[3] != '8') || (Buffer[5] != 'a')) ||
|
|
((Buffer[4] != '7') && (Buffer[4] != '9'))) {
|
|
GIFFreeMemory(piff);
|
|
piff->Error = IFFERR_HEADER;
|
|
return FSERR_INVALID_FORMAT;
|
|
}
|
|
|
|
piff->prstream->read(&piff->lsd, sizeof(LSD));
|
|
|
|
piff->Bits = (int) (piff->lsd.b.ceGCT + 1);
|
|
piff->PaletteSize = 3 * (1 << piff->Bits);
|
|
|
|
if (piff->lsd.b.fGCT) {
|
|
piff->Palette = (PBYTE) lcMalloc(piff->PaletteSize);
|
|
if (!piff->prstream->doRead(piff->Palette, piff->PaletteSize)) {
|
|
GIFFreeMemory(piff);
|
|
piff->Error = IFFERR_IO_READ;
|
|
return FSERR_TRUNCATED;
|
|
}
|
|
}
|
|
|
|
for (;;) {
|
|
if (piff->prstream->m_fEndOfFile) {
|
|
piff->Error = IFFERR_IO_READ;
|
|
return FSERR_CORRUPTED_FILE;
|
|
}
|
|
Buffer[0] = piff->prstream->cget();
|
|
|
|
switch (*Buffer) {
|
|
case 0x21: // control extension
|
|
if (!piff->prstream->doRead(Buffer, 2)) {
|
|
GIFFreeMemory(piff);
|
|
piff->Error = IFFERR_IO_READ;
|
|
return FSERR_TRUNCATED;
|
|
}
|
|
|
|
switch (*Buffer) {
|
|
case 0xFF: // Application Specific Block
|
|
case 0xF9: // Graphics Control Extension
|
|
case 0x01: // Plain Text Extension
|
|
case 0xFE: // Comment Extension
|
|
while (Buffer[1] != 0) {
|
|
piff->prstream->seek((long) Buffer[1], SK_CUR);
|
|
|
|
if (piff->prstream->m_fEndOfFile) {
|
|
piff->Error = IFFERR_IO_READ;
|
|
return FSERR_CORRUPTED_FILE;
|
|
}
|
|
Buffer[1] = piff->prstream->cget();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT_COMMENT(FALSE, "The following code needs verification");
|
|
piff->prstream->seek(Buffer[1] + 1, SK_CUR);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0x3B: // end of file
|
|
return FSERR_NONE;
|
|
|
|
case 0x2C:
|
|
if (!piff->prstream->doRead((Buffer + 1), 9)) {
|
|
GIFFreeMemory(piff);
|
|
piff->Error = IFFERR_IO_READ;
|
|
return FSERR_TRUNCATED;
|
|
}
|
|
|
|
short IntData[2];
|
|
memcpy(IntData, Buffer + 5, 2 * sizeof(short));
|
|
INTELSWAP16 (IntData[0]);
|
|
INTELSWAP16 (IntData[1]);
|
|
|
|
piff->DimX = IntData[0];
|
|
piff->DimY = IntData[1];
|
|
|
|
if (Buffer[9] & 0x40)
|
|
piff->Sequence = IFFSEQ_INTERLACED;
|
|
else
|
|
piff->Sequence = IFFSEQ_TOPDOWN;
|
|
|
|
if (Buffer[9] & 0x80) {
|
|
piff->Bits = (Buffer[9] & 0x07) + 1;
|
|
int Size = (3 * (1 << piff->Bits));
|
|
|
|
// Ignore local color table for now
|
|
|
|
piff->prstream->seek(Size, SK_CUR);
|
|
}
|
|
|
|
BYTE CodeSize = piff->prstream->cget();
|
|
|
|
piff->BytesPerLine = piff->DimX;
|
|
|
|
piff->CompBufferSize = MAX_BUFFER_SIZE;
|
|
piff->DecompBufferSize = (MAX_BUFFER_SIZE >> 1) + (MAX_BUFFER_SIZE >> 2);
|
|
|
|
// calculate the number of lines in a strip
|
|
|
|
piff->StripLines = 0;
|
|
piff->LinesPerStrip = piff->DecompBufferSize / piff->BytesPerLine;
|
|
piff->DecompBufferSize = (piff->LinesPerStrip * piff->BytesPerLine);
|
|
piff->BytesInRWBuffer = 0;
|
|
piff->BytesLeft = 0;
|
|
|
|
piff->RWBuffer = (PBYTE) lcMalloc(piff->CompBufferSize);
|
|
piff->DecompBuffer = (PBYTE) lcMalloc(piff->DecompBufferSize);
|
|
piff->rw_ptr = piff->RWBuffer;
|
|
piff->dcmp_ptr = piff->DecompBuffer;
|
|
|
|
piff->curline = 0;
|
|
|
|
piff->plzData = LZInitialize(CodeSize);
|
|
if (piff->plzData == NULL) {
|
|
GIFFreeMemory(piff);
|
|
piff->Error = IFFERR_MEMORY;
|
|
return FSERR_INSF_MEMORY;
|
|
}
|
|
return FSERR_NONE;
|
|
}
|
|
}
|
|
|
|
return FSERR_NONE;
|
|
}
|
|
|
|
STATIC void GIFFreeMemory(IFFPTR piff)
|
|
{
|
|
if (piff->plzData != NULL)
|
|
LZCleanUp(piff->plzData);
|
|
|
|
if (piff->Palette)
|
|
lcClearFree(&piff->Palette);
|
|
if (piff->RWBuffer)
|
|
lcClearFree(&piff->RWBuffer);
|
|
if (piff->DecompBuffer)
|
|
lcClearFree(&piff->DecompBuffer);
|
|
|
|
piff->plzData = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
INLINE void InvertLine(LPBYTE Buffer, int DimX)
|
|
{
|
|
int il = (DimX + 7) >> 3;
|
|
for (int i = 0; i < il; i++)
|
|
*Buffer++ = 255 - *Buffer;
|
|
}
|
|
|
|
FSERR SetupForRead(int pos, int iWhichImage, IFF_FID* piff)
|
|
{
|
|
piff->prstream->seek(pos);
|
|
BYTE CodeSize = piff->prstream->cget();
|
|
|
|
piff->BytesPerLine = piff->DimX;
|
|
|
|
piff->CompBufferSize = MAX_BUFFER_SIZE;
|
|
piff->DecompBufferSize = (MAX_BUFFER_SIZE >> 1) + (MAX_BUFFER_SIZE >> 2);
|
|
|
|
// calculate the number of lines in a strip
|
|
|
|
piff->StripLines = 0;
|
|
piff->LinesPerStrip = piff->DecompBufferSize / piff->BytesPerLine;
|
|
piff->DecompBufferSize = (piff->LinesPerStrip * piff->BytesPerLine);
|
|
piff->BytesInRWBuffer = 0;
|
|
piff->BytesLeft = 0;
|
|
|
|
if (piff->RWBuffer)
|
|
lcFree(piff->RWBuffer);
|
|
piff->RWBuffer = (PBYTE) lcMalloc(piff->CompBufferSize);
|
|
if (piff->DecompBuffer)
|
|
lcFree(piff->DecompBuffer);
|
|
piff->DecompBuffer = (PBYTE) lcMalloc(piff->DecompBufferSize);
|
|
piff->rw_ptr = piff->RWBuffer;
|
|
piff->dcmp_ptr = piff->DecompBuffer;
|
|
|
|
piff->curline = 0;
|
|
|
|
if (piff->plzData)
|
|
LZCleanUp(piff->plzData);
|
|
piff->plzData = LZInitialize(CodeSize);
|
|
if (piff->plzData == NULL) {
|
|
GIFFreeMemory(piff);
|
|
piff->Error = IFFERR_MEMORY;
|
|
return FSERR_UNSUPPORTED_GIF_FORMAT;
|
|
}
|
|
// piff->Sequence = pGifImage->fInterlaced ? IFFSEQ_INTERLACED : IFFSEQ_TOPDOWN;
|
|
|
|
return FSERR_NONE;
|
|
}
|
|
|
|
int IFF_PackLine(PBYTE ToBuff, const BYTE* FromBuff, int Pixels, int Bits)
|
|
{
|
|
int Mask;
|
|
int PMask;
|
|
int Pix;
|
|
int Shift;
|
|
int i;
|
|
|
|
switch (Bits) {
|
|
case 1:
|
|
case 4:
|
|
Mask = ((8 / Bits) - 1);
|
|
Pix = 0;
|
|
Shift = (WORD) (8 - Bits);
|
|
PMask = (WORD) (0xFF >> Shift);
|
|
for (i = 0; i < Pixels; i++)
|
|
{
|
|
Pix |= *FromBuff++ & PMask;
|
|
|
|
if ((i & Mask) == Mask)
|
|
{
|
|
*ToBuff++ = (BYTE) Pix;
|
|
Pix = 0;
|
|
}
|
|
else
|
|
Pix <<= Bits;
|
|
}
|
|
if ((i & Mask) != 0)
|
|
{
|
|
while ((++i & Mask) != 0)
|
|
Pix <<= Bits;
|
|
*ToBuff = (BYTE) Pix;
|
|
}
|
|
return (Pixels + Mask) / (Mask + 1);
|
|
break;
|
|
|
|
case 8: // degenerate case
|
|
memcpy(ToBuff, FromBuff, Pixels);
|
|
return Pixels;
|
|
|
|
default:
|
|
IASSERT_COMMENT(FALSE, "Invalid bit depth");
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
INLINE void ExpandReset(LZDATA* plzData)
|
|
{
|
|
plzData->BitPos = 0;
|
|
plzData->CurBits = plzData->CharSize + 1;
|
|
plzData->OutString = 0;
|
|
plzData->CodeJump = (1 << plzData->CurBits) - 1;
|
|
plzData->CodeJump++;
|
|
}
|
|
|
|
INLINE unsigned GetNextCode(LZDATA* plzData)
|
|
{
|
|
unsigned short code;
|
|
int newbitpos;
|
|
|
|
code = (unsigned short) (*plzData->CodeInput | (plzData->CodeInput[1] << 8));
|
|
|
|
code >>= plzData->BitPos; // ditch previous bits
|
|
code &= 0xFFFF >> (16 - plzData->CurBits); // ditch next bits
|
|
|
|
newbitpos = plzData->BitPos + plzData->CurBits;
|
|
|
|
if (newbitpos > 7)
|
|
++plzData->CodeInput; // used up at least one * byte
|
|
|
|
if (newbitpos >= 16)
|
|
++plzData->CodeInput; // used up two bytes
|
|
|
|
if (newbitpos > 16) { // need more bits
|
|
code |= (*plzData->CodeInput << (32 - newbitpos)) >> (16 - plzData->CurBits);
|
|
code &= 0xFFFF >> (16 - plzData->CurBits); // ditch next bits
|
|
}
|
|
plzData->BitPos = newbitpos & 7;
|
|
|
|
return code; // need mask in 32 bit
|
|
}
|
|
|
|
STATIC int LZExpand(LZDATA* plzData, LPBYTE ToBuff, LPBYTE FromBuff, unsigned ToCnt, unsigned FromCnt)
|
|
{
|
|
unsigned cnt;
|
|
LPBYTE outbuff = ToBuff;
|
|
int code, incode;
|
|
|
|
ASSERT(plzData);
|
|
plzData->CodeInput = FromBuff;
|
|
cnt = ToCnt;
|
|
|
|
while (plzData->OutString > 0 && cnt > 0) {
|
|
*outbuff++ = plzData->Stack[--plzData->OutString];
|
|
cnt--;
|
|
}
|
|
|
|
while (cnt > 0) {
|
|
if ((code = GetNextCode(plzData)) == END_INFO)
|
|
break;
|
|
|
|
if (code == plzData->ClearCode) {
|
|
|
|
ZeroMemory(plzData->CodeTable, sizeof(int*) * HASH_SIZE);
|
|
|
|
for (int i = 0; i < HASH_SIZE; i++) {
|
|
// plzData->CodeTable[i] = 0;
|
|
plzData->StringTable[i] = (unsigned char) i;
|
|
}
|
|
|
|
plzData->CurBits = plzData->CharSize + 1; // start beginning
|
|
plzData->TableEntry = FIRST_ENTRY;
|
|
plzData->OutString = 0;
|
|
plzData->CodeJump = (1 << plzData->CurBits) - 1;
|
|
plzData->CodeJump++;
|
|
plzData->LastChar = plzData->OldCode = GetNextCode(plzData);
|
|
if (plzData->OldCode == END_INFO)
|
|
break;
|
|
*outbuff++ = (unsigned char) plzData->LastChar; // output a code
|
|
cnt--;
|
|
}
|
|
else {
|
|
if ((incode = code) >= plzData->TableEntry) { // is_in_code_table ?
|
|
plzData->Stack[plzData->OutString++] = (unsigned char) plzData->LastChar; /* not in table */
|
|
code = plzData->OldCode;
|
|
}
|
|
while (code >= plzData->ClearCode) { // need decoding
|
|
if ((plzData->OutString >= (1L << 12)) || (code > HASH_SIZE))
|
|
return -1;
|
|
plzData->Stack[plzData->OutString++] = plzData->StringTable[code];
|
|
code = plzData->CodeTable[code];
|
|
}
|
|
|
|
if (code < 0 || code > HASH_SIZE || plzData->TableEntry >= HASH_SIZE) {
|
|
|
|
// pretend that we decoded all data.
|
|
|
|
return min((int) (plzData->CodeInput - FromBuff), (int) FromCnt);
|
|
}
|
|
plzData->Stack[plzData->OutString++] =
|
|
(BYTE) (plzData->LastChar =
|
|
plzData->StringTable[code]);
|
|
|
|
// output string
|
|
|
|
while (plzData->OutString > 0 && cnt > 0) {
|
|
*outbuff++ = plzData->Stack[--plzData->OutString];
|
|
cnt--;
|
|
}
|
|
|
|
plzData->CodeTable[plzData->TableEntry] = plzData->OldCode; // Add string to table
|
|
plzData->StringTable[plzData->TableEntry++] = (unsigned char) plzData->LastChar;
|
|
if (plzData->TableEntry == plzData->CodeJump && plzData->CurBits < 12) {
|
|
plzData->CodeJump += 1 << plzData->CurBits;
|
|
plzData->CurBits++;
|
|
}
|
|
plzData->OldCode = incode;
|
|
}
|
|
} // End while
|
|
|
|
cnt = (UINT)(plzData->CodeInput - FromBuff);
|
|
|
|
return (cnt);
|
|
}
|
|
|
|
STATIC LZDATA* LZInitialize(int CharSize)
|
|
{
|
|
if (CharSize < 2)
|
|
CharSize = 2;
|
|
|
|
LZDATA* plzData = (LZDATA*) lcCalloc(sizeof(LZDATA));
|
|
|
|
plzData->CharSize = CharSize;
|
|
plzData->CodeSize = 12;
|
|
plzData->ClearCode = (1 << CharSize);
|
|
|
|
plzData->CodeTable = (int*) lcCalloc(sizeof(int) * HASH_SIZE);
|
|
plzData->StringTable = (PBYTE) lcCalloc(HASH_SIZE);
|
|
plzData->Stack = (PBYTE) lcCalloc(1 << 12);
|
|
|
|
ExpandReset(plzData);
|
|
|
|
return plzData;
|
|
}
|
|
|
|
STATIC void LZCleanUp(LZDATA* plzData)
|
|
{
|
|
if (plzData == NULL)
|
|
return;
|
|
|
|
if (plzData->CodeTable)
|
|
lcFree(plzData->CodeTable);
|
|
if (plzData->StringTable)
|
|
lcFree(plzData->StringTable);
|
|
if (plzData->HashTable)
|
|
lcFree(plzData->HashTable);
|
|
if (plzData->Stack)
|
|
lcFree(plzData->Stack);
|
|
|
|
lcFree(plzData);
|
|
}
|
|
|
|
INLINE int ColorsFromBitDepth(int bitsperpixel)
|
|
{
|
|
switch (bitsperpixel) {
|
|
case 1:
|
|
return 2;
|
|
|
|
case 4:
|
|
return 16;
|
|
|
|
case 8:
|
|
return 256;
|
|
break;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
INLINE int ScanLineWidth(int width, int bitcount)
|
|
{
|
|
switch (bitcount) {
|
|
case 1: // REVIEW: is this true?
|
|
return ((width + 31) & ~31) * bitcount / 8;
|
|
|
|
case 4:
|
|
return ((width * 4 + 31) / 32 * 4);
|
|
|
|
case 8:
|
|
break;
|
|
|
|
default:
|
|
IASSERT(!"Invalid bitcount value in ScanLineWidth");
|
|
break;
|
|
}
|
|
|
|
if (width & 0x03)
|
|
width += (4 - width % 4);
|
|
return width;
|
|
}
|