You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2727 lines
106 KiB
2727 lines
106 KiB
#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);
|
|
}
|
|
|
|
|