ClipStackTest.cpp revision 170bd792e17469769d145b7dc15dea6cd01b7966
1 2/* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8#include "Test.h" 9#if SK_SUPPORT_GPU 10 #include "GrReducedClip.h" 11#endif 12#include "SkClipStack.h" 13#include "SkPath.h" 14#include "SkRandom.h" 15#include "SkRect.h" 16#include "SkRegion.h" 17 18 19static void test_assign_and_comparison(skiatest::Reporter* reporter) { 20 SkClipStack s; 21 bool doAA = false; 22 23 REPORTER_ASSERT(reporter, 0 == s.getSaveCount()); 24 25 // Build up a clip stack with a path, an empty clip, and a rect. 26 s.save(); 27 REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); 28 29 SkPath p; 30 p.moveTo(5, 6); 31 p.lineTo(7, 8); 32 p.lineTo(5, 9); 33 p.close(); 34 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA); 35 36 s.save(); 37 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 38 39 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4); 40 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); 41 r = SkRect::MakeLTRB(10, 11, 12, 13); 42 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); 43 44 s.save(); 45 REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); 46 47 r = SkRect::MakeLTRB(14, 15, 16, 17); 48 s.clipDevRect(r, SkRegion::kUnion_Op, doAA); 49 50 // Test that assignment works. 51 SkClipStack copy = s; 52 REPORTER_ASSERT(reporter, s == copy); 53 54 // Test that different save levels triggers not equal. 55 s.restore(); 56 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 57 REPORTER_ASSERT(reporter, s != copy); 58 59 // Test that an equal, but not copied version is equal. 60 s.save(); 61 REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); 62 63 r = SkRect::MakeLTRB(14, 15, 16, 17); 64 s.clipDevRect(r, SkRegion::kUnion_Op, doAA); 65 REPORTER_ASSERT(reporter, s == copy); 66 67 // Test that a different op on one level triggers not equal. 68 s.restore(); 69 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 70 s.save(); 71 REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); 72 73 r = SkRect::MakeLTRB(14, 15, 16, 17); 74 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); 75 REPORTER_ASSERT(reporter, s != copy); 76 77 // Test that different state (clip type) triggers not equal. 78 // NO LONGER VALID: if a path contains only a rect, we turn 79 // it into a bare rect for performance reasons (working 80 // around Chromium/JavaScript bad pattern). 81/* 82 s.restore(); 83 s.save(); 84 SkPath rp; 85 rp.addRect(r); 86 s.clipDevPath(rp, SkRegion::kUnion_Op, doAA); 87 REPORTER_ASSERT(reporter, s != copy); 88*/ 89 90 // Test that different rects triggers not equal. 91 s.restore(); 92 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 93 s.save(); 94 REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); 95 96 r = SkRect::MakeLTRB(24, 25, 26, 27); 97 s.clipDevRect(r, SkRegion::kUnion_Op, doAA); 98 REPORTER_ASSERT(reporter, s != copy); 99 100 // Sanity check 101 s.restore(); 102 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 103 104 copy.restore(); 105 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount()); 106 REPORTER_ASSERT(reporter, s == copy); 107 s.restore(); 108 REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); 109 copy.restore(); 110 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount()); 111 REPORTER_ASSERT(reporter, s == copy); 112 113 // Test that different paths triggers not equal. 114 s.restore(); 115 REPORTER_ASSERT(reporter, 0 == s.getSaveCount()); 116 s.save(); 117 REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); 118 119 p.addRect(r); 120 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA); 121 REPORTER_ASSERT(reporter, s != copy); 122} 123 124static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack, 125 int count) { 126 SkClipStack::B2TIter iter(stack); 127 int counter = 0; 128 while (iter.next()) { 129 counter += 1; 130 } 131 REPORTER_ASSERT(reporter, count == counter); 132} 133 134// Exercise the SkClipStack's bottom to top and bidirectional iterators 135// (including the skipToTopmost functionality) 136static void test_iterators(skiatest::Reporter* reporter) { 137 SkClipStack stack; 138 139 static const SkRect gRects[] = { 140 { 0, 0, 40, 40 }, 141 { 60, 0, 100, 40 }, 142 { 0, 60, 40, 100 }, 143 { 60, 60, 100, 100 } 144 }; 145 146 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) { 147 // the union op will prevent these from being fused together 148 stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false); 149 } 150 151 assert_count(reporter, stack, 4); 152 153 // bottom to top iteration 154 { 155 const SkClipStack::Element* element = NULL; 156 157 SkClipStack::B2TIter iter(stack); 158 int i; 159 160 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) { 161 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); 162 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]); 163 } 164 165 SkASSERT(i == 4); 166 } 167 168 // top to bottom iteration 169 { 170 const SkClipStack::Element* element = NULL; 171 172 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); 173 int i; 174 175 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) { 176 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); 177 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]); 178 } 179 180 SkASSERT(i == -1); 181 } 182 183 // skipToTopmost 184 { 185 const SkClipStack::Element* element = NULL; 186 187 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); 188 189 element = iter.skipToTopmost(SkRegion::kUnion_Op); 190 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); 191 REPORTER_ASSERT(reporter, element->getRect() == gRects[3]); 192 } 193} 194 195// Exercise the SkClipStack's getConservativeBounds computation 196static void test_bounds(skiatest::Reporter* reporter, bool useRects) { 197 198 static const int gNumCases = 20; 199 static const SkRect gAnswerRectsBW[gNumCases] = { 200 // A op B 201 { 40, 40, 50, 50 }, 202 { 10, 10, 50, 50 }, 203 { 10, 10, 80, 80 }, 204 { 10, 10, 80, 80 }, 205 { 40, 40, 80, 80 }, 206 207 // invA op B 208 { 40, 40, 80, 80 }, 209 { 0, 0, 100, 100 }, 210 { 0, 0, 100, 100 }, 211 { 0, 0, 100, 100 }, 212 { 40, 40, 50, 50 }, 213 214 // A op invB 215 { 10, 10, 50, 50 }, 216 { 40, 40, 50, 50 }, 217 { 0, 0, 100, 100 }, 218 { 0, 0, 100, 100 }, 219 { 0, 0, 100, 100 }, 220 221 // invA op invB 222 { 0, 0, 100, 100 }, 223 { 40, 40, 80, 80 }, 224 { 0, 0, 100, 100 }, 225 { 10, 10, 80, 80 }, 226 { 10, 10, 50, 50 }, 227 }; 228 229 static const SkRegion::Op gOps[] = { 230 SkRegion::kIntersect_Op, 231 SkRegion::kDifference_Op, 232 SkRegion::kUnion_Op, 233 SkRegion::kXOR_Op, 234 SkRegion::kReverseDifference_Op 235 }; 236 237 SkRect rectA, rectB; 238 239 rectA.iset(10, 10, 50, 50); 240 rectB.iset(40, 40, 80, 80); 241 242 SkPath clipA, clipB; 243 244 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5)); 245 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5)); 246 247 SkClipStack stack; 248 SkRect devClipBound; 249 bool isIntersectionOfRects = false; 250 251 int testCase = 0; 252 int numBitTests = useRects ? 1 : 4; 253 for (int invBits = 0; invBits < numBitTests; ++invBits) { 254 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) { 255 256 stack.save(); 257 bool doInvA = SkToBool(invBits & 1); 258 bool doInvB = SkToBool(invBits & 2); 259 260 clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType : 261 SkPath::kEvenOdd_FillType); 262 clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType : 263 SkPath::kEvenOdd_FillType); 264 265 if (useRects) { 266 stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false); 267 stack.clipDevRect(rectB, gOps[op], false); 268 } else { 269 stack.clipDevPath(clipA, SkRegion::kIntersect_Op, false); 270 stack.clipDevPath(clipB, gOps[op], false); 271 } 272 273 REPORTER_ASSERT(reporter, !stack.isWideOpen()); 274 275 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound, 276 &isIntersectionOfRects); 277 278 if (useRects) { 279 REPORTER_ASSERT(reporter, isIntersectionOfRects == 280 (gOps[op] == SkRegion::kIntersect_Op)); 281 } else { 282 REPORTER_ASSERT(reporter, !isIntersectionOfRects); 283 } 284 285 SkASSERT(testCase < gNumCases); 286 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]); 287 ++testCase; 288 289 stack.restore(); 290 } 291 } 292} 293 294// Test out 'isWideOpen' entry point 295static void test_isWideOpen(skiatest::Reporter* reporter) { 296 297 SkRect rectA, rectB; 298 299 rectA.iset(10, 10, 40, 40); 300 rectB.iset(50, 50, 80, 80); 301 302 // Stack should initially be wide open 303 { 304 SkClipStack stack; 305 306 REPORTER_ASSERT(reporter, stack.isWideOpen()); 307 } 308 309 // Test out case where the user specifies a union that includes everything 310 { 311 SkClipStack stack; 312 313 SkPath clipA, clipB; 314 315 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5)); 316 clipA.setFillType(SkPath::kInverseEvenOdd_FillType); 317 318 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5)); 319 clipB.setFillType(SkPath::kInverseEvenOdd_FillType); 320 321 stack.clipDevPath(clipA, SkRegion::kReplace_Op, false); 322 stack.clipDevPath(clipB, SkRegion::kUnion_Op, false); 323 324 REPORTER_ASSERT(reporter, stack.isWideOpen()); 325 } 326 327 // Test out union w/ a wide open clip 328 { 329 SkClipStack stack; 330 331 stack.clipDevRect(rectA, SkRegion::kUnion_Op, false); 332 333 REPORTER_ASSERT(reporter, stack.isWideOpen()); 334 } 335 336 // Test out empty difference from a wide open clip 337 { 338 SkClipStack stack; 339 340 SkRect emptyRect; 341 emptyRect.setEmpty(); 342 343 stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false); 344 345 REPORTER_ASSERT(reporter, stack.isWideOpen()); 346 } 347 348 // Test out return to wide open 349 { 350 SkClipStack stack; 351 352 stack.save(); 353 354 stack.clipDevRect(rectA, SkRegion::kReplace_Op, false); 355 356 REPORTER_ASSERT(reporter, !stack.isWideOpen()); 357 358 stack.restore(); 359 360 REPORTER_ASSERT(reporter, stack.isWideOpen()); 361 } 362} 363 364static int count(const SkClipStack& stack) { 365 366 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); 367 368 const SkClipStack::Element* element = NULL; 369 int count = 0; 370 371 for (element = iter.prev(); element; element = iter.prev(), ++count) { 372 ; 373 } 374 375 return count; 376} 377 378// Test out SkClipStack's merging of rect clips. In particular exercise 379// merging of aa vs. bw rects. 380static void test_rect_merging(skiatest::Reporter* reporter) { 381 382 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50); 383 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80); 384 385 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90); 386 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60); 387 388 SkRect bound; 389 SkClipStack::BoundsType type; 390 bool isIntersectionOfRects; 391 392 // all bw overlapping - should merge 393 { 394 SkClipStack stack; 395 396 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false); 397 398 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); 399 400 REPORTER_ASSERT(reporter, 1 == count(stack)); 401 402 stack.getBounds(&bound, &type, &isIntersectionOfRects); 403 404 REPORTER_ASSERT(reporter, isIntersectionOfRects); 405 } 406 407 // all aa overlapping - should merge 408 { 409 SkClipStack stack; 410 411 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true); 412 413 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true); 414 415 REPORTER_ASSERT(reporter, 1 == count(stack)); 416 417 stack.getBounds(&bound, &type, &isIntersectionOfRects); 418 419 REPORTER_ASSERT(reporter, isIntersectionOfRects); 420 } 421 422 // mixed overlapping - should _not_ merge 423 { 424 SkClipStack stack; 425 426 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true); 427 428 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); 429 430 REPORTER_ASSERT(reporter, 2 == count(stack)); 431 432 stack.getBounds(&bound, &type, &isIntersectionOfRects); 433 434 REPORTER_ASSERT(reporter, !isIntersectionOfRects); 435 } 436 437 // mixed nested (bw inside aa) - should merge 438 { 439 SkClipStack stack; 440 441 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true); 442 443 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false); 444 445 REPORTER_ASSERT(reporter, 1 == count(stack)); 446 447 stack.getBounds(&bound, &type, &isIntersectionOfRects); 448 449 REPORTER_ASSERT(reporter, isIntersectionOfRects); 450 } 451 452 // mixed nested (aa inside bw) - should merge 453 { 454 SkClipStack stack; 455 456 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false); 457 458 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true); 459 460 REPORTER_ASSERT(reporter, 1 == count(stack)); 461 462 stack.getBounds(&bound, &type, &isIntersectionOfRects); 463 464 REPORTER_ASSERT(reporter, isIntersectionOfRects); 465 } 466 467 // reverse nested (aa inside bw) - should _not_ merge 468 { 469 SkClipStack stack; 470 471 stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false); 472 473 stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true); 474 475 REPORTER_ASSERT(reporter, 2 == count(stack)); 476 477 stack.getBounds(&bound, &type, &isIntersectionOfRects); 478 479 REPORTER_ASSERT(reporter, !isIntersectionOfRects); 480 } 481} 482 483/////////////////////////////////////////////////////////////////////////////////////////////////// 484 485#if SK_SUPPORT_GPU 486// Functions that add a shape to the clip stack. The shape is computed from a rectangle. 487// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the 488// stack. A fractional edge repeated in different elements may be rasterized fewer times using the 489// reduced stack. 490typedef void (*AddElementFunc) (const SkRect& rect, 491 bool invert, 492 SkRegion::Op op, 493 SkClipStack* stack); 494 495static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) { 496 SkPath path; 497 SkScalar rx = rect.width() / 10; 498 SkScalar ry = rect.height() / 20; 499 path.addRoundRect(rect, rx, ry); 500 if (invert) { 501 path.setFillType(SkPath::kInverseWinding_FillType); 502 } 503 stack->clipDevPath(path, op, false); 504}; 505 506static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) { 507 if (invert) { 508 SkPath path; 509 path.addRect(rect); 510 path.setFillType(SkPath::kInverseWinding_FillType); 511 stack->clipDevPath(path, op, false); 512 } else { 513 stack->clipDevRect(rect, op, false); 514 } 515}; 516 517static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) { 518 SkPath path; 519 path.addOval(rect); 520 if (invert) { 521 path.setFillType(SkPath::kInverseWinding_FillType); 522 } 523 stack->clipDevPath(path, op, false); 524}; 525 526static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) { 527 switch (element.getType()) { 528 case SkClipStack::Element::kRect_Type: 529 stack->clipDevRect(element.getRect(), element.getOp(), element.isAA()); 530 break; 531 case SkClipStack::Element::kPath_Type: 532 stack->clipDevPath(element.getPath(), element.getOp(), element.isAA()); 533 break; 534 case SkClipStack::Element::kEmpty_Type: 535 SkDEBUGFAIL("Why did the reducer produce an explicit empty."); 536 stack->clipEmpty(); 537 break; 538 } 539} 540 541static void add_elem_to_region(const SkClipStack::Element& element, 542 const SkIRect& bounds, 543 SkRegion* region) { 544 SkRegion elemRegion; 545 SkRegion boundsRgn(bounds); 546 547 switch (element.getType()) { 548 case SkClipStack::Element::kRect_Type: { 549 SkPath path; 550 path.addRect(element.getRect()); 551 elemRegion.setPath(path, boundsRgn); 552 break; 553 } 554 case SkClipStack::Element::kPath_Type: 555 elemRegion.setPath(element.getPath(), boundsRgn); 556 break; 557 case SkClipStack::Element::kEmpty_Type: 558 // 559 region->setEmpty(); 560 return; 561 } 562 region->op(elemRegion, element.getOp()); 563} 564 565// This can assist with debugging the clip stack reduction code when the test below fails. 566static void print_clip(const SkClipStack::Element& element) { 567 static const char* kOpStrs[] = { 568 "DF", 569 "IS", 570 "UN", 571 "XR", 572 "RD", 573 "RP", 574 }; 575 if (SkClipStack::Element::kEmpty_Type != element.getType()) { 576 const SkRect& bounds = element.getBounds(); 577 bool isRect = SkClipStack::Element::kRect_Type == element.getType(); 578 SkDebugf("%s %s %s [%f %f] x [%f %f]\n", 579 kOpStrs[element.getOp()], 580 (isRect ? "R" : "P"), 581 (element.isInverseFilled() ? "I" : " "), 582 bounds.fLeft, bounds.fRight, bounds.fTop, bounds.fBottom); 583 } else { 584 SkDebugf("EM\n"); 585 } 586} 587 588static void test_reduced_clip_stack(skiatest::Reporter* reporter) { 589 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that 590 // they are equal. 591 592 // All the clip elements will be contained within these bounds. 593 static const SkRect kBounds = SkRect::MakeWH(100, 100); 594 595 enum { 596 kNumTests = 200, 597 kMinElemsPerTest = 1, 598 kMaxElemsPerTest = 50, 599 }; 600 601 // min/max size of a clip element as a fraction of kBounds. 602 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5; 603 static const SkScalar kMaxElemSizeFrac = SK_Scalar1; 604 605 static const SkRegion::Op kOps[] = { 606 SkRegion::kDifference_Op, 607 SkRegion::kIntersect_Op, 608 SkRegion::kUnion_Op, 609 SkRegion::kXOR_Op, 610 SkRegion::kReverseDifference_Op, 611 SkRegion::kReplace_Op, 612 }; 613 614 // Replace operations short-circuit the optimizer. We want to make sure that we test this code 615 // path a little bit but we don't want it to prevent us from testing many longer traversals in 616 // the optimizer. 617 static const int kReplaceDiv = 4 * kMaxElemsPerTest; 618 619 // We want to test inverse fills. However, they are quite rare in practice so don't over do it. 620 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest; 621 622 static const AddElementFunc kElementFuncs[] = { 623 add_rect, 624 add_round_rect, 625 add_oval, 626 }; 627 628 SkRandom r; 629 630 for (int i = 0; i < kNumTests; ++i) { 631 // Randomly generate a clip stack. 632 SkClipStack stack; 633 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest); 634 for (int e = 0; e < numElems; ++e) { 635 SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))]; 636 if (op == SkRegion::kReplace_Op) { 637 if (r.nextU() % kReplaceDiv) { 638 --e; 639 continue; 640 } 641 } 642 643 // saves can change the clip stack behavior when an element is added. 644 bool doSave = r.nextBool(); 645 646 SkSize size = SkSize::Make( 647 SkScalarFloorToScalar(SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))), 648 SkScalarFloorToScalar(SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac)))); 649 650 SkPoint xy = {SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth)), 651 SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight))}; 652 653 SkRect rect = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight); 654 655 bool invert = r.nextBiasedBool(kFractionInverted); 656 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack); 657 if (doSave) { 658 stack.save(); 659 } 660 } 661 662 SkRect inflatedBounds = kBounds; 663 inflatedBounds.outset(kBounds.width() / 2, kBounds.height() / 2); 664 SkIRect inflatedIBounds; 665 inflatedBounds.roundOut(&inflatedIBounds); 666 667 typedef GrReducedClip::ElementList ElementList; 668 // Get the reduced version of the stack. 669 ElementList reducedClips; 670 671 GrReducedClip::InitialState initial; 672 GrReducedClip::GrReduceClipStack(stack, inflatedBounds, &reducedClips, &initial); 673 674 // Build a new clip stack based on the reduced clip elements 675 SkClipStack reducedStack; 676 if (GrReducedClip::kAllOut_InitialState == initial) { 677 // whether the result is bounded or not, the whole plane should start outside the clip. 678 reducedStack.clipEmpty(); 679 } 680 for (ElementList::Iter iter = reducedClips.headIter(); NULL != iter.get(); iter.next()) { 681 add_elem_to_stack(*iter.get(), &reducedStack); 682 } 683 684 // convert both the original stack and reduced stack to SkRegions and see if they're equal 685 SkRegion region; 686 SkRegion reducedRegion; 687 688 region.setRect(inflatedIBounds); 689 const SkClipStack::Element* element; 690 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); 691 while ((element = iter.next())) { 692 add_elem_to_region(*element, inflatedIBounds, ®ion); 693 } 694 695 reducedRegion.setRect(inflatedIBounds); 696 iter.reset(reducedStack, SkClipStack::Iter::kBottom_IterStart); 697 while ((element = iter.next())) { 698 add_elem_to_region(*element, inflatedIBounds, &reducedRegion); 699 } 700 701 REPORTER_ASSERT(reporter, region == reducedRegion); 702 } 703} 704 705#endif 706/////////////////////////////////////////////////////////////////////////////////////////////////// 707 708static void TestClipStack(skiatest::Reporter* reporter) { 709 SkClipStack stack; 710 711 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount()); 712 assert_count(reporter, stack, 0); 713 714 static const SkIRect gRects[] = { 715 { 0, 0, 100, 100 }, 716 { 25, 25, 125, 125 }, 717 { 0, 0, 1000, 1000 }, 718 { 0, 0, 75, 75 } 719 }; 720 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) { 721 stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op); 722 } 723 724 // all of the above rects should have been intersected, leaving only 1 rect 725 SkClipStack::B2TIter iter(stack); 726 const SkClipStack::Element* element = iter.next(); 727 SkRect answer; 728 answer.iset(25, 25, 75, 75); 729 730 REPORTER_ASSERT(reporter, NULL != element); 731 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); 732 REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp()); 733 REPORTER_ASSERT(reporter, element->getRect() == answer); 734 // now check that we only had one in our iterator 735 REPORTER_ASSERT(reporter, !iter.next()); 736 737 stack.reset(); 738 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount()); 739 assert_count(reporter, stack, 0); 740 741 test_assign_and_comparison(reporter); 742 test_iterators(reporter); 743 test_bounds(reporter, true); // once with rects 744 test_bounds(reporter, false); // once with paths 745 test_isWideOpen(reporter); 746 test_rect_merging(reporter); 747#if SK_SUPPORT_GPU 748 test_reduced_clip_stack(reporter); 749#endif 750} 751 752#include "TestClassDef.h" 753DEFINE_TESTCLASS("ClipStack", TestClipStackClass, TestClipStack) 754