/****************************************************************************/ // nbadisp.c // // RDP Bounds Accumulator display driver code // // Copyright (C) 1997-2000 Microsoft Corporation /****************************************************************************/ #include #define hdrstop #define TRC_FILE "nbadisp" #include #include #include #include #define DC_INCLUDE_DATA #include #undef DC_INCLUDE_DATA // No data. //#include #include // Instantiate common code. #include // 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 //#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