864 lines
32 KiB
C
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
|
|
|