/****************************************************************************/ // oe2.c // // RDP field compression utility code // // Copyright (C) 1997-2000 Microsoft Corporation /****************************************************************************/ #include #pragma hdrstop #define TRC_FILE "oe2" #include #include #define DC_INCLUDE_DATA #include #undef DC_INCLUDE_DATA #include #include /****************************************************************************/ // Local file prototypes /****************************************************************************/ BYTE *OE2EncodeFieldSingle(void *, BYTE *, unsigned, unsigned); BYTE *OE2EncodeFieldMultiple(void *, BYTE *, unsigned, unsigned, unsigned); #ifdef DC_DEBUG void OE2PerformUnitTests(); #endif /****************************************************************************/ // OE2_Reset // // Called on session reconnection or addition or removal of a shadower. // Clears the OE2 state to the protocol default start condition. /****************************************************************************/ void OE2_Reset(void) { #ifdef DC_DEBUG OE2PerformUnitTests(); #endif oe2LastOrderType = TS_ENC_PATBLT_ORDER; memset(&oe2LastBounds, 0, sizeof(oe2LastBounds)); } /****************************************************************************/ // OE2_CheckZeroFlagBytes // // Performs post-field-encoding logic to see if there are any zero field // encoding flag bytes, and if so shifts the entire contents of the order // bytes following the encoding flags to compensate. Returns the number of // bytes removed. /****************************************************************************/ unsigned OE2_CheckZeroFlagBytes( BYTE *pControlFlags, BYTE *pFieldFlags, unsigned NumFieldBytes, unsigned PostFlagsDataLength) { int i; unsigned NumZeroBytes; DC_BEGIN_FN("OE2_CheckZeroFlagBytes"); TRC_ASSERT((NumFieldBytes >= 1 && NumFieldBytes <= 3), (TB,"NumFieldBytes %u out of allowed range 1..3", NumFieldBytes)); // Count how many (if any!) contiguous zero field flag bytes there are // (going from the last byte to the first). NumZeroBytes = 0; for (i = (int)(NumFieldBytes - 1); i >= 0; i--) { if (pFieldFlags[i] != 0) break; NumZeroBytes++; } if (NumZeroBytes > 0) { // There are some zero field flag bytes. We now remove them and // store the number in two bits in the control flag byte. TRC_DBG((TB,"Remove NumZeroBytes=%u", NumZeroBytes)); TRC_ASSERT((NumZeroBytes <= 3), (TB,"Invalid NumZeroBytes %u", NumZeroBytes)); *pControlFlags |= (NumZeroBytes << TS_ZERO_FIELD_COUNT_SHIFT); memmove(pFieldFlags + NumFieldBytes - NumZeroBytes, pFieldFlags + NumFieldBytes, PostFlagsDataLength); } DC_END_FN(); return NumZeroBytes; } /****************************************************************************/ // OE2_EncodeBounds // // Used by order encoding paths to encode the order bounds rect if a clip // rect is used for the order. /****************************************************************************/ void OE2_EncodeBounds( BYTE *pControlFlags, BYTE **ppBuffer, RECTL *pRect) { BYTE *pFlags, *pNextFreeSpace; short Delta; DC_BEGIN_FN("OE2_EncodeBounds"); *pControlFlags |= TS_BOUNDS; // The encoding used is a byte of flags followed by a variable number // of 16bit coordinate values and/or 8bit delta coordinate values. pFlags = *ppBuffer; pNextFreeSpace = pFlags + 1; *pFlags = 0; // For each of the four coordinate values in the rectangle: If the // coordinate has not changed then the encoding is null. If the // coordinate can be encoded as an 8-bit delta then do so and set the // appropriate flag. Otherwise copy the coordinate as a 16-bit value // and set the appropriate flag. TRC_ASSERT((pRect->left <= 0xFFFF), (TB,"Rect.left %d will not fit in 16-bit wire encoding", pRect->left)); Delta = (short)(pRect->left - oe2LastBounds.left); if (Delta) { if (Delta != (short)(char)Delta) { *((UNALIGNED short *)pNextFreeSpace) = (short)pRect->left; pNextFreeSpace += sizeof(short); *pFlags |= TS_BOUND_LEFT; } else { *pNextFreeSpace++ = (char)Delta; *pFlags |= TS_BOUND_DELTA_LEFT; } } TRC_ASSERT((pRect->top <= 0xFFFF), (TB,"Rect.top %d will not fit in 16-bit wire encoding", pRect->top)); Delta = (short)(pRect->top - oe2LastBounds.top); if (Delta) { if (Delta != (short)(char)Delta) { *((UNALIGNED short *)pNextFreeSpace) = (short)pRect->top; pNextFreeSpace += sizeof(short); *pFlags |= TS_BOUND_TOP; } else { *pNextFreeSpace++ = (char)Delta; *pFlags |= TS_BOUND_DELTA_TOP; } } TRC_ASSERT((pRect->right <= 0xFFFF), (TB,"Rect.right %d will not fit in 16-bit wire encoding", pRect->right)); Delta = (short)(pRect->right - oe2LastBounds.right); if (Delta) { if (Delta != (short)(char)Delta) { *((UNALIGNED short *)pNextFreeSpace) = (short)pRect->right - 1; pNextFreeSpace += sizeof(short); *pFlags |= TS_BOUND_RIGHT; } else { *pNextFreeSpace++ = (char)Delta; *pFlags |= TS_BOUND_DELTA_RIGHT; } } TRC_ASSERT((pRect->bottom <= 0xFFFF), (TB,"Rect.bottom %d will not fit in 16-bit wire encoding", pRect->bottom)); Delta = (short)(pRect->bottom - oe2LastBounds.bottom); if (Delta) { if (Delta != (short)(char)Delta) { *((UNALIGNED short *)pNextFreeSpace) = (short)pRect->bottom - 1; pNextFreeSpace += sizeof(short); *pFlags |= TS_BOUND_BOTTOM; } else { *pNextFreeSpace++ = (char)Delta; *pFlags |= TS_BOUND_DELTA_BOTTOM; } } // Copy the rectangle for reference with the next encoding. oe2LastBounds = *pRect; // If no bounds were encoded (i.e. the rectangle is identical to the // previous one) set the no-change-in-bounds flag. if (*pFlags) *ppBuffer = pNextFreeSpace; else *pControlFlags |= TS_ZERO_BOUNDS_DELTAS; DC_END_FN(); } /****************************************************************************/ // OE2_TableEncodeOrderFields // // Uses an order field encoding table to encode an intermediate order // format into wire format. /****************************************************************************/ void OE2_TableEncodeOrderFields( BYTE *pControlFlags, PUINT32_UA pFieldFlags, BYTE **ppBuffer, PINT_FMT_FIELD pFieldTable, unsigned NumFields, BYTE *pIntFmt, BYTE *pPrevIntFmt) { BYTE UseDeltaCoords; BYTE *pNextFreeSpace; PBYTE pVariableField; UINT32 ThisFlag; unsigned i, j; unsigned NumReps; unsigned FieldLength; PINT_FMT_FIELD pTableEntry; DC_BEGIN_FN("OE2_TableEncodeOrderFields"); pNextFreeSpace = *ppBuffer; // Before we do the field encoding check all the field entries flagged // as coord to see if we can switch to TS_DELTA_COORDINATES mode. pTableEntry = pFieldTable; UseDeltaCoords = TS_DELTA_COORDINATES; // Loop through each fixed field in this order structure. i = 0; while (i < NumFields && pTableEntry->FieldType & OE2_ETF_FIXED) { // If this field entry is coord then compare it to the previous // coordinate we sent for this field to determine whether we can send // it as a delta. if (pTableEntry->FieldType & OE2_ETF_COORDINATES) { // We assume that coordinates are always signed. if (pTableEntry->FieldUnencodedLen == 4) { __int32 Temp; // Most common case: 4-byte source. Temp = (*((__int32 *)(pIntFmt + pTableEntry->FieldPos)) - *((__int32 *)(pPrevIntFmt + pTableEntry->FieldPos))); if (Temp != (INT32)(char)Temp) { UseDeltaCoords = FALSE; break; } } else if (pTableEntry->FieldUnencodedLen == 2) { short Temp; // Uncommon: 2-byte source. Temp = (*((short *)(pIntFmt + pTableEntry->FieldPos)) - *((short *)(pPrevIntFmt + pTableEntry->FieldPos))); if (Temp != (short)(char)Temp) { UseDeltaCoords = FALSE; break; } } else { TRC_ASSERT((pTableEntry->FieldUnencodedLen == 2), (TB,"Unhandled field size %d", pTableEntry->FieldUnencodedLen)); } TRC_DBG((TB, "Use Delta coord A %d", UseDeltaCoords)); } pTableEntry++; i++; } #ifdef USE_VARIABLE_COORDS // Next loop through each of the variable fields. pVariableField = pIntFmt + pTableEntry->FieldPos; while (i < NumFields && UseDeltaCoords) { // The length of the field (in bytes) is given in the first // UINT32 of the variable sized field structure. FieldLength = *(PUINT32)pVariableField; pVariableField += sizeof(UINT32); // If this field entry is a coord then compare it to the // previous coord we sent for this field to determine whether // we can send it as a delta. if (pTableEntry->FieldType & OE2_ETF_COORDINATES) { // The number of coords is given by the number of bytes in // the field divided by the size of each entry. NumReps = FieldLength / pTableEntry->FieldUnencodedLen; // We assume that coords are always signed. if (pTableEntry->FieldUnencodedLen == 4) { __int32 Temp; // Most common case: 4-byte source. for (j = 0; j < NumReps; j++) { Temp = (*((__int32 *)(pIntFmt + pTableEntry->FieldPos)) - *((__int32 *)(pPrevIntFmt + pTableEntry->FieldPos))); if (Temp != (__int32)(char)Temp) { UseDeltaCoords = FALSE; break; } } } else if (pTableEntry->FieldUnencodedLen == 2) { short Temp; // Uncommon: 2-byte source. for (j = 0; j < NumReps; j++) { Temp = (*((short *)(pIntFmt + pTableEntry->FieldPos)) - *((short *)(pPrevIntFmt + pTableEntry->FieldPos))); if (Temp != (short)(char)Temp) { UseDeltaCoords = FALSE; break; } } } else { TRC_ASSERT((pTableEntry->FieldUnencodedLen == 2), (TB,"Unhandled field size %d", pTableEntry->FieldUnencodedLen)); } TRC_DBG((TB, "Use Delta coord B %d", UseDeltaCoords)); } // Move on to the next field in the order structure. Note that // variable sized fields are packed on the send side (i.e. // increment pVariableField by fieldLength not by // pTableEntry->FieldLen). pVariableField += FieldLength; pTableEntry++; i++; } #endif // USE_VARIABLE_COORDS TRC_DBG((TB, "Final UseDeltaCoords: %d", UseDeltaCoords)); *pControlFlags |= UseDeltaCoords; // Now do the encoding. pTableEntry = pFieldTable; ThisFlag = 0x00000001; // First process all the fixed size fields in the order structure. // (These come before the variable sized fields.) i = 0; while (i < NumFields && pTableEntry->FieldType & OE2_ETF_FIXED) { // If the field has changed since it was previously transmitted then // we need to send it again. TRC_DBG((TB, "Processing field pos %u, type %u", pTableEntry->FieldPos, pTableEntry->FieldType)); if (memcmp(pIntFmt + pTableEntry->FieldPos, pPrevIntFmt + pTableEntry->FieldPos, pTableEntry->FieldUnencodedLen)) { TRC_DBG((TB, "Bothering to encode this")); // Update the encoding flags. *pFieldFlags |= ThisFlag; // If we are encoding in delta coordinate mode and this field // is a coordinate... if (UseDeltaCoords && (pTableEntry->FieldType & OE2_ETF_COORDINATES) != 0) { TRC_DBG((TB, "Using delta coords")); // We assume that coordinates are always signed. if (pTableEntry->FieldUnencodedLen == 4) { // Most common case: 4-byte source. *pNextFreeSpace++ = (char)(*((__int32 *)(pIntFmt + pTableEntry->FieldPos)) - *((__int32 *)(pPrevIntFmt + pTableEntry->FieldPos))); } else if (pTableEntry->FieldUnencodedLen == 2) { // Uncommon: 2-byte source. *pNextFreeSpace++ = (char)(*((short *)(pIntFmt + pTableEntry->FieldPos)) - *((short *)(pPrevIntFmt + pTableEntry->FieldPos))); } else { TRC_ASSERT((pTableEntry->FieldUnencodedLen == 2), (TB,"Unhandled field size %d", pTableEntry->FieldUnencodedLen)); } } else { TRC_DBG((TB, "Regular encoding")); // Update the data to be sent. pNextFreeSpace = OE2EncodeFieldSingle( pIntFmt + pTableEntry->FieldPos, pNextFreeSpace, pTableEntry->FieldUnencodedLen, pTableEntry->FieldEncodedLen); } // Save the current value for comparison next time. memcpy(pPrevIntFmt + pTableEntry->FieldPos, pIntFmt + pTableEntry->FieldPos, pTableEntry->FieldUnencodedLen); } // Move on to the next field in the structure. ThisFlag <<= 1; pTableEntry++; i++; } // Now process the variable sized entries. pVariableField = pIntFmt + pTableEntry->FieldPos; while (i < NumFields) { // The length of the field is given in the first UINT32 of the // variable sized field structure. FieldLength = *(PUINT32)pVariableField; TRC_DBG((TB, "Var field length %u", FieldLength)); // If the field has changed (either in size or in contents) then we // need to copy it across. if (memcmp(pVariableField, pPrevIntFmt + pTableEntry->FieldPos, FieldLength + sizeof(UINT32))) { // Update the encoding flags. *pFieldFlags |= ThisFlag; // Work out how many elements we are encoding for this field. NumReps = FieldLength / pTableEntry->FieldUnencodedLen; // Fill in the length of the field into the encoded buffer then // increment the pointer ready to encode the actual field. // Note that the length must always be set to the length // required for regular second level encoding of the field, // regardless of whether regular encoding or delta encoding is // used. if (pTableEntry->FieldType & OE2_ETF_LONG_VARIABLE) { *((PUINT16_UA)pNextFreeSpace) = (UINT16)(NumReps * pTableEntry->FieldEncodedLen); pNextFreeSpace += sizeof(UINT16); } else { *pNextFreeSpace++ = (BYTE)(NumReps * pTableEntry->FieldEncodedLen); } #ifdef USE_VARIABLE_COORDS // If we are encoding in delta coord mode and this field // is a coordinate... if (UseDeltaCoords && (pTableEntry->FieldType & OE2_ETF_COORDINATES) != 0) { // We assume that coordinates are always signed. if (pTableEntry->FieldUnencodedLen == 4) { // Most common case: 4-byte source. for (j = 0; j < NumReps; j++) *pNextFreeSpace++ = (char)(((__int32 *)(pVariableField + sizeof(DWORD)))[j] - ((__int32 *)(pPrevIntFmt + pTableEntry->FieldPos + sizeof(DWORD)))[j]); } else if (pTableEntry->FieldUnencodedLen == 2) { // Uncommon: 2-byte source. for (j = 0; j < NumReps; j++) *pNextFreeSpace++ = (char)(((short *)(pVariableField + sizeof(DWORD)))[j] - ((short *)(pPrevIntFmt + pTableEntry->FieldPos + sizeof(DWORD)))[j]); } else { TRC_ASSERT((fieldLength == 2), (TB,"Unhandled field size %d", pTableEntry->FieldUnencodedLen)); } } else #endif // USE_VARIABLE_COORDS { // Use regular encoding. TRC_DBG((TB, "Encode: encLen %u, unencLen %u reps %u", pTableEntry->FieldEncodedLen, pTableEntry->FieldUnencodedLen, NumReps)); pNextFreeSpace = OE2EncodeFieldMultiple( pVariableField + sizeof(UINT32), pNextFreeSpace, pTableEntry->FieldUnencodedLen, pTableEntry->FieldEncodedLen, NumReps); } // Keep data for comparison next time. // Note that the variable fields of pLastOrder are not packed // (unlike the order which we are encoding), so we can use // pTableEntry->FieldPos to get the start of the field. memcpy(pPrevIntFmt + pTableEntry->FieldPos, pVariableField, FieldLength + sizeof(UINT32)); } else { TRC_NRM((TB, "Duplicate var field length %u", FieldLength)); } // Move on to the next field in the order structure, remembering to // step. Note that past the size field. variable sized fields are // packed on the send side. (ie increment pVariableField by // fieldLength not by pTableEntry->FieldLen). pVariableField += FieldLength + sizeof(UINT32); // Make sure that we are at the next 4-byte boundary. pVariableField = (PBYTE)DC_ROUND_UP_4((UINT_PTR)pVariableField); ThisFlag <<= 1; pTableEntry++; i++; } *ppBuffer = pNextFreeSpace; DC_END_FN(); } /****************************************************************************/ // OE2_EncodeOrder // // Provided with buffer space and an intermediate (OE) representation of // order data, field-encodes the order into wire (OE2) format. /****************************************************************************/ unsigned OE2_EncodeOrder( BYTE *pBuffer, unsigned OrderType, unsigned NumFields, BYTE *pIntFmt, BYTE *pPrevIntFmt, PINT_FMT_FIELD pFieldTable, RECTL *pBoundRect) { BYTE *pControlFlags; PUINT32_UA pFieldFlags; unsigned NumFieldFlagBytes; DC_BEGIN_FN("OE2_EncodeOrder"); TRC_ASSERT((OrderType < TS_MAX_ORDERS), (TB,"Ordertype %u exceeds max", OrderType)); TRC_ASSERT((NumFields == OE2OrdAttr[OrderType].NumFields), (TB,"Ordertype %u does not have %u fields", OrderType, OE2OrdAttr[OrderType].NumFields)); TRC_ASSERT((pFieldTable == OE2OrdAttr[OrderType].pFieldTable), (TB,"Ord table %p does not match ordtype %u's table %p", pFieldTable, OrderType, OE2OrdAttr[OrderType].pFieldTable)); // The first byte is always a control flag byte. pControlFlags = pBuffer; *pControlFlags = TS_STANDARD; pBuffer++; // Add the order change if need be. OE2_EncodeOrderType(pControlFlags, &pBuffer, OrderType); // Make room for the field flags. pFieldFlags = (PUINT32_UA)pBuffer; *pFieldFlags = 0; NumFieldFlagBytes = ((NumFields + 1) + 7) / 8; pBuffer += NumFieldFlagBytes; // Bounds before encoded fields. if (pBoundRect != NULL) OE2_EncodeBounds(pControlFlags, &pBuffer, pBoundRect); // Use the translation table to convert the internal format to wire // format. OE2_TableEncodeOrderFields(pControlFlags, pFieldFlags, &pBuffer, pFieldTable, NumFields, pIntFmt, pPrevIntFmt); // Check to see if we can optimize the field flag bytes. pBuffer -= OE2_CheckZeroFlagBytes(pControlFlags, (BYTE *)pFieldFlags, NumFieldFlagBytes, (unsigned)(pBuffer - (BYTE *)pFieldFlags - NumFieldFlagBytes)); DC_END_FN(); return (unsigned)(pBuffer - pControlFlags); } /****************************************************************************/ // OE2EncodeFieldSingle // // Encodes an element by copying it to the destination, doing a width // conversion if need be. Returns the new pDest value incremented by the // length used. // // We can ignore signed values since we only ever truncate the data. // Consider the case where we have a 16 bit integer that we want to // convert to 8 bits. We know our values are permissable within the // lower integer size (ie. we know the unsigned value will be less // than 256 of that a signed value will be -128 >= value >= 127), so we // just need to make sure that we have the right high bit set. // But this must be the case for a 16-bit equivalent of an 8-bit // number. No problems - just take the truncated integer. /****************************************************************************/ BYTE *OE2EncodeFieldSingle( void *pSrc, BYTE *pDest, unsigned srcFieldLength, unsigned destFieldLength) { DC_BEGIN_FN("OE2EncodeFieldSingle"); // Note that the source should always be aligned, but the destination // may not be. // Most common case: 4-byte source. if (srcFieldLength == 4) { // Most common case: 2-byte destination. if (destFieldLength == 2) *((UNALIGNED unsigned short *)pDest) = *((unsigned short *)pSrc); // Second most common: 1-byte destination. else if (destFieldLength == 1) *pDest = *((BYTE *)pSrc); // Only other allowed case, very rare: 4-byte destination. else if (destFieldLength == 4) *((UNALIGNED DWORD *)pDest) = *((DWORD *)pSrc); else TRC_ASSERT((destFieldLength == 4), (TB,"Src len = 4, unhandled dest len %d", destFieldLength)); } // Next most common case: Color entries. Avoid pipeline-costly memcpy // since it's short. else if (srcFieldLength == 3) { pDest[0] = ((BYTE *)pSrc)[0]; pDest[1] = ((BYTE *)pSrc)[1]; pDest[2] = ((BYTE *)pSrc)[2]; } // Somewhat common (usually cache indices): 2-byte source. else if (srcFieldLength == 2) { if (destFieldLength == 2) *((UNALIGNED unsigned short *)pDest) = *((unsigned short *)pSrc); else if (destFieldLength == 1) *pDest = *((BYTE *)pSrc); else TRC_ASSERT((destFieldLength == 1), (TB,"Src len = 2, unhandled dest len %d", destFieldLength)); } // Next: Same-sized fields, including rare 1-byte fields and brushes etc. else if (srcFieldLength == destFieldLength) { memcpy(pDest, pSrc, srcFieldLength); } // We didn't handle the combination. else { TRC_ASSERT((destFieldLength == srcFieldLength), (TB,"Unhandled encode conbination, src len = %d, dest len %d", srcFieldLength, destFieldLength)); } DC_END_FN(); return pDest + destFieldLength; } /****************************************************************************/ // OE2EncodeFieldMultiple // Encodes an array of elements by copying them to the destination, doing a // width conversion if need be. Returns the new pDest value incremented by // the length used. // // See notes for OE2EncodeFieldSingle above for signed value truncation info. /****************************************************************************/ BYTE *OE2EncodeFieldMultiple( void *pSrc, BYTE *pDest, unsigned srcFieldLength, unsigned destFieldLength, unsigned numElements) { unsigned i; DC_BEGIN_FN("OE2EncodeFieldMultiple"); // Note that the source should always be aligned, but the destination // may not be. // Most common case: 1-byte source to 1-byte destination. if (srcFieldLength == 1) { memcpy(pDest, pSrc, numElements); } // Next most common: 4-byte source. else if (srcFieldLength == 4) { // Common: 2-byte destination. if (destFieldLength == 2) for (i = 0; i < numElements; i++) ((UNALIGNED unsigned short *)pDest)[i] = (unsigned short)((DWORD *)pSrc)[i]; // Less common: 1-byte destination. else if (destFieldLength == 1) for (i = 0; i < numElements; i++) pDest[i] = (BYTE)((DWORD *)pSrc)[i]; // Rare if any: 4-byte destination. else if (destFieldLength == 4) for (i = 0; i < numElements; i++) ((UNALIGNED DWORD *)pDest)[i] = ((DWORD *)pSrc)[i]; else TRC_ASSERT((destFieldLength == 4), (TB,"Src len = 4, unhandled dest len %d", destFieldLength)); } // We don't handle anything else. else { TRC_ASSERT((srcFieldLength == 4), (TB,"Unhandled encode conbination, src len = %d, dest len %d", srcFieldLength, destFieldLength)); } DC_END_FN(); return pDest + destFieldLength * numElements; } #ifdef DC_DEBUG /****************************************************************************/ // OE2PerformUnitTests // // Debug-only test code designed to ensure OE2 is functioning properly. /****************************************************************************/ // Data for OE2_EncodeBounds test. Note that EncBounds converts to inclusive // coordinates. const RECTL BoundsTest1_InputRect = { 0x100, 0x200, 0x300, 0x400 }; #define BoundsTest1_OutputLen 9 const BYTE BoundsTest1_Output[BoundsTest1_OutputLen] = { 0x0F, 0x00, 0x01, 0x00, 0x02, 0xFF, 0x02, 0xFF, 0x03 }; const RECTL BoundsTest2_InputRect = { 0x101, 0x202, 0x303, 0x404 }; #define BoundsTest2_OutputLen 5 const BYTE BoundsTest2_Output[BoundsTest2_OutputLen] = { 0xF0, 0x01, 0x02, 0x03, 0x04 }; const RECTL BoundsTest3_InputRect = { 0x101, 0x202, 0x303, 0x404 }; #define BoundsTest3_OutputLen 0 // Data for ScrBlt test encoding via OE2_EncodeOrder. const SCRBLT_ORDER OrderTest1_IntOrderFmt = { 0, 0, // dest left, top 0x200, 0x100, // width, height 0xCC, // rop=copyrop 0x201, 0x101 // src left, top }; SCRBLT_ORDER UnitTestPrevScrBlt; #define OrderTest1_OutputLen 12 const BYTE OrderTest1_Output[OrderTest1_OutputLen] = { 0x09, // Control flags: TS_STANDARD | TS_TYPE_CHANGE TS_ENC_SCRBLT_ORDER, 0x7C, // Field flags byte: width, height, rop, srcleft, srctop 0x00, 0x02, // width 0x00, 0x01, // height 0xCC, // rop 0x01, 0x02, // srcleft 0x01, 0x01, // srctop }; void OE2PerformUnitTests() { BYTE *pBuffer; BYTE ControlFlags; RECTL InputRect; BYTE OutputBuffer[256]; unsigned Len; DC_BEGIN_FN("OE2PerformUnitTests"); // Test OE2_EncodeBounds. // Reset the bounds, then perform a few rect encodings (regular, delta, // zero delta) to make sure the bounds are being encoded properly and // the control flags come out right. memset(&oe2LastBounds, 0, sizeof(oe2LastBounds)); // First rect: should result in non-delta, non-zero-delta encoding. ControlFlags = TS_STANDARD | TS_BOUNDS; pBuffer = OutputBuffer; OE2_EncodeBounds(&ControlFlags, &pBuffer, (RECTL *)&BoundsTest1_InputRect); Len = (unsigned)(pBuffer - OutputBuffer); TRC_ASSERT((ControlFlags == (TS_STANDARD | TS_BOUNDS)), (TB,"Bounds1: Control flag value 0x%02X does not match " "expected 0x%02X", ControlFlags, (TS_STANDARD | TS_BOUNDS))); TRC_ASSERT((Len == BoundsTest1_OutputLen), (TB,"Bounds1: Len %u != expected %u", Len, BoundsTest1_OutputLen)); TRC_ASSERT((!memcmp(OutputBuffer, BoundsTest1_Output, Len)), (TB,"Bounds1: Mem at %p != expected at %p (Len=%u)", OutputBuffer, BoundsTest1_Output, Len)); // Second rect: should result in delta encoding. ControlFlags = TS_STANDARD | TS_BOUNDS; pBuffer = OutputBuffer; OE2_EncodeBounds(&ControlFlags, &pBuffer, (RECTL *)&BoundsTest2_InputRect); Len = (unsigned)(pBuffer - OutputBuffer); TRC_ASSERT((ControlFlags == (TS_STANDARD | TS_BOUNDS)), (TB,"Bounds2: Control flag value 0x%02X does not match " "expected 0x%02X", ControlFlags, (TS_STANDARD | TS_BOUNDS))); TRC_ASSERT((Len == BoundsTest2_OutputLen), (TB,"Bounds2: Len %u != expected %u", Len, BoundsTest2_OutputLen)); TRC_ASSERT((!memcmp(OutputBuffer, BoundsTest2_Output, Len)), (TB,"Bounds2: Mem at %p != expected at %p (Len=%u)", OutputBuffer, BoundsTest2_Output, Len)); // Third rect: Should result in zero-delta encoding. ControlFlags = TS_STANDARD | TS_BOUNDS; pBuffer = OutputBuffer; OE2_EncodeBounds(&ControlFlags, &pBuffer, (RECTL *)&BoundsTest3_InputRect); Len = (unsigned)(pBuffer - OutputBuffer); TRC_ASSERT((ControlFlags == (TS_STANDARD | TS_BOUNDS | TS_ZERO_BOUNDS_DELTAS)), (TB,"Bounds3: Control flag value 0x%02X does not match " "expected 0x%02X", ControlFlags, (TS_STANDARD | TS_BOUNDS | TS_ZERO_BOUNDS_DELTAS))); TRC_ASSERT((Len == BoundsTest3_OutputLen), (TB,"Bounds3: Len %u != expected %u", Len, BoundsTest3_OutputLen)); // Reset the bounds after the encoding test. memset(&oe2LastBounds, 0, sizeof(oe2LastBounds)); // Test OE2_EncodeOrder by encoding a ScrBlt order. We need to make sure // oe2LastOrderType is reset to default (PatBlt). Reset the prev ScrBlt // to an initial state. oe2LastOrderType = TS_ENC_PATBLT_ORDER; memset(&UnitTestPrevScrBlt, 0, sizeof(UnitTestPrevScrBlt)); Len = OE2_EncodeOrder(OutputBuffer, TS_ENC_SCRBLT_ORDER, NUM_SCRBLT_FIELDS, (BYTE *)&OrderTest1_IntOrderFmt, (BYTE *)&UnitTestPrevScrBlt, etable_SB, NULL); TRC_ASSERT((Len == OrderTest1_OutputLen), (TB,"Order1: Len %u != expected %u", Len, OrderTest1_OutputLen)); TRC_ASSERT((!memcmp(OutputBuffer, OrderTest1_Output, Len)), (TB,"Order1: Mem at %p != expected at %p (Len=%u)", OutputBuffer, OrderTest1_Output, Len)); DC_END_FN(); } #endif // DC_DEBUG