Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1278 lines
55 KiB

  1. /****************************************************************************/
  2. // nbadisp.c
  3. //
  4. // RDP Bounds Accumulator display driver code
  5. //
  6. // Copyright (C) 1997-2000 Microsoft Corporation
  7. /****************************************************************************/
  8. #include <precmpdd.h>
  9. #define hdrstop
  10. #define TRC_FILE "nbadisp"
  11. #include <adcg.h>
  12. #include <atrcapi.h>
  13. #include <abaapi.h>
  14. #include <nbadisp.h>
  15. #define DC_INCLUDE_DATA
  16. #include <ndddata.c>
  17. #undef DC_INCLUDE_DATA
  18. // No data.
  19. //#include <nbaddat.c>
  20. #include <nbainl.h>
  21. // Instantiate common code.
  22. #include <abacom.c>
  23. // Local prototypes.
  24. #ifdef DC_DEBUG
  25. void BAPerformUnitTests();
  26. #endif
  27. /****************************************************************************/
  28. // BA_DDInit
  29. /****************************************************************************/
  30. void RDPCALL BA_DDInit(void)
  31. {
  32. DC_BEGIN_FN("BA_DDInit");
  33. // No data to declare, don't waste time opening the file.
  34. //#define DC_INIT_DATA
  35. //#include <nbaddat.c>
  36. //#undef DC_INIT_DATA
  37. #ifdef DC_DEBUG
  38. // Perform one-time checks on the algorithm.
  39. BAPerformUnitTests();
  40. #endif
  41. DC_END_FN();
  42. }
  43. /****************************************************************************/
  44. // BA_InitShm
  45. //
  46. // Init BA block in shared memory just after alloc.
  47. /****************************************************************************/
  48. void RDPCALL BA_InitShm(void)
  49. {
  50. unsigned i;
  51. DC_BEGIN_FN("BA_InitShm");
  52. // Initialize all members - shared memory is not zeroed on alloc.
  53. // Initialize rectangle array slots as unused, set up free list
  54. // containing all rects.
  55. pddShm->ba.firstRect = BA_INVALID_RECT_INDEX;
  56. pddShm->ba.rectsUsed = 0;
  57. pddShm->ba.totalArea = 0;
  58. pddShm->ba.firstFreeRect = 0;
  59. for (i = 0; i < BA_TOTAL_NUM_RECTS; i++) {
  60. pddShm->ba.bounds[i].inUse = FALSE;
  61. pddShm->ba.bounds[i].iNext = i + 1;
  62. }
  63. pddShm->ba.bounds[BA_TOTAL_NUM_RECTS - 1].iNext = BA_INVALID_RECT_INDEX;
  64. DC_END_FN();
  65. }
  66. /****************************************************************************/
  67. // BA_AddScreenData
  68. //
  69. // Adds a specified rectangle to the current Screen Data Area.
  70. /****************************************************************************/
  71. void RDPCALL BA_AddScreenData(PRECTL pRect)
  72. {
  73. DC_BEGIN_FN("BA_AddScreenData");
  74. // Check that the caller has passed a valid rectangle.
  75. // Make sure we add a rectangle with coordinates within the screen area.
  76. if((pRect->right > pRect->left) && (pRect->bottom > pRect->top) &&
  77. (pRect->left >= 0) && (pRect->left < ddDesktopWidth) &&
  78. (pRect->right > 0) && (pRect->right <= ddDesktopWidth) &&
  79. (pRect->top >= 0) && (pRect->top < ddDesktopHeight) &&
  80. (pRect->bottom > 0) && (pRect->bottom <= ddDesktopHeight)) {
  81. BAAddRect(pRect, 0);
  82. }
  83. DC_END_FN();
  84. }
  85. /****************************************************************************/
  86. // BAOverlap
  87. //
  88. // Detects overlap between two rectangles. Note that all rectangle
  89. // coordinates are exclusive. Returns one of the overlap return codes or
  90. // outcode combinations defined above.
  91. /****************************************************************************/
  92. int RDPCALL BAOverlap(PRECTL pRect1, PRECTL pRect2)
  93. {
  94. int externalEdges;
  95. int externalCount;
  96. int internalEdges;
  97. int internalCount;
  98. int rc;
  99. DC_BEGIN_FN("BAOverlap");
  100. // We start with special cases of the rects being immediately adjacent
  101. // or overlapping while lying side-by-side.
  102. if (pRect1->top == pRect2->top && pRect1->bottom == pRect2->bottom) {
  103. if (pRect1->left <= pRect2->right &&
  104. pRect1->left > pRect2->left &&
  105. pRect1->right > pRect2->right) {
  106. rc = OL_MERGE_LEFT;
  107. DC_QUIT;
  108. }
  109. if (pRect1->right >= pRect2->left &&
  110. pRect1->right < pRect2->right &&
  111. pRect1->left < pRect2->left) {
  112. rc = OL_MERGE_RIGHT;
  113. DC_QUIT;
  114. }
  115. }
  116. if (pRect1->left == pRect2->left && pRect1->right == pRect2->right) {
  117. if (pRect1->top <= pRect2->bottom &&
  118. pRect1->top > pRect2->top &&
  119. pRect1->bottom > pRect2->bottom) {
  120. rc = OL_MERGE_TOP;
  121. DC_QUIT;
  122. }
  123. if (pRect1->bottom >= pRect2->top &&
  124. pRect1->bottom < pRect2->bottom &&
  125. pRect1->top < pRect2->top) {
  126. rc = OL_MERGE_BOTTOM;
  127. DC_QUIT;
  128. }
  129. }
  130. // Check for no overlapping -- we've exhausted the cases where adjacency
  131. // can be taken advantage of.
  132. if (pRect1->left >= pRect2->right ||
  133. pRect1->top >= pRect2->bottom ||
  134. pRect1->right <= pRect2->left ||
  135. pRect1->bottom <= pRect2->top) {
  136. rc = OL_NONE;
  137. DC_QUIT;
  138. }
  139. // Use outcodes for Internal edge cases.
  140. // If 3 or more bits are set then rect1 is enclosed either partially or
  141. // completely within rect2 according to the OL_ENCLOSED and
  142. // OL_PART_ENCLOSED_XXX definitions. The negative of the outcode value
  143. // is retruned to ensure that it is distinct from the external edge
  144. // outcode returns (see below).
  145. internalCount = 0;
  146. internalEdges = 0;
  147. if (pRect1->left >= pRect2->left && pRect1->left < pRect2->right) {
  148. // Rect1 left is enclosed within rect2.
  149. internalEdges |= EE_LEFT;
  150. internalCount++;
  151. }
  152. if (pRect1->top >= pRect2->top && pRect1->top < pRect2->bottom) {
  153. // Rect1 top is enclosed within rect2.
  154. internalEdges |= EE_TOP;
  155. internalCount++;
  156. }
  157. if (pRect1->right > pRect2->left && pRect1->right <= pRect2->right) {
  158. // Rect1 right is enclosed within rect2.
  159. internalEdges |= EE_RIGHT;
  160. internalCount++;
  161. }
  162. if (pRect1->bottom > pRect2->top && pRect1->bottom <= pRect2->bottom) {
  163. // Rect1 bottom is enclosed within rect2.
  164. internalEdges |= EE_BOTTOM;
  165. internalCount++;
  166. }
  167. if (internalCount >= 3) {
  168. rc = -internalEdges;
  169. DC_QUIT;
  170. }
  171. // Use outcodes for External edge cases. These are the classic "line"
  172. // outcodes. If 2 or more bits are set then rect1 overlaps rect2 per the
  173. // OL_ENCLOSES_XXX and OL_SPLIT_XXX definitions.
  174. externalEdges = 0;
  175. externalCount = 0;
  176. if (pRect1->left <= pRect2->left) {
  177. // Rect1 left is left of rect2 left.
  178. externalEdges |= EE_LEFT;
  179. externalCount++;
  180. }
  181. if (pRect1->top <= pRect2->top) {
  182. // Rect1 top is above rect2 top.
  183. externalEdges |= EE_TOP;
  184. externalCount++;
  185. }
  186. if (pRect1->right >= pRect2->right) {
  187. // Rect1 right is right of rect2 right.
  188. externalEdges |= EE_RIGHT;
  189. externalCount++;
  190. }
  191. if (pRect1->bottom >= pRect2->bottom) {
  192. // Rect1 bottom is below rect2 bottom.
  193. externalEdges |= EE_BOTTOM;
  194. externalCount++;
  195. }
  196. if (externalCount >= 2) {
  197. rc = externalEdges;
  198. DC_QUIT;
  199. }
  200. // If get here then we failed to detect a valid case.
  201. TRC_ALT((TB, "Unrecognised Overlap: (%d,%d,%d,%d),(%d,%d,%d,%d)",
  202. pRect1->left, pRect1->top, pRect1->right, pRect1->bottom,
  203. pRect2->left, pRect2->top, pRect2->right, pRect2->bottom ));
  204. rc = OL_NONE;
  205. DC_EXIT_POINT:
  206. DC_END_FN();
  207. return rc;
  208. }
  209. /****************************************************************************/
  210. // BARemoveRectList
  211. //
  212. // Removes a rectangle from the list.
  213. /****************************************************************************/
  214. __inline void RDPCALL BARemoveRectList(unsigned iRect)
  215. {
  216. BA_RECT_INFO *pRect;
  217. DC_BEGIN_FN("BARemoveRectList");
  218. pRect = &(pddShm->ba.bounds[iRect]);
  219. // Unlink from used list.
  220. if (pRect->iPrev != BA_INVALID_RECT_INDEX)
  221. pddShm->ba.bounds[pRect->iPrev].iNext = pRect->iNext;
  222. else
  223. pddShm->ba.firstRect = pRect->iNext;
  224. if (pRect->iNext != BA_INVALID_RECT_INDEX)
  225. pddShm->ba.bounds[pRect->iNext].iPrev = pRect->iPrev;
  226. // Add to beginning of free list.
  227. pRect->inUse = FALSE;
  228. pRect->iNext = pddShm->ba.firstFreeRect;
  229. pddShm->ba.firstFreeRect = iRect;
  230. // Update bookkeeping variables.
  231. pddShm->ba.rectsUsed--;
  232. pddShm->ba.totalArea -= pRect->area;
  233. #ifdef DC_DEBUG
  234. // Check the list integrity.
  235. BACheckList();
  236. #endif
  237. DC_END_FN();
  238. }
  239. /****************************************************************************/
  240. // BARecalcArea
  241. //
  242. // Recalculates a rect area, preserving the totalArea central tally of areas.
  243. /****************************************************************************/
  244. __inline void RDPCALL BARecalcArea(unsigned iRect)
  245. {
  246. // Reset area to new size and update totalArea.
  247. pddShm->ba.totalArea -= pddShm->ba.bounds[iRect].area;
  248. pddShm->ba.bounds[iRect].area =
  249. COM_SIZEOF_RECT(pddShm->ba.bounds[iRect].coord);
  250. pddShm->ba.totalArea += pddShm->ba.bounds[iRect].area;
  251. }
  252. /****************************************************************************/
  253. // BAAddRect
  254. //
  255. // Accumulates rectangles. This is a complex routine, with the essential
  256. // algorithm as follows:
  257. //
  258. // - Start with the supplied rect as the candidate rect.
  259. //
  260. // - Compare the candidate against each of the existing accumulated rects.
  261. //
  262. // - If some form of overlap is detected between the candidate and an
  263. // existing rect, this may result in one of the following (see the cases of
  264. // the switch for details):
  265. //
  266. // - adjust the candidate or the existing rect or both
  267. // - merge the candidate into the existing rect
  268. // - discard the candidate as it is enclosed by an existing rect.
  269. //
  270. // - If the merge or adjustment results in a changed candidate, restart the
  271. // comparisons from the beginning of the list with the changed candidate.
  272. //
  273. // - If the adjustment results in a split (giving two candidate rects),
  274. // invoke this routine recursively with one of the two candidates as its
  275. // candidate.
  276. //
  277. // - If no overlap is detected against the existing rects, add the candidate
  278. // to the list of accumulated rectangles.
  279. //
  280. // - If the add results in more than BA_MAX_ACCUMULATED_RECTS accumulated
  281. // rects, do a forced merge of two of the accumulate rects (which include
  282. // the newly added candidate) - choosing the two rects where the merged rect
  283. // results in the smallest increase in area over the two non-merged rects.
  284. //
  285. // - After a forced merge, restart the comparisons from the beginning of the
  286. // list with the newly merged rectangle as the candidate.
  287. //
  288. // For a particular call, this process will continue until the candidate
  289. // (whether the supplied rect, an adjusted version of that rect, or a merged
  290. // rect):
  291. //
  292. // - does not find an overlap among the rects in the list and does not cause
  293. // a forced merge
  294. // - is discarded becuase it is enclosed within one of the rects in the list.
  295. //
  296. // Returns TRUE if rectangle was spoiled due to a complete overlap.
  297. /****************************************************************************/
  298. BOOL RDPCALL BAAddRect(PRECTL pCand, int level)
  299. {
  300. INT32 bestMergeIncrease;
  301. INT32 mergeIncrease;
  302. unsigned iBestMerge1;
  303. unsigned iBestMerge2;
  304. unsigned iExist;
  305. unsigned iTmp;
  306. BOOLEAN fRectToAdd;
  307. BOOLEAN fRectMerged;
  308. BOOLEAN fResetRects;
  309. RECTL rectNew;
  310. unsigned iLastMerge;
  311. int overlapType;
  312. BOOL rc = TRUE;
  313. DC_BEGIN_FN("BAAddRect");
  314. #ifdef DC_DEBUG
  315. // Check list, esp. the area calculations.
  316. BACheckList();
  317. #endif
  318. // Increase the level count in case we recurse.
  319. level++;
  320. // Start off by assuming the candidate rectangle will be added to the
  321. // accumulated list of rectangles, and that no forced merge has
  322. // occurred.
  323. fRectToAdd = TRUE;
  324. fRectMerged = FALSE;
  325. // Loop until no merges occur.
  326. do {
  327. TRC_DBG((TB, "Candidate rect: (%d,%d,%d,%d)",
  328. pCand->left,pCand->top,pCand->right,pCand->bottom));
  329. // Compare the current candidate rectangle against the rectangles
  330. // in the current accumulated list.
  331. iExist = pddShm->ba.firstRect;
  332. while (iExist != BA_INVALID_RECT_INDEX) {
  333. // Assume that the comparisons will run through the whole list.
  334. fResetRects = FALSE;
  335. // If the candidate and the existing rectangle are the same
  336. // then ignore. This occurs when an existing rectangle is
  337. // replaced by a candidate and the comparisons are restarted
  338. // from the front of the list - whereupon at some point the
  339. // candidate will be compared with itself.
  340. if (&pddShm->ba.bounds[iExist].coord == pCand) {
  341. TRC_DBG((TB, "OL_SAME - %d", iExist));
  342. iExist = pddShm->ba.bounds[iExist].iNext;
  343. continue;
  344. }
  345. // Switch on the overlap type (see Overlap routine).
  346. overlapType = BAOverlap(&(pddShm->ba.bounds[iExist].coord), pCand);
  347. switch (overlapType) {
  348. case OL_NONE:
  349. // No overlap.
  350. TRC_DBG((TB, "OL_NONE - %d", iExist));
  351. break;
  352. case OL_MERGE_LEFT:
  353. // Candidate abuts or overlaps existing rect at existing
  354. // rect's left.
  355. TRC_DBG((TB, "OL_MERGE_LEFT - %d", iExist));
  356. if (fRectToAdd) {
  357. // Candidate is the original rect; merge the
  358. // candidate into the existing, and make the existing
  359. // the new candidate.
  360. pddShm->ba.bounds[iExist].coord.left = pCand->left;
  361. pCand = &(pddShm->ba.bounds[iExist].coord);
  362. fRectToAdd = FALSE;
  363. iLastMerge = iExist;
  364. BARecalcArea(iExist);
  365. }
  366. else {
  367. // This is a merge of two existing rectangles (ie
  368. // the candidate is the result of a merge), merge the
  369. // overlapping existing into the candidate (the last
  370. // merged) and remove the existing.
  371. pCand->right = pddShm->ba.bounds[iExist].coord.right;
  372. BARemoveRectList(iExist);
  373. }
  374. // Start the comparisons again with the new candidate.
  375. fResetRects = TRUE;
  376. break;
  377. case OL_MERGE_RIGHT:
  378. // Candidate abuts or overlaps existing rect at existing
  379. // rect's right.
  380. TRC_DBG((TB, "OL_MERGE_RIGHT - %d", iExist));
  381. if (fRectToAdd) {
  382. // Candidate is the original rect; merge the
  383. // candidate into the existing, and make the existing
  384. // the new candidate.
  385. pddShm->ba.bounds[iExist].coord.right = pCand->right;
  386. pCand = &(pddShm->ba.bounds[iExist].coord);
  387. fRectToAdd = FALSE;
  388. iLastMerge = iExist;
  389. BARecalcArea(iExist);
  390. }
  391. else {
  392. // This is a merge of two existing rectangles (ie
  393. // the candidate is the result of a merge), merge the
  394. // overlapping existing into the candidate (the last
  395. // merged) and remove the existing.
  396. pCand->left = pddShm->ba.bounds[iExist].coord.left;
  397. BARemoveRectList(iExist);
  398. }
  399. // Start the comparisons again with the new candidate.
  400. fResetRects = TRUE;
  401. break;
  402. case OL_MERGE_TOP:
  403. // Candidate abuts or overlaps existing rect at existing
  404. // rect's top.
  405. TRC_DBG((TB, "OL_MERGE_TOP - %d", iExist));
  406. if (fRectToAdd) {
  407. // Candidate is the original rect; merge the
  408. // candidate into the existing, and make the existing
  409. // the new candidate.
  410. pddShm->ba.bounds[iExist].coord.top = pCand->top;
  411. pCand = &(pddShm->ba.bounds[iExist].coord);
  412. fRectToAdd = FALSE;
  413. iLastMerge = iExist;
  414. BARecalcArea(iExist);
  415. }
  416. else {
  417. // This is a merge of two existing rectangles (ie
  418. // the candidate is the result of a merge), merge the
  419. // overlapping existing into the candidate (the last
  420. // merged) and remove the existing.
  421. pCand->bottom =
  422. pddShm->ba.bounds[iExist].coord.bottom;
  423. BARemoveRectList(iExist);
  424. }
  425. // Start the comparisons again with the new candidate.
  426. fResetRects = TRUE;
  427. break;
  428. case OL_MERGE_BOTTOM:
  429. // Candidate abuts or overlaps existing rect at existing
  430. // rect's bottom.
  431. TRC_DBG((TB, "OL_MERGE_BOTTOM - %d", iExist));
  432. if (fRectToAdd) {
  433. // Candidate is the original rect; merge the
  434. // candidate into the existing, and make the existing
  435. // the new candidate.
  436. pddShm->ba.bounds[iExist].coord.bottom =
  437. pCand->bottom;
  438. pCand = &(pddShm->ba.bounds[iExist].coord);
  439. fRectToAdd = FALSE;
  440. iLastMerge = iExist;
  441. BARecalcArea(iExist);
  442. }
  443. else {
  444. // This is a merge of two existing rectangles (ie
  445. // the candidate is the result of a merge), merge the
  446. // overlapping existing into the candidate (the last
  447. // merged) and remove the existing.
  448. pCand->top = pddShm->ba.bounds[iExist].coord.top;
  449. BARemoveRectList(iExist);
  450. }
  451. // Start the comparisons again with the new candidate.
  452. fResetRects = TRUE;
  453. break;
  454. case OL_ENCLOSED:
  455. // The existing is enclosed by the candidate.
  456. //
  457. // 100,100 +----------------------+
  458. // | Cand |
  459. // | |
  460. // | 130,130 |
  461. // | +------------+ |
  462. // | | | |
  463. // | | Exist | |
  464. // | | | |
  465. // | +------------+ |
  466. // | 170,170 |
  467. // +----------------------+ 200,200
  468. TRC_DBG((TB, "OL_ENCLOSED - %d", iExist));
  469. if (fRectToAdd) {
  470. // Candidate is the original rect; replace the
  471. // existing with the candidate and make the new
  472. // existing the new candidate.
  473. pddShm->ba.bounds[iExist].coord = *pCand;
  474. pCand = &(pddShm->ba.bounds[iExist].coord);
  475. fRectToAdd = FALSE;
  476. iLastMerge = iExist;
  477. BARecalcArea(iExist);
  478. }
  479. else {
  480. // Candidate is an existing rect: Remove the other
  481. // existing rect.
  482. BARemoveRectList(iExist);
  483. }
  484. // Start the comparisons again with the new candidate.
  485. fResetRects = TRUE;
  486. break;
  487. case OL_PART_ENCLOSED_LEFT:
  488. // The existing is partially enclosed by the candidate
  489. // - but not on the right.
  490. //
  491. // 100,100 +----------------------+
  492. // | Cand |
  493. // | 130,130 +-----------+--------+
  494. // | | | |
  495. // | | Exist | |
  496. // | | | |
  497. // | +-----------+--------+
  498. // | | 220,170
  499. // +----------------------+
  500. // 200,200
  501. //
  502. // Adjust the existing rectangle to be the non-
  503. // overlapped portion.
  504. //
  505. // 100,100 +----------------------+
  506. // | |200,130
  507. // | |+-------+
  508. // | || |
  509. // | Cand || Exist |
  510. // | || |
  511. // | |+-------+
  512. // | | 220,170
  513. // +----------------------+
  514. // 200,200
  515. //
  516. // Note that this does not restart the comparisons.
  517. TRC_DBG((TB, "OL_PART_ENCLOSED_LEFT - %d", iExist));
  518. pddShm->ba.bounds[iExist].coord.left = pCand->right;
  519. BARecalcArea(iExist);
  520. break;
  521. case OL_PART_ENCLOSED_RIGHT:
  522. // The existing is partially enclosed by the candidate
  523. // - but not on the left. Adjust the existing rect to be
  524. // the non-overlapping portion, similar to
  525. // OL_PART_ENCLOSED_LEFT above.
  526. // Note that this does not restart the comparisons.
  527. TRC_DBG((TB, "OL_PART_ENCLOSED_RIGHT - %d", iExist));
  528. pddShm->ba.bounds[iExist].coord.right = pCand->left;
  529. BARecalcArea(iExist);
  530. break;
  531. case OL_PART_ENCLOSED_TOP:
  532. // The existing is partially enclosed by the candidate
  533. // - but not on the bottom. Adjust the existing rect to be
  534. // the non-overlapping portion, similar to
  535. // OL_PART_ENCLOSED_LEFT above.
  536. // Note that this does not restart the comparisons.
  537. TRC_DBG((TB, "OL_PART_ENCLOSED_TOP - %d", iExist));
  538. pddShm->ba.bounds[iExist].coord.top = pCand->bottom;
  539. BARecalcArea(iExist);
  540. break;
  541. case OL_PART_ENCLOSED_BOTTOM:
  542. // The existing is partially enclosed by the candidate
  543. // - but not on the top. Adjust the existing rect to be
  544. // the non-overlapping portion, similar to
  545. // OL_PART_ENCLOSED_LEFT above.
  546. // Note that this does not restart the comparisons.
  547. TRC_DBG((TB, "OL_PART_ENCLOSED_BOTTOM - %d", iExist));
  548. pddShm->ba.bounds[iExist].coord.bottom = pCand->top;
  549. BARecalcArea(iExist);
  550. break;
  551. case OL_ENCLOSES:
  552. // The existing encloses the candidate.
  553. //
  554. // 100,100 +----------------------+
  555. // | Exist |
  556. // | |
  557. // | 130,130 |
  558. // | +------------+ |
  559. // | | | |
  560. // | | Cand | |
  561. // | | | |
  562. // | +------------+ |
  563. // | 170,170 |
  564. // +----------------------+ 200,200
  565. TRC_DBG((TB, "OL_ENCLOSES - %d", iExist));
  566. // Return FALSE indicating that the rectangle is
  567. // already catered for by the existing bounds.
  568. rc = FALSE;
  569. DC_QUIT;
  570. case OL_PART_ENCLOSES_LEFT:
  571. // The existing partially encloses the candidate - but
  572. // not on the left.
  573. //
  574. // 100,100 +----------------------+
  575. // | Exist |
  576. // 70,130 | |
  577. // +-----+---------------+ |
  578. // | | | |
  579. // | | Cand | |
  580. // | | | |
  581. // +-----+---------------+ |
  582. // | 170,170 |
  583. // +----------------------+ 200,200
  584. //
  585. // Adjust the candidate rectangle to be the non-
  586. // overlapped portion.
  587. //
  588. // 100,100
  589. // +----------------------+
  590. // | |
  591. // 70,130 | |
  592. // +----+| |
  593. // | || |
  594. // |Cand|| Exist |
  595. // | || |
  596. // +----+| |
  597. // 100,170| |
  598. // +----------------------+ 200,200
  599. TRC_DBG((TB, "OL_PART_ENCLOSES_LEFT - %d", iExist));
  600. pCand->right = pddShm->ba.bounds[iExist].coord.left;
  601. // The candidate changed. Restart the comparisons to check
  602. // for overlaps between the adjusted candidate and other
  603. // existing rects.
  604. fResetRects = TRUE;
  605. BARecalcArea(iExist);
  606. break;
  607. case OL_PART_ENCLOSES_RIGHT:
  608. // The existing partially encloses the candidate - but
  609. // not on the right. Adjust the candidate rect to be
  610. // the non-overlapping portion, similar to
  611. // OL_PART_ENCLOSES_LEFT above.
  612. TRC_DBG((TB, "OL_PART_ENCLOSES_RIGHT - %d", iExist));
  613. pCand->left = pddShm->ba.bounds[iExist].coord.right;
  614. // The candidate changed. Restart the comparisons to check
  615. // for overlaps between the adjusted candidate and other
  616. // existing rects.
  617. fResetRects = TRUE;
  618. BARecalcArea(iExist);
  619. break;
  620. case OL_PART_ENCLOSES_TOP:
  621. // The existing partially encloses the candidate - but
  622. // not on the top. Adjust the candidate rect to be
  623. // the non-overlapping portion, similar to
  624. // OL_PART_ENCLOSES_LEFT above.
  625. TRC_DBG((TB, "OL_PART_ENCLOSES_TOP - %d", iExist));
  626. pCand->bottom = pddShm->ba.bounds[iExist].coord.top;
  627. // The candidate changed. Restart the comparisons to check
  628. // for overlaps between the adjusted candidate and other
  629. // existing rects.
  630. fResetRects = TRUE;
  631. BARecalcArea(iExist);
  632. break;
  633. case OL_PART_ENCLOSES_BOTTOM:
  634. // The existing partially encloses the candidate - but
  635. // not on the bottom. Adjust the candidate rect to be
  636. // the non-overlapping portion, similar to
  637. // OL_PART_ENCLOSES_LEFT above.
  638. TRC_DBG((TB, "OL_PART_ENCLOSES_BOTTOM - %d", iExist));
  639. pCand->top = pddShm->ba.bounds[iExist].coord.bottom;
  640. // The candidate changed. Restart the comparisons to check
  641. // for overlaps between the adjusted candidate and other
  642. // existing rects.
  643. fResetRects = TRUE;
  644. BARecalcArea(iExist);
  645. break;
  646. case OL_SPLIT_HORIZ:
  647. // The existing overlaps the candicate, but neither can
  648. // be merged or adjusted.
  649. //
  650. // 100,100 +--------+
  651. // 70,130 | Exist |
  652. // +-----+--------+------+
  653. // | | | |
  654. // | Cand| | |
  655. // | | | |
  656. // +-----+--------+------+180,160
  657. // | |
  658. // +--------+150,200
  659. //
  660. // Need to split candidate into left and right halves.
  661. // Only do a split if there is spare room in the list -
  662. // because both the split rectangles may need to be
  663. // added to the list.
  664. //
  665. // If there is spare room, split the candidate into a
  666. // smaller candidate on the left and a new rectangle on
  667. // the right. Call this routine recursively to handle
  668. // the new rectangle.
  669. //
  670. // 100,100 +--------+
  671. // 70,130 | |150,130
  672. // +----+| |+-----+
  673. // | || || |
  674. // |Cand|| Exist || New |
  675. // | || || |
  676. // +----+| |+-----+
  677. // 100,160| | 180,160
  678. // +--------+150,200
  679. TRC_DBG((TB, "OL_SPLIT_HORIZ - %d", iExist));
  680. if (pddShm->ba.rectsUsed < BA_MAX_ACCUMULATED_RECTS &&
  681. level < ADDR_RECURSE_LIMIT) {
  682. if (((pCand->bottom - pCand->top) *
  683. (pddShm->ba.bounds[iExist].coord.right -
  684. pddShm->ba.bounds[iExist].coord.left)) >
  685. MIN_OVERLAP_BYTES) {
  686. rectNew.left =
  687. pddShm->ba.bounds[iExist].coord.right;
  688. rectNew.right = pCand->right;
  689. rectNew.top = pCand->top;
  690. rectNew.bottom = pCand->bottom;
  691. pCand->right =
  692. pddShm->ba.bounds[iExist].coord.left;
  693. TRC_DBG((TB, "*** RECURSION ***"));
  694. BAAddRect(&rectNew, level);
  695. TRC_DBG((TB, "*** RETURN ***"));
  696. if (!fRectToAdd &&
  697. !pddShm->ba.bounds[iLastMerge].inUse) {
  698. TRC_DBG((TB, "FINISHED - %d", iLastMerge));
  699. DC_QUIT;
  700. }
  701. // After the recursion, because the candidate has
  702. // changed, restart the comparisons to check for
  703. // overlaps between the adjusted candidate and
  704. // other existing rectangles.
  705. fResetRects = TRUE;
  706. }
  707. }
  708. break;
  709. case OL_SPLIT_VERT:
  710. // The existing overlaps the candidate, but neither can
  711. // be merged or adjusted. Split candidate into top and
  712. // bottom similar to OL_SPLIT_HORIZ above.
  713. TRC_DBG((TB, "OL_SPLIT_VERT - %d", iExist));
  714. if (pddShm->ba.rectsUsed < BA_MAX_ACCUMULATED_RECTS &&
  715. level < ADDR_RECURSE_LIMIT) {
  716. if (((pCand->right - pCand->left) *
  717. (pddShm->ba.bounds[iExist].coord.bottom -
  718. pddShm->ba.bounds[iExist].coord.top)) >
  719. MIN_OVERLAP_BYTES) {
  720. rectNew.left = pCand->left;
  721. rectNew.right = pCand->right;
  722. rectNew.top =
  723. pddShm->ba.bounds[iExist].coord.bottom;
  724. rectNew.bottom = pCand->bottom;
  725. pCand->bottom =
  726. pddShm->ba.bounds[iExist].coord.top;
  727. TRC_DBG((TB, "*** RECURSION ***"));
  728. BAAddRect(&rectNew, level);
  729. TRC_DBG((TB, "*** RETURN ***"));
  730. if (!fRectToAdd &&
  731. !pddShm->ba.bounds[iLastMerge].inUse) {
  732. TRC_DBG((TB, "FINISHED - %d", iLastMerge));
  733. DC_QUIT;
  734. }
  735. // After the recursion, because the candidate has
  736. // changed, restart the comparisons to check for
  737. // overlaps between the adjusted candidate and
  738. // other existing rectangles.
  739. fResetRects = TRUE;
  740. }
  741. }
  742. break;
  743. case OL_SPLIT_LEFT_TOP:
  744. // The existing overlaps the candicate at the existing
  745. // rect's top left, but neither can be merged or adjusted.
  746. //
  747. // 100,100 +---------------+
  748. // | Cand |
  749. // | |
  750. // | 150,150 |
  751. // | +-------+-----+
  752. // | | | |
  753. // | | | |
  754. // +-------+-------+ |
  755. // | 200,200 |
  756. // | |
  757. // | Exist |
  758. // +-------------+ 250, 250
  759. //
  760. // Need to split candidate into top and left pieces.
  761. // Only do a split if there is spare room in the list -
  762. // because both the split rectangles may need to be
  763. // added to the list.
  764. //
  765. // If there is spare room, split the candidate into a
  766. // smaller candidate on the left and a new rectangle on
  767. // the top. Call this routine recursively to handle
  768. // the new rectangle.
  769. //
  770. // 151,100
  771. // 100,100 +-------+-------+
  772. // | | |
  773. // | | New |
  774. // | | |200,149
  775. // | +-------+-----+
  776. // | Cand |150,150 |
  777. // | | |
  778. // +-------+ Exist |
  779. // 150,200| |
  780. // | |
  781. // | |
  782. // +-------------+ 250,250
  783. TRC_DBG((TB, "OL_SPLIT_LEFT_TOP - %d", iExist));
  784. if (pddShm->ba.rectsUsed < BA_MAX_ACCUMULATED_RECTS &&
  785. level < ADDR_RECURSE_LIMIT) {
  786. if (((pCand->right -
  787. pddShm->ba.bounds[iExist].coord.left) *
  788. (pCand->bottom -
  789. pddShm->ba.bounds[iExist].coord.top)) >
  790. MIN_OVERLAP_BYTES) {
  791. rectNew.left =
  792. pddShm->ba.bounds[iExist].coord.left;
  793. rectNew.right = pCand->right;
  794. rectNew.top = pCand->top;
  795. rectNew.bottom =
  796. pddShm->ba.bounds[iExist].coord.top;
  797. pCand->right =
  798. pddShm->ba.bounds[iExist].coord.left;
  799. TRC_DBG((TB, "*** RECURSION ***"));
  800. BAAddRect(&rectNew, level);
  801. TRC_DBG((TB, "*** RETURN ***"));
  802. if (!fRectToAdd &&
  803. !pddShm->ba.bounds[iLastMerge].inUse) {
  804. TRC_DBG((TB, "FINISHED - %d", iLastMerge));
  805. DC_QUIT;
  806. }
  807. // After the recursion, because the candidate has
  808. // changed, restart the comparisons to check for
  809. // overlaps between the adjusted candidate and
  810. // other existing rectangles.
  811. fResetRects = TRUE;
  812. }
  813. }
  814. break;
  815. case OL_SPLIT_RIGHT_TOP:
  816. // The existing overlaps the candidate, but neither can
  817. // be merged or adjusted. Split into top and right
  818. // pieces if there is room in the list, similar to
  819. // OL_SPLIT_LEFT_TOP above.
  820. TRC_DBG((TB, "OL_SPLIT_RIGHT_TOP - %d", iExist));
  821. if (pddShm->ba.rectsUsed < BA_MAX_ACCUMULATED_RECTS &&
  822. level < ADDR_RECURSE_LIMIT) {
  823. if (((pddShm->ba.bounds[iExist].coord.right -
  824. pCand->left) * (pCand->bottom -
  825. pddShm->ba.bounds[iExist].coord.top)) >
  826. MIN_OVERLAP_BYTES) {
  827. rectNew.left = pCand->left;
  828. rectNew.right =
  829. pddShm->ba.bounds[iExist].coord.right;
  830. rectNew.top = pCand->top;
  831. rectNew.bottom =
  832. pddShm->ba.bounds[iExist].coord.top;
  833. pCand->left =
  834. pddShm->ba.bounds[iExist].coord.right;
  835. TRC_DBG((TB, "*** RECURSION ***"));
  836. BAAddRect(&rectNew, level);
  837. TRC_DBG((TB, "*** RETURN ***"));
  838. if (!fRectToAdd &&
  839. !pddShm->ba.bounds[iLastMerge].inUse) {
  840. TRC_DBG((TB, "FINISHED - %d", iLastMerge));
  841. DC_QUIT;
  842. }
  843. // After the recursion, because the candidate has
  844. // changed, restart the comparisons to check for
  845. // overlaps between the adjusted candidate and
  846. // other existing rectangles.
  847. fResetRects = TRUE;
  848. }
  849. }
  850. break;
  851. case OL_SPLIT_LEFT_BOTTOM:
  852. // The existing overlaps the candidate, but neither can
  853. // be merged or adjusted. Split into left and bottom
  854. // pieces if there is room in the list, similar to
  855. // OL_SPLIT_LEFT_TOP above.
  856. TRC_DBG((TB, "OL_SPLIT_LEFT_BOTTOM - %d", iExist));
  857. if (pddShm->ba.rectsUsed < BA_MAX_ACCUMULATED_RECTS &&
  858. level < ADDR_RECURSE_LIMIT) {
  859. if (((pCand->right -
  860. pddShm->ba.bounds[iExist].coord.left) *
  861. (pddShm->ba.bounds[iExist].coord.bottom -
  862. pCand->top)) > MIN_OVERLAP_BYTES) {
  863. rectNew.left =
  864. pddShm->ba.bounds[iExist].coord.left;
  865. rectNew.right = pCand->right;
  866. rectNew.top =
  867. pddShm->ba.bounds[iExist].coord.bottom;
  868. rectNew.bottom = pCand->bottom;
  869. pCand->right =
  870. pddShm->ba.bounds[iExist].coord.left;
  871. TRC_DBG((TB, "*** RECURSION ***"));
  872. BAAddRect(&rectNew, level);
  873. TRC_DBG((TB, "*** RETURN ***"));
  874. if (!fRectToAdd &&
  875. !pddShm->ba.bounds[iLastMerge].inUse) {
  876. TRC_DBG((TB, "FINISHED - %d", iLastMerge));
  877. DC_QUIT;
  878. }
  879. // After the recursion, because the candidate has
  880. // changed, restart the comparisons to check for
  881. // overlaps between the adjusted candidate and
  882. // other existing rectangles.
  883. fResetRects = TRUE;
  884. }
  885. }
  886. break;
  887. case OL_SPLIT_RIGHT_BOTTOM:
  888. // The existing overlaps the candidate, but neither can
  889. // be merged or adjusted. Split into right and bottom
  890. // pieces if there is room in the list, similar to
  891. // OL_SPLIT_LEFT_TOP above.
  892. TRC_DBG((TB, "OL_SPLIT_RIGHT_BOTTOM - %d", iExist));
  893. if (pddShm->ba.rectsUsed < BA_MAX_ACCUMULATED_RECTS &&
  894. level < ADDR_RECURSE_LIMIT) {
  895. if (((pddShm->ba.bounds[iExist].coord.right -
  896. pCand->left) *
  897. (pddShm->ba.bounds[iExist].coord.bottom -
  898. pCand->top)) > MIN_OVERLAP_BYTES) {
  899. rectNew.left = pCand->left;
  900. rectNew.right =
  901. pddShm->ba.bounds[iExist].coord.right;
  902. rectNew.top =
  903. pddShm->ba.bounds[iExist].coord.bottom;
  904. rectNew.bottom = pCand->bottom;
  905. pCand->left =
  906. pddShm->ba.bounds[iExist].coord.right;
  907. TRC_DBG((TB, "*** RECURSION ***"));
  908. BAAddRect(&rectNew, level);
  909. TRC_DBG((TB, "*** RETURN ***"));
  910. if (!fRectToAdd &&
  911. !pddShm->ba.bounds[iLastMerge].inUse) {
  912. TRC_DBG((TB, "FINISHED - %d", iLastMerge));
  913. DC_QUIT;
  914. }
  915. // After the recursion, because the candidate has
  916. // changed, restart the comparisons to check for
  917. // overlaps between the adjusted candidate and
  918. // other existing rectangles.
  919. fResetRects = TRUE;
  920. }
  921. }
  922. break;
  923. default:
  924. TRC_ERR((TB, "Unrecognised overlap case-%d",
  925. overlapType));
  926. break;
  927. }
  928. iExist = (!fResetRects) ? pddShm->ba.bounds[iExist].iNext :
  929. pddShm->ba.firstRect;
  930. }
  931. // Arriving here means that no overlap was found between the
  932. // candidate and the existing rectangles.
  933. // - If the candidate is the original rectangle, add it to the list.
  934. // - If the candidate is an existing rectangle, it is already in
  935. // the list.
  936. if (fRectToAdd)
  937. BAAddRectList(pCand);
  938. // The compare and add processing above is allowed to add a
  939. // rectangle to the list when there are already BA_MAX_NUM_RECTS
  940. // (eg. when doing a split or when there is no overlap at all with
  941. // the existing rectangles) - there is an extra slot for that purpose.
  942. // If we now have more than BA_MAX_NUM_RECTS rectangles, do a
  943. // forced merge, so that the next call to this routine has a spare
  944. // slot.
  945. fRectMerged = (pddShm->ba.rectsUsed > BA_MAX_ACCUMULATED_RECTS);
  946. if (fRectMerged) {
  947. // Start looking for merged rectangles. For each rectangle in the
  948. // list, compare it with the others, and Determine cost of
  949. // merging. We want to merge the two rectangles with the minimum
  950. // area difference, ie that will produce a merged rectangle that
  951. // covers the least superfluous screen area.
  952. bestMergeIncrease = 0x7FFFFFFF;
  953. // Recalculate the real areas of the current rectangles. We
  954. // cannot rely upon the current area value since the rect
  955. // edges may have changed during a merge and the area not
  956. // recalculated.
  957. for (iExist = pddShm->ba.firstRect;
  958. iExist != BA_INVALID_RECT_INDEX;
  959. iExist = pddShm->ba.bounds[iExist].iNext)
  960. BARecalcArea(iExist);
  961. for (iExist = pddShm->ba.firstRect;
  962. iExist != BA_INVALID_RECT_INDEX;
  963. iExist = pddShm->ba.bounds[iExist].iNext) {
  964. for (iTmp = pddShm->ba.bounds[iExist].iNext;
  965. iTmp != BA_INVALID_RECT_INDEX;
  966. iTmp = pddShm->ba.bounds[iTmp].iNext) {
  967. rectNew.left = min(pddShm->ba.bounds[iExist].coord.left,
  968. pddShm->ba.bounds[iTmp].coord.left );
  969. rectNew.top = min(pddShm->ba.bounds[iExist].coord.top,
  970. pddShm->ba.bounds[iTmp].coord.top );
  971. rectNew.right = max(pddShm->ba.bounds[iExist].coord.right,
  972. pddShm->ba.bounds[iTmp].coord.right );
  973. rectNew.bottom = max(pddShm->ba.bounds[iExist].coord.bottom,
  974. pddShm->ba.bounds[iTmp].coord.bottom );
  975. mergeIncrease = COM_SIZEOF_RECT(rectNew) -
  976. pddShm->ba.bounds[iExist].area -
  977. pddShm->ba.bounds[iTmp].area;
  978. if (bestMergeIncrease > mergeIncrease) {
  979. iBestMerge1 = iExist;
  980. iBestMerge2 = iTmp;
  981. bestMergeIncrease = mergeIncrease;
  982. }
  983. }
  984. }
  985. // Now do the merge.
  986. // We recalculate the size of the merged rectangle here -
  987. // alternatively we could remember the size of the best so far
  988. // in the loop above. The trade off is between calculating
  989. // twice or copying at least once but probably more than once
  990. // as we find successively better merges.
  991. TRC_DBG((TB, "Merge ix %d, (%d,%d,%d,%d)", iBestMerge1,
  992. pddShm->ba.bounds[iBestMerge1].coord.left,
  993. pddShm->ba.bounds[iBestMerge1].coord.top,
  994. pddShm->ba.bounds[iBestMerge1].coord.right,
  995. pddShm->ba.bounds[iBestMerge1].coord.bottom ));
  996. TRC_DBG((TB, "Merge ix %d, (%d,%d,%d,%d)", iBestMerge2,
  997. pddShm->ba.bounds[iBestMerge2].coord.left,
  998. pddShm->ba.bounds[iBestMerge2].coord.top,
  999. pddShm->ba.bounds[iBestMerge2].coord.right,
  1000. pddShm->ba.bounds[iBestMerge2].coord.bottom ));
  1001. pddShm->ba.bounds[iBestMerge1].coord.left =
  1002. min(pddShm->ba.bounds[iBestMerge1].coord.left,
  1003. pddShm->ba.bounds[iBestMerge2].coord.left);
  1004. pddShm->ba.bounds[iBestMerge1].coord.top =
  1005. min(pddShm->ba.bounds[iBestMerge1].coord.top,
  1006. pddShm->ba.bounds[iBestMerge2].coord.top );
  1007. pddShm->ba.bounds[iBestMerge1].coord.right =
  1008. max(pddShm->ba.bounds[iBestMerge1].coord.right,
  1009. pddShm->ba.bounds[iBestMerge2].coord.right);
  1010. pddShm->ba.bounds[iBestMerge1].coord.bottom =
  1011. max(pddShm->ba.bounds[iBestMerge1].coord.bottom,
  1012. pddShm->ba.bounds[iBestMerge2].coord.bottom);
  1013. // Remove the second best merge.
  1014. BARemoveRectList(iBestMerge2);
  1015. // The best merged rectangle becomes the candidate, and we fall
  1016. // back to the head of the comparison loop to start again.
  1017. pCand = &(pddShm->ba.bounds[iBestMerge1].coord);
  1018. iLastMerge = iBestMerge1;
  1019. fRectToAdd = FALSE;
  1020. }
  1021. } while (fRectMerged);
  1022. DC_EXIT_POINT:
  1023. DC_END_FN();
  1024. return rc;
  1025. }
  1026. #ifdef DC_DEBUG
  1027. /****************************************************************************/
  1028. // BAPerformUnitTests
  1029. //
  1030. // Verifies some basic assumptions of the BA algorithms, in case it breaks
  1031. // while being changed. Performed only on init.
  1032. /****************************************************************************/
  1033. // Test data. Note that these tests are designed for BA using exclusive rects.
  1034. typedef struct
  1035. {
  1036. const char *TestName;
  1037. const RECTL *MustBePresent;
  1038. unsigned NumPresent;
  1039. } Validation;
  1040. // Start with a rect out in the middle area.
  1041. const RECTL Test1StartAdd = { 10, 10, 20, 20 };
  1042. const Validation Test1StartVerify = { "Test1Start", &Test1StartAdd, 1 };
  1043. // Add a rect of the same height 2 pixels off the right edge of rect1.
  1044. // Should not merge with rect1.
  1045. const RECTL Test1Step1Add = { 21, 10, 31, 20 };
  1046. const RECTL Test1Step1Ver[2] = { { 10, 10, 20, 20 }, { 21, 10, 31, 20 } };
  1047. const Validation Test1Step1Verify = { "Test1Step1", Test1Step1Ver, 2 };
  1048. // Add a rect of the same width immediately left of but not intersecting
  1049. // with rect1. Should be merged into one large rect.
  1050. const RECTL Test1Step2Add = { 0, 10, 10, 20 };
  1051. const RECTL Test1Step2Ver[2] = { { 0, 10, 20, 20 }, { 21, 10, 31, 20 } };
  1052. const Validation Test1Step2Verify = { "Test1Step2", Test1Step2Ver, 2 };
  1053. // Add a rect of the same width intersecting rect2 at its top.
  1054. // Should be merged into one large rect.
  1055. const RECTL Test1Step3Add = { 21, 5, 31, 15 };
  1056. const RECTL Test1Step3Ver[2] = { { 0, 10, 20, 20 }, { 21, 5, 31, 20 } };
  1057. const Validation Test1Step3Verify = { "Test1Step3", Test1Step3Ver, 2 };
  1058. // Add a rect that intersects rect1 at its bottom but is smaller in
  1059. // width and partially enclosed within rect1. Rect should be split,
  1060. // the top half absorbed into rect1, the bottom a new rect.
  1061. const RECTL Test1Step4Add = { 5, 15, 10, 25 };
  1062. const RECTL Test1Step4Ver[3] = {
  1063. { 0, 10, 20, 20 }, { 21, 5, 31, 20 }, { 5, 20, 10, 25 }
  1064. };
  1065. const Validation Test1Step4Verify = { "Test1Step4", Test1Step4Ver, 3 };
  1066. // Worker func to verify the contents of the rects. Returns FALSE if
  1067. // the
  1068. void AddAndValidateRects(const RECTL *pAdd, const Validation *pVal)
  1069. {
  1070. BOOL rc = TRUE;
  1071. RECTL Rects[BA_MAX_ACCUMULATED_RECTS];
  1072. unsigned i, j;
  1073. unsigned NumRects;
  1074. BYTE RectFound[BA_MAX_ACCUMULATED_RECTS] = { 0 };
  1075. RECTL Add;
  1076. DC_BEGIN_FN("AddAndValidateRects");
  1077. // Make a copy of *pAdd since BAAddRect can modify it.
  1078. Add = *pAdd;
  1079. BAAddRect(&Add, 0);
  1080. BACopyBounds(Rects, &NumRects);
  1081. TRC_ASSERT((NumRects == pVal->NumPresent),
  1082. (TB,"%s failure: NumRects=%u, should be %u", pVal->TestName,
  1083. NumRects, pVal->NumPresent));
  1084. for (i = 0; i < NumRects; i++) {
  1085. for (j = 0; j < NumRects; j++) {
  1086. if (!memcmp(&Rects[i], &pVal->MustBePresent[j], sizeof(RECTL)))
  1087. RectFound[j] = TRUE;
  1088. }
  1089. }
  1090. for (i = 0; i < NumRects; i++) {
  1091. if (!RectFound[i]) {
  1092. TRC_ERR((TB,"BA validation error, rects:"));
  1093. for (j = 0; j < NumRects; j++)
  1094. TRC_ERR((TB," %u: (%u,%u,%u,%u)", j, Rects[j].left,
  1095. Rects[j].top, Rects[j].right, Rects[j].bottom));
  1096. TRC_ASSERT((RectFound[i]),(TB,"%s failure: MustBePresent rect %u "
  1097. "was not present", pVal->TestName, i));
  1098. }
  1099. }
  1100. DC_END_FN();
  1101. }
  1102. // The real function.
  1103. void BAPerformUnitTests()
  1104. {
  1105. RECTL Rect1, Rect2;
  1106. DC_BEGIN_FN("BAPerformUnitTests");
  1107. // Test1.
  1108. AddAndValidateRects(&Test1StartAdd, &Test1StartVerify);
  1109. AddAndValidateRects(&Test1Step1Add, &Test1Step1Verify);
  1110. AddAndValidateRects(&Test1Step2Add, &Test1Step2Verify);
  1111. AddAndValidateRects(&Test1Step3Add, &Test1Step3Verify);
  1112. AddAndValidateRects(&Test1Step4Add, &Test1Step4Verify);
  1113. }
  1114. #endif // DC_DEBUG