3.1.9 Interleaved RLE-Based Bitmap Compression
Bitmap data sent from server to client can be compressed using Interleaved RLE as described in section 2.2.9.1.1.3.1.2.4. The pseudo-code which follows shows how to decompress a compressed bitmap stream.
-
// // Bitmasks // BYTE g_MaskBit0 = 0x01; // Least significant bit BYTE g_MaskBit1 = 0x02; BYTE g_MaskBit2 = 0x04; BYTE g_MaskBit3 = 0x08; BYTE g_MaskBit4 = 0x10; BYTE g_MaskBit5 = 0x20; BYTE g_MaskBit6 = 0x40; BYTE g_MaskBit7 = 0x80; // Most significant bit BYTE g_MaskRegularRunLength = 0x1F; BYTE g_MaskLiteRunLength = 0x0F; BYTE g_MaskSpecialFgBg1 = 0x03; BYTE g_MaskSpecialFgBg2 = 0x05; // // Returns the color depth (in bytes per pixel) that was selected // for the RDP connection. // UINT GetColorDepth(); // // PIXEL is a dynamic type that is sized based on the current color // depth being used for the RDP connection. // // if (GetColorDepth() == 8) then PIXEL is an 8-bit unsigned integer // if (GetColorDepth() == 15) then PIXEL is a 16-bit unsigned integer // if (GetColorDepth() == 16) then PIXEL is a 16-bit unsigned integer // if (GetColorDepth() == 24) then PIXEL is a 24-bit unsigned integer // // // Writes a pixel to the specified buffer. // VOID WritePixel( BYTE* pbBuffer, PIXEL pixel ); // // Reads a pixel from the specified buffer. // PIXEL ReadPixel( BYTE* pbBuffer ); // // Returns the size of a pixel in bytes. // UINT GetPixelSize() { UINT colorDepth = GetColorDepth(); if (colorDepth == 8) { return 1; } else if (colorDepth == 15 || colorDepth == 16) { return 2; } else if (colorDepth == 24) { return 3; } } // // Returns a pointer to the next pixel in the specified buffer. // BYTE* NextPixel( BYTE* pbBuffer ) { return pbBuffer + GetPixelSize(); } // // Reads the supplied order header and extracts the compression // order code ID. // UINT ExtractCodeId( BYTE bOrderHdr ); // // Returns a pointer to the data that follows the compression // order header and optional run length. // BYTE* AdvanceOverOrderHeader( UINT codeId, BYTE* pbOrderHdr ); // // Returns TRUE if the supplied code identifier is for a regular-form // standard compression order. For example IsRegularCode(0x01) returns // TRUE as 0x01 is the code ID for a Regular Foreground Run Order. // BOOL IsRegularCode( UINT codeId ); // // Returns TRUE if the supplied code identifier is for a lite-form // standard compression order. For example IsLiteCode(0x0E) returns // TRUE as 0x0E is the code ID for a Lite Dithered Run Order. // BOOL IsLiteCode( UINT codeId ); // // Returns TRUE if the supplied code identifier is for a MEGA_MEGA // type extended compression order. For example IsMegaMegaCode(0xF0) // returns TRUE as 0xF0 is the code ID for a MEGA_MEGA Background // Run Order. // BOOL IsMegaMegaCode( UINT codeId ); // // Returns a black pixel. // PIXEL GetColorBlack() { UINT colorDepth = GetColorDepth(); if (colorDepth == 8) { return (PIXEL) 0x00; } else if (colorDepth == 15) { return (PIXEL) 0x0000; } else if (colorDepth == 16) { return (PIXEL) 0x0000; } else if (colorDepth == 24) { return (PIXEL) 0x000000; } } // // Returns a white pixel. // PIXEL GetColorWhite() { UINT colorDepth = GetColorDepth(); if (colorDepth == 8) { // // Palette entry #255 holds black. // return (PIXEL) 0xFF; } else if (colorDepth == 15) { // // 5 bits per RGB component: // 0111 1111 1111 1111 (binary) // return (PIXEL) 0x7FFF; } else if (colorDepth == 16) { // // 5 bits for red, 6 bits for green, 5 bits for green: // 1111 1111 1111 1111 (binary) // return (PIXEL) 0xFFFF; } else if (colorDepth == 24) { // // 8 bits per RGB component: // 1111 1111 1111 1111 1111 1111 (binary) // return (PIXEL) 0xFFFFFF; } } // // Extract the run length of a Regular-Form Foreground/Background // Image Order. // UINT ExtractRunLengthRegularFgBg( BYTE* pbOrderHdr ) { UINT runLength; runLength = *pbOrderHdr AND g_MaskRegularRunLength; if (runLength == 0) { runLength = *(pbOrderHdr + 1) + 1; } else { runLength = runLength * 8; } return runLength; } // // Extract the run length of a Lite-Form Foreground/Background // Image Order. // UINT ExtractRunLengthLiteFgBg( BYTE* pbOrderHdr ) { UINT runLength; runLength = *pbOrderHdr AND g_MaskLiteRunLength; if (runLength == 0) { runLength = *(pbOrderHdr + 1) + 1; } else { runLength = runLength * 8; } return runLength; } // // Extract the run length of a regular-form compression order. // UINT ExtractRunLengthRegular( BYTE* pbOrderHdr ) { UINT runLength; runLength = *pbOrderHdr AND g_MaskRegularRunLength; if (runLength == 0) { // // An extended (MEGA) run. // runLength = *(pbOrderHdr + 1) + 32; } return runLength; } // // Extract the run length of a lite-form compression order. // UINT ExtractRunLengthLite( BYTE* pbOrderHdr ) { UINT runLength; runLength = *pbOrderHdr AND g_MaskLiteRunLength; if (runLength == 0) { // // An extended (MEGA) run. // runLength = *(pbOrderHdr + 1) + 16; } return runLength; } // // Extract the run length of a MEGA_MEGA-type compression order. // UINT ExtractRunLengthMegaMega( BYTE* pbOrderHdr ) { UINT runLength; pbOrderHdr = pbOrderHdr + 1; runLength = ((UINT16) pbOrderHdr[0]) OR ((UINT16) pbOrderHdr[1] << 8); return runLength; } // // Extract the run length of a compression order. // UINT ExtractRunLength( UINT code, BYTE* pbOrderHdr ) { UINT runLength; if (code == REGULAR_FGBG_IMAGE) { runLength = ExtractRunLengthRegularFgBg(pbOrderHdr); } else if (code == LITE_SET_FG_FGBG_IMAGE) { runLength = ExtractRunLengthLiteFgBg(pbOrderHdr); } else if (IsRegularCode(code)) { runLength = ExtractRunLengthRegular(pbOrderHdr); } else if (IsLiteCode(code)) { runLength = ExtractRunLengthLite(pbOrderHdr); } else if (IsMegaMegaCode(code)) { runLength = ExtractRunLengthMegaMega(pbOrderHdr); } else { runLength = 0; } return runLength; } // // Write a foreground/background image to a destination buffer. // BYTE* WriteFgBgImage( BYTE* pbDest, UINT rowDelta, BYTE bitmask, PIXEL fgPel, UINT cBits ) { PIXEL xorPixel; xorPixel = ReadPixel(pbDest - rowDelta); if (bitmask AND g_MaskBit0) { WritePixel(pbDest, xorPixel XOR fgPel); } else { WritePixel(pbDest, xorPixel); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { xorPixel = ReadPixel(pbDest - rowDelta); if (bitmask AND g_MaskBit1) { WritePixel(pbDest, xorPixel XOR fgPel); } else { WritePixel(pbDest, xorPixel); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { xorPixel = ReadPixel(pbDest - rowDelta); if (bitmask AND g_MaskBit2) { WritePixel(pbDest, xorPixel XOR fgPel); } else { WritePixel(pbDest, xorPixel); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { xorPixel = ReadPixel(pbDest - rowDelta); if (bitmask AND g_MaskBit3) { WritePixel(pbDest, xorPixel XOR fgPel); } else { WritePixel(pbDest, xorPixel); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { xorPixel = ReadPixel(pbDest - rowDelta); if (bitmask AND g_MaskBit4) { WritePixel(pbDest, xorPixel XOR fgPel); } else { WritePixel(pbDest, xorPixel); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { xorPixel = ReadPixel(pbDest - rowDelta); if (bitmask AND g_MaskBit5) { WritePixel(pbDest, xorPixel XOR fgPel); } else { WritePixel(pbDest, xorPixel); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { xorPixel = ReadPixel(pbDest - rowDelta); if (bitmask AND g_MaskBit6) { WritePixel(pbDest, xorPixel XOR fgPel); } else { WritePixel(pbDest, xorPixel); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { xorPixel = ReadPixel(pbDest - rowDelta); if (bitmask AND g_MaskBit7) { WritePixel(pbDest, xorPixel XOR fgPel); } else { WritePixel(pbDest, xorPixel); } pbDest = NextPixel(pbDest); } } } } } } } return pbDest; } // // Write a foreground/background image to a destination buffer // for the first line of compressed data. // BYTE* WriteFirstLineFgBgImage( BYTE* pbDest, BYTE bitmask, PIXEL fgPel, UINT cBits ) { if (bitmask AND g_MaskBit0) { WritePixel(pbDest, fgPel); } else { WritePixel(pbDest, GetColorBlack()); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { if (bitmask AND g_MaskBit1) { WritePixel(pbDest, fgPel); } else { WritePixel(pbDest, GetColorBlack()); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { if (bitmask AND g_MaskBit2) { WritePixel(pbDest, fgPel); } else { WritePixel(pbDest, GetColorBlack()); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { if (bitmask AND g_MaskBit3) { WritePixel(pbDest, fgPel); } else { WritePixel(pbDest, GetColorBlack()); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { if (bitmask AND g_MaskBit4) { WritePixel(pbDest, fgPel); } else { WritePixel(pbDest, GetColorBlack()); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { if (bitmask AND g_MaskBit5) { WritePixel(pbDest, fgPel); } else { WritePixel(pbDest, GetColorBlack()); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { if (bitmask AND g_MaskBit6) { WritePixel(pbDest, fgPel); } else { WritePixel(pbDest, GetColorBlack()); } pbDest = NextPixel(pbDest); cBits = cBits - 1; if (cBits > 0) { if (bitmask AND g_MaskBit7) { WritePixel(pbDest, fgPel); } else { WritePixel(pbDest, GetColorBlack()); } pbDest = NextPixel(pbDest); } } } } } } } return pbDest; } // // Decompress an RLE compressed bitmap. // VOID RleDecompress( BYTE* pbSrcBuffer, // Source buffer containing compressed bitmap UINT cbSrcBuffer, // Size of source buffer in bytes BYTE* pbDestBuffer, // Destination buffer UINT rowDelta // Scanline length in bytes ) { BYTE* pbSrc = pbSrcBuffer; BYTE* pbEnd = pbSrcBuffer + cbSrcBuffer; BYTE* pbDest = pbDestBuffer; PIXEL fgPel = GetColorWhite(); BOOL fInsertFgPel = FALSE; BOOL fFirstLine = TRUE; BYTE bitmask; PIXEL pixelA, pixelB; UINT runLength; UINT code; while (pbSrc < pbEnd) { // // Watch out for the end of the first scanline. // if (fFirstLine) { if (pbDest - pbDestBuffer >= rowDelta) { fFirstLine = FALSE; fInsertFgPel = FALSE; } } // // Extract the compression order code ID from the compression // order header. // code = ExtractCodeId(*pbSrc); // // Handle Background Run Orders. // if (code == REGULAR_BG_RUN OR code == MEGA_MEGA_BG_RUN) { runLength = ExtractRunLength(code, pbSrc); pbSrc = AdvanceOverOrderHeader(code, pbSrc); if (fFirstLine) { if (fInsertFgPel) { WritePixel(pbDest, fgPel); pbDest = NextPixel(pbDest); runLength = runLength - 1; } while (runLength > 0) { WritePixel(pbDest, GetColorBlack()); pbDest = NextPixel(pbDest); runLength = runLength - 1; } } else { if (fInsertFgPel) { WritePixel( pbDest, ReadPixel(pbDest - rowDelta) XOR fgPel ); pbDest = NextPixel(pbDest); runLength = runLength - 1; } while (runLength > 0) { WritePixel(pbDest, ReadPixel(pbDest - rowDelta)); pbDest = NextPixel(pbDest); runLength = runLength - 1; } } // // A follow-on background run order will need a // foreground pel inserted. // fInsertFgPel = TRUE; continue; } // // For any of the other run-types a follow-on background run // order does not need a foreground pel inserted. // fInsertFgPel = FALSE; // // Handle Foreground Run Orders. // if (code == REGULAR_FG_RUN OR code == MEGA_MEGA_FG_RUN OR code == LITE_SET_FG_FG_RUN OR code == MEGA_MEGA_SET_FG_RUN) { runLength = ExtractRunLength(code, pbSrc); pbSrc = AdvanceOverOrderHeader(code, pbSrc); if (code == LITE_SET_FG_FG_RUN OR code == MEGA_MEGA_SET_FG_RUN) { fgPel = ReadPixel(pbSrc); pbSrc = NextPixel(pbSrc); } while (runLength > 0) { if (fFirstLine) { WritePixel(pbDest, fgPel); pbDest = NextPixel(pbDest); } else { WritePixel( pbDest, ReadPixel(pbDest - rowDelta) XOR fgPel ); pbDest = NextPixel(pbDest); } runLength = runLength - 1; } continue; } // // Handle Dithered Run Orders. // if (code == LITE_DITHERED_RUN OR code == MEGA_MEGA_DITHERED_RUN) { runLength = ExtractRunLength(code, pbSrc); pbSrc = AdvanceOverOrderHeader(code, pbSrc); pixelA = ReadPixel(pbSrc); pbSrc = NextPixel(pbSrc); pixelB = ReadPixel(pbSrc); pbSrc = NextPixel(pbSrc); while (runLength > 0) { WritePixel(pbDest, pixelA); pbDest = NextPixel(pbDest); WritePixel(pbDest, pixelB); pbDest = NextPixel(pbDest); runLength = runLength - 1; } continue; } // // Handle Color Run Orders. // if (code == REGULAR_COLOR_RUN OR code == MEGA_MEGA_COLOR_RUN) { runLength = ExtractRunLength(code, pbSrc); pbSrc = AdvanceOverOrderHeader(code, pbSrc); pixelA = ReadPixel(pbSrc); pbSrc = NextPixel(pbSrc); while (runLength > 0) { WritePixel(pbDest, pixelA); pbDest = NextPixel(pbDest); runLength = runLength - 1; } continue; } // // Handle Foreground/Background Image Orders. // if (code == REGULAR_FGBG_IMAGE OR code == MEGA_MEGA_FGBG_IMAGE OR code == LITE_SET_FG_FGBG_IMAGE OR code == MEGA_MEGA_SET_FGBG_IMAGE) { runLength = ExtractRunLength(code, pbSrc); pbSrc = AdvanceOverOrderHeader(code, pbSrc); if (code == LITE_SET_FG_FGBG_IMAGE OR code == MEGA_MEGA_SET_FGBG_IMAGE) { fgPel = ReadPixel(pbSrc); pbSrc = NextPixel(pbSrc); } while (runLength > 8) { bitmask = *pbSrc; pbSrc = pbSrc + 1; if (fFirstLine) { pbDest = WriteFirstLineFgBgImage( pbDest, bitmask, fgPel, 8 ); } else { pbDest = WriteFgBgImage( pbDest, rowDelta, bitmask, fgPel, 8 ); } runLength = runLength - 8; } if (runLength > 0) { bitmask = *pbSrc; pbSrc = pbSrc + 1; if (fFirstLine) { pbDest = WriteFirstLineFgBgImage( pbDest, bitmask, fgPel, runLength ); } else { pbDest = WriteFgBgImage( pbDest, rowDelta, bitmask, fgPel, runLength ); } } continue; } // // Handle Color Image Orders. // if (code == REGULAR_COLOR_IMAGE OR code == MEGA_MEGA_COLOR_IMAGE) { UINT byteCount; runLength = ExtractRunLength(code, pbSrc); pbSrc = AdvanceOverOrderHeader(code, pbSrc); byteCount = runLength * GetColorDepth(); while (byteCount > 0) { *pbDest = *pbSrc; pbDest = pbDest + 1; pbSrc = pbSrc + 1; byteCount = byteCount - 1; } continue; } // // Handle Special Order 1. // if (code == SPECIAL_FGBG_1) { if (fFirstLine) { pbDest = WriteFirstLineFgBgImage( pbDest, g_MaskSpecialFgBg1, fgPel, 8 ); } else { pbDest = WriteFgBgImage( pbDest, rowDelta, g_MaskSpecialFgBg1, fgPel, 8 ); } continue; } // // Handle Special Order 2. // if (code == SPECIAL_FGBG_2) { if (fFirstLine) { pbDest = WriteFirstLineFgBgImage( pbDest, g_MaskSpecialFgBg2, fgPel, 8 ); } else { pbDest = WriteFgBgImage( pbDest, rowDelta, g_MaskSpecialFgBg2, fgPel, 8 ); } continue; } // // Handle White Order. // if (code == WHITE) { WritePixel(pbDest, GetColorWhite()); pbDest = NextPixel(pbDest); continue; } // // Handle Black Order. // if (code == BLACK) { WritePixel(pbDest, GetColorBlack()); pbDest = NextPixel(pbDest); continue; } } }