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

1279 lines
55 KiB
C

/****************************************************************************/
// nbadisp.c
//
// RDP Bounds Accumulator display driver code
//
// Copyright (C) 1997-2000 Microsoft Corporation
/****************************************************************************/
#include <precmpdd.h>
#define hdrstop
#define TRC_FILE "nbadisp"
#include <adcg.h>
#include <atrcapi.h>
#include <abaapi.h>
#include <nbadisp.h>
#define DC_INCLUDE_DATA
#include <ndddata.c>
#undef DC_INCLUDE_DATA
// No data.
//#include <nbaddat.c>
#include <nbainl.h>
// Instantiate common code.
#include <abacom.c>
// Local prototypes.
#ifdef DC_DEBUG
void BAPerformUnitTests();
#endif
/****************************************************************************/
// BA_DDInit
/****************************************************************************/
void RDPCALL BA_DDInit(void)
{
DC_BEGIN_FN("BA_DDInit");
// No data to declare, don't waste time opening the file.
//#define DC_INIT_DATA
//#include <nbaddat.c>
//#undef DC_INIT_DATA
#ifdef DC_DEBUG
// Perform one-time checks on the algorithm.
BAPerformUnitTests();
#endif
DC_END_FN();
}
/****************************************************************************/
// BA_InitShm
//
// Init BA block in shared memory just after alloc.
/****************************************************************************/
void RDPCALL BA_InitShm(void)
{
unsigned i;
DC_BEGIN_FN("BA_InitShm");
// Initialize all members - shared memory is not zeroed on alloc.
// Initialize rectangle array slots as unused, set up free list
// containing all rects.
pddShm->ba.firstRect = BA_INVALID_RECT_INDEX;
pddShm->ba.rectsUsed = 0;
pddShm->ba.totalArea = 0;
pddShm->ba.firstFreeRect = 0;
for (i = 0; i < BA_TOTAL_NUM_RECTS; i++) {
pddShm->ba.bounds[i].inUse = FALSE;
pddShm->ba.bounds[i].iNext = i + 1;
}
pddShm->ba.bounds[BA_TOTAL_NUM_RECTS - 1].iNext = BA_INVALID_RECT_INDEX;
DC_END_FN();
}
/****************************************************************************/
// BA_AddScreenData
//
// Adds a specified rectangle to the current Screen Data Area.
/****************************************************************************/
void RDPCALL BA_AddScreenData(PRECTL pRect)
{
DC_BEGIN_FN("BA_AddScreenData");
// Check that the caller has passed a valid rectangle.
// Make sure we add a rectangle with coordinates within the screen area.
if((pRect->right > pRect->left) && (pRect->bottom > pRect->top) &&
(pRect->left >= 0) && (pRect->left < ddDesktopWidth) &&
(pRect->right > 0) && (pRect->right <= ddDesktopWidth) &&
(pRect->top >= 0) && (pRect->top < ddDesktopHeight) &&
(pRect->bottom > 0) && (pRect->bottom <= ddDesktopHeight)) {
BAAddRect(pRect, 0);
}
DC_END_FN();
}
/****************************************************************************/
// BAOverlap
//
// Detects overlap between two rectangles. Note that all rectangle
// coordinates are exclusive. Returns one of the overlap return codes or
// outcode combinations defined above.
/****************************************************************************/
int RDPCALL BAOverlap(PRECTL pRect1, PRECTL pRect2)
{
int externalEdges;
int externalCount;
int internalEdges;
int internalCount;
int rc;
DC_BEGIN_FN("BAOverlap");
// We start with special cases of the rects being immediately adjacent
// or overlapping while lying side-by-side.
if (pRect1->top == pRect2->top && pRect1->bottom == pRect2->bottom) {
if (pRect1->left <= pRect2->right &&
pRect1->left > pRect2->left &&
pRect1->right > pRect2->right) {
rc = OL_MERGE_LEFT;
DC_QUIT;
}
if (pRect1->right >= pRect2->left &&
pRect1->right < pRect2->right &&
pRect1->left < pRect2->left) {
rc = OL_MERGE_RIGHT;
DC_QUIT;
}
}
if (pRect1->left == pRect2->left && pRect1->right == pRect2->right) {
if (pRect1->top <= pRect2->bottom &&
pRect1->top > pRect2->top &&
pRect1->bottom > pRect2->bottom) {
rc = OL_MERGE_TOP;
DC_QUIT;
}
if (pRect1->bottom >= pRect2->top &&
pRect1->bottom < pRect2->bottom &&
pRect1->top < pRect2->top) {
rc = OL_MERGE_BOTTOM;
DC_QUIT;
}
}
// Check for no overlapping -- we've exhausted the cases where adjacency
// can be taken advantage of.
if (pRect1->left >= pRect2->right ||
pRect1->top >= pRect2->bottom ||
pRect1->right <= pRect2->left ||
pRect1->bottom <= pRect2->top) {
rc = OL_NONE;
DC_QUIT;
}
// Use outcodes for Internal edge cases.
// If 3 or more bits are set then rect1 is enclosed either partially or
// completely within rect2 according to the OL_ENCLOSED and
// OL_PART_ENCLOSED_XXX definitions. The negative of the outcode value
// is retruned to ensure that it is distinct from the external edge
// outcode returns (see below).
internalCount = 0;
internalEdges = 0;
if (pRect1->left >= pRect2->left && pRect1->left < pRect2->right) {
// Rect1 left is enclosed within rect2.
internalEdges |= EE_LEFT;
internalCount++;
}
if (pRect1->top >= pRect2->top && pRect1->top < pRect2->bottom) {
// Rect1 top is enclosed within rect2.
internalEdges |= EE_TOP;
internalCount++;
}
if (pRect1->right > pRect2->left && pRect1->right <= pRect2->right) {
// Rect1 right is enclosed within rect2.
internalEdges |= EE_RIGHT;
internalCount++;
}
if (pRect1->bottom > pRect2->top && pRect1->bottom <= pRect2->bottom) {
// Rect1 bottom is enclosed within rect2.
internalEdges |= EE_BOTTOM;
internalCount++;
}
if (internalCount >= 3) {
rc = -internalEdges;
DC_QUIT;
}
// Use outcodes for External edge cases. These are the classic "line"
// outcodes. If 2 or more bits are set then rect1 overlaps rect2 per the
// OL_ENCLOSES_XXX and OL_SPLIT_XXX definitions.
externalEdges = 0;
externalCount = 0;
if (pRect1->left <= pRect2->left) {
// Rect1 left is left of rect2 left.
externalEdges |= EE_LEFT;
externalCount++;
}
if (pRect1->top <= pRect2->top) {
// Rect1 top is above rect2 top.
externalEdges |= EE_TOP;
externalCount++;
}
if (pRect1->right >= pRect2->right) {
// Rect1 right is right of rect2 right.
externalEdges |= EE_RIGHT;
externalCount++;
}
if (pRect1->bottom >= pRect2->bottom) {
// Rect1 bottom is below rect2 bottom.
externalEdges |= EE_BOTTOM;
externalCount++;
}
if (externalCount >= 2) {
rc = externalEdges;
DC_QUIT;
}
// If get here then we failed to detect a valid case.
TRC_ALT((TB, "Unrecognised Overlap: (%d,%d,%d,%d),(%d,%d,%d,%d)",
pRect1->left, pRect1->top, pRect1->right, pRect1->bottom,
pRect2->left, pRect2->top, pRect2->right, pRect2->bottom ));
rc = OL_NONE;
DC_EXIT_POINT:
DC_END_FN();
return rc;
}
/****************************************************************************/
// BARemoveRectList
//
// Removes a rectangle from the list.
/****************************************************************************/
__inline void RDPCALL BARemoveRectList(unsigned iRect)
{
BA_RECT_INFO *pRect;
DC_BEGIN_FN("BARemoveRectList");
pRect = &(pddShm->ba.bounds[iRect]);
// Unlink from used list.
if (pRect->iPrev != BA_INVALID_RECT_INDEX)
pddShm->ba.bounds[pRect->iPrev].iNext = pRect->iNext;
else
pddShm->ba.firstRect = pRect->iNext;
if (pRect->iNext != BA_INVALID_RECT_INDEX)
pddShm->ba.bounds[pRect->iNext].iPrev = pRect->iPrev;
// Add to beginning of free list.
pRect->inUse = FALSE;
pRect->iNext = pddShm->ba.firstFreeRect;
pddShm->ba.firstFreeRect = iRect;
// Update bookkeeping variables.
pddShm->ba.rectsUsed--;
pddShm->ba.totalArea -= pRect->area;
#ifdef DC_DEBUG
// Check the list integrity.
BACheckList();
#endif
DC_END_FN();
}
/****************************************************************************/
// BARecalcArea
//
// Recalculates a rect area, preserving the totalArea central tally of areas.
/****************************************************************************/
__inline void RDPCALL BARecalcArea(unsigned iRect)
{
// Reset area to new size and update totalArea.
pddShm->ba.totalArea -= pddShm->ba.bounds[iRect].area;
pddShm->ba.bounds[iRect].area =
COM_SIZEOF_RECT(pddShm->ba.bounds[iRect].coord);
pddShm->ba.totalArea += pddShm->ba.bounds[iRect].area;
}
/****************************************************************************/
// BAAddRect
//
// Accumulates rectangles. This is a complex routine, with the essential
// algorithm as follows:
//
// - Start with the supplied rect as the candidate rect.
//
// - Compare the candidate against each of the existing accumulated rects.
//
// - If some form of overlap is detected between the candidate and an
// existing rect, this may result in one of the following (see the cases of
// the switch for details):
//
// - adjust the candidate or the existing rect or both
// - merge the candidate into the existing rect
// - discard the candidate as it is enclosed by an existing rect.
//
// - If the merge or adjustment results in a changed candidate, restart the
// comparisons from the beginning of the list with the changed candidate.
//
// - If the adjustment results in a split (giving two candidate rects),
// invoke this routine recursively with one of the two candidates as its
// candidate.
//
// - If no overlap is detected against the existing rects, add the candidate
// to the list of accumulated rectangles.
//
// - If the add results in more than BA_MAX_ACCUMULATED_RECTS accumulated
// rects, do a forced merge of two of the accumulate rects (which include
// the newly added candidate) - choosing the two rects where the merged rect
// results in the smallest increase in area over the two non-merged rects.
//
// - After a forced merge, restart the comparisons from the beginning of the
// list with the newly merged rectangle as the candidate.
//
// For a particular call, this process will continue until the candidate
// (whether the supplied rect, an adjusted version of that rect, or a merged
// rect):
//
// - does not find an overlap among the rects in the list and does not cause
// a forced merge
// - is discarded becuase it is enclosed within one of the rects in the list.
//
// Returns TRUE if rectangle was spoiled due to a complete overlap.
/****************************************************************************/
BOOL RDPCALL BAAddRect(PRECTL pCand, int level)
{
INT32 bestMergeIncrease;
INT32 mergeIncrease;
unsigned iBestMerge1;
unsigned iBestMerge2;
unsigned iExist;
unsigned iTmp;
BOOLEAN fRectToAdd;
BOOLEAN fRectMerged;
BOOLEAN fResetRects;
RECTL rectNew;
unsigned iLastMerge;
int overlapType;
BOOL rc = TRUE;
DC_BEGIN_FN("BAAddRect");
#ifdef DC_DEBUG
// Check list, esp. the area calculations.
BACheckList();
#endif
// Increase the level count in case we recurse.
level++;
// Start off by assuming the candidate rectangle will be added to the
// accumulated list of rectangles, and that no forced merge has
// occurred.
fRectToAdd = TRUE;
fRectMerged = FALSE;
// Loop until no merges occur.
do {
TRC_DBG((TB, "Candidate rect: (%d,%d,%d,%d)",
pCand->left,pCand->top,pCand->right,pCand->bottom));
// Compare the current candidate rectangle against the rectangles
// in the current accumulated list.
iExist = pddShm->ba.firstRect;
while (iExist != BA_INVALID_RECT_INDEX) {
// Assume that the comparisons will run through the whole list.
fResetRects = FALSE;
// If the candidate and the existing rectangle are the same
// then ignore. This occurs when an existing rectangle is
// replaced by a candidate and the comparisons are restarted
// from the front of the list - whereupon at some point the
// candidate will be compared with itself.
if (&pddShm->ba.bounds[iExist].coord == pCand) {
TRC_DBG((TB, "OL_SAME - %d", iExist));
iExist = pddShm->ba.bounds[iExist].iNext;
continue;
}
// Switch on the overlap type (see Overlap routine).
overlapType = BAOverlap(&(pddShm->ba.bounds[iExist].coord), pCand);
switch (overlapType) {
case OL_NONE:
// No overlap.
TRC_DBG((TB, "OL_NONE - %d", iExist));
break;
case OL_MERGE_LEFT:
// Candidate abuts or overlaps existing rect at existing
// rect's left.
TRC_DBG((TB, "OL_MERGE_LEFT - %d", iExist));
if (fRectToAdd) {
// Candidate is the original rect; merge the
// candidate into the existing, and make the existing
// the new candidate.
pddShm->ba.bounds[iExist].coord.left = pCand->left;
pCand = &(pddShm->ba.bounds[iExist].coord);
fRectToAdd = FALSE;
iLastMerge = iExist;
BARecalcArea(iExist);
}
else {
// This is a merge of two existing rectangles (ie
// the candidate is the result of a merge), merge the
// overlapping existing into the candidate (the last
// merged) and remove the existing.
pCand->right = pddShm->ba.bounds[iExist].coord.right;
BARemoveRectList(iExist);
}
// Start the comparisons again with the new candidate.
fResetRects = TRUE;
break;
case OL_MERGE_RIGHT:
// Candidate abuts or overlaps existing rect at existing
// rect's right.
TRC_DBG((TB, "OL_MERGE_RIGHT - %d", iExist));
if (fRectToAdd) {
// Candidate is the original rect; merge the
// candidate into the existing, and make the existing
// the new candidate.
pddShm->ba.bounds[iExist].coord.right = pCand->right;
pCand = &(pddShm->ba.bounds[iExist].coord);
fRectToAdd = FALSE;
iLastMerge = iExist;
BARecalcArea(iExist);
}
else {
// This is a merge of two existing rectangles (ie
// the candidate is the result of a merge), merge the
// overlapping existing into the candidate (the last
// merged) and remove the existing.
pCand->left = pddShm->ba.bounds[iExist].coord.left;
BARemoveRectList(iExist);
}
// Start the comparisons again with the new candidate.
fResetRects = TRUE;
break;
case OL_MERGE_TOP:
// Candidate abuts or overlaps existing rect at existing
// rect's top.
TRC_DBG((TB, "OL_MERGE_TOP - %d", iExist));
if (fRectToAdd) {
// Candidate is the original rect; merge the
// candidate into the existing, and make the existing
// the new candidate.
pddShm->ba.bounds[iExist].coord.top = pCand->top;
pCand = &(pddShm->ba.bounds[iExist].coord);
fRectToAdd = FALSE;
iLastMerge = iExist;
BARecalcArea(iExist);
}
else {
// This is a merge of two existing rectangles (ie
// the candidate is the result of a merge), merge the
// overlapping existing into the candidate (the last
// merged) and remove the existing.
pCand->bottom =
pddShm->ba.bounds[iExist].coord.bottom;
BARemoveRectList(iExist);
}
// Start the comparisons again with the new candidate.
fResetRects = TRUE;
break;
case OL_MERGE_BOTTOM:
// Candidate abuts or overlaps existing rect at existing
// rect's bottom.
TRC_DBG((TB, "OL_MERGE_BOTTOM - %d", iExist));
if (fRectToAdd) {
// Candidate is the original rect; merge the
// candidate into the existing, and make the existing
// the new candidate.
pddShm->ba.bounds[iExist].coord.bottom =
pCand->bottom;
pCand = &(pddShm->ba.bounds[iExist].coord);
fRectToAdd = FALSE;
iLastMerge = iExist;
BARecalcArea(iExist);
}
else {
// This is a merge of two existing rectangles (ie
// the candidate is the result of a merge), merge the
// overlapping existing into the candidate (the last
// merged) and remove the existing.
pCand->top = pddShm->ba.bounds[iExist].coord.top;
BARemoveRectList(iExist);
}
// Start the comparisons again with the new candidate.
fResetRects = TRUE;
break;
case OL_ENCLOSED:
// The existing is enclosed by the candidate.
//
// 100,100 +----------------------+
// | Cand |
// | |
// | 130,130 |
// | +------------+ |
// | | | |
// | | Exist | |
// | | | |
// | +------------+ |
// | 170,170 |
// +----------------------+ 200,200
TRC_DBG((TB, "OL_ENCLOSED - %d", iExist));
if (fRectToAdd) {
// Candidate is the original rect; replace the
// existing with the candidate and make the new
// existing the new candidate.
pddShm->ba.bounds[iExist].coord = *pCand;
pCand = &(pddShm->ba.bounds[iExist].coord);
fRectToAdd = FALSE;
iLastMerge = iExist;
BARecalcArea(iExist);
}
else {
// Candidate is an existing rect: Remove the other
// existing rect.
BARemoveRectList(iExist);
}
// Start the comparisons again with the new candidate.
fResetRects = TRUE;
break;
case OL_PART_ENCLOSED_LEFT:
// The existing is partially enclosed by the candidate
// - but not on the right.
//
// 100,100 +----------------------+
// | Cand |
// | 130,130 +-----------+--------+
// | | | |
// | | Exist | |
// | | | |
// | +-----------+--------+
// | | 220,170
// +----------------------+
// 200,200
//
// Adjust the existing rectangle to be the non-
// overlapped portion.
//
// 100,100 +----------------------+
// | |200,130
// | |+-------+
// | || |
// | Cand || Exist |
// | || |
// | |+-------+
// | | 220,170
// +----------------------+
// 200,200
//
// Note that this does not restart the comparisons.
TRC_DBG((TB, "OL_PART_ENCLOSED_LEFT - %d", iExist));
pddShm->ba.bounds[iExist].coord.left = pCand->right;
BARecalcArea(iExist);
break;
case OL_PART_ENCLOSED_RIGHT:
// The existing is partially enclosed by the candidate
// - but not on the left. Adjust the existing rect to be
// the non-overlapping portion, similar to
// OL_PART_ENCLOSED_LEFT above.
// Note that this does not restart the comparisons.
TRC_DBG((TB, "OL_PART_ENCLOSED_RIGHT - %d", iExist));
pddShm->ba.bounds[iExist].coord.right = pCand->left;
BARecalcArea(iExist);
break;
case OL_PART_ENCLOSED_TOP:
// The existing is partially enclosed by the candidate
// - but not on the bottom. Adjust the existing rect to be
// the non-overlapping portion, similar to
// OL_PART_ENCLOSED_LEFT above.
// Note that this does not restart the comparisons.
TRC_DBG((TB, "OL_PART_ENCLOSED_TOP - %d", iExist));
pddShm->ba.bounds[iExist].coord.top = pCand->bottom;
BARecalcArea(iExist);
break;
case OL_PART_ENCLOSED_BOTTOM:
// The existing is partially enclosed by the candidate
// - but not on the top. Adjust the existing rect to be
// the non-overlapping portion, similar to
// OL_PART_ENCLOSED_LEFT above.
// Note that this does not restart the comparisons.
TRC_DBG((TB, "OL_PART_ENCLOSED_BOTTOM - %d", iExist));
pddShm->ba.bounds[iExist].coord.bottom = pCand->top;
BARecalcArea(iExist);
break;
case OL_ENCLOSES:
// The existing encloses the candidate.
//
// 100,100 +----------------------+
// | Exist |
// | |
// | 130,130 |
// | +------------+ |
// | | | |
// | | Cand | |
// | | | |
// | +------------+ |
// | 170,170 |
// +----------------------+ 200,200
TRC_DBG((TB, "OL_ENCLOSES - %d", iExist));
// Return FALSE indicating that the rectangle is
// already catered for by the existing bounds.
rc = FALSE;
DC_QUIT;
case OL_PART_ENCLOSES_LEFT:
// The existing partially encloses the candidate - but
// not on the left.
//
// 100,100 +----------------------+
// | Exist |
// 70,130 | |
// +-----+---------------+ |
// | | | |
// | | Cand | |
// | | | |
// +-----+---------------+ |
// | 170,170 |
// +----------------------+ 200,200
//
// Adjust the candidate rectangle to be the non-
// overlapped portion.
//
// 100,100
// +----------------------+
// | |
// 70,130 | |
// +----+| |
// | || |
// |Cand|| Exist |
// | || |
// +----+| |
// 100,170| |
// +----------------------+ 200,200
TRC_DBG((TB, "OL_PART_ENCLOSES_LEFT - %d", iExist));
pCand->right = pddShm->ba.bounds[iExist].coord.left;
// The candidate changed. Restart the comparisons to check
// for overlaps between the adjusted candidate and other
// existing rects.
fResetRects = TRUE;
BARecalcArea(iExist);
break;
case OL_PART_ENCLOSES_RIGHT:
// The existing partially encloses the candidate - but
// not on the right. Adjust the candidate rect to be
// the non-overlapping portion, similar to
// OL_PART_ENCLOSES_LEFT above.
TRC_DBG((TB, "OL_PART_ENCLOSES_RIGHT - %d", iExist));
pCand->left = pddShm->ba.bounds[iExist].coord.right;
// The candidate changed. Restart the comparisons to check
// for overlaps between the adjusted candidate and other
// existing rects.
fResetRects = TRUE;
BARecalcArea(iExist);
break;
case OL_PART_ENCLOSES_TOP:
// The existing partially encloses the candidate - but
// not on the top. Adjust the candidate rect to be
// the non-overlapping portion, similar to
// OL_PART_ENCLOSES_LEFT above.
TRC_DBG((TB, "OL_PART_ENCLOSES_TOP - %d", iExist));
pCand->bottom = pddShm->ba.bounds[iExist].coord.top;
// The candidate changed. Restart the comparisons to check
// for overlaps between the adjusted candidate and other
// existing rects.
fResetRects = TRUE;
BARecalcArea(iExist);
break;
case OL_PART_ENCLOSES_BOTTOM:
// The existing partially encloses the candidate - but
// not on the bottom. Adjust the candidate rect to be
// the non-overlapping portion, similar to
// OL_PART_ENCLOSES_LEFT above.
TRC_DBG((TB, "OL_PART_ENCLOSES_BOTTOM - %d", iExist));
pCand->top = pddShm->ba.bounds[iExist].coord.bottom;
// The candidate changed. Restart the comparisons to check
// for overlaps between the adjusted candidate and other
// existing rects.
fResetRects = TRUE;
BARecalcArea(iExist);
break;
case OL_SPLIT_HORIZ:
// The existing overlaps the candicate, but neither can
// be merged or adjusted.
//
// 100,100 +--------+
// 70,130 | Exist |
// +-----+--------+------+
// | | | |
// | Cand| | |
// | | | |
// +-----+--------+------+180,160
// | |
// +--------+150,200
//
// Need to split candidate into left and right halves.
// Only do a split if there is spare room in the list -
// because both the split rectangles may need to be
// added to the list.
//
// If there is spare room, split the candidate into a
// smaller candidate on the left and a new rectangle on
// the right. Call this routine recursively to handle
// the new rectangle.
//
// 100,100 +--------+
// 70,130 | |150,130
// +----+| |+-----+
// | || || |
// |Cand|| Exist || New |
// | || || |
// +----+| |+-----+
// 100,160| | 180,160
// +--------+150,200
TRC_DBG((TB, "OL_SPLIT_HORIZ - %d", iExist));
if (pddShm->ba.rectsUsed < BA_MAX_ACCUMULATED_RECTS &&
level < ADDR_RECURSE_LIMIT) {
if (((pCand->bottom - pCand->top) *
(pddShm->ba.bounds[iExist].coord.right -
pddShm->ba.bounds[iExist].coord.left)) >
MIN_OVERLAP_BYTES) {
rectNew.left =
pddShm->ba.bounds[iExist].coord.right;
rectNew.right = pCand->right;
rectNew.top = pCand->top;
rectNew.bottom = pCand->bottom;
pCand->right =
pddShm->ba.bounds[iExist].coord.left;
TRC_DBG((TB, "*** RECURSION ***"));
BAAddRect(&rectNew, level);
TRC_DBG((TB, "*** RETURN ***"));
if (!fRectToAdd &&
!pddShm->ba.bounds[iLastMerge].inUse) {
TRC_DBG((TB, "FINISHED - %d", iLastMerge));
DC_QUIT;
}
// After the recursion, because the candidate has
// changed, restart the comparisons to check for
// overlaps between the adjusted candidate and
// other existing rectangles.
fResetRects = TRUE;
}
}
break;
case OL_SPLIT_VERT:
// The existing overlaps the candidate, but neither can
// be merged or adjusted. Split candidate into top and
// bottom similar to OL_SPLIT_HORIZ above.
TRC_DBG((TB, "OL_SPLIT_VERT - %d", iExist));
if (pddShm->ba.rectsUsed < BA_MAX_ACCUMULATED_RECTS &&
level < ADDR_RECURSE_LIMIT) {
if (((pCand->right - pCand->left) *
(pddShm->ba.bounds[iExist].coord.bottom -
pddShm->ba.bounds[iExist].coord.top)) >
MIN_OVERLAP_BYTES) {
rectNew.left = pCand->left;
rectNew.right = pCand->right;
rectNew.top =
pddShm->ba.bounds[iExist].coord.bottom;
rectNew.bottom = pCand->bottom;
pCand->bottom =
pddShm->ba.bounds[iExist].coord.top;
TRC_DBG((TB, "*** RECURSION ***"));
BAAddRect(&rectNew, level);
TRC_DBG((TB, "*** RETURN ***"));
if (!fRectToAdd &&
!pddShm->ba.bounds[iLastMerge].inUse) {
TRC_DBG((TB, "FINISHED - %d", iLastMerge));
DC_QUIT;
}
// After the recursion, because the candidate has
// changed, restart the comparisons to check for
// overlaps between the adjusted candidate and
// other existing rectangles.
fResetRects = TRUE;
}
}
break;
case OL_SPLIT_LEFT_TOP:
// The existing overlaps the candicate at the existing
// rect's top left, but neither can be merged or adjusted.
//
// 100,100 +---------------+
// | Cand |
// | |
// | 150,150 |
// | +-------+-----+
// | | | |
// | | | |
// +-------+-------+ |
// | 200,200 |
// | |
// | Exist |
// +-------------+ 250, 250
//
// Need to split candidate into top and left pieces.
// Only do a split if there is spare room in the list -
// because both the split rectangles may need to be
// added to the list.
//
// If there is spare room, split the candidate into a
// smaller candidate on the left and a new rectangle on
// the top. Call this routine recursively to handle
// the new rectangle.
//
// 151,100
// 100,100 +-------+-------+
// | | |
// | | New |
// | | |200,149
// | +-------+-----+
// | Cand |150,150 |
// | | |
// +-------+ Exist |
// 150,200| |
// | |
// | |
// +-------------+ 250,250
TRC_DBG((TB, "OL_SPLIT_LEFT_TOP - %d", iExist));
if (pddShm->ba.rectsUsed < BA_MAX_ACCUMULATED_RECTS &&
level < ADDR_RECURSE_LIMIT) {
if (((pCand->right -
pddShm->ba.bounds[iExist].coord.left) *
(pCand->bottom -
pddShm->ba.bounds[iExist].coord.top)) >
MIN_OVERLAP_BYTES) {
rectNew.left =
pddShm->ba.bounds[iExist].coord.left;
rectNew.right = pCand->right;
rectNew.top = pCand->top;
rectNew.bottom =
pddShm->ba.bounds[iExist].coord.top;
pCand->right =
pddShm->ba.bounds[iExist].coord.left;
TRC_DBG((TB, "*** RECURSION ***"));
BAAddRect(&rectNew, level);
TRC_DBG((TB, "*** RETURN ***"));
if (!fRectToAdd &&
!pddShm->ba.bounds[iLastMerge].inUse) {
TRC_DBG((TB, "FINISHED - %d", iLastMerge));
DC_QUIT;
}
// After the recursion, because the candidate has
// changed, restart the comparisons to check for
// overlaps between the adjusted candidate and
// other existing rectangles.
fResetRects = TRUE;
}
}
break;
case OL_SPLIT_RIGHT_TOP:
// The existing overlaps the candidate, but neither can
// be merged or adjusted. Split into top and right
// pieces if there is room in the list, similar to
// OL_SPLIT_LEFT_TOP above.
TRC_DBG((TB, "OL_SPLIT_RIGHT_TOP - %d", iExist));
if (pddShm->ba.rectsUsed < BA_MAX_ACCUMULATED_RECTS &&
level < ADDR_RECURSE_LIMIT) {
if (((pddShm->ba.bounds[iExist].coord.right -
pCand->left) * (pCand->bottom -
pddShm->ba.bounds[iExist].coord.top)) >
MIN_OVERLAP_BYTES) {
rectNew.left = pCand->left;
rectNew.right =
pddShm->ba.bounds[iExist].coord.right;
rectNew.top = pCand->top;
rectNew.bottom =
pddShm->ba.bounds[iExist].coord.top;
pCand->left =
pddShm->ba.bounds[iExist].coord.right;
TRC_DBG((TB, "*** RECURSION ***"));
BAAddRect(&rectNew, level);
TRC_DBG((TB, "*** RETURN ***"));
if (!fRectToAdd &&
!pddShm->ba.bounds[iLastMerge].inUse) {
TRC_DBG((TB, "FINISHED - %d", iLastMerge));
DC_QUIT;
}
// After the recursion, because the candidate has
// changed, restart the comparisons to check for
// overlaps between the adjusted candidate and
// other existing rectangles.
fResetRects = TRUE;
}
}
break;
case OL_SPLIT_LEFT_BOTTOM:
// The existing overlaps the candidate, but neither can
// be merged or adjusted. Split into left and bottom
// pieces if there is room in the list, similar to
// OL_SPLIT_LEFT_TOP above.
TRC_DBG((TB, "OL_SPLIT_LEFT_BOTTOM - %d", iExist));
if (pddShm->ba.rectsUsed < BA_MAX_ACCUMULATED_RECTS &&
level < ADDR_RECURSE_LIMIT) {
if (((pCand->right -
pddShm->ba.bounds[iExist].coord.left) *
(pddShm->ba.bounds[iExist].coord.bottom -
pCand->top)) > MIN_OVERLAP_BYTES) {
rectNew.left =
pddShm->ba.bounds[iExist].coord.left;
rectNew.right = pCand->right;
rectNew.top =
pddShm->ba.bounds[iExist].coord.bottom;
rectNew.bottom = pCand->bottom;
pCand->right =
pddShm->ba.bounds[iExist].coord.left;
TRC_DBG((TB, "*** RECURSION ***"));
BAAddRect(&rectNew, level);
TRC_DBG((TB, "*** RETURN ***"));
if (!fRectToAdd &&
!pddShm->ba.bounds[iLastMerge].inUse) {
TRC_DBG((TB, "FINISHED - %d", iLastMerge));
DC_QUIT;
}
// After the recursion, because the candidate has
// changed, restart the comparisons to check for
// overlaps between the adjusted candidate and
// other existing rectangles.
fResetRects = TRUE;
}
}
break;
case OL_SPLIT_RIGHT_BOTTOM:
// The existing overlaps the candidate, but neither can
// be merged or adjusted. Split into right and bottom
// pieces if there is room in the list, similar to
// OL_SPLIT_LEFT_TOP above.
TRC_DBG((TB, "OL_SPLIT_RIGHT_BOTTOM - %d", iExist));
if (pddShm->ba.rectsUsed < BA_MAX_ACCUMULATED_RECTS &&
level < ADDR_RECURSE_LIMIT) {
if (((pddShm->ba.bounds[iExist].coord.right -
pCand->left) *
(pddShm->ba.bounds[iExist].coord.bottom -
pCand->top)) > MIN_OVERLAP_BYTES) {
rectNew.left = pCand->left;
rectNew.right =
pddShm->ba.bounds[iExist].coord.right;
rectNew.top =
pddShm->ba.bounds[iExist].coord.bottom;
rectNew.bottom = pCand->bottom;
pCand->left =
pddShm->ba.bounds[iExist].coord.right;
TRC_DBG((TB, "*** RECURSION ***"));
BAAddRect(&rectNew, level);
TRC_DBG((TB, "*** RETURN ***"));
if (!fRectToAdd &&
!pddShm->ba.bounds[iLastMerge].inUse) {
TRC_DBG((TB, "FINISHED - %d", iLastMerge));
DC_QUIT;
}
// After the recursion, because the candidate has
// changed, restart the comparisons to check for
// overlaps between the adjusted candidate and
// other existing rectangles.
fResetRects = TRUE;
}
}
break;
default:
TRC_ERR((TB, "Unrecognised overlap case-%d",
overlapType));
break;
}
iExist = (!fResetRects) ? pddShm->ba.bounds[iExist].iNext :
pddShm->ba.firstRect;
}
// Arriving here means that no overlap was found between the
// candidate and the existing rectangles.
// - If the candidate is the original rectangle, add it to the list.
// - If the candidate is an existing rectangle, it is already in
// the list.
if (fRectToAdd)
BAAddRectList(pCand);
// The compare and add processing above is allowed to add a
// rectangle to the list when there are already BA_MAX_NUM_RECTS
// (eg. when doing a split or when there is no overlap at all with
// the existing rectangles) - there is an extra slot for that purpose.
// If we now have more than BA_MAX_NUM_RECTS rectangles, do a
// forced merge, so that the next call to this routine has a spare
// slot.
fRectMerged = (pddShm->ba.rectsUsed > BA_MAX_ACCUMULATED_RECTS);
if (fRectMerged) {
// Start looking for merged rectangles. For each rectangle in the
// list, compare it with the others, and Determine cost of
// merging. We want to merge the two rectangles with the minimum
// area difference, ie that will produce a merged rectangle that
// covers the least superfluous screen area.
bestMergeIncrease = 0x7FFFFFFF;
// Recalculate the real areas of the current rectangles. We
// cannot rely upon the current area value since the rect
// edges may have changed during a merge and the area not
// recalculated.
for (iExist = pddShm->ba.firstRect;
iExist != BA_INVALID_RECT_INDEX;
iExist = pddShm->ba.bounds[iExist].iNext)
BARecalcArea(iExist);
for (iExist = pddShm->ba.firstRect;
iExist != BA_INVALID_RECT_INDEX;
iExist = pddShm->ba.bounds[iExist].iNext) {
for (iTmp = pddShm->ba.bounds[iExist].iNext;
iTmp != BA_INVALID_RECT_INDEX;
iTmp = pddShm->ba.bounds[iTmp].iNext) {
rectNew.left = min(pddShm->ba.bounds[iExist].coord.left,
pddShm->ba.bounds[iTmp].coord.left );
rectNew.top = min(pddShm->ba.bounds[iExist].coord.top,
pddShm->ba.bounds[iTmp].coord.top );
rectNew.right = max(pddShm->ba.bounds[iExist].coord.right,
pddShm->ba.bounds[iTmp].coord.right );
rectNew.bottom = max(pddShm->ba.bounds[iExist].coord.bottom,
pddShm->ba.bounds[iTmp].coord.bottom );
mergeIncrease = COM_SIZEOF_RECT(rectNew) -
pddShm->ba.bounds[iExist].area -
pddShm->ba.bounds[iTmp].area;
if (bestMergeIncrease > mergeIncrease) {
iBestMerge1 = iExist;
iBestMerge2 = iTmp;
bestMergeIncrease = mergeIncrease;
}
}
}
// Now do the merge.
// We recalculate the size of the merged rectangle here -
// alternatively we could remember the size of the best so far
// in the loop above. The trade off is between calculating
// twice or copying at least once but probably more than once
// as we find successively better merges.
TRC_DBG((TB, "Merge ix %d, (%d,%d,%d,%d)", iBestMerge1,
pddShm->ba.bounds[iBestMerge1].coord.left,
pddShm->ba.bounds[iBestMerge1].coord.top,
pddShm->ba.bounds[iBestMerge1].coord.right,
pddShm->ba.bounds[iBestMerge1].coord.bottom ));
TRC_DBG((TB, "Merge ix %d, (%d,%d,%d,%d)", iBestMerge2,
pddShm->ba.bounds[iBestMerge2].coord.left,
pddShm->ba.bounds[iBestMerge2].coord.top,
pddShm->ba.bounds[iBestMerge2].coord.right,
pddShm->ba.bounds[iBestMerge2].coord.bottom ));
pddShm->ba.bounds[iBestMerge1].coord.left =
min(pddShm->ba.bounds[iBestMerge1].coord.left,
pddShm->ba.bounds[iBestMerge2].coord.left);
pddShm->ba.bounds[iBestMerge1].coord.top =
min(pddShm->ba.bounds[iBestMerge1].coord.top,
pddShm->ba.bounds[iBestMerge2].coord.top );
pddShm->ba.bounds[iBestMerge1].coord.right =
max(pddShm->ba.bounds[iBestMerge1].coord.right,
pddShm->ba.bounds[iBestMerge2].coord.right);
pddShm->ba.bounds[iBestMerge1].coord.bottom =
max(pddShm->ba.bounds[iBestMerge1].coord.bottom,
pddShm->ba.bounds[iBestMerge2].coord.bottom);
// Remove the second best merge.
BARemoveRectList(iBestMerge2);
// The best merged rectangle becomes the candidate, and we fall
// back to the head of the comparison loop to start again.
pCand = &(pddShm->ba.bounds[iBestMerge1].coord);
iLastMerge = iBestMerge1;
fRectToAdd = FALSE;
}
} while (fRectMerged);
DC_EXIT_POINT:
DC_END_FN();
return rc;
}
#ifdef DC_DEBUG
/****************************************************************************/
// BAPerformUnitTests
//
// Verifies some basic assumptions of the BA algorithms, in case it breaks
// while being changed. Performed only on init.
/****************************************************************************/
// Test data. Note that these tests are designed for BA using exclusive rects.
typedef struct
{
const char *TestName;
const RECTL *MustBePresent;
unsigned NumPresent;
} Validation;
// Start with a rect out in the middle area.
const RECTL Test1StartAdd = { 10, 10, 20, 20 };
const Validation Test1StartVerify = { "Test1Start", &Test1StartAdd, 1 };
// Add a rect of the same height 2 pixels off the right edge of rect1.
// Should not merge with rect1.
const RECTL Test1Step1Add = { 21, 10, 31, 20 };
const RECTL Test1Step1Ver[2] = { { 10, 10, 20, 20 }, { 21, 10, 31, 20 } };
const Validation Test1Step1Verify = { "Test1Step1", Test1Step1Ver, 2 };
// Add a rect of the same width immediately left of but not intersecting
// with rect1. Should be merged into one large rect.
const RECTL Test1Step2Add = { 0, 10, 10, 20 };
const RECTL Test1Step2Ver[2] = { { 0, 10, 20, 20 }, { 21, 10, 31, 20 } };
const Validation Test1Step2Verify = { "Test1Step2", Test1Step2Ver, 2 };
// Add a rect of the same width intersecting rect2 at its top.
// Should be merged into one large rect.
const RECTL Test1Step3Add = { 21, 5, 31, 15 };
const RECTL Test1Step3Ver[2] = { { 0, 10, 20, 20 }, { 21, 5, 31, 20 } };
const Validation Test1Step3Verify = { "Test1Step3", Test1Step3Ver, 2 };
// Add a rect that intersects rect1 at its bottom but is smaller in
// width and partially enclosed within rect1. Rect should be split,
// the top half absorbed into rect1, the bottom a new rect.
const RECTL Test1Step4Add = { 5, 15, 10, 25 };
const RECTL Test1Step4Ver[3] = {
{ 0, 10, 20, 20 }, { 21, 5, 31, 20 }, { 5, 20, 10, 25 }
};
const Validation Test1Step4Verify = { "Test1Step4", Test1Step4Ver, 3 };
// Worker func to verify the contents of the rects. Returns FALSE if
// the
void AddAndValidateRects(const RECTL *pAdd, const Validation *pVal)
{
BOOL rc = TRUE;
RECTL Rects[BA_MAX_ACCUMULATED_RECTS];
unsigned i, j;
unsigned NumRects;
BYTE RectFound[BA_MAX_ACCUMULATED_RECTS] = { 0 };
RECTL Add;
DC_BEGIN_FN("AddAndValidateRects");
// Make a copy of *pAdd since BAAddRect can modify it.
Add = *pAdd;
BAAddRect(&Add, 0);
BACopyBounds(Rects, &NumRects);
TRC_ASSERT((NumRects == pVal->NumPresent),
(TB,"%s failure: NumRects=%u, should be %u", pVal->TestName,
NumRects, pVal->NumPresent));
for (i = 0; i < NumRects; i++) {
for (j = 0; j < NumRects; j++) {
if (!memcmp(&Rects[i], &pVal->MustBePresent[j], sizeof(RECTL)))
RectFound[j] = TRUE;
}
}
for (i = 0; i < NumRects; i++) {
if (!RectFound[i]) {
TRC_ERR((TB,"BA validation error, rects:"));
for (j = 0; j < NumRects; j++)
TRC_ERR((TB," %u: (%u,%u,%u,%u)", j, Rects[j].left,
Rects[j].top, Rects[j].right, Rects[j].bottom));
TRC_ASSERT((RectFound[i]),(TB,"%s failure: MustBePresent rect %u "
"was not present", pVal->TestName, i));
}
}
DC_END_FN();
}
// The real function.
void BAPerformUnitTests()
{
RECTL Rect1, Rect2;
DC_BEGIN_FN("BAPerformUnitTests");
// Test1.
AddAndValidateRects(&Test1StartAdd, &Test1StartVerify);
AddAndValidateRects(&Test1Step1Add, &Test1Step1Verify);
AddAndValidateRects(&Test1Step2Add, &Test1Step2Verify);
AddAndValidateRects(&Test1Step3Add, &Test1Step3Verify);
AddAndValidateRects(&Test1Step4Add, &Test1Step4Verify);
}
#endif // DC_DEBUG