windows-nt/Source/XPSP1/NT/enduser/netmeeting/as/cpi32/bcd.cpp
2020-09-26 16:20:57 +08:00

2727 lines
103 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "precomp.h"
//
// BCD.CPP
// Bitmap Compression-Decompression
//
// Copyright(c) Microsoft 1997-
//
#define MLZ_FILE_ZONE ZONE_ORDER
//
// Introduction
//
// These functions take a bitmap and encode it according to the codes
// defined in bcd.h. Although there are some complexities in the
// encoding (particularly with the "sliding palette" encoding for
// compressing 8 bit down to 4 bit) the encodings should be self
// explanatory. bcd describes some nuances of the encoding scheme.
//
// The important thing to note is that, when used in conjunction with a
// dictionary based compression scheme the objective of this function is
// not to minimize the output but to "prime" it such that the GDC can
// perform faster and more effectively on the data.
//
// Specifically we must NOT encode short runs in the data, even though we
// know that they reduce the output from this stage, as they will
// invariably reduce the efficiency of the GDC compression by a greater
// factor! The break even point seems to be about a 5/6 byte run. To
// illustrate this, consider the following run
// xxxxyyyyyxxxyyyxxxxxyyyyyyxxxyyyxxxxyyy We would encode this as
// 4x5y3x3y5x5y3x3y4x3y The compression factor is only *2 and yet the
// output data is now much more random - the tokenized look of the input
// has been lost.
//
// Encodings that are not context independent are particularly bad. A FG
// run in one position may become a SET+FG run in another position, thus
// "randomizing" the data.
//
// Bottom line is that all of the apparently arbitrary numbers below have
// been carefully tuned to prep the data for input to GDC. Screwing them
// down does increase the compression of this stage in some cases by as
// much as 20%, but loses about 20% post GDC. Frustrating! Be warned.
//
//
//
// BCD_ShareStarting()
// Creates resources needed for bitmap compression/decompression
//
BOOL ASShare::BCD_ShareStarting(void)
{
BOOL rc = FALSE;
DebugEntry(ASShare::BCD_ShareStarting);
// Allocate BCD scratch buffers
m_abNormal = new BYTE[BCD_NORMALSIZE];
if (!m_abNormal)
{
ERROR_OUT(("BCD_ShareStarting: failed to alloc m_abNormal"));
DC_QUIT;
}
m_abXor = new BYTE[BCD_XORSIZE];
if (!m_abXor)
{
ERROR_OUT(("BCD_ShareStarting: failed to alloc m_abXor"));
DC_QUIT;
}
m_amatch = new MATCH[BCD_MATCHCOUNT];
if (!m_amatch)
{
ERROR_OUT(("BCD_ShareStarting: failed to alloc m_amatch"));
DC_QUIT;
}
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(ASShare::BCD_ShareStarting, rc);
return(rc);
}
//
// BCD_ShareEnded()
//
void ASShare::BCD_ShareEnded(void)
{
DebugEntry(ASShare::BCD_ShareEnded);
//
// Free the BCD scratch buffers
//
if (m_amatch)
{
delete[] m_amatch;
m_amatch = NULL;
}
if (m_abXor)
{
delete[] m_abXor;
m_abXor = NULL;
}
if (m_abNormal)
{
delete[] m_abNormal;
m_abNormal = NULL;
}
DebugExitVOID(ASShare::BCD_ShareEnded);
}
//
// BC_CompressBitmap(..)
//
BOOL ASShare::BC_CompressBitmap
(
LPBYTE pSrcBitmap,
LPBYTE pDstBuffer,
LPUINT pDstBufferSize,
UINT bitmapWidth,
UINT bitmapHeight,
UINT cBpp,
LPBOOL pLossy
)
{
BOOL fCompressedData = FALSE;
UINT cbScanWidth;
PCD_HEADER pCompDataHeader;
LPBYTE pCompData;
UINT cbUncompressedDataSize;
UINT cbFreeDstBytes;
UINT cbCompFirstRowSize;
UINT cbCompMainBodySize;
DebugEntry(ASShare::BC_CompressBitmap);
//
// We support 4 and 8 bpp only
//
if ((cBpp != 4) && (cBpp != 8))
{
TRACE_OUT(("BC_CompressBitmap: No compression at %d bpp", cBpp));
DC_QUIT;
}
//
// If we don't have scratch buffers, can't do it either
// But for now, we just won't enter into a share if we can't allocate
// themm.
//
ASSERT(m_abNormal);
ASSERT(m_abXor);
ASSERT(m_amatch);
cbScanWidth = BYTES_IN_SCANLINE(bitmapWidth, cBpp);
//
// Take a local copy of the destination buffer size.
//
cbFreeDstBytes = *pDstBufferSize;
//
// Calculate the size of the uncompressed src data.
//
cbUncompressedDataSize = cbScanWidth * bitmapHeight;
//
// Check that the size of the uncompressed data is less than our max.
//
ASSERT(cbUncompressedDataSize < TSHR_MAX_SEND_PKT);
//
// We write a compressed data header at the start of the dst buffer.
// Reserve space for it now, and fill in the size of the uncompressed
// data.
//
if (sizeof(CD_HEADER) >= cbFreeDstBytes)
{
WARNING_OUT(("BC_CompressBitmap: Dest buffer too small: %d", cbFreeDstBytes));
DC_QUIT;
}
pCompDataHeader = (PCD_HEADER)pDstBuffer;
pCompDataHeader->cbUncompressedSize = (TSHR_UINT16)cbUncompressedDataSize;
pCompData = ((LPBYTE)pCompDataHeader) + sizeof(CD_HEADER);
cbFreeDstBytes -= sizeof(CD_HEADER);
//
// Compress the bitmap data.
// We just pass the complete image into the compression function.
// The header size in the packet is set to 0 and the whole thing
// flows as the main body
//
cbCompFirstRowSize = 0; // lonchanc: a must for V2
cbCompMainBodySize = CompressV2Int(pSrcBitmap, pCompData,
bitmapWidth*bitmapHeight, cBpp, cbScanWidth, cbFreeDstBytes,
pLossy, m_abNormal, m_abXor, m_amatch);
if (cbCompMainBodySize == 0)
{
WARNING_OUT(("BC_CompressBitmap: Compression failed"));
DC_QUIT;
}
//
// Fill in the compressed data header.
//
pCompDataHeader->cbCompFirstRowSize = (TSHR_UINT16)cbCompFirstRowSize;
pCompDataHeader->cbCompMainBodySize = (TSHR_UINT16)cbCompMainBodySize;
pCompDataHeader->cbScanWidth = (TSHR_UINT16)cbScanWidth;
ASSERT(IsV2CompressedDataHeader(pCompDataHeader));
//
// Write back the new (compressed) packet size.
//
*pDstBufferSize = sizeof(CD_HEADER) + cbCompFirstRowSize + cbCompMainBodySize;
TRACE_OUT(("Bitmap Compressed %u bytes to %u",
cbUncompressedDataSize, *pDstBufferSize));
fCompressedData = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(ASShare::BC_CompressBitmap, fCompressedData);
return(fCompressedData);
}
//
// BD_DecompressBitmap(..)
//
BOOL ASShare::BD_DecompressBitmap
(
LPBYTE pCompressedData,
LPBYTE pDstBitmap,
UINT cbSrcData,
UINT bitmapWidth,
UINT bitmapHeight,
UINT cBpp
)
{
BOOL fDecompressedData = FALSE;
PCD_HEADER pCompDataHeader;
LPBYTE pCompDataFirstRow;
LPBYTE pCompDataMainBody;
UINT decompSize;
DebugEntry(ASShare::BD_DecompressBitmap);
//
// We currently support 4 and 8 bpp bitmaps only
//
if ((cBpp != 4) && (cBpp != 8))
{
ERROR_OUT(("BD_DecompressBitmap: Unsupported bpp %d", cBpp));
DC_QUIT;
}
//
// Work out the location in the source data of each component.
//
pCompDataHeader = (PCD_HEADER)pCompressedData;
pCompDataFirstRow = (LPBYTE)pCompDataHeader + sizeof(CD_HEADER);
pCompDataMainBody = pCompDataFirstRow +
pCompDataHeader->cbCompFirstRowSize;
ASSERT(IsV2CompressedDataHeader(pCompDataHeader));
TRACE_OUT(( "FirstRowSize(%u) MainBodySize(%u) ScanWidth(%u)",
pCompDataHeader->cbCompFirstRowSize,
pCompDataHeader->cbCompMainBodySize,
pCompDataHeader->cbScanWidth ));
//
// Check that the supplied data size matches our expectations.
//
if (cbSrcData != sizeof(CD_HEADER) +
pCompDataHeader->cbCompFirstRowSize +
pCompDataHeader->cbCompMainBodySize )
{
ERROR_OUT(("BD_DecompressBitmap: Supplied packet size %u does not match bitmap header",
cbSrcData));
DC_QUIT;
}
//
// As with compression, the V2 decompression function just takes
// the whole image for decompression.
// THE ABSENCE OF A FIRST LINE COUNT DOES, IN FACT, INDICATE TO US
// THAT THIS IS A V2 COMPRESSED BITMAP.
//
if (pCompDataHeader->cbCompFirstRowSize != 0)
{
ERROR_OUT(("BD_DecompressBitmap: Bogus header data"));
}
else
{
ASSERT(m_abXor);
decompSize = DecompressV2Int(pCompDataFirstRow, pDstBitmap,
pCompDataHeader->cbCompMainBodySize, cBpp,
pCompDataHeader->cbScanWidth, m_abXor);
TRACE_OUT(("Bitmap Exploded %u bytes from %u", decompSize, cbSrcData));
fDecompressedData = TRUE;
}
DC_EXIT_POINT:
DebugExitBOOL(ASShare::BD_DecompressBitmap, fDecompressedData);
return(fDecompressedData);
}
//
//
// Create a second copy of the source, which consists of all lines XORed,
// if there is a rowDelta specified
//
// Scan both the non-xored and the xored buffers for matches
//
// A best matches are built up in an array which contains an index to the
// match type, together with the match type. Non repetetive sequences are
// stored in this array as color image strings.
//
//
//
// The following constant controls the threshold at which we decide that
// a lossy compress is a pointless overhead. For low bandwidth connections
// DC-Share will always initially request a lossy compress to get some
// data out quickly. If we find that the percentage of COLOR_IMAGE data
// is below this threshold then we turn off lossy compression for this
// bitmap, redo the analysis, perform non-lossy compression and return
// an indication to the caller that the compression was non-lossy.
//
#define LOSSY_THRESHOLD 75
//
// The following functions have been carefully coded to ensure that the
// 16 bit compiler can minimize its switching of segment registers.
// However, this will not impair its performance on 32 bit systems.
//
//
// Utility macros for encoding orders
//
//
// Encode a combined order and set fg color
//
#define ENCODE_SET_ORDER_MEGA(buffer, \
order_code, \
length, \
mega_order_code, \
DEF_LENGTH_ORDER, \
DEF_LENGTH_LONG_ORDER) \
if (length <= DEF_LENGTH_ORDER) \
{ \
*buffer++ = (BYTE)((BYTE)order_code | (BYTE)length); \
} \
else \
{ \
if (length <= DEF_LENGTH_LONG_ORDER) \
{ \
*buffer++ = (BYTE)order_code; \
*buffer++ = (BYTE)(length-DEF_LENGTH_ORDER-1); \
} \
else \
{ \
*buffer++ = (BYTE)mega_order_code; \
INSERT_TSHR_UINT16_UA( buffer, (TSHR_UINT16)length); \
buffer += 2; \
} \
} \
*buffer++ = fgChar;
//
// Encode a combined order and set fg color for a special FGBG image
//
#define ENCODE_SET_ORDER_MEGA_FGBG(buffer, \
order_code, \
length, \
mega_order_code, \
DEF_LENGTH_ORDER, \
DEF_LENGTH_LONG_ORDER) \
if (((length & 0x0007) == 0) && \
(length <= DEF_LENGTH_ORDER)) \
{ \
*buffer++ = (BYTE)((BYTE)order_code | (BYTE)(length/8));\
} \
else \
{ \
if (length <= DEF_LENGTH_LONG_ORDER) \
{ \
*buffer++ = (BYTE)order_code; \
*buffer++ = (BYTE)(length-1); \
} \
else \
{ \
*buffer++ = (BYTE)mega_order_code; \
INSERT_TSHR_UINT16_UA( buffer, (TSHR_UINT16)length); \
buffer += 2; \
} \
} \
*buffer++ = fgChar;
//
// Encode an order for a standard run
//
#define ENCODE_ORDER_MEGA(buffer, \
order_code, \
length, \
mega_order_code, \
DEF_LENGTH_ORDER, \
DEF_LENGTH_LONG_ORDER) \
if (length <= DEF_LENGTH_ORDER) \
{ \
*buffer++ = (BYTE)((BYTE)order_code | (BYTE)length); \
} \
else \
{ \
if (length <= DEF_LENGTH_LONG_ORDER) \
{ \
*buffer++ = (BYTE)order_code; \
*buffer++ = (BYTE)(length-DEF_LENGTH_ORDER-1); \
} \
else \
{ \
*buffer++ = (BYTE)mega_order_code; \
INSERT_TSHR_UINT16_UA( buffer, (TSHR_UINT16)length); \
buffer += 2; \
} \
}
//
// Encode a special FGBG image
//
#define ENCODE_ORDER_MEGA_FGBG(buffer, \
order_code, \
length, \
mega_order_code, \
DEF_LENGTH_ORDER, \
DEF_LENGTH_LONG_ORDER) \
if (((length & 0x0007) == 0) && \
(length <= DEF_LENGTH_ORDER)) \
{ \
*buffer++ = (BYTE)((BYTE)order_code | (BYTE)(length/8));\
} \
else \
{ \
if (length <= DEF_LENGTH_LONG_ORDER) \
{ \
*buffer++ = (BYTE)order_code; \
*buffer++ = (BYTE)(length-1); \
} \
else \
{ \
*buffer++ = (BYTE)mega_order_code; \
INSERT_TSHR_UINT16_UA( buffer, (TSHR_UINT16)length); \
buffer += 2; \
} \
}
//
// Macros to extract the length from order codes
//
#define EXTRACT_LENGTH(buffer, length) \
length = *buffer++ & MAX_LENGTH_ORDER; \
if (length == 0) \
{ \
length = *buffer++ + MAX_LENGTH_ORDER + 1; \
}
#define EXTRACT_LENGTH_LITE(buffer, length) \
length = *buffer++ & MAX_LENGTH_ORDER_LITE; \
if (length == 0) \
{ \
length = *buffer++ + MAX_LENGTH_ORDER_LITE + 1; \
}
#define EXTRACT_LENGTH_FGBG(buffer, length) \
length = *buffer++ & MAX_LENGTH_ORDER; \
if (length == 0) \
{ \
length = *buffer++ + 1; \
} \
else \
{ \
length = length << 3; \
}
#define EXTRACT_LENGTH_FGBG_LITE(buffer, length) \
length = *buffer++ & MAX_LENGTH_ORDER_LITE; \
if (length == 0) \
{ \
length = *buffer++ + 1; \
} \
else \
{ \
length = length << 3; \
}
//
// RunSingle
//
// Determine the length of the current run
//
// RunSingle may only be called if the buffer has at least four
// consecutive identical bytes from the start position
//
// For 16 bit processing there are two versions of this macro. For 32
// bit the nulling of NEAR/FAR will make them the same.
//
#define RUNSINGLE_XOR(buffer, length, result) \
{ \
BYTE NEAR *buf = buffer+4; \
BYTE NEAR *endbuf = buffer+length-4; \
while ((buf < endbuf) && \
(EXTRACT_TSHR_UINT32_UA(buf) == EXTRACT_TSHR_UINT32_UA(buf-4))) \
{ \
buf += 4; \
} \
endbuf += 4; \
while(buf < endbuf && (*buf == *(buf-1))) \
{ \
buf++; \
} \
result = (DWORD)(buf - (buffer)); \
}
#define RUNSINGLE_NRM(buffer, length, result) \
{ \
BYTE FAR *buf = buffer+4; \
BYTE FAR *endbuf = buffer+length-4; \
while ((buf < endbuf) && \
(EXTRACT_TSHR_UINT32_UA(buf) == EXTRACT_TSHR_UINT32_UA(buf-4))) \
{ \
buf += 4; \
} \
endbuf += 4; \
while(buf < endbuf && (*buf == *(buf-1))) \
{ \
buf++; \
} \
result = (DWORD)(buf - (buffer)); \
}
//
// RunDouble
//
// Determine the length of the current run of paired bytes
//
#define RunDouble(buffer, length, result) \
{ \
int len = ((int)length); \
BYTE FAR *buf = buffer; \
BYTE testchar1 = *buf; \
BYTE testchar2 = *(buf+1); \
result = 0; \
while(len > 1) \
{ \
if (*buf++ != testchar1) \
{ \
break; \
} \
if (*buf++ != testchar2) \
{ \
break; \
} \
result += 2; \
len -= 2; \
} \
}
//
// RUNFGBG
//
// Determine the length of the run of bytes that consist
// only of black or a single FG color
// We exit the loop when
// - the next character is not a fg or bg color
// - we hit a run of 24 of the FG or BG color
// 24 may seem excessive, but note the following sample compression:
// 12 16 20 24 28
// Pre GDC 3845 3756 3712 3794 3822
// Post GDC 2401 2313 2286 2189 2209
//
//
#define RUNFGBG(buffer, length, result, work) \
{ \
BYTE NEAR *buf = buffer; \
BYTE NEAR *endbuf = buffer + length; \
result = 0; \
work = *buf; \
while (TRUE) \
{ \
buf++; \
result++; \
if (buf >= endbuf) \
{ \
break; \
} \
\
if ((*buf != work) && (*buf != 0)) \
{ \
break; \
} \
\
if ((result & 0x0007) == 0) \
{ \
if ((*buf == *(buf+1)) && \
(EXTRACT_TSHR_UINT16_UA(buf) == \
EXTRACT_TSHR_UINT16_UA(buf+ 2)) && \
(EXTRACT_TSHR_UINT32_UA(buf) == \
EXTRACT_TSHR_UINT32_UA(buf+ 4)) && \
(EXTRACT_TSHR_UINT32_UA(buf) == \
EXTRACT_TSHR_UINT32_UA(buf+ 8)) && \
(EXTRACT_TSHR_UINT32_UA(buf) == \
EXTRACT_TSHR_UINT32_UA(buf+12)) && \
(EXTRACT_TSHR_UINT32_UA(buf) == \
EXTRACT_TSHR_UINT32_UA(buf+16)) && \
(EXTRACT_TSHR_UINT32_UA(buf) == \
EXTRACT_TSHR_UINT32_UA(buf+20)) ) \
{ \
break; \
} \
} \
} \
}
//
// Determine whether a run is better than any previous run
// For efficiency we take any run of 32 pels or more without looking
// further.
//
#define CHECK_BEST_RUN(run_type, run_length, bestrun_length, bestrun_type) \
if (run_length > bestrun_length) \
{ \
bestrun_length = run_length; \
bestrun_type = run_type; \
if (bestrun_length >= 32) \
{ \
break; \
} \
}
//
// SETFGCHAR
//
// Set up a new value in fgChar and recalculate the shift
//
#define CHECK_WORK(workchar)
#define SETFGCHAR(newchar, curchar, curshift) \
curchar = newchar; \
{ \
BYTE workchar = curchar; \
curshift = 0; \
CHECK_WORK(workchar); \
while ((workchar & 0x01) == 0) \
{ \
curshift++; \
workchar = (BYTE)(workchar>>1); \
} \
}
//
// Macro to store an FGBG image
//
#define STORE_FGBG(xorbyte, fgbgChar, fgChar, bits) \
{ \
UINT numbits = bits; \
if (fgbgChar & 0x01) \
{ \
*destbuf++ = (BYTE)(xorbyte ^ fgChar); \
} \
else \
{ \
*destbuf++ = xorbyte; \
} \
if (--numbits > 0) \
{ \
if (fgbgChar & 0x02) \
{ \
*destbuf++ = (BYTE)(xorbyte ^ fgChar); \
} \
else \
{ \
*destbuf++ = xorbyte; \
} \
if (--numbits > 0) \
{ \
if (fgbgChar & 0x04) \
{ \
*destbuf++ = (BYTE)(xorbyte ^ fgChar); \
} \
else \
{ \
*destbuf++ = xorbyte; \
} \
if (--numbits > 0) \
{ \
if (fgbgChar & 0x08) \
{ \
*destbuf++ = (BYTE)(xorbyte ^ fgChar); \
} \
else \
{ \
*destbuf++ = xorbyte; \
} \
if (--numbits > 0) \
{ \
if (fgbgChar & 0x10) \
{ \
*destbuf++ = (BYTE)(xorbyte ^ fgChar); \
} \
else \
{ \
*destbuf++ = xorbyte; \
} \
if (--numbits > 0) \
{ \
if (fgbgChar & 0x20) \
{ \
*destbuf++ = (BYTE)(xorbyte ^ fgChar); \
} \
else \
{ \
*destbuf++ = xorbyte; \
} \
if (--numbits > 0) \
{ \
if (fgbgChar & 0x40) \
{ \
*destbuf++ = (BYTE)(xorbyte ^ fgChar); \
} \
else \
{ \
*destbuf++ = xorbyte; \
} \
if (--numbits > 0) \
{ \
if (fgbgChar & 0x80) \
{ \
*destbuf++ = (BYTE)(xorbyte ^ fgChar); \
} \
else \
{ \
*destbuf++ = xorbyte; \
} \
} \
} \
} \
} \
} \
} \
} \
}
//
// ENCODEFGBG
//
// Encode 8 bytes of FG and black into a one byte bitmap representation
//
// The FgChar will always be non-zero, and therefore must have at least one
// bit set.
//
// We arrange that all bytes have this bit in their lowest position
//
// The zero pels will still have a 0 in the lowest bit.
//
// Getting the result is a 4 stage process
//
// 1) Get the wanted bits into bit 0 of each byte
//
// <***************work1*****************>
// 31 0
// 0000 000d 0000 000c 0000 000b 0000 000a
// ^ ^ ^ ^
// <***************work2*****************>
// 31 0
// 0000 000h 0000 000g 0000 000f 0000 000e
// ^ ^ ^ ^
//
// a..h = bits that we want to output
//
// We just need to collect the indicated bits and squash them into a single
// byte.
//
// 2) Compress down to 32 bits
//
// <***************work1*****************>
// 31 0
// 000h 000d 000g 000c 000f 000b 000e 000a
// ^ ^ ^ ^ ^ ^ ^ ^
//
// 3) Compress down to 16 bits
//
// <******work*******>
// 15 0
// 0h0f 0d0b 0g0e 0c0a
// ^ ^ ^ ^
//
// 4) Compress down to 8 bits
//
// hgfedcba
//
#define ENCODEFGBG(result) \
{ \
UINT work1; \
UINT work2; \
UINT work; \
\
work1 = (((UINT)(xorbuf[srcOffset]) ) | \
((UINT)(xorbuf[srcOffset+1]) << 8) | \
((UINT)(xorbuf[srcOffset+2]) << 16) | \
((UINT)(xorbuf[srcOffset+3]) << 24)); \
work2 = (((UINT)(xorbuf[srcOffset+4]) ) | \
((UINT)(xorbuf[srcOffset+5]) << 8) | \
((UINT)(xorbuf[srcOffset+6]) << 16) | \
((UINT)(xorbuf[srcOffset+7]) << 24)); \
\
work1 = (work1 >> fgShift) & 0x01010101; \
work2 = (work2 >> fgShift) & 0x01010101; \
\
work1 = (work2 << 4) | work1; \
\
work = work1 | (work1 >> 14); \
\
result = ((BYTE)(((BYTE)(work>>7)) | ((BYTE)work))); \
}
//
// Unpack4bpp
//
// Convert a 4bpp bitmap into an 8bpp one
//
void Unpack4bpp(LPBYTE destbuf,
LPBYTE srcbuf,
UINT srclen)
{
do
{
*destbuf++ = (BYTE)((*srcbuf) >> 4);
*destbuf++ = (BYTE)((*srcbuf) & 0x0F);
srcbuf++;
} while (--srclen > 0);
}
//
// Pack4bpp
//
// Convert an 8bpp bitmap back to 4bpp
//
void Pack4bpp(LPBYTE destbuf,
LPBYTE srcbuf,
UINT srclen)
{
BYTE work1, work2;
DebugEntry(Pack4bpp);
while (srclen > 1)
{
work1 = (BYTE)(*srcbuf++ << 4);
work2 = (BYTE)(*srcbuf++ & 0x0F);
*destbuf++ = (BYTE)(work1 | work2);
srclen -= 2;
}
if (srclen > 0)
{
*destbuf++ = (BYTE)(*srcbuf++ << 4);
}
DebugExitVOID(Pack4bpp);
}
//
// XORBuffer
//
// Create an XOR image of the input bitmap
//
// Note: This function assumes that rowDelta is always a multiple of 4, and
// that destbuf and srcbuf start on a 4 byte boundary. It does not deal
// with unaligned accesses if this is not true.
//
void XORBuffer(BYTE NEAR *destbuf,
BYTE FAR *srcbuf,
UINT srclen,
int rowDelta)
{
UINT NEAR *dwdest = (UINT NEAR *)destbuf;
DebugEntry(XORBuffer);
ASSERT((rowDelta % 4 == 0));
ASSERT((((UINT_PTR)destbuf) % 4 == 0));
ASSERT((((UINT_PTR)srcbuf) % 4 == 0));
while (srclen > 8)
{
*dwdest++ = *((LPUINT)srcbuf) ^ *((LPUINT)(srcbuf+rowDelta));
srclen -= 4;
srcbuf += 4;
*dwdest++ = *((LPUINT)srcbuf) ^ *((LPUINT)(srcbuf+rowDelta));
srclen -= 4;
srcbuf += 4;
}
if (srclen)
{
destbuf = (BYTE NEAR *)dwdest;
while(srclen)
{
*destbuf++ = (BYTE)(*srcbuf++ ^ *(srcbuf+rowDelta));
srclen--;
}
}
DebugExitVOID(XORBuffer);
}
//
// CompressV2Int
//
// Internal compresssion function
//
// The work buffer addresses are moved onto the stack, thus eliminating any
// need to use DS to address the default data segment. This allows the
// compiler to perform more general optimizations.
//
UINT CompressV2Int(LPBYTE pSrc,
LPBYTE pDst,
UINT numPels,
UINT bpp,
UINT rowDelta,
UINT dstBufferSize,
LPBOOL pLossy,
LPBYTE nrmbuf,
LPBYTE xorbuf,
MATCH FAR *match)
{
int i;
UINT srcOffset;
UINT matchindex;
UINT bestRunLength;
UINT nextRunLength;
UINT runLength;
UINT bestFGRunLength;
UINT checkFGBGLength;
UINT scanCount;
BOOL firstLine;
UINT saveNumPels;
BOOL saveLossy;
BOOL lossy;
BYTE bestRunType = 0;
LPBYTE destbuf = pDst;
BYTE fgChar = 0xFF;
BYTE fgCharWork = 0xFF;
BYTE fgShift = 0;
BOOL lossyStarted = FALSE;
BOOL inColorRun = FALSE;
UINT compressedLength = 0;
DebugEntry(CompressV2Int);
//
// Validate the line length
//
if ((numPels < rowDelta) ||
(rowDelta & 0x0003) ||
(numPels & 0x0003))
{
WARNING_OUT(( "Lines must be a multiple of 4 pels"));
DC_QUIT;
}
//
// First create the character and XOR buffers
//
if (bpp == 4)
{
Unpack4bpp(nrmbuf, pSrc, numPels/2);
}
else
{
nrmbuf = pSrc;
}
//
// Set up the first portion of the XORBUF to contain the source buffer
//
memcpy(xorbuf, nrmbuf, rowDelta);
//
// Calculate the rest of the XOR buffer
//
XORBuffer( xorbuf+rowDelta,
nrmbuf+rowDelta,
numPels-rowDelta,
-(int)rowDelta);
//
// Loop processing the input
// We perform the loop twice, the first time for the non-xor portion
// of the buffer and the second for the XOR portion
// Note that we start the run at a match index of 2 to avoid having
// to special case the startup condition in some of the match
// merging code
// The first time through is always a non-lossy pass. If we find
// enough incompressible data then we redo the compression in lossy
// mode. To achieve this we set saveLossy = FALSE here and reset it
// following the first scan.
//
saveLossy = FALSE;
RESTART_COMPRESSION_IN_LOSSY_MODE:
srcOffset = 0;
firstLine = TRUE;
match[0].type = 0;
match[1].type = 0;
matchindex = 2;
saveNumPels = numPels;
//
// Until we enter XOR mode we do not allow lossy compression on a
// non-XOR request so set up to process just the first line.
// Also, if the user is requesting a lossy compression then we
// perform an initial full non-lossy pass to see if the request is
// worthwhile.
//
lossy = FALSE;
numPels = rowDelta;
for (scanCount = 0; scanCount < 2; scanCount++)
{
while (srcOffset < numPels)
{
//
// Give up if we are nearing the end of the match array
//
if (matchindex >= BCD_MATCHCOUNT)
{
DC_QUIT;
}
//
// Start a while loop to allow a more structured break when we
// hit the first run type we want to encode (We can't afford
// the overheads of a function call to provide the scope here.)
//
while (TRUE)
{
bestRunLength = 0;
bestFGRunLength = 0;
//
// If we are hitting the end of the buffer then just take
// color characters now - take them one at a time so that
// lossy encoding still works. We will only hit this
// condition if we break out of a run just before the end
// of the buffer, so this should not be too common a
// situation, which is good given that we are encoding the
// final 6 bytes uncompressed.
//
if (srcOffset+6 >= numPels)
{
bestRunType = IMAGE_COLOR;
bestRunLength = 1;
break;
}
//
// First do the scans on the XOR buffer. Look for a
// character run or a BG run. Note that if there is no row
// delta then xorbuf actually points to the normal buffer.
// We must do the test independent of how long the run
// might be because even for a 1 pel BG run our later logic
// requires that we detect it seperately. This code is
// absolute main path so fastpath as much as possible. In
// particular detect short bg runs early and allow
// RunSingle to presuppose at least 4 matching bytes
//
if (xorbuf[srcOffset] == 0x00)
{
if (((srcOffset+1) >= numPels) ||
(xorbuf[srcOffset+1] != 0x00))
{
bestRunType = RUN_BG;
bestRunLength = 1;
if (!inColorRun)
{
break;
}
}
else
{
if (((srcOffset+2) >= numPels) ||
(xorbuf[srcOffset+2] != 0x00))
{
bestRunType = RUN_BG;
bestRunLength = 2;
if (!inColorRun)
{
break;
}
}
else
{
if (((srcOffset+3) >= numPels) ||
(xorbuf[srcOffset+3] != 0x00))
{
bestRunType = RUN_BG;
bestRunLength = 3;
if (!inColorRun)
{
break;
}
}
else
{
RUNSINGLE_XOR(xorbuf+srcOffset,
numPels-srcOffset,
bestFGRunLength);
CHECK_BEST_RUN(RUN_BG,
bestFGRunLength,
bestRunLength,
bestRunType);
if (!inColorRun)
{
break;
}
}
}
}
}
else
{
//
// No point in starting if FG run less than 4 bytes so
// check the first dword as quickly as possible Note
// that we don't need to check for an end-buffer
// condition here because our XOR buffer always has
// some free space at the end and the RUNSINGLE_XOR
// will break at the correct place
//
if ( (xorbuf[srcOffset] == xorbuf[srcOffset+1]) &&
(xorbuf[srcOffset] == xorbuf[srcOffset+2]) &&
(xorbuf[srcOffset] == xorbuf[srcOffset+3]) )
{
RUNSINGLE_XOR(xorbuf+srcOffset,
numPels-srcOffset,
bestFGRunLength);
//
// Don't permit a short FG run to prevent a FGBG
// image from starting up. Only take if >= 5
//
if (bestFGRunLength > 5)
{
CHECK_BEST_RUN(RUN_FG,
bestFGRunLength,
bestRunLength,
bestRunType);
}
}
}
//
// Look for sequences in the non XOR buffer In this case we
// insist upon a run of at least 6 pels
//
if ( (nrmbuf[srcOffset] == nrmbuf[srcOffset + 2]) &&
(nrmbuf[srcOffset] == nrmbuf[srcOffset + 4]) &&
(nrmbuf[srcOffset + 1] == nrmbuf[srcOffset + 3]) &&
(nrmbuf[srcOffset + 1] == nrmbuf[srcOffset + 5]) )
{
//
// Now do the scan on the normal buffer for a character
// run Don't bother if first line because we will have
// found it already in the XOR buffer, since we just
// copy nrmbuf to xorbuf for the first line
//
if (*(nrmbuf+srcOffset) == *(nrmbuf+srcOffset+1))
{
if (!firstLine)
{
RUNSINGLE_NRM(nrmbuf+srcOffset,
numPels-srcOffset,
nextRunLength);
if (nextRunLength > 5)
{
CHECK_BEST_RUN(RUN_COLOR,
nextRunLength,
bestRunLength,
bestRunType);
}
}
}
else
{
//
// Look for a dither on the nrm buffer Dithers are
// not very efficient for short runs so only take
// if 8 or longer
//
RunDouble(nrmbuf+srcOffset,
numPels-srcOffset,
nextRunLength);
if (nextRunLength > 9)
{
CHECK_BEST_RUN(RUN_DITHER,
nextRunLength,
bestRunLength,
bestRunType);
}
}
}
//
// If nothing so far then look for a FGBG run (The 6 is
// carefully tuned!)
//
if (bestRunLength < 6)
{
//
// But first look for a single fg bit breaking up a BG
// run. If so then encode a BG run. Careful of the
// enforced BG run break across the first line
// non-XOR/XOR boundary.
//
if ((EXTRACT_TSHR_UINT32_UA(xorbuf+srcOffset+1) == 0) &&
(*(xorbuf+srcOffset) == fgChar) &&
(match[matchindex-1].type == RUN_BG) &&
(srcOffset != (TSHR_UINT16)rowDelta))
{
RUNSINGLE_XOR(xorbuf+srcOffset+1,
numPels-srcOffset-1,
nextRunLength);
nextRunLength++;
CHECK_BEST_RUN(RUN_BG_PEL,
nextRunLength,
bestRunLength,
bestRunType);
}
else
{
//
// If we have not found a run then look for a FG/BG
// image. The disruptive effect of a short FGBG
// run on GDC is such that it is worth preventing
// one unless we are certain of the benefits.
// However, if the alternative is a color run then
// allow a lower value.
//
RUNFGBG( xorbuf+srcOffset,
numPels-srcOffset,
nextRunLength,
fgCharWork );
checkFGBGLength = 48;
if (fgCharWork == fgChar)
{
checkFGBGLength -= 16;
}
if ((nextRunLength & 0x0007) == 0)
{
checkFGBGLength -= 8;
}
if (nextRunLength >= checkFGBGLength)
{
CHECK_BEST_RUN(IMAGE_FGBG,
nextRunLength,
bestRunLength,
bestRunType);
}
}
}
//
// If nothing useful so far then allow a short run, if any
// Don't do this if we are accumulating a color run because
// it will really mess up GDC compression if we allow lots
// of little runs. Also require that it is a regular short
// run, rather than one that disturbs the fgChar
//
if (!inColorRun)
{
if (bestRunLength < 6)
{
if ((bestFGRunLength > 4) &&
(xorbuf[srcOffset] == fgChar))
{
if (match[matchindex-1].type == RUN_FG)
{
match[matchindex-1].length += (WORD)bestFGRunLength;
srcOffset += bestFGRunLength;
continue;
}
else
{
bestRunLength = bestFGRunLength;
bestRunType = RUN_FG;
}
}
else
{
//
// If we decided to take a run earlier then
// allow it now. (May be a short BG run, for
// example) If nothing so far then take color
// image)
//
if (bestRunLength == 0)
{
bestRunType = IMAGE_COLOR;
bestRunLength = 1;
}
}
}
}
else
{
//
// May seem restrictive, but it is important for our
// lossy compression that a color run is rather
// "sticky", in particular not broken by random FGBG
// runs which do appear from time to time.
//
if (lossy)
{
if ((bestRunLength < 8) ||
((bestRunType == IMAGE_FGBG) &&
(bestRunLength < 16)))
{
bestRunType = IMAGE_COLOR;
bestRunLength = 1;
}
}
else
{
if ((bestRunLength < 6) ||
((bestRunType != RUN_BG) && (bestRunLength < 8)))
{
bestRunType = IMAGE_COLOR;
bestRunLength = 1;
}
}
}
break;
}
//
// When we get here we have found the best run. Now check for
// various amalamation conditions with the previous run type.
// Note that we may already have done amalgamation of short
// runs, but we had to do multiple samples for the longer runs
// so we repeat the checks here
//
//
// If we are encoding a color run then
// - process it for lossy compression
// - combine it with an existing run if possible
//
if (bestRunType == IMAGE_COLOR)
{
//
// Flag that we are within a color run
//
inColorRun = TRUE;
//
// If we are doing a lossy compression then process
// even/odd lines differently
//
if (lossy)
{
//
// For even lines duplicate every other character,
// discarding the original value
//
if (((srcOffset/rowDelta)%2) == 0)
{
if ((match[matchindex-1].type == IMAGE_COLOR) &&
(match[matchindex-1].length%2 == 1))
{
nrmbuf[srcOffset] = nrmbuf[srcOffset-1];
//
// If we are not on the final line of the
// bitmap then propagate the update down to the
// next XORed line
//
if (numPels-srcOffset > rowDelta)
{
xorbuf[srcOffset+rowDelta] =
(BYTE)(nrmbuf[srcOffset+rowDelta] ^
nrmbuf[srcOffset]);
}
}
}
else
{
//
// For odd lines we will just encode nulls which
// will replicate the previous line. However, if
// the last run was a BG run then we will
// inadvertently insert a pel, so if we hit this
// situation then leave a single color char
//
bestRunType = IMAGE_LOSSY_ODD;
//
// No need to adjust the buffers for this, except
// to update the next XOR line to reflect the fact
// that the decoder will be operating on a
// replicated line. Therefore we replace the
// character in the next line of the XOR buffer
// with the value it would have if the current line
// was identical with the previous line
//
if (numPels-srcOffset > (TSHR_UINT16)rowDelta)
{
xorbuf[srcOffset+rowDelta] =
(BYTE)(nrmbuf[srcOffset+rowDelta] ^
nrmbuf[srcOffset-rowDelta]);
}
}
}
//
// Merge the color run immediately, if possible
//
if (match[matchindex-1].type == bestRunType)
{
match[matchindex-1].length += (WORD)bestRunLength;
srcOffset += bestRunLength;
continue;
}
}
else
{
//
// We are no longer encoding a COLOR_IMAGE of any kind
//
inColorRun = FALSE;
//
// Keep track of the fg Color The macro that searches for
// FGBG runs leaves the character in fgCharWork.
//
if (bestRunType == RUN_FG)
{
fgChar = xorbuf[srcOffset];
}
else
{
if (bestRunType == IMAGE_FGBG)
{
fgChar = fgCharWork;
}
}
}
//
// If we can amalgamate the entry then do so without creating a
// new array entry. We must amalgamate a lossy ODD with a
// RUN_BG because otherwise the lossy would trigger a pel
// insertion. Our search for FGBG runs is dependent upon that
// type of run being amalgamated because we break every 64
// characters so that our mode switch detection works OK.
//
// Take care not to merge across the non-xor/xor boundary
//
if (srcOffset == (TSHR_UINT16)rowDelta)
{
//
// Just bump the source offset
//
srcOffset += bestRunLength;
}
else
{
//
// Bump srcOffset and try a merge
//
srcOffset += bestRunLength;
//
// The simpler merges are where the types are identical
//
if (bestRunType == match[matchindex-1].type)
{
//
// COLOR IMAGES and BG images are trivial
//
if ((bestRunType == IMAGE_LOSSY_ODD) ||
(bestRunType == RUN_BG))
{
match[matchindex-1].length += (WORD)bestRunLength;
continue;
}
//
// FG runs and FGBG images merge if fgChars match
//
if (((bestRunType == RUN_FG) ||
(bestRunType == IMAGE_FGBG)) &&
(fgChar == match[matchindex-1].fgChar))
{
match[matchindex-1].length += (WORD)bestRunLength;
TRACE_OUT(( "Merged %u with preceding, giving %u",
match[matchindex-1].type,
match[matchindex-1].length));
continue;
}
}
//
// BG RUNs merge with LOSSY odd lines It is important that
// we do this merging because otherwise we will get
// inadvertent pel insertion due to the broken BG runs.
//
if (((bestRunType == RUN_BG) ||
(bestRunType == IMAGE_LOSSY_ODD)) &&
((match[matchindex-1].type == RUN_BG) ||
(match[matchindex-1].type == IMAGE_LOSSY_ODD) ||
(match[matchindex-1].type == RUN_BG_PEL)))
{
match[matchindex-1].length += (WORD)bestRunLength;
continue;
}
//
// If it is a normal FGBG run which follows a short BG run
// then it is better to merge them.
//
if ((bestRunType == IMAGE_FGBG) &&
(match[matchindex-1].type == RUN_BG) &&
(match[matchindex-1].length < 8))
{
match[matchindex-1].type = IMAGE_FGBG;
match[matchindex-1].length += (WORD)bestRunLength;
match[matchindex-1].fgChar = fgChar;
TRACE_OUT(( "Merged FGBG with preceding BG run -> %u",
match[matchindex-1].length));
continue;
}
//
// If it is a BG run following a FGBG run then merge in the
// pels to make the FGBG a multiple of 8 bits. The if the
// remaining BG run is < 16 merge it in also otherwise just
// write the shortened BG run
//
if (((bestRunType == RUN_BG) ||
(bestRunType == RUN_BG_PEL)) &&
(match[matchindex-1].type == IMAGE_FGBG) &&
(match[matchindex-1].length & 0x0007))
{
UINT mergelen = 8 -
(match[matchindex-1].length & 0x0007);
if (mergelen > bestRunLength)
{
mergelen = bestRunLength;
}
match[matchindex-1].length += (WORD)mergelen;
bestRunLength -= mergelen;
TRACE_OUT(( "Added %u pels to FGBG giving %u leaving %u",
mergelen, match[matchindex-1].length,bestRunLength));
if (bestRunLength < 9)
{
match[matchindex-1].length += (WORD)bestRunLength;
TRACE_OUT(( "Merged BG with preceding FGBG gives %u",
match[matchindex-1].length));
continue;
}
}
//
// Finally, if it is a color run spanning any kind of
// single pel entity then merge that last two entries.
//
if ((bestRunType == IMAGE_COLOR) &&
(match[matchindex-2].type == IMAGE_COLOR) &&
(match[matchindex-1].length == 1))
{
match[matchindex-2].length += bestRunLength + 1;
matchindex--;
TRACE_OUT(( "Merged color with preceding color gives %u",
match[matchindex-1].length));
continue;
}
}
//
// Handle runs that will not amalgamate by adding a new array
// entry
//
match[matchindex].type = bestRunType;
match[matchindex].length = (WORD)bestRunLength;
match[matchindex].fgChar = fgChar;
TRACE_OUT(( "Best run of type %u (index %u) has length %u",
match[matchindex-1].type,
matchindex-1,
match[matchindex-1].length));
TRACE_OUT(( "Trying run of type %u (index %u) length %u",
match[matchindex].type,
matchindex,
match[matchindex].length));
matchindex++;
}
//
// If we have just done our scan of the first line then now do the
// rest of the buffer. Reset our saved pel count.
//
numPels = saveNumPels;
lossy = saveLossy;
firstLine = FALSE;
}
//
// END OF INITIAL TWO PASS SCAN OF THE INPUT
//
//
// We have parsed the buffer so now we can go ahead and encode it.
// First we should check to see whether we want to redo the encoding
// in lossy mode. We only do this if requested and worthwhile.
//
if (!saveLossy && (pLossy != NULL) && *pLossy)
{
UINT lossyCharCount = 0;
UINT divisor;
for (i = 2; i < (int)matchindex; i++)
{
if ((match[i].type == IMAGE_COLOR) ||
(match[i].type == IMAGE_LOSSY_ODD))
{
lossyCharCount += match[i].length;
}
}
divisor = max(numPels/100, 1);
if (lossyCharCount/divisor > LOSSY_THRESHOLD)
{
saveLossy = TRUE;
goto RESTART_COMPRESSION_IN_LOSSY_MODE;
}
else
{
*pLossy = FALSE;
}
}
//
// Now do the encoding
//
srcOffset = 0;
firstLine = TRUE;
lossy = FALSE;
fgChar = 0xFF;
for (i = 2; i < (int)matchindex; i++)
{
//
// First check for our approaching the end of the destination
// buffer and get out if this is the case. We allow for the
// largest general run order (a mega-mega set run = 4 bytes).
// Orders which may be larger are checked within the case arm
//
if ((UINT)(destbuf - pDst + 4) > dstBufferSize)
{
//
// We are about to blow it so just get out
//
DC_QUIT;
}
//
// While we are encoding the first line keep checking for the end
// of line to switch encoding states
//
if (firstLine)
{
if (srcOffset >= rowDelta)
{
firstLine = FALSE;
lossy = saveLossy;
}
}
switch (match[i].type)
{
//
// BG_RUN, FG_RUN, COLOR, PACKED COLOR and FGBG are normal
// precision codes
//
case RUN_BG:
case RUN_BG_PEL:
ENCODE_ORDER_MEGA(destbuf,
CODE_BG_RUN,
match[i].length,
CODE_MEGA_MEGA_BG_RUN,
MAX_LENGTH_ORDER,
MAX_LENGTH_LONG_ORDER);
TRACE_OUT(( "BG RUN %u",match[i].length));
srcOffset += match[i].length;
break;
case IMAGE_LOSSY_ODD:
//
// For a lossy odd line we encode a background run
// Note that we do not need to encode a start lossy
// because the decode does not need to distinguish this
// from a regular bg run
//
ENCODE_ORDER_MEGA(destbuf,
CODE_BG_RUN,
match[i].length,
CODE_MEGA_MEGA_BG_RUN,
MAX_LENGTH_ORDER,
MAX_LENGTH_LONG_ORDER);
TRACE_OUT(( "BG RUN %u",match[i].length));
srcOffset += match[i].length;
break;
case RUN_FG:
//
// If the fg char is not yet set then encode a set+run code
//
if (fgChar != match[i].fgChar)
{
SETFGCHAR(match[i].fgChar, fgChar, fgShift);
//
// Encode the order
//
ENCODE_SET_ORDER_MEGA(destbuf,
CODE_SET_FG_FG_RUN,
match[i].length,
CODE_MEGA_MEGA_SET_FG_RUN,
MAX_LENGTH_ORDER_LITE,
MAX_LENGTH_LONG_ORDER_LITE);
TRACE_OUT(( "SET_FG_FG_RUN %u",match[i].length));
srcOffset += match[i].length;
}
else
{
ENCODE_ORDER_MEGA(destbuf,
CODE_FG_RUN,
match[i].length,
CODE_MEGA_MEGA_FG_RUN,
MAX_LENGTH_ORDER,
MAX_LENGTH_LONG_ORDER);
TRACE_OUT(( "FG_RUN %u",match[i].length));
srcOffset += match[i].length;
}
break;
case IMAGE_FGBG:
//
// IMAGE_FGBG
//
runLength = match[i].length;
//
// First check for our approaching the end of the
// destination buffer and get out if this is the case.
//
if ((destbuf-pDst+(runLength+7)/8+4) > dstBufferSize)
{
//
// We are about to blow it so just get out
//
DC_QUIT;
}
//
// We need to convert FGBG runs into the pixel form
//
if (fgChar != match[i].fgChar)
{
SETFGCHAR(match[i].fgChar, fgChar, fgShift);
ENCODE_SET_ORDER_MEGA_FGBG(destbuf,
CODE_SET_FG_FG_BG,
runLength,
CODE_MEGA_MEGA_SET_FGBG,
MAX_LENGTH_FGBG_ORDER_LITE,
MAX_LENGTH_LONG_FGBG_ORDER);
TRACE_OUT(( "SET_FG_FG_BG %u",match[i].length));
while (runLength >= 8)
{
ENCODEFGBG(*destbuf);
destbuf++;
srcOffset += 8;
runLength -= 8;
}
if (runLength)
{
ENCODEFGBG(*destbuf);
//
// Keep the final partial byte clean to help GDC
// packing
//
*destbuf &= ((0x01 << runLength) - 1);
destbuf++;
srcOffset += runLength;
}
}
else
{
if (runLength == 8)
{
BYTE fgbgChar;
//
// See if it is one of the high probability bytes
//
ENCODEFGBG(fgbgChar);
//
// Check for single byte encoding of FGBG images
//
switch (fgbgChar)
{
case SPECIAL_FGBG_CODE_1:
*destbuf++ = CODE_SPECIAL_FGBG_1;
break;
case SPECIAL_FGBG_CODE_2:
*destbuf++ = CODE_SPECIAL_FGBG_2;
break;
default:
ENCODE_ORDER_MEGA_FGBG(destbuf,
CODE_FG_BG_IMAGE,
runLength,
CODE_MEGA_MEGA_FGBG,
MAX_LENGTH_FGBG_ORDER,
MAX_LENGTH_LONG_FGBG_ORDER);
*destbuf++ = fgbgChar;
break;
}
srcOffset += 8;
}
else
{
//
// Encode as standard FGBG
//
ENCODE_ORDER_MEGA_FGBG(destbuf,
CODE_FG_BG_IMAGE,
runLength,
CODE_MEGA_MEGA_FGBG,
MAX_LENGTH_FGBG_ORDER,
MAX_LENGTH_LONG_FGBG_ORDER);
TRACE_OUT(( "FG_BG %u",match[i].length));
while (runLength >= 8)
{
ENCODEFGBG(*destbuf);
destbuf++;
srcOffset += 8;
runLength -= 8;
}
if (runLength)
{
ENCODEFGBG(*destbuf);
*destbuf &= ((0x01 << runLength) - 1);
destbuf++;
srcOffset += runLength;
}
}
}
break;
case RUN_COLOR:
//
// COLOR RUN
//
ENCODE_ORDER_MEGA(destbuf,
CODE_COLOR_RUN,
match[i].length,
CODE_MEGA_MEGA_COLOR_RUN,
MAX_LENGTH_ORDER,
MAX_LENGTH_LONG_ORDER);
TRACE_OUT(( "COLOR_RUN %u",match[i].length));
*destbuf++ = nrmbuf[srcOffset];
srcOffset += match[i].length;
break;
case RUN_DITHER:
//
// DITHERED RUN
//
{
UINT ditherlen = match[i].length/2;
ENCODE_ORDER_MEGA(destbuf,
CODE_DITHERED_RUN,
ditherlen,
CODE_MEGA_MEGA_DITHER,
MAX_LENGTH_ORDER_LITE,
MAX_LENGTH_LONG_ORDER_LITE);
TRACE_OUT(( "DITHERED_RUN %u",match[i].length));
//
// First check for our approaching the end of the
// destination buffer and get out if this is the case.
//
if ((UINT)(destbuf - pDst + 2) > dstBufferSize)
{
//
// We are about to blow it so just get out
//
DC_QUIT;
}
*destbuf++ = nrmbuf[srcOffset];
*destbuf++ = nrmbuf[srcOffset+1];
srcOffset += match[i].length;
}
break;
case IMAGE_COLOR:
//
// IMAGE_COLOR
//
//
// A length of 1 can possibly be encoded as a single
// "BLACK"
//
if (match[i].length == 1)
{
if (nrmbuf[srcOffset] == 0x00)
{
*destbuf++ = CODE_BLACK;
srcOffset++;
break;
}
if (nrmbuf[srcOffset] == 0xFF)
{
*destbuf++ = CODE_WHITE;
srcOffset++;
break;
}
}
//
// If lossy compression is requested then indicate it
// immediately we get a color image to encode here
//
if (lossy & !lossyStarted)
{
lossyStarted = TRUE;
*destbuf++ = CODE_START_LOSSY;
}
//
// For 4bpp data pack color runs into nibbles
//
if (bpp == 4)
{
//
// Store the data in packed format
//
ENCODE_ORDER_MEGA(destbuf,
CODE_PACKED_COLOR_IMAGE,
match[i].length,
CODE_MEGA_MEGA_PACKED_CLR,
MAX_LENGTH_ORDER,
MAX_LENGTH_LONG_ORDER);
TRACE_OUT(( "PACKED COLOR %u",match[i].length));
//
// If we are not doing lossy compress then just copy
// the data over, packing two to a byte
//
if (!lossy)
{
//
// First check for our approaching the end of the
// destination buffer and get out if this is the
// case.
//
if ((destbuf - pDst + (UINT)(match[i].length + 1) / 2) >
dstBufferSize)
{
//
// We are about to blow it so just get out
//
DC_QUIT;
}
Pack4bpp(destbuf, nrmbuf+srcOffset, match[i].length);
destbuf += (match[i].length+1)/2;
srcOffset += match[i].length;
}
else
{
//
// First check for our approaching the end of the
// destination buffer and get out if this is the
// case.
//
if ((destbuf - pDst + (UINT)(match[i].length + 3) / 4) >
dstBufferSize)
{
//
// We are about to blow it so just get out
//
DC_QUIT;
}
//
// For a lossy compress we need to discard every
// even byte
//
while (match[i].length > 2)
{
*destbuf++ =
(BYTE)((*(nrmbuf+srcOffset)<<4) |
(*(nrmbuf+srcOffset+2) & 0x0F));
if (match[i].length > 3)
{
srcOffset += 4;
match[i].length -= 4;
}
else
{
srcOffset += 3;
match[i].length -= 3;
}
}
if (match[i].length > 0)
{
*destbuf++ = (BYTE)(*(nrmbuf+srcOffset)<<4);
srcOffset += match[i].length;
}
}
}
else
{
//
// For 8bpp we don't bother trying to detect packed
// data. Doing so disturbs GDC.
//
if (!lossy)
{
//
// Store the data in non-compressed form
//
ENCODE_ORDER_MEGA(destbuf,
CODE_COLOR_IMAGE,
match[i].length,
CODE_MEGA_MEGA_CLR_IMG,
MAX_LENGTH_ORDER,
MAX_LENGTH_LONG_ORDER);
TRACE_OUT(( "COLOR_IMAGE %u",match[i].length));
//
// First check for our approaching the end of the
// destination buffer and get out if this is the
// case.
//
if ((destbuf - pDst + (UINT)match[i].length) > dstBufferSize)
{
//
// We are about to blow it so just get out
//
DC_QUIT;
}
//
// Now just copy the data over
//
memcpy(destbuf, nrmbuf+srcOffset, match[i].length);
destbuf += match[i].length;
srcOffset += match[i].length;
}
else
{
//
// Lossy compression - store the data with
// discarding
//
ENCODE_ORDER_MEGA(destbuf,
CODE_COLOR_IMAGE,
match[i].length,
CODE_MEGA_MEGA_CLR_IMG,
MAX_LENGTH_ORDER,
MAX_LENGTH_LONG_ORDER);
TRACE_OUT(( "COLOR_IMAGE %u",match[i].length));
//
// First check for our approaching the end of the
// destination buffer and get out if this is the
// case.
//
if ((destbuf - pDst + (UINT)(match[i].length + 1) / 2) >
dstBufferSize)
{
//
// We are about to blow it so just get out
//
DC_QUIT;
}
//
// For a lossy compress we need to discard every
// even byte
//
while (match[i].length > 1)
{
*destbuf++ = *(nrmbuf+srcOffset);
srcOffset += 2;
match[i].length -= 2;
}
if (match[i].length == 1)
{
*destbuf++ = *(nrmbuf+srcOffset);
srcOffset++;
}
}
}
break;
default:
ERROR_OUT(( "Invalid run type %u",match[i].type));
}
}
//
// return the size of the compressed buffer
//
compressedLength = (UINT)(destbuf-pDst);
DC_EXIT_POINT:
DebugExitDWORD(CompressV2Int, compressedLength);
return(compressedLength);
}
//
// DecompressV2Int
//
UINT DecompressV2Int(LPBYTE pSrc,
LPBYTE pDst,
UINT bytes,
UINT bpp,
UINT rowDelta,
LPBYTE nrmbuf)
{
UINT codeLength;
BYTE codeByte;
BYTE codeByte2;
BYTE decode;
BYTE decodeLite;
BYTE decodeMega;
BYTE fgChar = 0xFF;
BYTE NEAR *destbuf = nrmbuf;
LPBYTE endSrc = pSrc + bytes;
BOOL backgroundNeedsPel = FALSE;
BOOL lossyStarted = FALSE;
UINT resultSize = 0;
BOOL firstLine = TRUE;
DebugEntry(DecompressV2Int);
//
// Loop processing the input
//
while(pSrc < endSrc)
{
//
// While we are processing the first line we should keep a look out
// for the end of the line
//
if (firstLine)
{
if ((UINT)(destbuf - nrmbuf) >= rowDelta)
{
firstLine = FALSE;
backgroundNeedsPel = FALSE;
}
}
//
// Trace out the source data for debugging
//
TRACE_OUT(( "Next code is %2.2x%2.2x%2.2x%2.2x",
*pSrc,
*(pSrc+1),
*(pSrc+2),
*(pSrc+3)));
//
// Get the decode
//
decode = (BYTE)(*pSrc & CODE_MASK);
decodeLite = (BYTE)(*pSrc & CODE_MASK_LITE);
decodeMega = (BYTE)(*pSrc);
//
// BG RUN
//
if ((decode == CODE_BG_RUN) ||
(decodeMega == CODE_MEGA_MEGA_BG_RUN))
{
if (decode == CODE_BG_RUN)
{
EXTRACT_LENGTH(pSrc, codeLength);
}
else
{
codeLength = EXTRACT_TSHR_UINT16_UA(pSrc+1);
pSrc += 3;
}
TRACE_OUT(( "Background run %u",codeLength));
if (!firstLine)
{
if (backgroundNeedsPel)
{
*destbuf++ = (BYTE)(*(destbuf - rowDelta) ^ fgChar);
codeLength--;
}
while (codeLength-- > 0)
{
*destbuf++ = *(destbuf - rowDelta);
}
}
else
{
if (backgroundNeedsPel)
{
*destbuf++ = fgChar;
codeLength--;
}
while (codeLength-- > 0)
{
*destbuf++ = 0x00;
}
}
//
// A follow on BG run will need a pel inserted
//
backgroundNeedsPel = TRUE;
continue;
}
//
// For any of the other runtypes a follow on BG run does not need
// a FG pel inserted
//
backgroundNeedsPel = FALSE;
//
// FGBG IMAGE
//
if ((decode == CODE_FG_BG_IMAGE) ||
(decodeLite == CODE_SET_FG_FG_BG) ||
(decodeMega == CODE_MEGA_MEGA_FGBG) ||
(decodeMega == CODE_MEGA_MEGA_SET_FGBG))
{
if ((decodeMega == CODE_MEGA_MEGA_FGBG) ||
(decodeMega == CODE_MEGA_MEGA_SET_FGBG))
{
codeLength = EXTRACT_TSHR_UINT16_UA(pSrc+1);
pSrc += 3;
}
else
{
if (decode == CODE_FG_BG_IMAGE)
{
EXTRACT_LENGTH_FGBG(pSrc, codeLength);
}
else
{
EXTRACT_LENGTH_FGBG_LITE(pSrc, codeLength);
}
}
if ((decodeLite == CODE_SET_FG_FG_BG) ||
(decodeMega == CODE_MEGA_MEGA_SET_FGBG))
{
fgChar = *pSrc++;
TRACE_OUT(( "Set FGBG image %u",codeLength));
}
else
{
TRACE_OUT(( "FGBG image %u",codeLength));
}
while (codeLength > 8)
{
codeByte = *pSrc++;
if (firstLine)
{
STORE_FGBG(0x00, codeByte, fgChar, 8);
}
else
{
STORE_FGBG(*(destbuf - rowDelta), codeByte, fgChar, 8);
}
codeLength -= 8;
}
if (codeLength > 0)
{
codeByte = *pSrc++;
if (firstLine)
{
STORE_FGBG(0x00, codeByte, fgChar, codeLength);
}
else
{
STORE_FGBG(*(destbuf - rowDelta),
codeByte,
fgChar,
codeLength);
}
}
continue;
}
//
// FG RUN
//
if ((decode == CODE_FG_RUN) ||
(decodeLite == CODE_SET_FG_FG_RUN) ||
(decodeMega == CODE_MEGA_MEGA_FG_RUN) ||
(decodeMega == CODE_MEGA_MEGA_SET_FG_RUN))
{
if ((decodeMega == CODE_MEGA_MEGA_FG_RUN) ||
(decodeMega == CODE_MEGA_MEGA_SET_FG_RUN))
{
codeLength = EXTRACT_TSHR_UINT16_UA(pSrc+1);
pSrc += 3;
}
else
{
if (decode == CODE_FG_RUN)
{
EXTRACT_LENGTH(pSrc, codeLength);
}
else
{
EXTRACT_LENGTH_LITE(pSrc, codeLength);
}
}
//
// Push the old fgChar down to the ALT position
//
if ((decodeLite == CODE_SET_FG_FG_RUN) ||
(decodeMega == CODE_MEGA_MEGA_SET_FG_RUN))
{
TRACE_OUT(( "Set FG run %u",codeLength));
fgChar = *pSrc++;
}
else
{
TRACE_OUT(( "FG run %u",codeLength));
}
while (codeLength-- > 0)
{
if (!firstLine)
{
*destbuf++ = (BYTE)(*(destbuf - rowDelta) ^ fgChar);
}
else
{
*destbuf++ = fgChar;
}
}
continue;
}
//
// DITHERED RUN
//
if ((decodeLite == CODE_DITHERED_RUN) ||
(decodeMega == CODE_MEGA_MEGA_DITHER))
{
if (decodeMega == CODE_MEGA_MEGA_DITHER)
{
codeLength = EXTRACT_TSHR_UINT16_UA(pSrc+1);
pSrc += 3;
}
else
{
EXTRACT_LENGTH_LITE(pSrc, codeLength);
}
TRACE_OUT(( "Dithered run %u",codeLength));
codeByte = *pSrc++;
codeByte2 = *pSrc++;
while (codeLength-- > 0)
{
*destbuf++ = codeByte;
*destbuf++ = codeByte2;
}
continue;
}
//
// COLOR IMAGE
//
if ((decode == CODE_COLOR_IMAGE) ||
(decodeMega == CODE_MEGA_MEGA_CLR_IMG))
{
if (decodeMega == CODE_MEGA_MEGA_CLR_IMG)
{
codeLength = EXTRACT_TSHR_UINT16_UA(pSrc+1);
pSrc += 3;
}
else
{
EXTRACT_LENGTH(pSrc, codeLength);
}
TRACE_OUT(( "Color image %u",codeLength));
//
// If not doing lossy compression then just copy the bytes
//
if (!lossyStarted)
{
while (codeLength-- > 0)
{
//
// Update the target with the character
//
*destbuf++ = *pSrc++;
}
}
else
{
//
// For lossy compression we must duplicate all the bytes,
// bar the final odd byte
//
while (codeLength > 3)
{
//
// Dither the bytes unless they are black in which
// case a non-dither is preferable
//
*destbuf++ = *pSrc;
if (*pSrc == 0)
{
*destbuf++ = *(pSrc);
*destbuf++ = *(pSrc+1);
*destbuf++ = *(pSrc+1);
pSrc += 2;
}
else
{
*destbuf++ = *(pSrc+1);
*destbuf++ = *pSrc++;
*destbuf++ = *pSrc++;
}
codeLength -= 4;
}
if (codeLength == 3)
{
*destbuf++ = *pSrc;
*destbuf++ = *(pSrc+1);
*destbuf++ = *pSrc;
pSrc += 2;
}
else
{
if (codeLength == 2)
{
*destbuf++ = *pSrc;
*destbuf++ = *pSrc++;
}
else
{
if (codeLength == 1)
{
*destbuf++ = *pSrc++;
}
}
}
}
continue;
}
//
// PACKED COLOR IMAGE
//
if ((decode == CODE_PACKED_COLOR_IMAGE) ||
(decodeMega == CODE_MEGA_MEGA_PACKED_CLR))
{
if (decodeMega == CODE_MEGA_MEGA_PACKED_CLR)
{
codeLength = EXTRACT_TSHR_UINT16_UA(pSrc+1);
pSrc += 3;
}
else
{
EXTRACT_LENGTH(pSrc, codeLength);
}
TRACE_OUT(( "Packed color %u",codeLength));
//
// If not doing lossy compression then we just unpack the 4bpp
// data two pels per byte
//
if (!lossyStarted)
{
if (bpp == 4)
{
UINT worklen = (codeLength)/2;
BYTE workchar;
while (worklen--)
{
workchar = *pSrc++;
*destbuf++ = (BYTE)(workchar>>4);
*destbuf++ = (BYTE)(workchar & 0x0F);
}
if (codeLength & 0x0001)
{
*destbuf++ = (BYTE)(*pSrc++>>4);
}
}
else
{
ERROR_OUT(( "Don't support packed color for 8bpp"));
}
}
else
{
//
// For lossy compression we must duplicate all the bytes,
// bar the final odd byte, again unpacking as we go
//
while (codeLength > 3)
{
*destbuf++ = (BYTE)((*pSrc) >> 4);
*destbuf++ = (BYTE)((*pSrc) >> 4);
*destbuf++ = (BYTE)((*pSrc) & 0x0F);
*destbuf++ = (BYTE)((*pSrc) & 0x0F);
pSrc++;
codeLength -= 4;
}
if (codeLength > 0)
{
if (codeLength-- > 0)
{
*destbuf++ = (BYTE)((*pSrc) >> 4);
}
if (codeLength-- > 0)
{
*destbuf++ = (BYTE)((*pSrc) >> 4);
}
if (codeLength-- > 0)
{
*destbuf++ = (BYTE)((*pSrc) & 0x0F);
}
if (codeLength-- > 0)
{
*destbuf++ = (BYTE)((*pSrc) & 0x0F);
}
pSrc++;
}
}
continue;
}
//
// COLOR RUN
//
if ((decode == CODE_COLOR_RUN) ||
(decodeMega == CODE_MEGA_MEGA_COLOR_RUN))
{
if (decodeMega == CODE_MEGA_MEGA_COLOR_RUN)
{
codeLength = EXTRACT_TSHR_UINT16_UA(pSrc+1);
pSrc += 3;
}
else
{
EXTRACT_LENGTH(pSrc, codeLength);
}
TRACE_OUT(( "Color run %u",codeLength));
codeByte = *pSrc++;
while (codeLength-- > 0)
{
*destbuf++ = codeByte;
}
continue;
}
//
// If we get here then the code must be a special one
//
TRACE_OUT(( "Special code %x",decodeMega));
switch (decodeMega)
{
case CODE_BLACK:
*destbuf++ = 0x00;
break;
case CODE_WHITE:
*destbuf++ = 0xFF;
break;
//
// Ignore the unreachable code warnings that follow
// Simply because we use the STORE_FGBG macro with a constant
// value
//
case CODE_SPECIAL_FGBG_1:
if (firstLine)
{
STORE_FGBG(0x00, SPECIAL_FGBG_CODE_1, fgChar, 8);
}
else
{
STORE_FGBG(*(destbuf - rowDelta),
SPECIAL_FGBG_CODE_1,
fgChar,
8);
}
break;
case CODE_SPECIAL_FGBG_2:
if (firstLine)
{
STORE_FGBG(0x00,
SPECIAL_FGBG_CODE_2,
fgChar,
8);
}
else
{
STORE_FGBG(*(destbuf - rowDelta),
SPECIAL_FGBG_CODE_2,
fgChar,
8);
}
break;
case CODE_START_LOSSY:
lossyStarted = TRUE;
break;
default:
ERROR_OUT(( "Invalid compression data %x",decodeMega));
break;
}
pSrc++;
}
//
// Our final task is to copy the decoded image into the target buffer
// compacting if we are generating a 4bpp image
//
resultSize = (UINT)(destbuf-nrmbuf);
if (bpp == 4)
{
//
// Zero the final byte to eliminate single byte packing problems
//
*destbuf = 0x00;
Pack4bpp(pDst, nrmbuf, resultSize);
}
else
{
memcpy(pDst, nrmbuf, resultSize);
}
TRACE_OUT(( "Returning %u bytes",resultSize));
//
// Return the number of pixels decoded
//
DebugExitDWORD(DecompressV2Int, resultSize);
return(resultSize);
}