#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); }