|
|
// 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; }
|