/****************************************************************************/ /* abcapi.cpp */ /* */ /* Bitmap Compressor API functions. */ /* */ /* Copyright(c) Microsoft, PictureTel 1992-1997 */ /* Copyright(c) Microsoft 1997-1999 */ /****************************************************************************/ #ifdef DLL_DISP #include #include #include #include #define _pShm pddShm #else #include #define _pShm m_pShm #endif #ifdef COMP_STATS /****************************************************************************/ /* Define some globals for storing useful stats data. */ /****************************************************************************/ UINT32 ulPreCompData = 0; UINT32 ulTotalCompTime = 0; UINT32 ulCompRate = 0; #endif #ifdef DC_DEBUG // compression testing #include #endif #ifdef Unused // Restore this instead of macro if data added to abcdata.c /****************************************************************************/ /* API FUNCTION: BC_Init */ /* */ /* Initializes the Bitmap Compressor. */ /****************************************************************************/ void RDPCALL SHCLASS BC_Init(void) { DC_BEGIN_FN("BC_Init"); #define DC_INIT_DATA #include #undef DC_INIT_DATA DC_END_FN(); } #endif /****************************************************************************/ /* API FUNCTION: BC_CompressBitmap */ /* */ /* Compresses the supplied bitmap into the supplied memory buffer. */ /* */ /* PARAMETERS: */ /* */ /* pSrcBitmap - a pointer to the source bitmap data bits. */ /* */ /* pDstBuffer - a pointer to the destination memory buffer (where the */ /* compressed data will be written). */ /* */ /* dstBufferSize - the size in bytes of the destination buffer */ /* */ /* pCompressedDataSize - pointer to variable that receives the compressed */ /* data size */ /* */ /* bitmapWidth - width of the src bitmap in pels, should be divisible by 4. */ /* */ /* bitmapHeight - the height of the source bitmap in pels. */ /* */ /* RETURNS: */ /* */ /* TRUE - the bitmap data was successfully compressed. */ /* *pCompressedDataSize is updated */ /* */ /* FALSE - the bitmap data could not be compressed. */ /****************************************************************************/ #ifdef DC_HICOLOR BOOL RDPCALL SHCLASS BC_CompressBitmap( PBYTE pSrcBitmap, PBYTE pDstBuffer, PBYTE pBCWorkingBuffer, unsigned dstBufferSize, unsigned *pCompressedDataSize, unsigned bitmapWidth, unsigned bitmapHeight, unsigned bpp) #else BOOL RDPCALL SHCLASS BC_CompressBitmap( PBYTE pSrcBitmap, PBYTE pDstBuffer, unsigned dstBufferSize, unsigned *pCompressedDataSize, unsigned bitmapWidth, unsigned bitmapHeight) #endif { BOOL rc; PTS_CD_HEADER_UA pCompDataHeader; unsigned cbUncompressedDataSize; unsigned cbCompMainBodySize; #ifdef COMP_STATS UINT32 ulStartCompTime; UINT32 ulEndCompTime; #endif DC_BEGIN_FN("BC_CompressBitmap"); #ifdef COMP_STATS /************************************************************************/ /* Record the start time. */ /************************************************************************/ COM_GETTICKCOUNT(ulStartCompTime); #endif TRC_ASSERT(((bitmapWidth & 3) == 0),(TB,"Width not divisible by 4")); TRC_ASSERT((dstBufferSize > 0),(TB,"No destination space!")); // Trace the important parameters. TRC_DBG((TB, "pSrc(%p) pDst(%p) dstBufferSize(%#x)", pSrcBitmap, pDstBuffer, dstBufferSize)); TRC_DBG((TB, "width(%u) height(%u)", bitmapWidth, bitmapHeight)); // Calculate the size of the uncompressed src data. Make sure it // is within our allowed size. #ifdef DC_HICOLOR cbUncompressedDataSize = bitmapWidth * bitmapHeight * ((bpp + 7) / 8); #else cbUncompressedDataSize = bitmapWidth * bitmapHeight; #endif TRC_ASSERT((cbUncompressedDataSize <= MAX_UNCOMPRESSED_DATA_SIZE || pBCWorkingBuffer), (TB,"Bitmap size > max: size=%u, max=%u", cbUncompressedDataSize, MAX_UNCOMPRESSED_DATA_SIZE)); // Do we send the bitmap compression header? if (_pShm->bc.noBitmapCompressionHdr) { #ifdef DC_HICOLOR switch (bpp) { case 32: { TRC_DBG((TB, "Compress 32 bpp")); cbCompMainBodySize = CompressV2Int32(pSrcBitmap, pDstBuffer, cbUncompressedDataSize, bitmapWidth * 4, dstBufferSize, pBCWorkingBuffer ? pBCWorkingBuffer : _pShm->bc.xor_buffer, _pShm->bc.match); } break; case 24: { TRC_DBG((TB, "Compress 24 bpp")); cbCompMainBodySize = CompressV2Int24(pSrcBitmap, pDstBuffer, cbUncompressedDataSize, bitmapWidth * 3, dstBufferSize, pBCWorkingBuffer ? pBCWorkingBuffer : _pShm->bc.xor_buffer, _pShm->bc.match); } break; case 16: { TRC_DBG((TB, "Compress 16bpp")); cbCompMainBodySize = CompressV2Int16(pSrcBitmap, pDstBuffer, cbUncompressedDataSize, bitmapWidth * 2, dstBufferSize, pBCWorkingBuffer ? pBCWorkingBuffer : _pShm->bc.xor_buffer, _pShm->bc.match); } break; case 15: { TRC_DBG((TB, "Compress 15bpp")); cbCompMainBodySize = CompressV2Int15(pSrcBitmap, pDstBuffer, cbUncompressedDataSize, bitmapWidth * 2, dstBufferSize, pBCWorkingBuffer ? pBCWorkingBuffer : _pShm->bc.xor_buffer, _pShm->bc.match); } break; case 8: default: { TRC_DBG((TB, "Compress 8bpp")); cbCompMainBodySize = CompressV2Int(pSrcBitmap, pDstBuffer, cbUncompressedDataSize, bitmapWidth, dstBufferSize, pBCWorkingBuffer ? pBCWorkingBuffer : _pShm->bc.xor_buffer); } break; } #else cbCompMainBodySize = CompressV2Int(pSrcBitmap, pDstBuffer, cbUncompressedDataSize, bitmapWidth, dstBufferSize, _pShm->bc.xor_buffer); #endif if (cbCompMainBodySize != 0) { // Write back the new (compressed) packet size. *pCompressedDataSize = cbCompMainBodySize; TRC_DBG((TB, "*pCompressedDataSize(%u)", *pCompressedDataSize)); rc = TRUE; } else { TRC_NRM((TB, "Failed to compress main body")); rc = FALSE; } } else { if (dstBufferSize > sizeof(TS_CD_HEADER)) { // Compress the bitmap data. #ifdef DC_HICOLOR switch (bpp) { case 32: { TRC_DBG((TB, "Compress 32 bpp")); cbCompMainBodySize = CompressV2Int32(pSrcBitmap, pDstBuffer + sizeof(TS_CD_HEADER), cbUncompressedDataSize, bitmapWidth * 4, dstBufferSize - sizeof(TS_CD_HEADER), pBCWorkingBuffer ? pBCWorkingBuffer : _pShm->bc.xor_buffer, _pShm->bc.match); } break; case 24: { TRC_DBG((TB, "Compress 24 bpp")); cbCompMainBodySize = CompressV2Int24(pSrcBitmap, pDstBuffer + sizeof(TS_CD_HEADER), cbUncompressedDataSize, bitmapWidth * 3, dstBufferSize - sizeof(TS_CD_HEADER), pBCWorkingBuffer ? pBCWorkingBuffer : _pShm->bc.xor_buffer, _pShm->bc.match); } break; case 16: { TRC_DBG((TB, "Compress 16bpp")); cbCompMainBodySize = CompressV2Int16(pSrcBitmap, pDstBuffer + sizeof(TS_CD_HEADER), cbUncompressedDataSize, bitmapWidth * 2, dstBufferSize - sizeof(TS_CD_HEADER), pBCWorkingBuffer ? pBCWorkingBuffer : _pShm->bc.xor_buffer, _pShm->bc.match); } break; case 15: { TRC_DBG((TB, "Compress 15bpp")); cbCompMainBodySize = CompressV2Int15(pSrcBitmap, pDstBuffer + sizeof(TS_CD_HEADER), cbUncompressedDataSize, bitmapWidth * 2, dstBufferSize - sizeof(TS_CD_HEADER), pBCWorkingBuffer ? pBCWorkingBuffer : _pShm->bc.xor_buffer, _pShm->bc.match); } break; case 8: default: { TRC_DBG((TB, "Compress 8bpp")); cbCompMainBodySize = CompressV2Int(pSrcBitmap, pDstBuffer + sizeof(TS_CD_HEADER), cbUncompressedDataSize, bitmapWidth, dstBufferSize - sizeof(TS_CD_HEADER), pBCWorkingBuffer ? pBCWorkingBuffer : _pShm->bc.xor_buffer); } break; } #else cbCompMainBodySize = CompressV2Int(pSrcBitmap, pDstBuffer + sizeof(TS_CD_HEADER), cbUncompressedDataSize, bitmapWidth, dstBufferSize - sizeof(TS_CD_HEADER), _pShm->bc.xor_buffer); #endif if (cbCompMainBodySize != 0) { // Fill in the compressed data header. // FirstRowSize is 0 by historical convention. pCompDataHeader = (PTS_CD_HEADER_UA)pDstBuffer; pCompDataHeader->cbCompFirstRowSize = 0; pCompDataHeader->cbCompMainBodySize = (UINT16)cbCompMainBodySize; if (bpp > 8) { pCompDataHeader->cbScanWidth = TS_BYTES_IN_SCANLINE(bitmapWidth, bpp); } else { pCompDataHeader->cbScanWidth = (UINT16)bitmapWidth; } pCompDataHeader->cbUncompressedSize = (UINT16)cbUncompressedDataSize; // Write back the new (compressed) packet size. *pCompressedDataSize = sizeof(TS_CD_HEADER) + cbCompMainBodySize; TRC_DBG((TB, "*pCompressedDataSize(%u)", *pCompressedDataSize)); rc = TRUE; } else { TRC_NRM((TB, "Failed to compress main body")); rc = FALSE; } } else { TRC_NRM((TB, "Not enough buffer space for header: %u", dstBufferSize)); rc = FALSE; } } #if 0 /************************************************************************/ /* Check that the compressed output decompresses to the same thing */ /************************************************************************/ if (cbCompMainBodySize) { HRESULT hr; hr = BD_DecompressBitmap( #ifndef DLL_DISP m_pTSWd, #endif pDstBuffer + (_pShm->bc.noBitmapCompressionHdr ? 0 : 8), _pShm->bc.decompBuffer, cbCompMainBodySize, TRUE, (BYTE)bpp, (UINT16)bitmapWidth, (UINT16)bitmapHeight); if (FAILED(hr) || memcmp(pSrcBitmap, _pShm->bc.decompBuffer,cbUncompressedDataSize)) { // TRC_ASSERT(FALSE, (TB, "Decompression failure")); } } #endif #ifdef COMP_STATS /************************************************************************/ /* Work out how long the compression took, in ms. */ /************************************************************************/ COM_GETTICKCOUNT(ulEndCompTime); ulTotalCompTime += (ulEndCompTime - ulStartCompTime) / 10000; if (ulTotalCompTime != 0) ulCompRate = ulPreCompData / ulTotalCompTime; #endif DC_END_FN(); return rc; } /****************************************************************************/ /* Bitmap Compression core code. */ /* */ /* A cunning multidimensional RLE compression scheme, particularly suitable */ /* for compressing bitmaps containing captured images of Windows */ /* applications. For images which use lots of different colors intermixed */ /* (full-color pictures, etc.) this compression sceme will be inefficient. */ /* */ /* These functions and macros encode a bitmap according to the codes */ /* defined in abcapi.h. Although there are some complexities in the */ /* encoding, the encodings should be self-explanatory. abcapi.h describes */ /* some nuances of the encoding scheme. */ /****************************************************************************/ /****************************************************************************/ /* Utility macros for encoding orders */ /****************************************************************************/ /****************************************************************************/ /* 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; \ *(PUINT16_UA)(buffer) = (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; \ *(PUINT16_UA)(buffer) = (UINT16)length; \ buffer += 2; \ } /****************************************************************************/ /* 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 */ /****************************************************************************/ #define RUNSINGLE(buffer, length, result) \ { \ BYTE *buf = buffer + 4; \ BYTE *endbuf = buffer + length - 4; \ while (buf < endbuf && \ (*(PUINT32_UA)(buf) == *(PUINT32_UA)(buf - 4))) \ buf += 4; \ endbuf += 4; \ while (buf < endbuf && *buf == *(buf - 1)) \ buf++; \ result = (unsigned)(buf - (buffer)); \ } /****************************************************************************/ // RunDouble // // Determine the length of the current run of dithered bytes. Assumes that // the dither pattern resides in the first 2 bytes of buffer. /****************************************************************************/ #define RunDouble(buffer, length, result) \ { \ int len = ((int)length) - 2; \ BYTE *buf = (buffer) + 2; \ UINT16 Pattern = *(PUINT16_UA)(buffer); \ result = 2; \ while (len > 1) { \ if (*(PUINT16_UA)buf != Pattern) \ break; \ buf += 2; \ result += 2; \ len -= 2; \ } \ } /****************************************************************************/ // RUNFGBG // // Determine the length of the run of bytes that consist only of black (0x00) // 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 // Example compression calculations: // Lookahead KBytes* Comp CPU ("hits") // 24 54846 148497 // 20 54885 151827 // 16 54967 156809 // * = KBytes server->client WinBench98 Graphics WinMark minus CorelDRAW, // measured in NetMon on Ethernet. /****************************************************************************/ #define RUNFGBG(buffer, length, result, work) \ { \ BYTE *buf = buffer; \ BYTE *endbuf = buffer + length; \ result = 0; \ work = *buf; \ while (TRUE) { \ buf++; \ result++; \ if (buf < endbuf) { \ if (*buf != work && *buf != 0) \ break; \ \ if ((result & 0x0007) == 0) { \ if ((*buf == *(buf + 1)) && \ (*(PUINT16_UA)(buf) == *(PUINT16_UA)(buf + 2)) && \ (*(PUINT32_UA)(buf) == *(PUINT32_UA)(buf + 4)) && \ (*(PUINT32_UA)(buf) == *(PUINT32_UA)(buf + 8)) && \ (*(PUINT32_UA)(buf) == *(PUINT32_UA)(buf + 12)) && \ (*(PUINT32_UA)(buf) == *(PUINT32_UA)(buf + 16)) && \ (*(PUINT32_UA)(buf) == *(PUINT32_UA)(buf + 20))) \ { \ break; \ } \ } \ } \ else { \ break; \ } \ } \ } /****************************************************************************/ // Determine whether a run is better than any previous run. // For efficiency we take the run if over a threshold. Threshold comparisons: // Threshold KBytes* Comp CPU ("hits") // 32 54846 148497 // 28 54817 145085 // 24 54825 144366 // 20 54852 143662 // 16 54858 146343 // * = KBytes server->client WinBench98 Graphics WinMark minus CorelDRAW, // measured in NetMon on Ethernet. /****************************************************************************/ #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 >= 20) \ break; \ } /****************************************************************************/ /* SETFGCHAR */ /* */ /* Set up a new value in fgChar and recalculate the shift */ /****************************************************************************/ #define SETFGCHAR(newchar, curchar, curshift) \ curchar = newchar; \ { \ BYTE workchar = curchar; \ curshift = 0; \ while ((workchar & 0x01) == 0) { \ curshift++; \ workchar = (BYTE)(workchar >> 1); \ } \ } /****************************************************************************/ /* 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) \ { \ UINT32 work1; \ UINT32 work2; \ unsigned work; \ \ work1 = ((*(PUINT32_UA)(xorbuf + EncodeSrcOffset)) >> fgShift) & \ 0x01010101; \ work2 = ((*(PUINT32_UA)(xorbuf + EncodeSrcOffset + 4)) >> fgShift) & \ 0x01010101; \ work1 = (work2 << 4) | work1; \ work = work1 | (work1 >> 14); \ result = ((BYTE)(((BYTE)(work >> 7)) | ((BYTE)work))); \ } #ifndef DC_HICOLOR /****************************************************************************/ // The following structure contains the results of our intermediate scan of // the buffer. /****************************************************************************/ typedef struct { unsigned length; BYTE type; BYTE fgChar; } MATCH; #endif /****************************************************************************/ // Critical minimum limit on a run size -- magic number that determines // color run search characteristics. Minimum is 4 for hard-coded DWORD-size // checks below. Comparisons of values: // MinRunSize KBytes* Comp CPU ("hits") // 4 52487 115842 // 5 52697 115116 // 6 52980 120565 // 7 53306 123680 // * = KBytes server->client WinBench98 Graphics WinMark minus CorelDRAW, // measured in NetMon on Ethernet. /****************************************************************************/ #define MinRunSize 5 /****************************************************************************/ // CompressV2Int // // Compresses a bitmap in one call, returning the size of the space used in // the destination buffer or zero if the buffer was not large enough. // // Implementation notes: We use a length-2 array of MATCH elements as a // running lookbehind buffer, allowing us to combine current run analysis // results with previous entries before encoding into the destination buffer. /****************************************************************************/ #ifdef DC_HICOLOR unsigned RDPCALL SHCLASS CompressV2Int( PBYTE pSrc, PBYTE pDst, unsigned numPels, unsigned rowDelta, unsigned dstBufferSize, BYTE *xorbuf) { unsigned srcOffset; unsigned EncodeSrcOffset; unsigned bestRunLength; unsigned nextRunLength; unsigned runLength; unsigned bestFGRunLength; unsigned scanCount; unsigned saveNumPels; BOOLEAN inColorRun = FALSE; BOOLEAN bEncodeAllMatches; BYTE bestRunType = 0; BYTE fgPel = 0xFF; BYTE fgPelWork = 0xFF; BYTE fgShift = 0; BYTE EncodeFGPel; PBYTE destbuf = pDst; unsigned compressedLength = 0; MATCH match[2]; DC_BEGIN_FN("CompressV2Int"); /************************************************************************/ // Validate params. /************************************************************************/ TRC_ASSERT((numPels >= rowDelta),(TB,"numPels < rowDelta")); TRC_ASSERT((!(rowDelta & 0x3)),(TB,"rowDelta not multiple of 4")); TRC_ASSERT((!(numPels & 0x3)),(TB,"numPels not multiple of 4")); TRC_ASSERT((!((UINT_PTR)pSrc & 0x3)), (TB, "Possible unaligned access, pSrc = %p", pSrc)); /************************************************************************/ // Create XOR buffer - first row is copied from src, succeeding rows // are the corresponding src row XOR'd with the next src row. /************************************************************************/ memcpy(xorbuf, pSrc, rowDelta); { BYTE *srcbuf = pSrc + rowDelta; unsigned srclen = numPels - rowDelta; UINT32 *dwdest = (UINT32 *)(xorbuf + rowDelta); while (srclen >= 8) { *dwdest++ = *((PUINT32)srcbuf) ^ *((PUINT32)(srcbuf - (int)rowDelta)); srcbuf += 4; *dwdest++ = *((PUINT32)srcbuf) ^ *((PUINT32)(srcbuf - (int)rowDelta)); srcbuf += 4; srclen -= 8; } if (srclen) { // Since we're 4-byte aligned we can only have a single DWORD // remaining. *dwdest = *((PUINT32)srcbuf) ^ *((PUINT32)(srcbuf - (int)rowDelta)); } } /************************************************************************/ // Set up encoding state variables. /************************************************************************/ srcOffset = 0; // Offset in src buf where we are analyzing. EncodeSrcOffset = 0; // Offset in src buf from where we are encoding. EncodeFGPel = 0xFF; // Foreground color for encoding. bEncodeAllMatches = FALSE; // Used to force encoding of all matches. match[0].type = 0; // Initially no match types. match[1].type = 0; saveNumPels = numPels; numPels = rowDelta; /************************************************************************/ // Loop processing the input. // We perform the loop twice, the first time for the non-XOR first line // of the buffer and the second for the XOR portion, adjusting numPels // to the needed value for each pass. /************************************************************************/ for (scanCount = 0; ; scanCount++) { while (srcOffset < numPels) { /****************************************************************/ /* 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. 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 MinRunSize bytes // uncompressed. /************************************************************/ if ((srcOffset + MinRunSize) < numPels) { goto ContinueScan; } else { bestRunType = IMAGE_COLOR; bestRunLength = numPels - srcOffset; break; } ContinueScan: /************************************************************/ // First do the scans on the XOR buffer. Look for a // character run or a BG run. // 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(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. /********************************************************/ if (xorbuf[srcOffset] == xorbuf[srcOffset + 1] && *(PUINT16_UA)(xorbuf + srcOffset) == *(PUINT16_UA)(xorbuf + srcOffset + 2)) { RUNSINGLE(xorbuf+srcOffset, numPels-srcOffset, bestFGRunLength); /****************************************************/ // Don't permit a short FG run to prevent a FGBG // image from starting up. /****************************************************/ if (bestFGRunLength >= MinRunSize) { CHECK_BEST_RUN(RUN_FG, bestFGRunLength, bestRunLength, bestRunType); } } } /************************************************************/ // Look for solid or dithered sequences in the normal // (non-XOR) buffer. /************************************************************/ if ( (pSrc[srcOffset] == pSrc[srcOffset + 2]) && (pSrc[srcOffset + 1] == pSrc[srcOffset + 3])) { /********************************************************/ // 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 pSrc to xorbuf for the first line. We insist on // a run of at least MinRunSize pixels. /********************************************************/ if (*(pSrc + srcOffset) == *(pSrc + srcOffset + 1)) { if (srcOffset >= rowDelta) { RUNSINGLE(pSrc + srcOffset, numPels - srcOffset, nextRunLength); if (nextRunLength >= MinRunSize) { 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. Note that our check against // numPels above for MinRunSize will be overrun here // so we need to make sure we don't go over the // end of the buffer. /****************************************************/ if (((numPels - srcOffset) > 8) && (*(PUINT32_UA)(pSrc + srcOffset) == *(PUINT32_UA)(pSrc + srcOffset + 4))) { RunDouble(pSrc + srcOffset + 6, numPels - srcOffset - 6, nextRunLength); nextRunLength += 6; CHECK_BEST_RUN(RUN_DITHER, nextRunLength, bestRunLength, bestRunType); } } } /************************************************************/ // If nothing so far then look for a FGBG run. /************************************************************/ if (bestRunLength < MinRunSize) { // Check this is not a single FG bit breaking up a BG // run. If so then encode a BG_PEL run. Careful of the // enforced BG run break across the first line // non-XOR/XOR boundary. if (*(PUINT32_UA)(xorbuf + srcOffset + 1) != 0 || *(xorbuf + srcOffset) != fgPel || match[1].type != RUN_BG || srcOffset == rowDelta) { // If we have not found a run then look for a FG/BG // image. Bandwidth/CPU comparisons: // chkFGBGLen* KBytes** Comp CPU ("hits") // 48/16/8 54856 140178 // 32/16/8 53177 129343 // 24/8/8 53020 130583 // 16/8/8 52874 126454 // 8/8/0 52980 120565 // no check 59753 101091 // * = minimum run length for checking best: // start val / subtract for workchar==fgPel / // subtract for nextRunLen divisible by 8 // ** = KBytes server->client WinBench98 Graphics // WinMark minus CorelDRAW, measured in NetMon // on Ethernet. RUNFGBG(xorbuf + srcOffset, numPels - srcOffset, nextRunLength, fgPelWork); if (fgPelWork == fgPel || nextRunLength >= 8) { CHECK_BEST_RUN(IMAGE_FGBG, nextRunLength, bestRunLength, bestRunType); } } else { RUNSINGLE(xorbuf + srcOffset + 1, numPels - srcOffset - 1, nextRunLength); nextRunLength++; CHECK_BEST_RUN(RUN_BG_PEL, nextRunLength, bestRunLength, bestRunType); } } /************************************************************/ /* If nothing useful so far then allow a short run. */ /* 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 fgPel */ /************************************************************/ if (!inColorRun) { if (bestRunLength < MinRunSize) { if (bestFGRunLength >= MinRunSize && xorbuf[srcOffset] == fgPel) { /************************************************/ /* We mustn't merge with the previous code */ /* if we have just crossed the non-XOR/XOR */ /* boundary. */ /************************************************/ if (match[1].type == RUN_FG && srcOffset != rowDelta) { match[1].length += 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 { // We're in a color run. Keep small runs of other types // from breaking up the color run and increasing the // encoded size. if (bestRunLength < (unsigned)(bestRunType == RUN_BG ? MinRunSize : (MinRunSize + 2))) { bestRunType = IMAGE_COLOR; bestRunLength = 1; } } // Get out of the loop after all checks are completed. break; } /****************************************************************/ /* When we get here we have found the best run. Now check for */ /* various amalgamation 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 combine it with an // existing run if possible. /****************************************************************/ if (bestRunType != IMAGE_COLOR) { /************************************************************/ /* We are no longer encoding a COLOR_IMAGE of any kind */ /************************************************************/ inColorRun = FALSE; // If we can amalgamate the entry then do so without creating // a new array entry. 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 != rowDelta) { // Bump srcOffset and try a merge. srcOffset += bestRunLength; switch (bestRunType) { case RUN_BG: // BG runs merge with BG and BG_PEL runs. if (match[1].type == RUN_BG || match[1].type == RUN_BG_PEL) { match[1].length += bestRunLength; TRC_DBG((TB, "Merged BG with preceding, " "giving %u", match[1].length)); continue; } // Deliberate fallthrough to BG_PEL. case RUN_BG_PEL: // If it is a BG run following a FGBG run then // merge in the pels to make the FGBG length a // multiple of 8. If the remaining BG run is <= 8 // (which would translate to one extra byte in // the previous FGBG as well as one byte of BG), // merge it in also, otherwise just write the // shortened BG run. Note that for RUN_BG_PEL, // FG color will be the same as for the // FGBG, no need to check. if (match[1].type == IMAGE_FGBG && match[1].length & 0x0007) { unsigned mergelen = 8 - (match[1].length & 0x0007); if (mergelen > bestRunLength) mergelen = bestRunLength; match[1].length += mergelen; bestRunLength -= mergelen; TRC_DBG((TB,"Add %u pels to FGBG giving %u " "leaving %u", mergelen, match[1].length, bestRunLength)); if (bestRunLength <= 8) { match[1].length += bestRunLength; TRC_DBG((TB,"Merge BG with prev FGBG " "gives %u", match[1].length)); continue; } } break; case RUN_FG: // Keep track of the FG color. Remember to // subtract bestRunLength since we incremented // it before the switch statement. fgPel = xorbuf[srcOffset - bestRunLength]; // FG run merges with previous FG if FG color is same. if (match[1].type == RUN_FG && match[1].fgPel == fgPel) { match[1].length += bestRunLength; TRC_DBG((TB, "Merged FG with preceding, giving %u", match[1].length)); continue; } break; case IMAGE_FGBG: // FGBG leaves the foreground character in // fgPelWork. fgPel = fgPelWork; // FGBG merges with previous if the FG colors are // the same. if (match[1].type == IMAGE_FGBG && match[1].fgPel == fgPel) { match[1].length += bestRunLength; TRC_DBG((TB, "Merged FGBG with preceding " "FGBG, giving %u", match[1].length)); continue; } // FGBG merges with with small BG runs. if (match[1].type == RUN_BG && match[1].length < 8) { match[1].type = IMAGE_FGBG; match[1].length += bestRunLength; match[1].fgPel = fgPel; TRC_DBG((TB, "Merged FGBG with preceding " "BG run -> %u", match[1].length)); continue; } break; } } else { // Keep track of the FG color. The macro that searches for // FGBG runs leaves the character in fgPelWork. // Note this code is inlined into the merging code // before. if (bestRunType == RUN_FG) fgPel = xorbuf[srcOffset]; else if (bestRunType == IMAGE_FGBG) fgPel = fgPelWork; // We're at the end of the first line. Just bump the // source offset. srcOffset += bestRunLength; } } else { /************************************************************/ /* Flag that we are within a color run */ /************************************************************/ inColorRun = TRUE; srcOffset += bestRunLength; /************************************************************/ // Merge the color run immediately, if possible. Note color // runs are not restricted by the non-XOR/XOR boundary. /************************************************************/ if (match[1].type == IMAGE_COLOR) { match[1].length += bestRunLength; continue; } if (match[0].type == IMAGE_COLOR && match[1].length == 1) { // If it is a color run spanning any kind of single pel // entity then merge all three runs into one. // We have to create a special match queue condition // here -- the single merged entry needs to be placed // in the match[1] position and a null entry into [0] // to allow the rest of the code to continue to // be hardcoded to merge with [1]. match[1].length = match[0].length + bestRunLength + 1; match[1].type = IMAGE_COLOR; match[0].type = 0; TRC_DBG((TB, "Merged color with preceding color gives %u", match[1].length)); continue; } } /****************************************************************/ // The current run could not be merged with a previous match // queue entry, We have to encode the [0] slot then add the // current run the the queue. /****************************************************************/ TRC_DBG((TB, "Best run of type %u has length %u", bestRunType, bestRunLength)); DoEncoding: // 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 ((unsigned)(destbuf - pDst + 4) <= dstBufferSize) goto ContinueEncoding; else DC_QUIT; ContinueEncoding: switch (match[0].type) { case 0: // Unused entry. break; case RUN_BG: case RUN_BG_PEL: // Note that for BG_PEL we utilize the code sequence // BG,BG which would not otherwise appear as a special // case meaning insert one current FG char between // the two runs. ENCODE_ORDER_MEGA(destbuf, CODE_BG_RUN, match[0].length, CODE_MEGA_MEGA_BG_RUN, MAX_LENGTH_ORDER, MAX_LENGTH_LONG_ORDER); TRC_DBG((TB, "BG RUN %u",match[0].length)); EncodeSrcOffset += match[0].length; break; case RUN_FG: // If the fg value is different from the current // then encode a set+run code. if (EncodeFGPel != match[0].fgPel) { SETFGCHAR((BYTE)match[0].fgPel, EncodeFGPel, fgShift); ENCODE_ORDER_MEGA(destbuf, CODE_SET_FG_FG_RUN, match[0].length, CODE_MEGA_MEGA_SET_FG_RUN, MAX_LENGTH_ORDER_LITE, MAX_LENGTH_LONG_ORDER_LITE); *destbuf++ = EncodeFGPel; TRC_DBG((TB, "SET_FG_FG_RUN %u", match[0].length)); } else { ENCODE_ORDER_MEGA(destbuf, CODE_FG_RUN, match[0].length, CODE_MEGA_MEGA_FG_RUN, MAX_LENGTH_ORDER, MAX_LENGTH_LONG_ORDER); TRC_DBG((TB, "FG_RUN %u", match[0].length)); } EncodeSrcOffset += match[0].length; break; case IMAGE_FGBG: runLength = match[0].length; // 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) goto ContinueFGBG; else DC_QUIT; ContinueFGBG: // We need to convert FGBG runs into the pixel form. if (EncodeFGPel != match[0].fgPel) { SETFGCHAR((BYTE)match[0].fgPel, EncodeFGPel, fgShift); ENCODE_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); *destbuf++ = EncodeFGPel; TRC_DBG((TB, "SET_FG_FG_BG %u", match[0].length)); while (runLength >= 8) { ENCODEFGBG(*destbuf); destbuf++; EncodeSrcOffset += 8; runLength -= 8; } if (runLength) { ENCODEFGBG(*destbuf); // Keep the final partial byte clean to help GDC // packing. *destbuf &= ((0x01 << runLength) - 1); destbuf++; EncodeSrcOffset += 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; } EncodeSrcOffset += 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); TRC_DBG((TB, "FG_BG %u", match[0].length)); while (runLength >= 8) { ENCODEFGBG(*destbuf); destbuf++; EncodeSrcOffset += 8; runLength -= 8; } if (runLength) { ENCODEFGBG(*destbuf); *destbuf &= ((0x01 << runLength) - 1); destbuf++; EncodeSrcOffset += runLength; } } } break; case RUN_COLOR: ENCODE_ORDER_MEGA(destbuf, CODE_COLOR_RUN, match[0].length, CODE_MEGA_MEGA_COLOR_RUN, MAX_LENGTH_ORDER, MAX_LENGTH_LONG_ORDER); TRC_DBG((TB, "COLOR_RUN %u", match[0].length)); *destbuf++ = pSrc[EncodeSrcOffset]; EncodeSrcOffset += match[0].length; break; case RUN_DITHER: { unsigned ditherlen = match[0].length / 2; ENCODE_ORDER_MEGA(destbuf, CODE_DITHERED_RUN, ditherlen, CODE_MEGA_MEGA_DITHER, MAX_LENGTH_ORDER_LITE, MAX_LENGTH_LONG_ORDER_LITE); TRC_DBG((TB, "DITHERED_RUN %u", match[0].length)); // First check for our approaching the end of the // destination buffer and get out if this is the case. if ((unsigned)(destbuf - pDst + 2) <= dstBufferSize) { *destbuf++ = pSrc[EncodeSrcOffset]; *destbuf++ = pSrc[EncodeSrcOffset + 1]; EncodeSrcOffset += match[0].length; } else { DC_QUIT; } } break; case IMAGE_COLOR: // Length 1 can possibly be encoded as a single BLACK/WHITE. if (match[0].length == 1) { if (pSrc[EncodeSrcOffset] == 0x00) { *destbuf++ = CODE_BLACK; EncodeSrcOffset++; break; } if (pSrc[EncodeSrcOffset] == 0xFF) { *destbuf++ = CODE_WHITE; EncodeSrcOffset++; break; } } // Store the data in non-compressed form. ENCODE_ORDER_MEGA(destbuf, CODE_COLOR_IMAGE, match[0].length, CODE_MEGA_MEGA_CLR_IMG, MAX_LENGTH_ORDER, MAX_LENGTH_LONG_ORDER); TRC_DBG((TB, "COLOR_IMAGE %u", match[0].length)); // First check for our approaching the end of the // destination buffer and get out if this is the case. if ((destbuf - pDst + (UINT_PTR)match[0].length) <= dstBufferSize) { // Now just copy the data over. memcpy(destbuf, pSrc+EncodeSrcOffset, match[0].length); destbuf += match[0].length; EncodeSrcOffset += match[0].length; } else { DC_QUIT; } break; #ifdef DC_DEBUG default: TRC_ERR((TB, "Invalid run type %u",match[0].type)); break; #endif } /****************************************************************/ // Done encoding, what we do next is determined by whether we're // flushing the match queue after everything is scanned. /****************************************************************/ match[0] = match[1]; if (!bEncodeAllMatches) { // Push the current run into the top of the queue. match[1].type = bestRunType; match[1].length = bestRunLength; match[1].fgPel = fgPel; } else { // We need to check to see if we're really finished. Since // our maximum queue depth is 2, if we're done then the only // remaining entry has an encoding type of 0. if (match[0].type == 0) { goto PostScan; } else { match[1].type = 0; goto DoEncoding; } } } if (scanCount == 0) { // 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; } else { // When we are done with the second pass (we've reached the end of // the buffer) we have to force the remaining items in the match // queue to be encoded. Yes this is similar to old BASIC // code in using gotos, but we cannot place the encoding code into // a function because of the number of params required, and // we cannot duplicate it because it is too big. This code is // some of the most used in the system so the cost is worth it. bEncodeAllMatches = TRUE; goto DoEncoding; } } PostScan: // Success, calculate the amount of space we used. compressedLength = (unsigned)(destbuf - pDst); DC_EXIT_POINT: DC_END_FN(); return compressedLength; } #else unsigned RDPCALL SHCLASS CompressV2Int( PBYTE pSrc, PBYTE pDst, unsigned numPels, unsigned rowDelta, unsigned dstBufferSize, BYTE *xorbuf) { unsigned srcOffset; unsigned EncodeSrcOffset; unsigned bestRunLength; unsigned nextRunLength; unsigned runLength; unsigned bestFGRunLength; unsigned scanCount; unsigned saveNumPels; BOOLEAN inColorRun = FALSE; BOOLEAN bEncodeAllMatches; BYTE bestRunType = 0; BYTE fgChar = 0xFF; BYTE fgCharWork = 0xFF; BYTE fgShift = 0; BYTE EncodeFGChar; PBYTE destbuf = pDst; unsigned compressedLength = 0; MATCH match[2]; DC_BEGIN_FN("CompressV2Int"); /************************************************************************/ // Validate params. /************************************************************************/ TRC_ASSERT((numPels >= rowDelta),(TB,"numPels < rowDelta")); TRC_ASSERT((!(rowDelta & 0x3)),(TB,"rowDelta not multiple of 4")); TRC_ASSERT((!(numPels & 0x3)),(TB,"numPels not multiple of 4")); TRC_ASSERT((!((UINT_PTR)pSrc & 0x3)), (TB, "Possible unaligned access, pSrc = %p", pSrc)); /************************************************************************/ // Create XOR buffer - first row is copied from src, succeeding rows // are the corresponding src row XOR'd with the next src row. /************************************************************************/ memcpy(xorbuf, pSrc, rowDelta); { BYTE *srcbuf = pSrc + rowDelta; unsigned srclen = numPels - rowDelta; UINT32 *dwdest = (UINT32 *)(xorbuf + rowDelta); while (srclen >= 8) { *dwdest++ = *((PUINT32)srcbuf) ^ *((PUINT32)(srcbuf - (int)rowDelta)); srcbuf += 4; *dwdest++ = *((PUINT32)srcbuf) ^ *((PUINT32)(srcbuf - (int)rowDelta)); srcbuf += 4; srclen -= 8; } if (srclen) { // Since we're 4-byte aligned we can only have a single DWORD // remaining. *dwdest = *((PUINT32)srcbuf) ^ *((PUINT32)(srcbuf - (int)rowDelta)); } } /************************************************************************/ // Set up encoding state variables. /************************************************************************/ srcOffset = 0; // Offset in src buf where we are analyzing. EncodeSrcOffset = 0; // Offset in src buf from where we are encoding. EncodeFGChar = 0xFF; // Foreground color for encoding. bEncodeAllMatches = FALSE; // Used to force encoding of all matches. match[0].type = 0; // Initially no match types. match[1].type = 0; saveNumPels = numPels; numPels = rowDelta; /************************************************************************/ // Loop processing the input. // We perform the loop twice, the first time for the non-XOR first line // of the buffer and the second for the XOR portion, adjusting numPels // to the needed value for each pass. /************************************************************************/ for (scanCount = 0; ; scanCount++) { while (srcOffset < numPels) { /****************************************************************/ /* 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. 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 MinRunSize bytes // uncompressed. /************************************************************/ if ((srcOffset + MinRunSize) < numPels) { goto ContinueScan; } else { bestRunType = IMAGE_COLOR; bestRunLength = numPels - srcOffset; break; } ContinueScan: /************************************************************/ // First do the scans on the XOR buffer. Look for a // character run or a BG run. // 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(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. /********************************************************/ if (xorbuf[srcOffset] == xorbuf[srcOffset + 1] && *(PUINT16_UA)(xorbuf + srcOffset) == *(PUINT16_UA)(xorbuf + srcOffset + 2)) { RUNSINGLE(xorbuf + srcOffset, numPels - srcOffset, bestFGRunLength); /****************************************************/ // Don't permit a short FG run to prevent a FGBG // image from starting up. /****************************************************/ if (bestFGRunLength >= MinRunSize) { CHECK_BEST_RUN(RUN_FG, bestFGRunLength, bestRunLength, bestRunType); } } } /************************************************************/ // Look for solid or dithered sequences in the normal // (non-XOR) buffer. /************************************************************/ if ( (pSrc[srcOffset] == pSrc[srcOffset + 2]) && (pSrc[srcOffset + 1] == pSrc[srcOffset + 3])) { /********************************************************/ // 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 pSrc to xorbuf for the first line. We insist on // a run of at least MinRunSize pixels. /********************************************************/ if (*(pSrc + srcOffset) == *(pSrc + srcOffset + 1)) { if (srcOffset >= rowDelta) { RUNSINGLE(pSrc + srcOffset, numPels - srcOffset, nextRunLength); if (nextRunLength >= MinRunSize) { 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 */ /****************************************************/ if (*(PUINT32_UA)(pSrc + srcOffset) == *(PUINT32_UA)(pSrc + srcOffset + 4)) { RunDouble(pSrc + srcOffset + 6, numPels - srcOffset - 6, nextRunLength); nextRunLength += 6; CHECK_BEST_RUN(RUN_DITHER, nextRunLength, bestRunLength, bestRunType); } } } /************************************************************/ // If nothing so far then look for a FGBG run. /************************************************************/ if (bestRunLength < MinRunSize) { // Check this is not a single FG bit breaking up a BG // run. If so then encode a BG_PEL run. Careful of the // enforced BG run break across the first line // non-XOR/XOR boundary. if (*(PUINT32_UA)(xorbuf + srcOffset + 1) != 0 || *(xorbuf + srcOffset) != fgChar || match[1].type != RUN_BG || srcOffset == rowDelta) { // If we have not found a run then look for a FG/BG // image. Bandwidth/CPU comparisons: // chkFGBGLen* KBytes** Comp CPU ("hits") // 48/16/8 54856 140178 // 32/16/8 53177 129343 // 24/8/8 53020 130583 // 16/8/8 52874 126454 // 8/8/0 52980 120565 // no check 59753 101091 // * = minimum run length for checking best: // start val / subtract for workchar==fgChar / // subtract for nextRunLen divisible by 8 // ** = KBytes server->client WinBench98 Graphics // WinMark minus CorelDRAW, measured in NetMon // on Ethernet. RUNFGBG(xorbuf + srcOffset, numPels - srcOffset, nextRunLength, fgCharWork); if (fgCharWork == fgChar || nextRunLength >= 8) { CHECK_BEST_RUN(IMAGE_FGBG, nextRunLength, bestRunLength, bestRunType); } } else { RUNSINGLE(xorbuf + srcOffset + 1, numPels - srcOffset - 1, nextRunLength); nextRunLength++; CHECK_BEST_RUN(RUN_BG_PEL, nextRunLength, bestRunLength, bestRunType); } } /************************************************************/ /* If nothing useful so far then allow a short run. */ /* 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 < MinRunSize) { if (bestFGRunLength >= MinRunSize && xorbuf[srcOffset] == fgChar) { /************************************************/ /* We mustn't merge with the previous code */ /* if we have just crossed the non-XOR/XOR */ /* boundary. */ /************************************************/ if (match[1].type == RUN_FG && srcOffset != rowDelta) { match[1].length += 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 { // We're in a color run. Keep small runs of other types // from breaking up the color run and increasing the // encoded size. if (bestRunLength < (unsigned)(bestRunType == RUN_BG ? MinRunSize : (MinRunSize + 2))) { bestRunType = IMAGE_COLOR; bestRunLength = 1; } } // Get out of the loop after all checks are completed. break; } /****************************************************************/ /* When we get here we have found the best run. Now check for */ /* various amalgamation 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 combine it with an // existing run if possible. /****************************************************************/ if (bestRunType != IMAGE_COLOR) { /************************************************************/ /* We are no longer encoding a COLOR_IMAGE of any kind */ /************************************************************/ inColorRun = FALSE; // If we can amalgamate the entry then do so without creating // a new array entry. 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 != rowDelta) { // Bump srcOffset and try a merge. srcOffset += bestRunLength; switch (bestRunType) { case RUN_BG: // BG runs merge with BG and BG_PEL runs. if (match[1].type == RUN_BG || match[1].type == RUN_BG_PEL) { match[1].length += bestRunLength; TRC_DBG((TB, "Merged BG with preceding, " "giving %u", match[1].length)); continue; } // Deliberate fallthrough to BG_PEL. case RUN_BG_PEL: // If it is a BG run following a FGBG run then // merge in the pels to make the FGBG length a // multiple of 8. If the remaining BG run is <= 8 // (which would translate to one extra byte in // the previous FGBG as well as one byte of BG), // merge it in also, otherwise just write the // shortened BG run. Note that for RUN_BG_PEL, // FG color will be the same as for the // FGBG, no need to check. if (match[1].type == IMAGE_FGBG && match[1].length & 0x0007) { unsigned mergelen = 8 - (match[1].length & 0x0007); if (mergelen > bestRunLength) mergelen = bestRunLength; match[1].length += mergelen; bestRunLength -= mergelen; TRC_DBG((TB,"Add %u pels to FGBG giving %u " "leaving %u", mergelen, match[1].length, bestRunLength)); if (bestRunLength <= 8) { match[1].length += bestRunLength; TRC_DBG((TB,"Merge BG with prev FGBG " "gives %u", match[1].length)); continue; } } break; case RUN_FG: // Keep track of the FG color. Remember to // subtract bestRunLength since we incremented // it before the switch statement. fgChar = xorbuf[srcOffset - bestRunLength]; // FG run merges with previous FG if FG color is same. if (match[1].type == RUN_FG && match[1].fgChar == fgChar) { match[1].length += bestRunLength; TRC_DBG((TB, "Merged FG with preceding, giving %u", match[1].length)); continue; } break; case IMAGE_FGBG: // FGBG leaves the foreground character in // fgCharWork. fgChar = fgCharWork; // FGBG merges with previous if the FG colors are // the same. if (match[1].type == IMAGE_FGBG && match[1].fgChar == fgChar) { match[1].length += bestRunLength; TRC_DBG((TB, "Merged FGBG with preceding " "FGBG, giving %u", match[1].length)); continue; } // FGBG merges with with small BG runs. if (match[1].type == RUN_BG && match[1].length < 8) { match[1].type = IMAGE_FGBG; match[1].length += bestRunLength; match[1].fgChar = fgChar; TRC_DBG((TB, "Merged FGBG with preceding " "BG run -> %u", match[1].length)); continue; } break; } } else { // Keep track of the FG color. The macro that searches for // FGBG runs leaves the character in fgCharWork. // Note this code is inlined into the merging code // before. if (bestRunType == RUN_FG) fgChar = xorbuf[srcOffset]; else if (bestRunType == IMAGE_FGBG) fgChar = fgCharWork; // We're at the end of the first line. Just bump the // source offset. srcOffset += bestRunLength; } } else { /************************************************************/ /* Flag that we are within a color run */ /************************************************************/ inColorRun = TRUE; srcOffset += bestRunLength; /************************************************************/ // Merge the color run immediately, if possible. Note color // runs are not restricted by the non-XOR/XOR boundary. /************************************************************/ if (match[1].type == IMAGE_COLOR) { match[1].length += bestRunLength; continue; } if (match[0].type == IMAGE_COLOR && match[1].length == 1) { // If it is a color run spanning any kind of single pel // entity then merge all three runs into one. // We have to create a special match queue condition // here -- the single merged entry needs to be placed // in the match[1] position and a null entry into [0] // to allow the rest of the code to continue to // be hardcoded to merge with [1]. match[1].length = match[0].length + bestRunLength + 1; match[1].type = IMAGE_COLOR; match[0].type = 0; TRC_DBG((TB, "Merged color with preceding color gives %u", match[1].length)); continue; } } /****************************************************************/ // The current run could not be merged with a previous match // queue entry, We have to encode the [0] slot then add the // current run the the queue. /****************************************************************/ TRC_DBG((TB, "Best run of type %u has length %u", bestRunType, bestRunLength)); DoEncoding: // 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 ((unsigned)(destbuf - pDst + 4) <= dstBufferSize) goto ContinueEncoding; else DC_QUIT; ContinueEncoding: switch (match[0].type) { case 0: // Unused entry. break; case RUN_BG: case RUN_BG_PEL: // Note that for BG_PEL we utilize the code sequence // BG,BG which would not otherwise appear as a special // case meaning insert one current FG char between // the two runs. ENCODE_ORDER_MEGA(destbuf, CODE_BG_RUN, match[0].length, CODE_MEGA_MEGA_BG_RUN, MAX_LENGTH_ORDER, MAX_LENGTH_LONG_ORDER); TRC_DBG((TB, "BG RUN %u",match[0].length)); EncodeSrcOffset += match[0].length; break; case RUN_FG: // If the fg value is different from the current // then encode a set+run code. if (EncodeFGChar != match[0].fgChar) { SETFGCHAR(match[0].fgChar, EncodeFGChar, fgShift); ENCODE_ORDER_MEGA(destbuf, CODE_SET_FG_FG_RUN, match[0].length, CODE_MEGA_MEGA_SET_FG_RUN, MAX_LENGTH_ORDER_LITE, MAX_LENGTH_LONG_ORDER_LITE); *destbuf++ = EncodeFGChar; TRC_DBG((TB, "SET_FG_FG_RUN %u", match[0].length)); } else { ENCODE_ORDER_MEGA(destbuf, CODE_FG_RUN, match[0].length, CODE_MEGA_MEGA_FG_RUN, MAX_LENGTH_ORDER, MAX_LENGTH_LONG_ORDER); TRC_DBG((TB, "FG_RUN %u", match[0].length)); } EncodeSrcOffset += match[0].length; break; case IMAGE_FGBG: runLength = match[0].length; // 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) goto ContinueFGBG; else DC_QUIT; ContinueFGBG: // We need to convert FGBG runs into the pixel form. if (EncodeFGChar != match[0].fgChar) { SETFGCHAR(match[0].fgChar, EncodeFGChar, fgShift); ENCODE_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); *destbuf++ = EncodeFGChar; TRC_DBG((TB, "SET_FG_FG_BG %u", match[0].length)); while (runLength >= 8) { ENCODEFGBG(*destbuf); destbuf++; EncodeSrcOffset += 8; runLength -= 8; } if (runLength) { ENCODEFGBG(*destbuf); // Keep the final partial byte clean to help GDC // packing. *destbuf &= ((0x01 << runLength) - 1); destbuf++; EncodeSrcOffset += 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; } EncodeSrcOffset += 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); TRC_DBG((TB, "FG_BG %u", match[0].length)); while (runLength >= 8) { ENCODEFGBG(*destbuf); destbuf++; EncodeSrcOffset += 8; runLength -= 8; } if (runLength) { ENCODEFGBG(*destbuf); *destbuf &= ((0x01 << runLength) - 1); destbuf++; EncodeSrcOffset += runLength; } } } break; case RUN_COLOR: ENCODE_ORDER_MEGA(destbuf, CODE_COLOR_RUN, match[0].length, CODE_MEGA_MEGA_COLOR_RUN, MAX_LENGTH_ORDER, MAX_LENGTH_LONG_ORDER); TRC_DBG((TB, "COLOR_RUN %u", match[0].length)); *destbuf++ = pSrc[EncodeSrcOffset]; EncodeSrcOffset += match[0].length; break; case RUN_DITHER: { unsigned ditherlen = match[0].length / 2; ENCODE_ORDER_MEGA(destbuf, CODE_DITHERED_RUN, ditherlen, CODE_MEGA_MEGA_DITHER, MAX_LENGTH_ORDER_LITE, MAX_LENGTH_LONG_ORDER_LITE); TRC_DBG((TB, "DITHERED_RUN %u", match[0].length)); // First check for our approaching the end of the // destination buffer and get out if this is the case. if ((unsigned)(destbuf - pDst + 2) <= dstBufferSize) { *destbuf++ = pSrc[EncodeSrcOffset]; *destbuf++ = pSrc[EncodeSrcOffset + 1]; EncodeSrcOffset += match[0].length; } else { DC_QUIT; } } break; case IMAGE_COLOR: // Length 1 can possibly be encoded as a single BLACK/WHITE. if (match[0].length == 1) { if (pSrc[EncodeSrcOffset] == 0x00) { *destbuf++ = CODE_BLACK; EncodeSrcOffset++; break; } if (pSrc[EncodeSrcOffset] == 0xFF) { *destbuf++ = CODE_WHITE; EncodeSrcOffset++; break; } } // Store the data in non-compressed form. ENCODE_ORDER_MEGA(destbuf, CODE_COLOR_IMAGE, match[0].length, CODE_MEGA_MEGA_CLR_IMG, MAX_LENGTH_ORDER, MAX_LENGTH_LONG_ORDER); TRC_DBG((TB, "COLOR_IMAGE %u", match[0].length)); // First check for our approaching the end of the // destination buffer and get out if this is the case. if ((destbuf - pDst + (UINT_PTR)match[0].length) <= dstBufferSize) { // Now just copy the data over. memcpy(destbuf, pSrc+EncodeSrcOffset, match[0].length); destbuf += match[0].length; EncodeSrcOffset += match[0].length; } else { DC_QUIT; } break; #ifdef DC_DEBUG default: TRC_ERR((TB, "Invalid run type %u",match[0].type)); break; #endif } /****************************************************************/ // Done encoding, what we do next is determined by whether we're // flushing the match queue after everything is scanned. /****************************************************************/ match[0] = match[1]; if (!bEncodeAllMatches) { // Push the current run into the top of the queue. match[1].type = bestRunType; match[1].length = bestRunLength; match[1].fgChar = fgChar; } else { // We need to check to see if we're really finished. Since // our maximum queue depth is 2, if we're done then the only // remaining entry has an encoding type of 0. if (match[0].type == 0) { goto PostScan; } else { match[1].type = 0; goto DoEncoding; } } } if (scanCount == 0) { // 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; } else { // When we are done with the second pass (we've reached the end of // the buffer) we have to force the remaining items in the match // queue to be encoded. Yes this is similar to old BASIC // code in using gotos, but we cannot place the encoding code into // a function because of the number of params required, and // we cannot duplicate it because it is too big. This code is // some of the most used in the system so the cost is worth it. bEncodeAllMatches = TRUE; goto DoEncoding; } } PostScan: // Success, calculate the amount of space we used. compressedLength = (unsigned)(destbuf - pDst); DC_EXIT_POINT: DC_END_FN(); return compressedLength; } #endif #ifdef DC_HICOLOR /****************************************************************************/ /* Hi res color compression functions */ /****************************************************************************/ /****************************************************************************/ /* 15bpp version of CompressV2Int */ /****************************************************************************/ unsigned RDPCALL SHCLASS CompressV2Int15(PBYTE pSrc, PBYTE pDst, unsigned numBytes, unsigned rowDelta, unsigned dstBufferSize, BYTE * xorbuf, MATCH * match) { /****************************************************************************/ /* Function name */ /****************************************************************************/ #define BC_FN_NAME "CompressV2Int15" /****************************************************************************/ /* Data type of a pixel */ /****************************************************************************/ #define BC_PIXEL TSUINT16 /****************************************************************************/ /* Length in bytes of a pixel */ /****************************************************************************/ #define BC_PIXEL_LEN 2 /****************************************************************************/ /* Default fgPel */ /****************************************************************************/ #define BC_DEFAULT_FGPEL 0x0000FF7F /****************************************************************************/ /* Macro to move to the next pixel in the buffer (modifies pPos) */ /****************************************************************************/ #define BC_TO_NEXT_PIXEL(pPos) pPos += 2 /****************************************************************************/ /* Macro to returns the value of the pixel at pPos (doesn't modify pPos) */ /****************************************************************************/ #define BC_GET_PIXEL(pPos) ((TSUINT16) ((((PTSUINT8)(pPos))[1]) & 0x7f) | \ (TSUINT16) (((((PTSUINT8)(pPos))[0])) << 8) ) /****************************************************************************/ /* Macro to insert a pixel value pel at position pPos (doesn't modify pPos) */ /* */ /* pel may well be an expression (e.g. a BC_GET_PIXEL macro) so evaluate */ /* it once into a local variable. */ /****************************************************************************/ #define BC_SET_PIXEL(pPos, pel) \ { \ BC_PIXEL val = pel; \ (((PTSUINT8)(pPos))[1]) = (TSUINT8)( (val) & 0x007F); \ (((PTSUINT8)(pPos))[0]) = (TSUINT8)(((val) >> 8) & 0x00FF); \ } /****************************************************************************/ /* Include the function body */ /****************************************************************************/ #include /****************************************************************************/ /* Undefine everything */ /****************************************************************************/ #undef BC_FN_NAME #undef BC_PIXEL #undef BC_PIXEL_LEN #undef BC_TO_NEXT_PIXEL #undef BC_GET_PIXEL #undef BC_SET_PIXEL #undef BC_DEFAULT_FGPEL } /****************************************************************************/ /* 16bpp version of CompressV2Int */ /****************************************************************************/ unsigned RDPCALL SHCLASS CompressV2Int16(PBYTE pSrc, PBYTE pDst, unsigned numBytes, unsigned rowDelta, unsigned dstBufferSize, BYTE * xorbuf, MATCH * match) { /****************************************************************************/ /* Function name */ /****************************************************************************/ #define BC_FN_NAME "CompressV2Int16" /****************************************************************************/ /* Data type of a pixel */ /****************************************************************************/ #define BC_PIXEL TSUINT16 /****************************************************************************/ /* Length in bytes of a pixel */ /****************************************************************************/ #define BC_PIXEL_LEN 2 /****************************************************************************/ /* Default fgPel */ /****************************************************************************/ #define BC_DEFAULT_FGPEL 0x0000FFFF /****************************************************************************/ /* Macro to move to the next pixel in the buffer (modifies pPos) */ /****************************************************************************/ #define BC_TO_NEXT_PIXEL(pPos) pPos += 2 /****************************************************************************/ /* Macro to returns the value of the pixel at pPos (doesn't modify pPos) */ /****************************************************************************/ #define BC_GET_PIXEL(pPos) ((TSUINT16) (((PTSUINT8)(pPos))[1]) | \ (TSUINT16) ((((PTSUINT8)(pPos))[0]) << 8) ) /****************************************************************************/ /* Macro to insert a pixel value pel at position pPos (doesn't modify pPos) */ /* */ /* pel may well be an expression (e.g. a BC_GET_PIXEL macro) so evaluate */ /* it once into a local variable. */ /****************************************************************************/ #define BC_SET_PIXEL(pPos, pel) \ { \ BC_PIXEL val = pel; \ (((PTSUINT8)(pPos))[1]) = (TSUINT8)( (val) & 0x00FF); \ (((PTSUINT8)(pPos))[0]) = (TSUINT8)(((val)>>8) & 0x00FF); \ } /****************************************************************************/ /* Include the function body */ /****************************************************************************/ #include /****************************************************************************/ /* Undefine everything */ /****************************************************************************/ #undef BC_FN_NAME #undef BC_PIXEL #undef BC_PIXEL_LEN #undef BC_TO_NEXT_PIXEL #undef BC_GET_PIXEL #undef BC_SET_PIXEL #undef BC_DEFAULT_FGPEL } /****************************************************************************/ /* 24bpp version of CompressV2Int */ /****************************************************************************/ unsigned RDPCALL SHCLASS CompressV2Int24(PBYTE pSrc, PBYTE pDst, unsigned numBytes, unsigned rowDelta, unsigned dstBufferSize, BYTE * xorbuf, MATCH * match) { /****************************************************************************/ /* Function name */ /****************************************************************************/ #define BC_FN_NAME "CompressV2Int24" /****************************************************************************/ /* Data type of a pixel */ /****************************************************************************/ #define BC_PIXEL TSUINT32 /****************************************************************************/ /* Length in bytes of a pixel */ /****************************************************************************/ #define BC_PIXEL_LEN 3 /****************************************************************************/ /* Default fgPel */ /****************************************************************************/ #define BC_DEFAULT_FGPEL 0x00FFFFFF /****************************************************************************/ /* Macro to move to the next pixel in the buffer (modifies pPos) */ /****************************************************************************/ #define BC_TO_NEXT_PIXEL(pPos) pPos += 3 /****************************************************************************/ /* Macro to returns the value of the pixel at pPos (doesn't modify pPos) */ /****************************************************************************/ #define BC_GET_PIXEL(pPos) ((TSUINT32) (((PTSUINT8)(pPos))[2]) | \ (TSUINT32) ((((PTSUINT8)(pPos))[1]) << 8) | \ (TSUINT32) ((((PTSUINT8)(pPos))[0]) << 16) ) /****************************************************************************/ /* Macro to insert a pixel value pel at position pPos (doesn't modify pPos) */ /* */ /* pel may well be an expression (e.g. a BC_GET_PIXEL macro) so evaluate */ /* it once into a local variable. */ /****************************************************************************/ #define BC_SET_PIXEL(pPos, pel) \ { \ BC_PIXEL val = pel; \ (((PTSUINT8)(pPos))[2]) = (TSUINT8)((val) & 0x000000FF); \ (((PTSUINT8)(pPos))[1]) = (TSUINT8)(((val)>>8) & 0x000000FF); \ (((PTSUINT8)(pPos))[0]) = (TSUINT8)(((val)>>16) & 0x000000FF); \ } /****************************************************************************/ /* Include the function body */ /****************************************************************************/ #include /****************************************************************************/ /* Undefine everything */ /****************************************************************************/ #undef BC_FN_NAME #undef BC_PIXEL #undef BC_PIXEL_LEN #undef BC_TO_NEXT_PIXEL #undef BC_GET_PIXEL #undef BC_SET_PIXEL #undef BC_DEFAULT_FGPEL } /****************************************************************************/ /* 32bpp version of CompressV2Int */ /****************************************************************************/ unsigned RDPCALL SHCLASS CompressV2Int32(PBYTE pSrc, PBYTE pDst, unsigned numBytes, unsigned rowDelta, unsigned dstBufferSize, BYTE * xorbuf, MATCH * match) { /****************************************************************************/ /* Function name */ /****************************************************************************/ #define BC_FN_NAME "CompressV2Int32" /****************************************************************************/ /* Data type of a pixel */ /****************************************************************************/ #define BC_PIXEL TSUINT32 /****************************************************************************/ /* Length in bytes of a pixel */ /****************************************************************************/ #define BC_PIXEL_LEN 4 /****************************************************************************/ /* Default fgPel */ /****************************************************************************/ #define BC_DEFAULT_FGPEL 0xFFFFFFFF /****************************************************************************/ /* Macro to move to the next pixel in the buffer (modifies pPos) */ /****************************************************************************/ #define BC_TO_NEXT_PIXEL(pPos) pPos += 4 /****************************************************************************/ /* Macro to returns the value of the pixel at pPos (doesn't modify pPos) */ /****************************************************************************/ #define BC_GET_PIXEL(pPos) ( \ (TSUINT32) ( (TSUINT16)(((PTSUINT8)(pPos))[3]) ) | \ (TSUINT32) (((TSUINT16)(((PTSUINT8)(pPos))[2])) << 8) | \ (TSUINT32) (((TSUINT32)(((PTSUINT8)(pPos))[1])) << 16) | \ (TSUINT32) (((TSUINT32)(((PTSUINT8)(pPos))[0])) << 24)) /****************************************************************************/ /* Macro to insert a pixel value pel at position pPos (doesn't modify pPos) */ /* */ /* pel may well be an expression (e.g. a BC_GET_PIXEL macro) so evaluate */ /* it once into a local variable. */ /****************************************************************************/ #define BC_SET_PIXEL(pPos, pel) \ { \ BC_PIXEL val = pel; \ (((PTSUINT8)(pPos))[3]) = (TSUINT8)((val) & 0x000000FF); \ (((PTSUINT8)(pPos))[2]) = (TSUINT8)(((val)>>8) & 0x000000FF); \ (((PTSUINT8)(pPos))[1]) = (TSUINT8)(((val)>>16) & 0x000000FF); \ (((PTSUINT8)(pPos))[0]) = (TSUINT8)(((val)>>24) & 0x000000FF); \ } /****************************************************************************/ /* Include the function body */ /****************************************************************************/ #include /****************************************************************************/ /* Undefine everything */ /****************************************************************************/ #undef BC_FN_NAME #undef BC_PIXEL #undef BC_PIXEL_LEN #undef BC_TO_NEXT_PIXEL #undef BC_GET_PIXEL #undef BC_SET_PIXEL #undef BC_DEFAULT_FGPEL } #endif /* DC_HICOLOR */ #ifdef DC_DEBUG // compression testing #include #endif