Windows2003-3790/termsrv/drivers/rdp/rdpdd/oe2.c
2020-09-30 16:53:55 +02:00

864 lines
32 KiB
C

/****************************************************************************/
// oe2.c
//
// RDP field compression utility code
//
// Copyright (C) 1997-2000 Microsoft Corporation
/****************************************************************************/
#include <precmpdd.h>
#pragma hdrstop
#define TRC_FILE "oe2"
#include <adcg.h>
#include <adcs.h>
#define DC_INCLUDE_DATA
#include <ndddata.c>
#undef DC_INCLUDE_DATA
#include <oe2.h>
#include <oe2data.c>
/****************************************************************************/
// 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