windows-nt/Source/XPSP1/NT/enduser/netmeeting/as/dd/ba.c

2509 lines
100 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "precomp.h"
//
// BA.C
// Bounds Accumulation, disply driver side
//
// Copyright(c) Microsoft 1997-
//
//
//
// BA_DDProcessRequest() - see ba.h
//
//
BOOL BA_DDProcessRequest
(
DWORD fnEscape,
LPOSI_ESCAPE_HEADER pRequest,
DWORD cbRequest,
LPOSI_ESCAPE_HEADER pResult,
DWORD cbResult
)
{
BOOL rc = TRUE;
LPBA_BOUNDS_INFO pBoundsInfo;
UINT i;
RECT rect;
DebugEntry(BA_DDProcessRequest);
if ((cbRequest != sizeof(BA_BOUNDS_INFO)) ||
(cbResult != sizeof(BA_BOUNDS_INFO)))
{
ERROR_OUT(("BA_DDProcessRequest: Invalid sizes %d, %d for BA_ESC", cbRequest, cbResult));
rc = FALSE;
DC_QUIT;
}
switch (fnEscape)
{
case BA_ESC_GET_BOUNDS:
{
//
// The share core is calling us to get the current bounds
// (presumably to try to send them). While the share core is
// processing the bounds, we reset the bounds, but take a copy
// first to use for spoiling orders by SDA. When the share
// core has completed processing the bounds, it will call us
// again with a BA_ESC_RETURN_BOUNDS escape (even if it has
// sent all the bounds).
//
// So, we have to:
// - return the bounds to the share core
// - set up the spoiling rects to be these bounds
// - clear our main bounds.
//
//
// This will copy the current bounds to the caller's buffer and
// clear our current bounds.
// NOTE: We keep these in globals because the caller will shortly
// call us to return any unsent bounds rects.
//
BA_CopyBounds(g_baSpoilingRects, &g_baNumSpoilingRects, TRUE);
//
// Return the bounds info to the share core
//
TRACE_OUT(( "Returning %d rects to share core", g_baNumSpoilingRects));
pBoundsInfo = (LPBA_BOUNDS_INFO)pResult;
pBoundsInfo->numRects = g_baNumSpoilingRects;
for (i = 0; i < g_baNumSpoilingRects; i++)
{
RECT_TO_RECTL(&g_baSpoilingRects[i], &pBoundsInfo->rects[i]);
}
}
break;
case BA_ESC_RETURN_BOUNDS:
{
//
// The share core has completed its processing of the bounds
// which we passed on the BA_ESC_GET_BOUNDS escape. We have to
// reset the spoiling rectangles and add any bounds which the
// share core failed to process into our current bounds.
//
//
// To reset the spoiling bounds we just have to reset the
// number of bounds.
//
g_baNumSpoilingRects = 0;
//
// Now add the share core's bounds into our current bounds
//
pBoundsInfo = (LPBA_BOUNDS_INFO)pRequest;
TRACE_OUT(( "Received %d rects from share core",
pBoundsInfo->numRects));
for (i = 0 ; i < pBoundsInfo->numRects ; i++)
{
RECTL_TO_RECT(&pBoundsInfo->rects[i], &rect);
TRACE_OUT(( "Rect %d, {%d, %d, %d, %d}",
i, rect.left, rect.top, rect.right, rect.bottom));
BA_AddScreenData(&rect);
}
}
break;
default:
{
ERROR_OUT(( "Unrecognised BA escape"));
rc = FALSE;
}
break;
}
DC_EXIT_POINT:
DebugExitBOOL(BA_DDProcessRequest, rc);
return(rc);
}
//
// BA_DDInit - see ba.h for description.
//
void BA_DDInit(void)
{
DebugEntry(BA_DDInit);
BA_ResetBounds();
DebugExitVOID(BA_DDInit);
}
//
// This gets a current version of our bound rect list, and clears it
// afterwards if requested.
//
void BA_CopyBounds
(
LPRECT pRects,
LPUINT pNumRects,
BOOL fReset
)
{
UINT i;
#ifdef DEBUG
UINT cRects = 0;
#endif
DebugEntry(BA_CopyBounds);
if (*pNumRects = g_baRectsUsed)
{
//
// Return the bounds that have been accumulated.
//
TRACE_OUT(( "num rects : %d", g_baRectsUsed));
//
// We can return the bounds in any order - we don't care how we
// order the SDA rectangles.
//
// Also note that we must compare BA_NUM_RECTS + 1 sets of
// rectangles because that's the number actually used by the add
// rectangle code and while it guarantees that it will only use
// BA_NUM_RECTS rectangles, it does not guarantee that the last
// element in the array is the merge rectangle.
//
for (i = 0; i <= BA_NUM_RECTS; i++)
{
if (g_baBounds[i].InUse)
{
TRACE_OUT(("Found rect: {%04d,%04d,%04d,%04d}",
g_baBounds[i].Coord.left, g_baBounds[i].Coord.top,
g_baBounds[i].Coord.right, g_baBounds[i].Coord.bottom));
*pRects = g_baBounds[i].Coord;
pRects++;
#ifdef DEBUG
cRects++;
#endif
}
}
//
// Check for self-consistency
//
ASSERT(cRects == *pNumRects);
if (fReset)
BA_ResetBounds();
}
DebugExitVOID(BACopyBounds);
}
//
//
// BA_AddScreenData(..)
//
// Adds the specified rectangle to the current Screen Data Area.
//
// Called by the GDI interception code for orders that it cannot send as
// orders.
//
// NOTE that the rectangle is INCLUSIVE coords
//
//
void BA_AddScreenData(LPRECT pRect)
{
RECT preRects[BA_NUM_RECTS];
RECT postRects[BA_NUM_RECTS];
UINT numPreRects;
UINT numPostRects;
UINT i;
DebugEntry(BA_AddScreenData);
//
// Check that the caller has passed a valid rectangle. If not, do a
// trace alert, and then return immediately (as an invalid rectangle
// shouldn't contribute to the accumulated bounds) - but report an OK
// return code, so we keep running.
//
if ((pRect->right < pRect->left) ||
(pRect->bottom < pRect->top) )
{
WARNING_OUT(( "Invalid Add Rect (%d,%d,%d,%d)",
pRect->left,
pRect->top,
pRect->right,
pRect->bottom ));
DC_QUIT;
}
if ((g_oaFlow == OAFLOW_SLOW) && g_baSpoilByNewSDAEnabled)
{
//
// We are spoiling existing orders by new SDA, so query the current
// bounds.
//
BA_CopyBounds(preRects, &numPreRects, FALSE);
}
//
// Add the rect to the bounds.
//
if (BAAddRect(pRect, 0))
{
if ((pRect->right > pRect->left) && (pRect->bottom > pRect->top))
{
LPBA_FAST_DATA lpbaFast;
lpbaFast = BA_FST_START_WRITING;
SHM_CheckPointer(lpbaFast);
lpbaFast->totalSDA += COM_SizeOfRectInclusive(pRect);
TRACE_OUT(("Added rect to bounds, giving %ld of SD", lpbaFast->totalSDA));
//
// This is where the Win95 product would make a call to
// DCS_TriggerEarlyTimer
//
BA_FST_STOP_WRITING;
}
if ((g_oaFlow == OAFLOW_SLOW) && g_baSpoilByNewSDAEnabled)
{
//
// Adding the new rectangle changed the existing bounds so
// query the new bounds
//
BA_CopyBounds(postRects, &numPostRects, FALSE);
//
// Try to spoil existing orders using each of the rectangles
// which have changed.
//
for (i = 0; i < numPostRects; i++)
{
if ( (i > numPreRects) ||
(postRects[i].left != preRects[i].left) ||
(postRects[i].right != preRects[i].right) ||
(postRects[i].top != preRects[i].top) ||
(postRects[i].bottom != preRects[i].bottom) )
{
OA_DDSpoilOrdersByRect(&postRects[i]);
}
}
}
}
DC_EXIT_POINT:
DebugExitVOID(BA_AddScreenData);
}
//
//
// BA_QuerySpoilingBounds() - see ba.h
//
//
void BA_QuerySpoilingBounds(LPRECT pRects, LPUINT pNumRects)
{
DebugEntry(BA_QuerySpoilingBounds);
//
// Just have to return the number of spoiling rectangles, and the
// rectangles themselves. No rectangles is perfectly valid.
//
TRACE_OUT(( "Num rects %d", g_baNumSpoilingRects));
*pNumRects = g_baNumSpoilingRects;
memcpy(pRects, g_baSpoilingRects, g_baNumSpoilingRects*sizeof(RECT));
DebugExitVOID(BA_QuerySpoilingBounds);
}
void BA_ResetBounds(void)
{
UINT i;
DebugEntry(BA_ResetBounds);
//
// Clear the bounds - reset the number we are using, mark all slots as
// free, and clean the list.
//
for ( i = 0; i <= BA_NUM_RECTS; i++ )
{
g_baBounds[i].InUse = FALSE;
g_baBounds[i].iNext = BA_INVALID_RECT_INDEX;
}
g_baFirstRect = BA_INVALID_RECT_INDEX;
g_baLastRect = BA_INVALID_RECT_INDEX;
g_baRectsUsed = 0;
DebugExitVOID(BA_ResetBounds);
}
//
// Name: BAOverlap
//
// Description: Detects overlap between two rectangles.
//
// - check for no overlap using loose test that lets through
// adjacent/overlapping merges
// - check for adjacent/overlapping merges
// - check for no overlap (using strict test)
// - use outcodes to check internal edge cases
// - use outcodes to check external edge cases
//
// If at each stage the check detects that the two rectangles
// meet the criteria, the function returns the appropriate
// return or outcode combination.
//
// Note that all rectangle coordinates are inclusive, ie
// a rectangle of 0,0,0,0 has an area of 1 pel.
//
// This function does not alter either of the rectangles.
//
// Params (IN): pRect1 - first rectangle
// pRect2 - second rectangle
//
// Returns: One of the overlap return codes or outcode combinations
// defined above.
//
//
int BAOverlap(LPRECT pRect1, LPRECT pRect2 )
{
int ExternalEdges;
int ExternalCount;
int InternalEdges;
int InternalCount;
//
// Check for no overlap.
//
// Note that this test is looser than strict no overlap, and will let
// through rectangles that do not overlap, but just abutt by one pel -
// so that we get a chance to detect adjacent merges.
//
// So (for example) for the following:
//
// - it detects no overlap when there is at least 1 pel between rects
//
// 10,10 52,10
// +----------++----------+
// | || |
// | || |
// | || |
// | Rect 1 || Rect 2 |
// | || |
// | || |
// | || |
// +----------++----------+
// 50,50 100,50
//
// - it allows rectangles through when they abutt and are mergable
//
// 10,10 51,10
// +----------++----------+
// | || |
// | || |
// | || |
// | Rect 1 || Rect 2 |
// | || |
// | || |
// | || |
// +----------++----------+
// 50,50 100,50
//
// - it allows rectangles through when they abutt, even where they are
// not mergable
//
// 10,10
// +----------+51,15
// | |+----------+
// | || |
// | || |
// | Rect 1 || |
// | || Rect 2 |
// | || |
// | || |
// +----------+| |
// 50,50+----------+
// 100,55
//
// - it allows rectangles through when they overlap in some way
//
// 40,0
// +------------+
// 10,10 | |
// +-------+--+ |
// | | | |
// | | | Rect 2 |
// | | | |
// |Rect 1 | | |
// | | | |
// | +--+---------+
// | | 90,40
// +----------+
// 50,50
//
//
if (!((pRect1->left <= pRect2->right + 1) &&
(pRect1->top <= pRect2->bottom + 1) &&
(pRect1->right >= pRect2->left - 1) &&
(pRect1->bottom >= pRect2->top - 1) ))
{
return(OL_NONE);
}
//
// Check for adjoining/overlapping rectangles which can be merged.
//
// These tests detect (for example for the XMAX variant), where:
//
// - the rectangles abutt and can be merged
//
// 10,10 51,10
// +----------++----------+
// | || |
// | || |
// | || |
// | Rect 1 || Rect 2 |
// | || |
// | || |
// | || |
// +----------++----------+
// 50,50 100,50
//
// - the rectangles overlap and can be merged
//
// 10,10 40,10
// +-------+--+------+
// | | | |
// | | | |
// | | | |
// |Rect 1 | |Rect 2|
// | | | |
// | | | |
// | | | |
// +-------+--+------+
// 50,50 90,50
//
// - the rectangles abutt and cannot be merged - this case is detected
// by the strict overlap case below
//
// 10,10
// +----------+51,15
// | |+----------+
// | || |
// | || |
// | Rect 1 || |
// | || Rect 2 |
// | || |
// | || |
// +----------+| |
// 50,50+----------+
// 100,55
//
// - the rectangles overlap and cannot be merged - this case is
// detected by the outcode tests below
//
// 40,0
// +------------+
// 10,10 | |
// +-------+--+ |
// | | | |
// | | | Rect 2 |
// | | | |
// |Rect 1 | | |
// | | | |
// | +--+---------+
// | | 90,40
// +----------+
// 50,50
//
// - rectangle 2 is enclosed in rectangle 1 and should not be merged -
// this case is detected by the outcode tests below.
//
// 10,10 40,10
// +-------+------+-----+
// | | | |
// | | | |
// | | | |
// |Rect 1 |Rect 2| |
// | | | |
// | | | |
// | | | |
// +-------+------+-----+
// 60,50 90,50
// Rect2 Rect1
//
//
if ( (pRect1->left <= pRect2->right + 1) &&
(pRect1->left > pRect2->left ) &&
(pRect1->right > pRect2->right ) &&
(pRect1->top == pRect2->top ) &&
(pRect1->bottom == pRect2->bottom ) )
{
return(OL_MERGE_XMIN);
}
if ( (pRect1->top <= pRect2->bottom + 1) &&
(pRect1->top > pRect2->top ) &&
(pRect1->bottom > pRect2->bottom ) &&
(pRect1->left == pRect2->left ) &&
(pRect1->right == pRect2->right ) )
{
return(OL_MERGE_YMIN);
}
if ( (pRect1->right >= pRect2->left - 1) &&
(pRect1->right < pRect2->right ) &&
(pRect1->left < pRect2->left ) &&
(pRect1->top == pRect2->top ) &&
(pRect1->bottom == pRect2->bottom ) )
{
return(OL_MERGE_XMAX);
}
if ( (pRect1->bottom >= pRect2->top - 1) &&
(pRect1->bottom < pRect2->bottom ) &&
(pRect1->top < pRect2->top ) &&
(pRect1->left == pRect2->left ) &&
(pRect1->right == pRect2->right ) )
{
return(OL_MERGE_YMAX);
}
//
// Check for no overlap.
// Note that this test is a stricter version than the earlier one, so
// that we now only continue testing rectangles that do genuinely
// overlap.
//
if (!((pRect1->left <= pRect2->right) &&
(pRect1->top <= pRect2->bottom) &&
(pRect1->right >= pRect2->left) &&
(pRect1->bottom >= pRect2->top) ))
{
return(OL_NONE);
}
//
// Use outcodes for Internal edge cases, as follows:
//
// EE_XMIN - rect1 xmin is enclosed within rect2
// EE_YMIN - rect1 ymin is enclosed within rect2
// EE_XMAX - rect1 xmax is enclosed within rect2
// EE_YMAX - rect1 ymax is enclosed within rect2
//
// If 3 or more bits are set then rect1 is enclosed either partially or
// completely within rect2 as follows (see individual switch cases for
// diagrams).
//
// OL_ENCLOSED = EE_XMIN | EE_YMIN | EE_XMAX | EE_YMAX
// OL_PART_ENCLOSED_XMIN = EE_XMIN | EE_YMIN | EE_YMAX
// OL_PART_ENCLOSED_YMIN = EE_XMIN | EE_YMIN | EE_XMAX
// OL_PART_ENCLOSED_XMAX = EE_YMIN | EE_XMAX | EE_YMAX
// OL_PART_ENCLOSED_YMAX = EE_XMIN | EE_XMAX | EE_YMAX
//
// In practice, if 3 or more bits are set, 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)
{
InternalEdges |= EE_XMIN;
InternalCount ++;
}
if ( pRect1->top >= pRect2->top && pRect1->top <= pRect2->bottom)
{
InternalEdges |= EE_YMIN;
InternalCount ++;
}
if ( pRect1->right >= pRect2->left && pRect1->right <= pRect2->right)
{
InternalEdges |= EE_XMAX;
InternalCount ++;
}
if ( pRect1->bottom >= pRect2->top && pRect1->bottom <= pRect2->bottom)
{
InternalEdges |= EE_YMAX;
InternalCount ++;
}
if ( InternalCount >= 3)
{
return(-InternalEdges);
}
//
// Use outcodes for External edge cases as follows.
//
// EE_XMIN - rect1 xmin is left of rect2 xmin
// EE_YMIN - rect1 ymin is above rect2 ymin
// EE_XMAX - rect1 xmax is right of rect2 xmax
// EE_YMAX - rect1 ymax is below rect2 ymax
//
// These are the classic "line" outcodes.
//
// If 2 or more bits are set then rect1 overlaps rect2 as follows (see
// individual switch cases for diagrams).
//
// OL_ENCLOSES = EE_XMIN | EE_YMIN | EE_XMAX | EE_YMAX
// OL_PART_ENCLOSES_XMIN = EE_YMIN | EE_XMAX | EE_YMAX
// OL_PART_ENCLOSES_XMAX = EE_XMIN | EE_YMIN | EE_YMAX
// OL_PART_ENCLOSES_YMIN = EE_XMIN | EE_XMAX | EE_YMAX
// OL_PART_ENCLOSES_YMAX = EE_XMIN | EE_YMIN | EE_XMAX
// OL_SPLIT_X = EE_YMIN | EE_YMAX
// OL_SPLIT_Y = EE_XMIN | EE_XMAX
// OL_SPLIT_XMIN_YMIN = EE_XMAX | EE_YMAX
// OL_SPLIT_XMAX_YMIN = EE_XMIN | EE_YMAX
// OL_SPLIT_XMIN_YMAX = EE_YMIN | EE_XMAX
// OL_SPLIT_XMAX_YMAX = EE_XMIN | EE_YMIN
//
// The accumulated outcode value is returned.
//
//
ExternalEdges = 0;
ExternalCount = 0;
if ( pRect1->left <= pRect2->left )
{
ExternalEdges |= EE_XMIN;
ExternalCount ++;
}
if ( pRect1->top <= pRect2->top )
{
ExternalEdges |= EE_YMIN;
ExternalCount ++;
}
if ( pRect1->right >= pRect2->right )
{
ExternalEdges |= EE_XMAX;
ExternalCount ++;
}
if ( pRect1->bottom >= pRect2->bottom )
{
ExternalEdges |= EE_YMAX;
ExternalCount ++;
}
if (ExternalCount >= 2)
{
return(ExternalEdges);
}
//
// If get here then we failed to detect a valid case.
//
WARNING_OUT(( "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 ));
return(OL_NONE);
}
//
// Name: BAAddRectList
//
// Description: Adds a rectangle to the list of accumulated rectangles.
//
// - find a free slot in the array
// - add slot record to list
// - fill slot record with rect and mark as in use.
//
// Params (IN): pRect - rectangle to add
//
// Returns:
//
//
void BAAddRectList(LPRECT pRect)
{
UINT i;
BOOL fFoundFreeSlot;
DebugEntry(BAAddRectList);
//
// Find a free slot in the array. Note that the loop searches to
// BA_NUM_RECTS+1, because:
//
// - the array is defined as having one more slot than BA_NUM_RECTS
//
// - we may need to add a rect in that slot when BA_NUM_RECTS are
// in use prior to a forced merge.
//
fFoundFreeSlot = FALSE;
for ( i = 0; i <= BA_NUM_RECTS; i++ )
{
if (!g_baBounds[i].InUse)
{
fFoundFreeSlot = TRUE;
break;
}
}
if (!fFoundFreeSlot)
{
WARNING_OUT(( "No space in array for rect (%d,%d,%d,%d)",
pRect->left,
pRect->top,
pRect->right,
pRect->bottom));
for ( i = 0; i <= BA_NUM_RECTS; i++ )
{
WARNING_OUT((
"Entry %i:Next(%lx),(%d,%d,%d,%d),Index(%d),InUse(%d)",
g_baBounds[i].iNext,
g_baBounds[i].Coord.left,
g_baBounds[i].Coord.top,
g_baBounds[i].Coord.right,
g_baBounds[i].Coord.bottom,
i,
g_baBounds[i].InUse));
}
DC_QUIT;
}
//
// If first rect, then set up list.
// If not, add to tail of list.
//
if (g_baRectsUsed == 0)
{
g_baFirstRect = i;
g_baLastRect = i;
}
else
{
g_baBounds[g_baLastRect].iNext = i;
g_baLastRect = i;
}
g_baBounds[i].iNext = BA_INVALID_RECT_INDEX;
//
// Fill in slot and mark as in use.
//
g_baBounds[i].InUse = TRUE;
g_baBounds[i].Coord = *pRect;
//
// Increment number of rectangles.
//
TRACE_OUT(( "Add Rect : ix - %d, (%d,%d,%d,%d)", i,
pRect->left,pRect->top,pRect->right,pRect->bottom));
g_baRectsUsed++;
DC_EXIT_POINT:
DebugExitVOID(BAAddRectList);
}
//
// Name: BA_RemoveRectList
//
// Description: Removes a rectangle from the list of accumulated
// rectangles.
//
// - find the rectangle in the list
// - unlink it from the list and mark the slot as free
//
// Params (IN): pRect - rectangle to remove
//
// Returns:
//
//
void BA_RemoveRectList(LPRECT pRect)
{
UINT i;
UINT j;
DebugEntry(BA_RemoveRectList);
//
// If rectangle to remove is first...
// Remove it by adjusting first pointer and mark as free.
// Note that the check for tail adjustment has to be done before we
// change first.
//
if ( g_baBounds[g_baFirstRect].Coord.left == pRect->left &&
g_baBounds[g_baFirstRect].Coord.top == pRect->top &&
g_baBounds[g_baFirstRect].Coord.right == pRect->right &&
g_baBounds[g_baFirstRect].Coord.bottom == pRect->bottom )
{
TRACE_OUT(( "Remove first"));
if (g_baFirstRect == g_baLastRect)
{
g_baLastRect = BA_INVALID_RECT_INDEX;
}
g_baBounds[g_baFirstRect].InUse = FALSE;
g_baFirstRect = g_baBounds[g_baFirstRect].iNext;
}
//
// If rectangle to remove is not first...
// Find it in list, remove it by adjusting previous pointer and mark it
// as free.
// Note that the check for tail adjustment has to be done before we
// change the previous pointer.
//
else
{
TRACE_OUT(( "Remove not first"));
for ( j = g_baFirstRect;
g_baBounds[j].iNext != BA_INVALID_RECT_INDEX;
j = g_baBounds[j].iNext )
{
if ( (g_baBounds[g_baBounds[j].iNext].Coord.left == pRect->left) &&
(g_baBounds[g_baBounds[j].iNext].Coord.top == pRect->top) &&
(g_baBounds[g_baBounds[j].iNext].Coord.right == pRect->right) &&
(g_baBounds[g_baBounds[j].iNext].Coord.bottom == pRect->bottom) )
{
break;
}
}
if (j == BA_INVALID_RECT_INDEX)
{
WARNING_OUT(( "Couldn't remove rect (%d,%d,%d,%d)",
pRect->left,
pRect->top,
pRect->right,
pRect->bottom ));
for ( i = 0; i <= BA_NUM_RECTS; i++ )
{
WARNING_OUT((
"Entry %i:Next(%lx),(%d,%d,%d,%d),Index(%d),InUse(%d)",
g_baBounds[i].iNext,
g_baBounds[i].Coord.left,
g_baBounds[i].Coord.top,
g_baBounds[i].Coord.right,
g_baBounds[i].Coord.bottom,
i,
g_baBounds[i].InUse));
}
return;
}
if (g_baBounds[j].iNext == g_baLastRect )
{
g_baLastRect = j;
}
g_baBounds[g_baBounds[j].iNext].InUse = FALSE;
g_baBounds[j].iNext = g_baBounds[g_baBounds[j].iNext].iNext;
}
//
// One less rect...
//
g_baRectsUsed--;
DebugExitVOID(BA_RemoveRectList);
}
//
// Name: BAAddRect
//
// Description: Accumulates rectangles.
//
// This is a complex routine, with the essential algorithm
// as follows.
//
// - Start with the supplied rectangle as the candidate
// rectangle.
//
// - Compare the candidate against each of the existing
// accumulated rectangles.
//
// - If some form of overlap is detected between the
// candidate and an existing rectangle, this may result in
// one of the following (see the cases of the switch for
// details):
//
// - adjust the candidate or the existing rectangle or both
// - merge the candidate into the existing rectangle
// - discard the candidate as it is enclosed by an existing
// rectangle.
//
// - 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 rectangles), invoke this routine recursively
// with one of the two candidates as its candidate.
//
// - If no overlap is detected against the existing rectangles,
// add the candidate to the list of accumulated rectangles.
//
// - If the add results in more than BA_NUM_RECTS
// accumulated rectangles, do a forced merge of two of the
// accumulate rectangles (which include the newly added
// candidate) - choosing the two rectangles where the merged
// rectangle results in the smallest increase in area over
// the two non-merged rectangles.
//
// - 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 rectangle, an adjusted
// version of that rectangle, or a merged rectangle):
//
// - does not find an overlap among the rectangles in the list
// and does not cause a forced merge
// - is discarded becuase it is enclosed within one of the
// rectangles in the list.
//
// Note that all rectangle coordinates are inclusive, ie
// a rectangle of 0,0,0,0 has an area of 1 pel.
//
// Params (IN): pCand - new candidate rectangle
// level - recursion level
//
// Returns: TRUE if rectandle was spoilt due to a complete overlap.
//
//
BOOL BAAddRect
(
LPRECT pCand,
int level
)
{
int bestMergeIncrease;
int mergeIncrease;
UINT iBestMerge1;
UINT iBestMerge2;
UINT iExist;
UINT iTmp;
BOOL fRectToAdd;
BOOL fRectMerged;
BOOL fResetRects;
RECT rectNew;
UINT iLastMerge;
int OverlapType;
BOOL rc = TRUE;
DebugEntry(BAAddRect);
//
// 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 merges will occur.
//
fRectToAdd = TRUE;
fRectMerged = FALSE;
//
// Loop until no merges occur.
//
do
{
TRACE_OUT(( "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 = g_baFirstRect;
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 ( &g_baBounds[iExist].Coord == pCand )
{
TRACE_OUT(( "OL_SAME - %d", iExist));
iExist = g_baBounds[iExist].iNext;
continue;
}
//
// Switch on the overlap type (see Overlap routine).
//
OverlapType = BAOverlap(&(g_baBounds[iExist].Coord), pCand);
switch (OverlapType)
{
case OL_NONE:
//
// No overlap.
//
TRACE_OUT(( "OL_NONE - %d", iExist));
break;
case OL_MERGE_XMIN:
//
// - either the candidate abutts the existing rectangle
// on the left
//
// 10,10 51,10
// +----------++----------+
// | || |
// | || |
// | || |
// | Cand || Exist |
// | || |
// | || |
// | || |
// +----------++----------+
// 50,50 100,50
//
// - or the candidate overlaps the existing on the left
// and can be merged
//
// 10,10 40,10
// +-------+--+------+
// | | | |
// | | | |
// | | | |
// | Cand | |Exist |
// | | | |
// | | | |
// | | | |
// +-------+--+------+
// 50,50 90,50
//
// If the candidate is the original, merge the
// candidate into the existing, and make the existing
// the new candidate.
//
// If 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.
//
// For both, start the comparisons again with the new
// candidate.
//
TRACE_OUT(( "OL_MERGE_XMIN - %d", iExist));
if ( fRectToAdd )
{
g_baBounds[iExist].Coord.left = pCand->left;
pCand = &(g_baBounds[iExist].Coord);
fRectToAdd = FALSE;
iLastMerge = iExist;
}
else
{
pCand->right = g_baBounds[iExist].Coord.right;
BA_RemoveRectList(&(g_baBounds[iExist].Coord));
}
fResetRects = TRUE;
break;
case OL_MERGE_XMAX:
//
// - either the candidate abutts the existing rectangle
// on the right
//
// 10,10 51,10
// +----------++----------+
// | || |
// | || |
// | || |
// | Exist || Cand |
// | || |
// | || |
// | || |
// +----------++----------+
// 50,50 100,50
//
// - or the candidate overlaps the existing on the right
// and can be merged
//
// 10,10 40,10
// +-------+--+------+
// | | | |
// | | | |
// | | | |
// | Exist | | Cand |
// | | | |
// | | | |
// | | | |
// +-------+--+------+
// 50,50 90,50
//
// If the candidate is the original, merge the
// candidate into the existing, and make the existing
// the new candidate.
//
// If 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.
//
// For both, start the comparisons again with the new
// candidate.
//
TRACE_OUT(( "OL_MERGE_XMAX - %d", iExist));
if ( fRectToAdd )
{
g_baBounds[iExist].Coord.right = pCand->right;
pCand = &(g_baBounds[iExist].Coord);
fRectToAdd = FALSE;
iLastMerge = iExist;
}
else
{
pCand->left = g_baBounds[iExist].Coord.left;
BA_RemoveRectList(&(g_baBounds[iExist].Coord));
}
fResetRects = TRUE;
break;
case OL_MERGE_YMIN:
//
// - either the candidate abutts the existing rectangle
// on the top
//
// 10,10
// +---------+
// | |
// | |
// | |
// | Cand |
// | |
// | |
// | |
// +---------+50,50
// 10,51+---------+
// | |
// | |
// | |
// | Exist |
// | |
// | |
// | |
// +---------+50,100
//
// - or the candidate overlaps the existing on the top
// and can be merged
//
// 10,10
// +---------+
// | |
// | |
// | |
// | Cand |
// | |
// | |
// Exist 10,40+---------+
// | |
// | |
// | |
// +---------+50,60 Cand
// | |
// | Exist |
// | |
// | |
// | |
// +---------+50,100
//
// If the candidate is the original, merge the
// candidate into the existing, and make the existing
// the new candidate.
//
// If 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.
//
// For both, start the comparisons again with the new
// candidate.
//
TRACE_OUT(( "OL_MERGE_YMIN - %d", iExist));
if ( fRectToAdd )
{
g_baBounds[iExist].Coord.top = pCand->top;
pCand = &(g_baBounds[iExist].Coord);
fRectToAdd = FALSE;
iLastMerge = iExist;
}
else
{
pCand->bottom = g_baBounds[iExist].Coord.bottom;
BA_RemoveRectList(&(g_baBounds[iExist].Coord));
}
fResetRects = TRUE;
break;
case OL_MERGE_YMAX:
//
// - either the candidate abutts the existing rectangle
// from below
//
// 10,10
// +---------+
// | |
// | |
// | |
// | Exist |
// | |
// | |
// | |
// +---------+50,50
// 10,51+---------+
// | |
// | |
// | |
// | Cand |
// | |
// | |
// | |
// +---------+50,100
//
// - or the candidate overlaps the existing from below
// and can be merged
//
// 10,10
// +---------+
// | |
// | |
// | |
// | Exist |
// | |
// | |
// Cand 10,40+---------+
// | |
// | |
// | |
// +---------+50,60 Exist
// | |
// | Cand |
// | |
// | |
// | |
// +---------+50,100
//
// If the candidate is the original, merge the
// candidate into the existing, and make the existing
// the new candidate.
//
// If 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.
//
// For both, start the comparisons again with the new
// candidate.
//
TRACE_OUT(( "OL_MERGE_YMAX - %d", iExist));
if ( fRectToAdd )
{
g_baBounds[iExist].Coord.bottom = pCand->bottom;
pCand = &(g_baBounds[iExist].Coord);
fRectToAdd = FALSE;
iLastMerge = iExist;
}
else
{
pCand->top = g_baBounds[iExist].Coord.top;
BA_RemoveRectList(&(g_baBounds[iExist].Coord));
}
fResetRects = TRUE;
break;
case OL_ENCLOSED:
//
// The existing is enclosed by the candidate.
//
// 100,100
// +----------------------+
// | Cand |
// | |
// | 130,130 |
// | +------------+ |
// | | | |
// | | | |
// | | Exist | |
// | | | |
// | +------------+ |
// | 170,170 |
// | |
// +----------------------+
// 200,200
//
// If the candidate is the original, replace the
// existing by the candidate, and make the new existing
// the new candidate.
//
// If the candidate is an existing rectangle, remove
// the other existing rectangle.
//
// For both, start the comparisons again with the new
// candidate.
//
TRACE_OUT(( "OL_ENCLOSED - %d", iExist));
if ( fRectToAdd )
{
g_baBounds[iExist].Coord = *pCand;
pCand = &(g_baBounds[iExist].Coord);
fRectToAdd = FALSE;
iLastMerge = iExist;
}
else
{
BA_RemoveRectList(&(g_baBounds[iExist].Coord));
}
fResetRects = TRUE;
break;
case OL_PART_ENCLOSED_XMIN:
//
// 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
// +----------------------+
// | |
// | |201,130
// | |+--+
// | ||E |
// | ||x |
// | Cand ||i |
// | ||s |
// | ||t |
// | || |
// | |+--+
// | | 220,170
// +----------------------+
// 200,200
//
// Note that this does not restart the comparisons.
//
TRACE_OUT(( "OL_PART_ENCLOSED_XMIN - %d", iExist));
g_baBounds[iExist].Coord.left = pCand->right + 1;
break;
case OL_PART_ENCLOSED_XMAX:
//
// The existing is partially enclosed by the candidate
// - but not on the left.
//
// 100,100
// +----------------------+
// | Cand |
// 70,130 | |
// +-----+---------------+ |
// | | | |
// | | | |
// | | Exist | |
// | | | |
// +-----+---------------+ |
// | 170,170 |
// | |
// +----------------------+
// 200,200
//
// Adjust the existing rectangle to be the non-
// overlapped portion.
//
// 100,100
// +----------------------+
// 70,130 | |
// +----+| |
// | E || |
// | x || |
// | i || Cand |
// | s || |
// | t || |
// | || |
// +----+| |
// 99,170| |
// | |
// +----------------------+
// 200,200
//
// Note that this does not restart the comparisons.
//
TRACE_OUT(( "OL_PART_ENCLOSED_XMAX - %d", iExist));
g_baBounds[iExist].Coord.right = pCand->left - 1;
break;
case OL_PART_ENCLOSED_YMIN:
//
// The existing is partially enclosed by the candidate
// - but not on the bottom.
//
// 100,100
// +----------------------+
// | Cand |
// | 130,130 |
// | +--------+ |
// | | | |
// | | Exist | |
// | | | |
// | | | |
// | | | |
// | | | |
// | | | |
// +-----+--------+-------+
// | | 200,200
// | |
// | |
// +--------+170,230
//
// Adjust the existing rectangle to be the non-
// overlapped portion.
//
//
// 100,100
// +----------------------+
// | |
// | |
// | |
// | |
// | |
// | Cand |
// | |
// | |
// | |
// | |
// +----------------------+
// 130,201+---------+ 200,200
// | |
// | Exist |
// | |
// +---------+170,230
//
// Note that this does not restart the comparisons.
//
TRACE_OUT(( "OL_PART_ENCLOSED_YMIN - %d", iExist));
g_baBounds[iExist].Coord.top = pCand->bottom + 1;
break;
case OL_PART_ENCLOSED_YMAX:
//
// The existing is partially enclosed by the candidate
// - but not on the top.
//
// 70,130
// +---------+
// | |
// | |
// 100,100 | |
// +-----+---------+------+
// | | | |
// | | | |
// | | | |
// | | | |
// | | Exist | |
// | | | |
// | | | |
// | +---------+ |
// | 170,170 |
// | |
// | Cand |
// +----------------------+
// 200,200
//
// Adjust the existing rectangle to be the non-
// overlapped portion.
//
// 70,130
// +---------+
// | |
// | Exist |
// | |
// 100,100 +---------+170,99
// +----------------------+
// | |
// | |
// | |
// | |
// | |
// | Cand |
// | |
// | |
// | |
// | |
// +----------------------+
// 200,200
//
// Note that this does not restart the comparisons.
//
TRACE_OUT(( "OL_PART_ENCLOSED_YMAX - %d", iExist));
g_baBounds[iExist].Coord.bottom = pCand->top - 1;
break;
case OL_ENCLOSES:
//
// The existing encloses the candidate.
//
// 100,100
// +----------------------+
// | Exist |
// | |
// | 130,130 |
// | +------------+ |
// | | | |
// | | | |
// | | Cand | |
// | | | |
// | | | |
// | +------------+ |
// | 170,170 |
// | |
// +----------------------+
// 200,200
//
// Just discard the candidate by exiting.
//
//
TRACE_OUT(( "OL_ENCLOSES - %d", iExist));
//
// Return FALSE indicating that the rectangle is
// already catered for by the existing bounds
//
rc= FALSE;
DC_QUIT;
break;
case OL_PART_ENCLOSES_XMIN:
//
// 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 | |
// +----+| |
// | || |
// | C || |
// | a || |
// | n || Exist |
// | d || |
// | || |
// +----+| |
// 99,170| |
// | |
// +----------------------+
// 200,200
//
// Because this affects the candidate, restart the
// comparisons to check for overlaps between the
// adjusted candidate and other existing rectangles.
//
//
TRACE_OUT(( "OL_PART_ENCLOSES_XMIN - %d", iExist));
pCand->right = g_baBounds[iExist].Coord.left - 1;
fResetRects = TRUE;
break;
case OL_PART_ENCLOSES_XMAX:
//
// The existing partially encloses the candidate - but
// not on the right.
//
// 100,100
// +----------------------+
// | Exist |
// | |
// | 130,130 |
// | +-----------------+---+
// | | | |
// | | | |
// | | Cand | |
// | | | |
// | +-----------------+---+
// | | 220,170
// | |
// +----------------------+
// 200,200
//
// Adjust the candidate rectangle to be the non-
// overlapped portion.
//
// 100,100
// +----------------------+
// | |201,130
// | |+--+
// | || |
// | ||C |
// | Exist ||a |
// | ||n |
// | ||d |
// | || |
// | |+--+
// | | 220,170
// +----------------------+
// 200,200
//
// Because this affects the candidate, restart the
// comparisons to check for overlaps between the
// adjusted candidate and other existing rectangles.
//
//
TRACE_OUT(( "OL_PART_ENCLOSES_XMAX - %d", iExist));
pCand->left = g_baBounds[iExist].Coord.right + 1;
fResetRects = TRUE;
break;
case OL_PART_ENCLOSES_YMIN:
//
// The existing partially encloses the candidate - but
// not on the top.
//
// 70,130
// +---------+
// | |
// | |
// 100,100 | |
// +-----+---------+------+
// | | | |
// | | | |
// | | Cand | |
// | | | |
// | | | |
// | +---------+ |
// | 170,170 |
// | |
// | Exist |
// +----------------------+
// 200,200
//
// Adjust the candidate rectangle to be the non-
// overlapped portion.
//
//
// 70,130
// +---------+
// | |
// | Cand |
// | |
// 100,100 +---------+170,99
// +----------------------+
// | |
// | |
// | |
// | |
// | Exist |
// | |
// | |
// | |
// +----------------------+
// 200,200
//
// Because this affects the candidate, restart the
// comparisons to check for overlaps between the
// adjusted candidate and other existing rectangles.
//
//
TRACE_OUT(( "OL_PART_ENCLOSES_YMIN - %d", iExist));
pCand->bottom = g_baBounds[iExist].Coord.top - 1;
fResetRects = TRUE;
break;
case OL_PART_ENCLOSES_YMAX:
//
// The existing partially encloses the candidate - but
// not on the bottom.
//
// 100,100
// +----------------------+
// | Exist |
// | |
// | 130,130 |
// | +--------+ |
// | | | |
// | | | |
// | | Cand | |
// | | | |
// | | | |
// | | | |
// +-----+--------+-------+
// | | 200,200
// | |
// | |
// +--------+170,230
//
// Adjust the candidate rectangle to be the non-
// overlapped portion.
//
//
// 100,100
// +----------------------+
// | |
// | |
// | |
// | |
// | |
// | Exist |
// | |
// | |
// | |
// | |
// +----------------------+
// 130,201+---------+ 200,200
// | |
// | Cand |
// | |
// +---------+170,230
//
// Because this affects the candidate, restart the
// comparisons to check for overlaps between the
// adjusted candidate and other existing rectangles.
//
//
TRACE_OUT(( "OL_PART_ENCLOSES_YMAX - %d", iExist));
pCand->top = g_baBounds[iExist].Coord.bottom + 1;
fResetRects = TRUE;
break;
case OL_SPLIT_X:
//
// 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 | |151,130
// +----+| |+-----+
// | || || |
// | || || |
// |Cand|| Exist || New |
// | || || |
// | || || |
// +----+| |+-----+
// 99,160| | 180,160
// | |
// +--------+150,200
//
// After the recursion, because the candidate has
// changed, restart the comparisons to check for
// overlaps between the adjusted candidate and other
// existing rectangles.
//
//
TRACE_OUT(( "OL_SPLIT_X - %d", iExist));
if ((g_baRectsUsed < BA_NUM_RECTS) &&
(level < ADDR_RECURSE_LIMIT))
{
rectNew.left = g_baBounds[iExist].Coord.right + 1;
rectNew.right = pCand->right;
rectNew.top = pCand->top;
rectNew.bottom = pCand->bottom;
pCand->right = g_baBounds[iExist].Coord.left - 1;
TRACE_OUT(( "*** RECURSION ***"));
BAAddRect(&rectNew, level);
TRACE_OUT(( "*** RETURN ***"));
if (!fRectToAdd && !g_baBounds[iLastMerge].InUse)
{
TRACE_OUT(( "FINISHED - %d", iLastMerge));
DC_QUIT;
}
fResetRects = TRUE;
}
break;
case OL_SPLIT_Y:
//
// The existing overlaps the candicate, but neither can
// be merged or adjusted.
//
// 100,100
// +--------+
// | |
// 70,130 | Cand |
// +-----+--------+------+
// | | | |
// | | | |
// |Exist| | |
// | | | |
// | | | |
// +-----+--------+------+180,160
// | |
// | |
// +--------+150,200
//
// Need to split candidate into top and bottom 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 top and a new rectangle on
// the bottom. Call this routine recursively to handle
// the new rectangle.
//
// 100,100
// +--------+
// | Cand |
// 70,130 +--------+150,129
// +---------------------+
// | |
// | |
// | |
// | |
// | |
// +---------------------+180,160
// 100,161+--------+
// | New |
// +--------+150,200
//
// After the recursion, because the candidate has
// changed, restart the comparisons to check for
// overlaps between the adjusted candidate and other
// existing rectangles.
//
//
TRACE_OUT(( "OL_SPLIT_Y - %d", iExist));
if ((g_baRectsUsed < BA_NUM_RECTS) &&
(level < ADDR_RECURSE_LIMIT))
{
rectNew.left = pCand->left;
rectNew.right = pCand->right;
rectNew.top = g_baBounds[iExist].Coord.bottom + 1;
rectNew.bottom = pCand->bottom;
pCand->bottom = g_baBounds[iExist].Coord.top - 1;
TRACE_OUT(( "*** RECURSION ***"));
BAAddRect(&rectNew, level);
TRACE_OUT(( "*** RETURN ***"));
if (!fRectToAdd && !g_baBounds[iLastMerge].InUse)
{
TRACE_OUT(( "FINISHED - %d", iLastMerge));
DC_QUIT;
}
fResetRects = TRUE;
}
break;
case OL_SPLIT_XMIN_YMIN:
//
// The existing overlaps the candicate, 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.
//
// 100,100 151,100
// +-------+-------+
// | | |
// | | New |
// | | |
// | | |200,149
// | +-------+-----+
// | Cand |150,150 |
// | | |
// | | |
// | | |
// | | Exist |
// +-------+ |
// 150,200| |
// | |
// | |
// | |
// +-------------+
// 250,250
//
// After the recursion, because the candidate has
// changed, restart the comparisons to check for
// overlaps between the adjusted candidate and other
// existing rectangles.
//
//
TRACE_OUT(( "OL_SPLIT_XMIN_YMIN - %d", iExist));
if ( g_baRectsUsed < BA_NUM_RECTS )
{
rectNew.left = g_baBounds[iExist].Coord.left;
rectNew.right = pCand->right;
rectNew.top = pCand->top;
rectNew.bottom = g_baBounds[iExist].Coord.top - 1;
pCand->right = g_baBounds[iExist].Coord.left - 1;
TRACE_OUT(( "*** RECURSION ***"));
BAAddRect(&rectNew, level);
TRACE_OUT(( "*** RETURN ***"));
if (!fRectToAdd && !g_baBounds[iLastMerge].InUse)
{
TRACE_OUT(( "FINISHED - %d", iLastMerge));
DC_QUIT;
}
fResetRects = TRUE;
}
break;
case OL_SPLIT_XMAX_YMIN:
//
// The existing overlaps the candicate, but neither can
// be merged or adjusted.
//
// 150,100
// +---------------+
// | |
// | Cand |
// 100,150 | |
// +------+--------+ |
// | | | |
// | | | |
// | | | |
// | | | |
// | | | |
// | | | |
// | +--------+------+
// | | 250,200
// | Exist |
// | |
// +---------------+
// 200,250
//
// Need to split candidate into top and right 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 right and a new rectangle
// on the top. Call this routine recursively to handle
// the new rectangle.
//
// 150,100 201,100
// +--------+------+
// | New | |
// | | |
// 100,150 | 200,149| |
// +------+--------+ |
// | | Cand |
// | | |
// | | |
// | | |
// | Exist | |
// | | |
// | +------+
// | | 250,200
// | |
// | |
// +---------------+
// 200,250
//
// After the recursion, because the candidate has
// changed, restart the comparisons to check for
// overlaps between the adjusted candidate and other
// existing rectangles.
//
//
TRACE_OUT(( "OL_SPLIT_XMAX_YMIN - %d", iExist));
if ((g_baRectsUsed < BA_NUM_RECTS) &&
(level < ADDR_RECURSE_LIMIT))
{
rectNew.left = pCand->left;
rectNew.right = g_baBounds[iExist].Coord.right;
rectNew.top = pCand->top;
rectNew.bottom = g_baBounds[iExist].Coord.top - 1;
pCand->left = g_baBounds[iExist].Coord.right + 1;
TRACE_OUT(( "*** RECURSION ***"));
BAAddRect(&rectNew, level);
TRACE_OUT(( "*** RETURN ***"));
if (!fRectToAdd && !g_baBounds[iLastMerge].InUse)
{
TRACE_OUT(( "FINISHED - %d", iLastMerge));
DC_QUIT;
}
fResetRects = TRUE;
}
break;
case OL_SPLIT_XMIN_YMAX:
//
// The existing overlaps the candicate, but neither can
// be merged or adjusted.
//
// 150,100
// +---------------+
// | |
// | Exist |
// 100,150 | |
// +------+--------+ |
// | | | |
// | | | |
// | | | |
// | | | |
// | | | |
// | | | |
// | +--------+------+
// | | 250,200
// | Cand |
// | |
// +---------------+
// 200,250
//
// Need to split candidate into left and bottom 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 bottom. Call this routine recursively to handle
// the new rectangle.
//
// 150,100
// +---------------+
// | |
// | |
// 100,150 | |
// +------+ |
// | | |
// | | |
// | | |
// | | |
// | Cand | |
// | | |
// | +--------+------+
// | |151,200 | 250,200
// | | |
// | | New |
// +------+--------+
// 149,250 200,250
//
// After the recursion, because the candidate has
// changed, restart the comparisons to check for
// overlaps between the adjusted candidate and other
// existing rectangles.
//
//
TRACE_OUT(( "OL_SPLIT_XMIN_YMAX - %d", iExist));
if ((g_baRectsUsed < BA_NUM_RECTS) &&
(level < ADDR_RECURSE_LIMIT))
{
rectNew.left = g_baBounds[iExist].Coord.left;
rectNew.right = pCand->right;
rectNew.top = g_baBounds[iExist].Coord.bottom + 1;
rectNew.bottom = pCand->bottom;
pCand->right = g_baBounds[iExist].Coord.left - 1;
TRACE_OUT(( "*** RECURSION ***"));
BAAddRect(&rectNew, level);
TRACE_OUT(( "*** RETURN ***"));
if (!fRectToAdd && !g_baBounds[iLastMerge].InUse)
{
TRACE_OUT(( "FINISHED - %d", iLastMerge));
DC_QUIT;
}
fResetRects = TRUE;
}
break;
case OL_SPLIT_XMAX_YMAX:
//
// The existing overlaps the candicate, but neither can
// be merged or adjusted.
//
// 100,100
// +---------------+
// | Exist |
// | |
// | |
// | 150,150 |
// | +-------+-----+
// | | | |
// | | | |
// | | | |
// | | | |
// | | | |
// +-------+-------+ |
// | 200,200 |
// | |
// | Cand |
// | |
// +-------------+
// 250,250
//
// Need to split candidate into bottom and right 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 right and a new rectangle
// on the bottom. Call this routine recursively to
// handle the new rectangle.
//
// 100,100
// +---------------+
// | |
// | |
// | |
// | |201,150
// | Exist +-----+
// | | |
// | | |
// | | |
// | |Cand |
// | 200,200| |
// +-------+-------+ |
// 150,201| | |
// | | |
// | New | |
// | | |
// +-------+-----+
// 200,250 250,250
//
// After the recursion, because the candidate has
// changed, restart the comparisons to check for
// overlaps between the adjusted candidate and other
// existing rectangles.
//
//
TRACE_OUT(( "OL_SPLIT_XMAX_YMAX - %d", iExist));
if ((g_baRectsUsed < BA_NUM_RECTS) &&
(level < ADDR_RECURSE_LIMIT))
{
rectNew.left = pCand->left;
rectNew.right = g_baBounds[iExist].Coord.right;
rectNew.top = g_baBounds[iExist].Coord.bottom + 1;
rectNew.bottom = pCand->bottom;
pCand->left = g_baBounds[iExist].Coord.right + 1;
TRACE_OUT(( "*** RECURSION ***"));
BAAddRect(&rectNew, level);
TRACE_OUT(( "*** RETURN ***"));
if (!fRectToAdd && !g_baBounds[iLastMerge].InUse)
{
TRACE_OUT(( "FINISHED - %d", iLastMerge));
DC_QUIT;
}
fResetRects = TRUE;
}
break;
default:
//
// This should not happen.
//
ERROR_OUT(( "Unrecognised overlap case-%d",OverlapType));
break;
}
iExist = (fResetRects) ? g_baFirstRect :
g_baBounds[iExist].iNext;
}
//
// 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_NUM_RECTS
// (eg. when doing a split or when there is no overlap at all with
// the existing rectangles) - and there is an extra slot for that
// purpose.
//
// If we now have more than BA_NUM_RECTS rectangles, do a
// forced merge, so that the next call to this routine has a spare
// slot.
//
//
fRectMerged = ( g_baRectsUsed > BA_NUM_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.
//
// Note that we calculate the areas of the rectangles here
// (rather than on the fly as they are created/ manipulated in
// the loop), as the statistics show that forced merges occur
// very much less frequently than non-forced manipulations (ie
// splits, adds etc.
//
//
bestMergeIncrease = 0x7FFFFFFF;
for ( iExist = g_baFirstRect;
iExist != BA_INVALID_RECT_INDEX;
iExist = g_baBounds[iExist].iNext )
{
g_baBounds[iExist].Area =
COM_SizeOfRectInclusive(&g_baBounds[iExist].Coord);
}
#ifdef _DEBUG
iBestMerge1 = BA_INVALID_RECT_INDEX;
iBestMerge2 = BA_INVALID_RECT_INDEX;
#endif
for ( iExist = g_baFirstRect;
iExist != BA_INVALID_RECT_INDEX;
iExist = g_baBounds[iExist].iNext )
{
for ( iTmp = g_baBounds[iExist].iNext;
iTmp != BA_INVALID_RECT_INDEX;
iTmp = g_baBounds[iTmp].iNext )
{
rectNew.left = min( g_baBounds[iExist].Coord.left,
g_baBounds[iTmp].Coord.left );
rectNew.top = min( g_baBounds[iExist].Coord.top,
g_baBounds[iTmp].Coord.top );
rectNew.right = max( g_baBounds[iExist].Coord.right,
g_baBounds[iTmp].Coord.right );
rectNew.bottom = max( g_baBounds[iExist].Coord.bottom,
g_baBounds[iTmp].Coord.bottom );
mergeIncrease = COM_SizeOfRectInclusive(&rectNew) -
g_baBounds[iExist].Area - g_baBounds[iTmp].Area;
if (bestMergeIncrease > mergeIncrease)
{
iBestMerge1 = iExist;
iBestMerge2 = iTmp;
bestMergeIncrease = mergeIncrease;
}
}
}
ASSERT(iBestMerge1 != BA_INVALID_RECT_INDEX);
ASSERT(iBestMerge2 != BA_INVALID_RECT_INDEX);
//
// 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.
//
TRACE_OUT(("BestMerge1 %d, (%d,%d,%d,%d)", iBestMerge1,
g_baBounds[iBestMerge1].Coord.left,
g_baBounds[iBestMerge1].Coord.top,
g_baBounds[iBestMerge1].Coord.right,
g_baBounds[iBestMerge1].Coord.bottom ));
TRACE_OUT(("BestMerge2 %d, (%d,%d,%d,%d)", iBestMerge2,
g_baBounds[iBestMerge2].Coord.left,
g_baBounds[iBestMerge2].Coord.top,
g_baBounds[iBestMerge2].Coord.right,
g_baBounds[iBestMerge2].Coord.bottom ));
g_baBounds[iBestMerge1].Coord.left =
min( g_baBounds[iBestMerge1].Coord.left,
g_baBounds[iBestMerge2].Coord.left );
g_baBounds[iBestMerge1].Coord.top =
min( g_baBounds[iBestMerge1].Coord.top,
g_baBounds[iBestMerge2].Coord.top );
g_baBounds[iBestMerge1].Coord.right =
max( g_baBounds[iBestMerge1].Coord.right,
g_baBounds[iBestMerge2].Coord.right );
g_baBounds[iBestMerge1].Coord.bottom =
max( g_baBounds[iBestMerge1].Coord.bottom,
g_baBounds[iBestMerge2].Coord.bottom );
//
// Remove the second best merge.
//
BA_RemoveRectList(&(g_baBounds[iBestMerge2].Coord));
//
// The best merged rectangle becomes the candidate, and we fall
// g_back to the head of the comparison loop to start again.
//
pCand = &(g_baBounds[iBestMerge1].Coord);
iLastMerge = iBestMerge1;
fRectToAdd = FALSE;
}
} while ( fRectMerged );
DC_EXIT_POINT:
DebugExitBOOL(BAAddRect, rc);
return(rc);
}