2727 lines
103 KiB
C++
2727 lines
103 KiB
C++
#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);
|
||
}
|
||
|
||
|