1 /* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 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 static void test_assign_and_comparison(skiatest::Reporter* reporter) { 19 SkClipStack s; 20 bool doAA = false; 21 22 REPORTER_ASSERT(reporter, 0 == s.getSaveCount()); 23 24 // Build up a clip stack with a path, an empty clip, and a rect. 25 s.save(); 26 REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); 27 28 SkPath p; 29 p.moveTo(5, 6); 30 p.lineTo(7, 8); 31 p.lineTo(5, 9); 32 p.close(); 33 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA); 34 35 s.save(); 36 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 37 38 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4); 39 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); 40 r = SkRect::MakeLTRB(10, 11, 12, 13); 41 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); 42 43 s.save(); 44 REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); 45 46 r = SkRect::MakeLTRB(14, 15, 16, 17); 47 s.clipDevRect(r, SkRegion::kUnion_Op, doAA); 48 49 // Test that assignment works. 50 SkClipStack copy = s; 51 REPORTER_ASSERT(reporter, s == copy); 52 53 // Test that different save levels triggers not equal. 54 s.restore(); 55 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 56 REPORTER_ASSERT(reporter, s != copy); 57 58 // Test that an equal, but not copied version is equal. 59 s.save(); 60 REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); 61 r = SkRect::MakeLTRB(14, 15, 16, 17); 62 s.clipDevRect(r, SkRegion::kUnion_Op, doAA); 63 REPORTER_ASSERT(reporter, s == copy); 64 65 // Test that a different op on one level triggers not equal. 66 s.restore(); 67 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 68 s.save(); 69 REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); 70 r = SkRect::MakeLTRB(14, 15, 16, 17); 71 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); 72 REPORTER_ASSERT(reporter, s != copy); 73 74 // Test that version constructed with rect-path rather than a rect is still considered equal. 75 s.restore(); 76 s.save(); 77 SkPath rp; 78 rp.addRect(r); 79 s.clipDevPath(rp, SkRegion::kUnion_Op, doAA); 80 REPORTER_ASSERT(reporter, s == copy); 81 82 // Test that different rects triggers not equal. 83 s.restore(); 84 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 85 s.save(); 86 REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); 87 88 r = SkRect::MakeLTRB(24, 25, 26, 27); 89 s.clipDevRect(r, SkRegion::kUnion_Op, doAA); 90 REPORTER_ASSERT(reporter, s != copy); 91 92 // Sanity check 93 s.restore(); 94 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 95 96 copy.restore(); 97 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount()); 98 REPORTER_ASSERT(reporter, s == copy); 99 s.restore(); 100 REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); 101 copy.restore(); 102 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount()); 103 REPORTER_ASSERT(reporter, s == copy); 104 105 // Test that different paths triggers not equal. 106 s.restore(); 107 REPORTER_ASSERT(reporter, 0 == s.getSaveCount()); 108 s.save(); 109 REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); 110 111 p.addRect(r); 112 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA); 113 REPORTER_ASSERT(reporter, s != copy); 114 } 115 116 static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack, 117 int count) { 118 SkClipStack::B2TIter iter(stack); 119 int counter = 0; 120 while (iter.next()) { 121 counter += 1; 122 } 123 REPORTER_ASSERT(reporter, count == counter); 124 } 125 126 // Exercise the SkClipStack's bottom to top and bidirectional iterators 127 // (including the skipToTopmost functionality) 128 static void test_iterators(skiatest::Reporter* reporter) { 129 SkClipStack stack; 130 131 static const SkRect gRects[] = { 132 { 0, 0, 40, 40 }, 133 { 60, 0, 100, 40 }, 134 { 0, 60, 40, 100 }, 135 { 60, 60, 100, 100 } 136 }; 137 138 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) { 139 // the union op will prevent these from being fused together 140 stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false); 141 } 142 143 assert_count(reporter, stack, 4); 144 145 // bottom to top iteration 146 { 147 const SkClipStack::Element* element = nullptr; 148 149 SkClipStack::B2TIter iter(stack); 150 int i; 151 152 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) { 153 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); 154 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]); 155 } 156 157 SkASSERT(i == 4); 158 } 159 160 // top to bottom iteration 161 { 162 const SkClipStack::Element* element = nullptr; 163 164 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); 165 int i; 166 167 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) { 168 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); 169 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]); 170 } 171 172 SkASSERT(i == -1); 173 } 174 175 // skipToTopmost 176 { 177 const SkClipStack::Element* element = nullptr; 178 179 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); 180 181 element = iter.skipToTopmost(SkRegion::kUnion_Op); 182 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); 183 REPORTER_ASSERT(reporter, element->getRect() == gRects[3]); 184 } 185 } 186 187 // Exercise the SkClipStack's getConservativeBounds computation 188 static void test_bounds(skiatest::Reporter* reporter, SkClipStack::Element::Type primType) { 189 static const int gNumCases = 20; 190 static const SkRect gAnswerRectsBW[gNumCases] = { 191 // A op B 192 { 40, 40, 50, 50 }, 193 { 10, 10, 50, 50 }, 194 { 10, 10, 80, 80 }, 195 { 10, 10, 80, 80 }, 196 { 40, 40, 80, 80 }, 197 198 // invA op B 199 { 40, 40, 80, 80 }, 200 { 0, 0, 100, 100 }, 201 { 0, 0, 100, 100 }, 202 { 0, 0, 100, 100 }, 203 { 40, 40, 50, 50 }, 204 205 // A op invB 206 { 10, 10, 50, 50 }, 207 { 40, 40, 50, 50 }, 208 { 0, 0, 100, 100 }, 209 { 0, 0, 100, 100 }, 210 { 0, 0, 100, 100 }, 211 212 // invA op invB 213 { 0, 0, 100, 100 }, 214 { 40, 40, 80, 80 }, 215 { 0, 0, 100, 100 }, 216 { 10, 10, 80, 80 }, 217 { 10, 10, 50, 50 }, 218 }; 219 220 static const SkRegion::Op gOps[] = { 221 SkRegion::kIntersect_Op, 222 SkRegion::kDifference_Op, 223 SkRegion::kUnion_Op, 224 SkRegion::kXOR_Op, 225 SkRegion::kReverseDifference_Op 226 }; 227 228 SkRect rectA, rectB; 229 230 rectA.iset(10, 10, 50, 50); 231 rectB.iset(40, 40, 80, 80); 232 233 SkRRect rrectA, rrectB; 234 rrectA.setOval(rectA); 235 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2)); 236 237 SkPath pathA, pathB; 238 239 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5)); 240 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5)); 241 242 SkClipStack stack; 243 SkRect devClipBound; 244 bool isIntersectionOfRects = false; 245 246 int testCase = 0; 247 int numBitTests = SkClipStack::Element::kPath_Type == primType ? 4 : 1; 248 for (int invBits = 0; invBits < numBitTests; ++invBits) { 249 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) { 250 251 stack.save(); 252 bool doInvA = SkToBool(invBits & 1); 253 bool doInvB = SkToBool(invBits & 2); 254 255 pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType : 256 SkPath::kEvenOdd_FillType); 257 pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType : 258 SkPath::kEvenOdd_FillType); 259 260 switch (primType) { 261 case SkClipStack::Element::kEmpty_Type: 262 SkDEBUGFAIL("Don't call this with kEmpty."); 263 break; 264 case SkClipStack::Element::kRect_Type: 265 stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false); 266 stack.clipDevRect(rectB, gOps[op], false); 267 break; 268 case SkClipStack::Element::kRRect_Type: 269 stack.clipDevRRect(rrectA, SkRegion::kIntersect_Op, false); 270 stack.clipDevRRect(rrectB, gOps[op], false); 271 break; 272 case SkClipStack::Element::kPath_Type: 273 stack.clipDevPath(pathA, SkRegion::kIntersect_Op, false); 274 stack.clipDevPath(pathB, gOps[op], false); 275 break; 276 } 277 278 REPORTER_ASSERT(reporter, !stack.isWideOpen()); 279 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID()); 280 281 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound, 282 &isIntersectionOfRects); 283 284 if (SkClipStack::Element::kRect_Type == primType) { 285 REPORTER_ASSERT(reporter, isIntersectionOfRects == 286 (gOps[op] == SkRegion::kIntersect_Op)); 287 } else { 288 REPORTER_ASSERT(reporter, !isIntersectionOfRects); 289 } 290 291 SkASSERT(testCase < gNumCases); 292 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]); 293 ++testCase; 294 295 stack.restore(); 296 } 297 } 298 } 299 300 // Test out 'isWideOpen' entry point 301 static void test_isWideOpen(skiatest::Reporter* reporter) { 302 { 303 // Empty stack is wide open. Wide open stack means that gen id is wide open. 304 SkClipStack stack; 305 REPORTER_ASSERT(reporter, stack.isWideOpen()); 306 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); 307 } 308 309 SkRect rectA, rectB; 310 311 rectA.iset(10, 10, 40, 40); 312 rectB.iset(50, 50, 80, 80); 313 314 // Stack should initially be wide open 315 { 316 SkClipStack stack; 317 318 REPORTER_ASSERT(reporter, stack.isWideOpen()); 319 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); 320 } 321 322 // Test out case where the user specifies a union that includes everything 323 { 324 SkClipStack stack; 325 326 SkPath clipA, clipB; 327 328 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5)); 329 clipA.setFillType(SkPath::kInverseEvenOdd_FillType); 330 331 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5)); 332 clipB.setFillType(SkPath::kInverseEvenOdd_FillType); 333 334 stack.clipDevPath(clipA, SkRegion::kReplace_Op, false); 335 stack.clipDevPath(clipB, SkRegion::kUnion_Op, false); 336 337 REPORTER_ASSERT(reporter, stack.isWideOpen()); 338 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); 339 } 340 341 // Test out union w/ a wide open clip 342 { 343 SkClipStack stack; 344 345 stack.clipDevRect(rectA, SkRegion::kUnion_Op, false); 346 347 REPORTER_ASSERT(reporter, stack.isWideOpen()); 348 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); 349 } 350 351 // Test out empty difference from a wide open clip 352 { 353 SkClipStack stack; 354 355 SkRect emptyRect; 356 emptyRect.setEmpty(); 357 358 stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false); 359 360 REPORTER_ASSERT(reporter, stack.isWideOpen()); 361 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); 362 } 363 364 // Test out return to wide open 365 { 366 SkClipStack stack; 367 368 stack.save(); 369 370 stack.clipDevRect(rectA, SkRegion::kReplace_Op, false); 371 372 REPORTER_ASSERT(reporter, !stack.isWideOpen()); 373 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID()); 374 375 stack.restore(); 376 377 REPORTER_ASSERT(reporter, stack.isWideOpen()); 378 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); 379 } 380 } 381 382 static int count(const SkClipStack& stack) { 383 384 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); 385 386 const SkClipStack::Element* element = nullptr; 387 int count = 0; 388 389 for (element = iter.prev(); element; element = iter.prev(), ++count) { 390 ; 391 } 392 393 return count; 394 } 395 396 static void test_rect_inverse_fill(skiatest::Reporter* reporter) { 397 // non-intersecting rectangles 398 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10); 399 400 SkPath path; 401 path.addRect(rect); 402 path.toggleInverseFillType(); 403 SkClipStack stack; 404 stack.clipDevPath(path, SkRegion::kIntersect_Op, false); 405 406 SkRect bounds; 407 SkClipStack::BoundsType boundsType; 408 stack.getBounds(&bounds, &boundsType); 409 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType); 410 REPORTER_ASSERT(reporter, bounds == rect); 411 } 412 413 static void test_rect_replace(skiatest::Reporter* reporter) { 414 SkRect rect = SkRect::MakeWH(100, 100); 415 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100); 416 417 SkRect bound; 418 SkClipStack::BoundsType type; 419 bool isIntersectionOfRects; 420 421 // Adding a new rect with the replace operator should not increase 422 // the stack depth. BW replacing BW. 423 { 424 SkClipStack stack; 425 REPORTER_ASSERT(reporter, 0 == count(stack)); 426 stack.clipDevRect(rect, SkRegion::kReplace_Op, false); 427 REPORTER_ASSERT(reporter, 1 == count(stack)); 428 stack.clipDevRect(rect, SkRegion::kReplace_Op, false); 429 REPORTER_ASSERT(reporter, 1 == count(stack)); 430 } 431 432 // Adding a new rect with the replace operator should not increase 433 // the stack depth. AA replacing AA. 434 { 435 SkClipStack stack; 436 REPORTER_ASSERT(reporter, 0 == count(stack)); 437 stack.clipDevRect(rect, SkRegion::kReplace_Op, true); 438 REPORTER_ASSERT(reporter, 1 == count(stack)); 439 stack.clipDevRect(rect, SkRegion::kReplace_Op, true); 440 REPORTER_ASSERT(reporter, 1 == count(stack)); 441 } 442 443 // Adding a new rect with the replace operator should not increase 444 // the stack depth. BW replacing AA replacing BW. 445 { 446 SkClipStack stack; 447 REPORTER_ASSERT(reporter, 0 == count(stack)); 448 stack.clipDevRect(rect, SkRegion::kReplace_Op, false); 449 REPORTER_ASSERT(reporter, 1 == count(stack)); 450 stack.clipDevRect(rect, SkRegion::kReplace_Op, true); 451 REPORTER_ASSERT(reporter, 1 == count(stack)); 452 stack.clipDevRect(rect, SkRegion::kReplace_Op, false); 453 REPORTER_ASSERT(reporter, 1 == count(stack)); 454 } 455 456 // Make sure replace clip rects don't collapse too much. 457 { 458 SkClipStack stack; 459 stack.clipDevRect(rect, SkRegion::kReplace_Op, false); 460 stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false); 461 REPORTER_ASSERT(reporter, 1 == count(stack)); 462 463 stack.save(); 464 stack.clipDevRect(rect, SkRegion::kReplace_Op, false); 465 REPORTER_ASSERT(reporter, 2 == count(stack)); 466 stack.getBounds(&bound, &type, &isIntersectionOfRects); 467 REPORTER_ASSERT(reporter, bound == rect); 468 stack.restore(); 469 REPORTER_ASSERT(reporter, 1 == count(stack)); 470 471 stack.save(); 472 stack.clipDevRect(rect, SkRegion::kReplace_Op, false); 473 stack.clipDevRect(rect, SkRegion::kReplace_Op, false); 474 REPORTER_ASSERT(reporter, 2 == count(stack)); 475 stack.restore(); 476 REPORTER_ASSERT(reporter, 1 == count(stack)); 477 478 stack.save(); 479 stack.clipDevRect(rect, SkRegion::kReplace_Op, false); 480 stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false); 481 stack.clipDevRect(rect, SkRegion::kReplace_Op, false); 482 REPORTER_ASSERT(reporter, 2 == count(stack)); 483 stack.restore(); 484 REPORTER_ASSERT(reporter, 1 == count(stack)); 485 } 486 } 487 488 // Simplified path-based version of test_rect_replace. 489 static void test_path_replace(skiatest::Reporter* reporter) { 490 SkRect rect = SkRect::MakeWH(100, 100); 491 SkPath path; 492 path.addCircle(50, 50, 50); 493 494 // Replace operation doesn't grow the stack. 495 { 496 SkClipStack stack; 497 REPORTER_ASSERT(reporter, 0 == count(stack)); 498 stack.clipDevPath(path, SkRegion::kReplace_Op, false); 499 REPORTER_ASSERT(reporter, 1 == count(stack)); 500 stack.clipDevPath(path, SkRegion::kReplace_Op, false); 501 REPORTER_ASSERT(reporter, 1 == count(stack)); 502 } 503 504 // Replacing rect with path. 505 { 506 SkClipStack stack; 507 stack.clipDevRect(rect, SkRegion::kReplace_Op, true); 508 REPORTER_ASSERT(reporter, 1 == count(stack)); 509 stack.clipDevPath(path, SkRegion::kReplace_Op, true); 510 REPORTER_ASSERT(reporter, 1 == count(stack)); 511 } 512 } 513 514 // Test out SkClipStack's merging of rect clips. In particular exercise 515 // merging of aa vs. bw rects. 516 static void test_rect_merging(skiatest::Reporter* reporter) { 517 518 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50); 519 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80); 520 521 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90); 522 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60); 523 524 SkRect bound; 525 SkClipStack::BoundsType type; 526 bool isIntersectionOfRects; 527 528 // all bw overlapping - should merge 529 { 530 SkClipStack stack; 531 532 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false); 533 534 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); 535 536 REPORTER_ASSERT(reporter, 1 == count(stack)); 537 538 stack.getBounds(&bound, &type, &isIntersectionOfRects); 539 540 REPORTER_ASSERT(reporter, isIntersectionOfRects); 541 } 542 543 // all aa overlapping - should merge 544 { 545 SkClipStack stack; 546 547 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true); 548 549 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true); 550 551 REPORTER_ASSERT(reporter, 1 == count(stack)); 552 553 stack.getBounds(&bound, &type, &isIntersectionOfRects); 554 555 REPORTER_ASSERT(reporter, isIntersectionOfRects); 556 } 557 558 // mixed overlapping - should _not_ merge 559 { 560 SkClipStack stack; 561 562 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true); 563 564 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); 565 566 REPORTER_ASSERT(reporter, 2 == count(stack)); 567 568 stack.getBounds(&bound, &type, &isIntersectionOfRects); 569 570 REPORTER_ASSERT(reporter, !isIntersectionOfRects); 571 } 572 573 // mixed nested (bw inside aa) - should merge 574 { 575 SkClipStack stack; 576 577 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true); 578 579 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false); 580 581 REPORTER_ASSERT(reporter, 1 == count(stack)); 582 583 stack.getBounds(&bound, &type, &isIntersectionOfRects); 584 585 REPORTER_ASSERT(reporter, isIntersectionOfRects); 586 } 587 588 // mixed nested (aa inside bw) - should merge 589 { 590 SkClipStack stack; 591 592 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false); 593 594 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true); 595 596 REPORTER_ASSERT(reporter, 1 == count(stack)); 597 598 stack.getBounds(&bound, &type, &isIntersectionOfRects); 599 600 REPORTER_ASSERT(reporter, isIntersectionOfRects); 601 } 602 603 // reverse nested (aa inside bw) - should _not_ merge 604 { 605 SkClipStack stack; 606 607 stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false); 608 609 stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true); 610 611 REPORTER_ASSERT(reporter, 2 == count(stack)); 612 613 stack.getBounds(&bound, &type, &isIntersectionOfRects); 614 615 REPORTER_ASSERT(reporter, !isIntersectionOfRects); 616 } 617 } 618 619 static void test_quickContains(skiatest::Reporter* reporter) { 620 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40); 621 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30); 622 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50); 623 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50); 624 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110); 625 626 SkPath insideCircle; 627 insideCircle.addCircle(25, 25, 5); 628 SkPath intersectingCircle; 629 intersectingCircle.addCircle(25, 40, 10); 630 SkPath outsideCircle; 631 outsideCircle.addCircle(25, 25, 50); 632 SkPath nonIntersectingCircle; 633 nonIntersectingCircle.addCircle(100, 100, 5); 634 635 { 636 SkClipStack stack; 637 stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false); 638 // return false because quickContains currently does not care for kDifference_Op 639 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 640 } 641 642 // Replace Op tests 643 { 644 SkClipStack stack; 645 stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false); 646 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); 647 } 648 649 { 650 SkClipStack stack; 651 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false); 652 stack.save(); // To prevent in-place substitution by replace OP 653 stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false); 654 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); 655 stack.restore(); 656 } 657 658 { 659 SkClipStack stack; 660 stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false); 661 stack.save(); // To prevent in-place substitution by replace OP 662 stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false); 663 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 664 stack.restore(); 665 } 666 667 // Verify proper traversal of multi-element clip 668 { 669 SkClipStack stack; 670 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false); 671 // Use a path for second clip to prevent in-place intersection 672 stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false); 673 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 674 } 675 676 // Intersect Op tests with rectangles 677 { 678 SkClipStack stack; 679 stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false); 680 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); 681 } 682 683 { 684 SkClipStack stack; 685 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false); 686 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 687 } 688 689 { 690 SkClipStack stack; 691 stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false); 692 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 693 } 694 695 { 696 SkClipStack stack; 697 stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false); 698 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 699 } 700 701 // Intersect Op tests with circle paths 702 { 703 SkClipStack stack; 704 stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false); 705 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); 706 } 707 708 { 709 SkClipStack stack; 710 stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false); 711 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 712 } 713 714 { 715 SkClipStack stack; 716 stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false); 717 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 718 } 719 720 { 721 SkClipStack stack; 722 stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false); 723 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 724 } 725 726 // Intersect Op tests with inverse filled rectangles 727 { 728 SkClipStack stack; 729 SkPath path; 730 path.addRect(outsideRect); 731 path.toggleInverseFillType(); 732 stack.clipDevPath(path, SkRegion::kIntersect_Op, false); 733 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 734 } 735 736 { 737 SkClipStack stack; 738 SkPath path; 739 path.addRect(insideRect); 740 path.toggleInverseFillType(); 741 stack.clipDevPath(path, SkRegion::kIntersect_Op, false); 742 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 743 } 744 745 { 746 SkClipStack stack; 747 SkPath path; 748 path.addRect(intersectingRect); 749 path.toggleInverseFillType(); 750 stack.clipDevPath(path, SkRegion::kIntersect_Op, false); 751 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 752 } 753 754 { 755 SkClipStack stack; 756 SkPath path; 757 path.addRect(nonIntersectingRect); 758 path.toggleInverseFillType(); 759 stack.clipDevPath(path, SkRegion::kIntersect_Op, false); 760 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); 761 } 762 763 // Intersect Op tests with inverse filled circles 764 { 765 SkClipStack stack; 766 SkPath path = outsideCircle; 767 path.toggleInverseFillType(); 768 stack.clipDevPath(path, SkRegion::kIntersect_Op, false); 769 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 770 } 771 772 { 773 SkClipStack stack; 774 SkPath path = insideCircle; 775 path.toggleInverseFillType(); 776 stack.clipDevPath(path, SkRegion::kIntersect_Op, false); 777 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 778 } 779 780 { 781 SkClipStack stack; 782 SkPath path = intersectingCircle; 783 path.toggleInverseFillType(); 784 stack.clipDevPath(path, SkRegion::kIntersect_Op, false); 785 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); 786 } 787 788 { 789 SkClipStack stack; 790 SkPath path = nonIntersectingCircle; 791 path.toggleInverseFillType(); 792 stack.clipDevPath(path, SkRegion::kIntersect_Op, false); 793 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); 794 } 795 } 796 797 /////////////////////////////////////////////////////////////////////////////////////////////////// 798 799 #if SK_SUPPORT_GPU 800 // Functions that add a shape to the clip stack. The shape is computed from a rectangle. 801 // AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the 802 // stack. A fractional edge repeated in different elements may be rasterized fewer times using the 803 // reduced stack. 804 typedef void (*AddElementFunc) (const SkRect& rect, 805 bool invert, 806 SkRegion::Op op, 807 SkClipStack* stack); 808 809 static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) { 810 SkScalar rx = rect.width() / 10; 811 SkScalar ry = rect.height() / 20; 812 if (invert) { 813 SkPath path; 814 path.addRoundRect(rect, rx, ry); 815 path.setFillType(SkPath::kInverseWinding_FillType); 816 stack->clipDevPath(path, op, false); 817 } else { 818 SkRRect rrect; 819 rrect.setRectXY(rect, rx, ry); 820 stack->clipDevRRect(rrect, op, false); 821 } 822 }; 823 824 static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) { 825 if (invert) { 826 SkPath path; 827 path.addRect(rect); 828 path.setFillType(SkPath::kInverseWinding_FillType); 829 stack->clipDevPath(path, op, false); 830 } else { 831 stack->clipDevRect(rect, op, false); 832 } 833 }; 834 835 static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) { 836 SkPath path; 837 path.addOval(rect); 838 if (invert) { 839 path.setFillType(SkPath::kInverseWinding_FillType); 840 } 841 stack->clipDevPath(path, op, false); 842 }; 843 844 static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) { 845 switch (element.getType()) { 846 case SkClipStack::Element::kRect_Type: 847 stack->clipDevRect(element.getRect(), element.getOp(), element.isAA()); 848 break; 849 case SkClipStack::Element::kRRect_Type: 850 stack->clipDevRRect(element.getRRect(), element.getOp(), element.isAA()); 851 break; 852 case SkClipStack::Element::kPath_Type: 853 stack->clipDevPath(element.getPath(), element.getOp(), element.isAA()); 854 break; 855 case SkClipStack::Element::kEmpty_Type: 856 SkDEBUGFAIL("Why did the reducer produce an explicit empty."); 857 stack->clipEmpty(); 858 break; 859 } 860 } 861 862 static void add_elem_to_region(const SkClipStack::Element& element, 863 const SkIRect& bounds, 864 SkRegion* region) { 865 SkRegion elemRegion; 866 SkRegion boundsRgn(bounds); 867 SkPath path; 868 869 switch (element.getType()) { 870 case SkClipStack::Element::kEmpty_Type: 871 elemRegion.setEmpty(); 872 break; 873 default: 874 element.asPath(&path); 875 elemRegion.setPath(path, boundsRgn); 876 break; 877 } 878 region->op(elemRegion, element.getOp()); 879 } 880 881 static void test_reduced_clip_stack(skiatest::Reporter* reporter) { 882 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that 883 // they are equal. 884 885 // All the clip elements will be contained within these bounds. 886 static const SkRect kBounds = SkRect::MakeWH(100, 100); 887 888 enum { 889 kNumTests = 200, 890 kMinElemsPerTest = 1, 891 kMaxElemsPerTest = 50, 892 }; 893 894 // min/max size of a clip element as a fraction of kBounds. 895 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5; 896 static const SkScalar kMaxElemSizeFrac = SK_Scalar1; 897 898 static const SkRegion::Op kOps[] = { 899 SkRegion::kDifference_Op, 900 SkRegion::kIntersect_Op, 901 SkRegion::kUnion_Op, 902 SkRegion::kXOR_Op, 903 SkRegion::kReverseDifference_Op, 904 SkRegion::kReplace_Op, 905 }; 906 907 // Replace operations short-circuit the optimizer. We want to make sure that we test this code 908 // path a little bit but we don't want it to prevent us from testing many longer traversals in 909 // the optimizer. 910 static const int kReplaceDiv = 4 * kMaxElemsPerTest; 911 912 // We want to test inverse fills. However, they are quite rare in practice so don't over do it. 913 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest; 914 915 static const AddElementFunc kElementFuncs[] = { 916 add_rect, 917 add_round_rect, 918 add_oval, 919 }; 920 921 SkRandom r; 922 923 for (int i = 0; i < kNumTests; ++i) { 924 // Randomly generate a clip stack. 925 SkClipStack stack; 926 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest); 927 for (int e = 0; e < numElems; ++e) { 928 SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))]; 929 if (op == SkRegion::kReplace_Op) { 930 if (r.nextU() % kReplaceDiv) { 931 --e; 932 continue; 933 } 934 } 935 936 // saves can change the clip stack behavior when an element is added. 937 bool doSave = r.nextBool(); 938 939 SkSize size = SkSize::Make( 940 SkScalarFloorToScalar(SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))), 941 SkScalarFloorToScalar(SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac)))); 942 943 SkPoint xy = {SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth)), 944 SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight))}; 945 946 SkRect rect = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight); 947 948 bool invert = r.nextBiasedBool(kFractionInverted); 949 950 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack); 951 if (doSave) { 952 stack.save(); 953 } 954 } 955 956 SkRect inflatedBounds = kBounds; 957 inflatedBounds.outset(kBounds.width() / 2, kBounds.height() / 2); 958 SkIRect inflatedIBounds; 959 inflatedBounds.roundOut(&inflatedIBounds); 960 961 typedef GrReducedClip::ElementList ElementList; 962 // Get the reduced version of the stack. 963 ElementList reducedClips; 964 int32_t reducedGenID; 965 GrReducedClip::InitialState initial; 966 SkIRect tBounds(inflatedIBounds); 967 SkIRect* tightBounds = r.nextBool() ? &tBounds : nullptr; 968 GrReducedClip::ReduceClipStack(stack, 969 inflatedIBounds, 970 &reducedClips, 971 &reducedGenID, 972 &initial, 973 tightBounds); 974 975 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID); 976 977 // Build a new clip stack based on the reduced clip elements 978 SkClipStack reducedStack; 979 if (GrReducedClip::kAllOut_InitialState == initial) { 980 // whether the result is bounded or not, the whole plane should start outside the clip. 981 reducedStack.clipEmpty(); 982 } 983 for (ElementList::Iter iter = reducedClips.headIter(); iter.get(); iter.next()) { 984 add_elem_to_stack(*iter.get(), &reducedStack); 985 } 986 987 // GrReducedClipStack assumes that the final result is clipped to the returned bounds 988 if (tightBounds) { 989 reducedStack.clipDevRect(*tightBounds, SkRegion::kIntersect_Op); 990 } 991 992 // convert both the original stack and reduced stack to SkRegions and see if they're equal 993 SkRegion region; 994 SkRegion reducedRegion; 995 996 region.setRect(inflatedIBounds); 997 const SkClipStack::Element* element; 998 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); 999 while ((element = iter.next())) { 1000 add_elem_to_region(*element, inflatedIBounds, ®ion); 1001 } 1002 1003 reducedRegion.setRect(inflatedIBounds); 1004 iter.reset(reducedStack, SkClipStack::Iter::kBottom_IterStart); 1005 while ((element = iter.next())) { 1006 add_elem_to_region(*element, inflatedIBounds, &reducedRegion); 1007 } 1008 SkString testCase; 1009 testCase.printf("Iteration %d", i); 1010 REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str()); 1011 } 1012 } 1013 1014 #if defined(WIN32) 1015 #define SUPPRESS_VISIBILITY_WARNING 1016 #else 1017 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden"))) 1018 #endif 1019 1020 static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) { 1021 { 1022 SkClipStack stack; 1023 stack.clipDevRect(SkRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op, true); 1024 stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkRegion::kReplace_Op, true); 1025 SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100); 1026 1027 GrReducedClip::ElementList reducedClips; 1028 int32_t reducedGenID; 1029 GrReducedClip::InitialState initial; 1030 SkIRect tightBounds; 1031 1032 GrReducedClip::ReduceClipStack(stack, 1033 inflatedIBounds, 1034 &reducedClips, 1035 &reducedGenID, 1036 &initial, 1037 &tightBounds); 1038 1039 REPORTER_ASSERT(reporter, reducedClips.count() == 1); 1040 // Clips will be cached based on the generation id. Make sure the gen id is valid. 1041 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID); 1042 } 1043 { 1044 SkClipStack stack; 1045 1046 // Create a clip with following 25.3, 25.3 boxes which are 25 apart: 1047 // A B 1048 // C D 1049 1050 stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kReplace_Op, true); 1051 int32_t genIDA = stack.getTopmostGenID(); 1052 stack.clipDevRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true); 1053 int32_t genIDB = stack.getTopmostGenID(); 1054 stack.clipDevRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true); 1055 int32_t genIDC = stack.getTopmostGenID(); 1056 stack.clipDevRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true); 1057 int32_t genIDD = stack.getTopmostGenID(); 1058 1059 1060 #define XYWH SkIRect::MakeXYWH 1061 1062 SkIRect unused; 1063 unused.setEmpty(); 1064 SkIRect stackBounds = XYWH(0, 0, 76, 76); 1065 1066 // The base test is to test each rect in two ways: 1067 // 1) The box dimensions. (Should reduce to "all in", no elements). 1068 // 2) A bit over the box dimensions. 1069 // In the case 2, test that the generation id is what is expected. 1070 // The rects are of fractional size so that case 2 never gets optimized to an empty element 1071 // list. 1072 1073 // Not passing in tighter bounds is tested for consistency. 1074 static const struct SUPPRESS_VISIBILITY_WARNING { 1075 SkIRect testBounds; 1076 int reducedClipCount; 1077 int32_t reducedGenID; 1078 GrReducedClip::InitialState initialState; 1079 SkIRect tighterBounds; // If this is empty, the query will not pass tighter bounds 1080 // parameter. 1081 } testCases[] = { 1082 // Rect A. 1083 { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 0, 25, 25) }, 1084 { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused }, 1085 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, XYWH(0, 0, 27, 27)}, 1086 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, unused }, 1087 1088 // Rect B. 1089 { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 0, 25, 25) }, 1090 { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused }, 1091 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, XYWH(50, 0, 26, 27) }, 1092 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, unused }, 1093 1094 // Rect C. 1095 { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 50, 25, 25) }, 1096 { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused }, 1097 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, XYWH(0, 50, 27, 26) }, 1098 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, unused }, 1099 1100 // Rect D. 1101 { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused }, 1102 { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 50, 25, 25)}, 1103 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState, unused }, 1104 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState, XYWH(50, 50, 26, 26)}, 1105 1106 // Other tests: 1107 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused }, 1108 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, stackBounds }, 1109 1110 // Rect in the middle, touches none. 1111 { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, unused }, 1112 { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, XYWH(26, 26, 24, 24) }, 1113 1114 // Rect in the middle, touches all the rects. GenID is the last rect. 1115 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused }, 1116 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, XYWH(24, 24, 27, 27) }, 1117 }; 1118 1119 #undef XYWH 1120 1121 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) { 1122 GrReducedClip::ElementList reducedClips; 1123 int32_t reducedGenID; 1124 GrReducedClip::InitialState initial; 1125 SkIRect tightBounds; 1126 1127 GrReducedClip::ReduceClipStack(stack, 1128 testCases[i].testBounds, 1129 &reducedClips, 1130 &reducedGenID, 1131 &initial, 1132 testCases[i].tighterBounds.isEmpty() ? nullptr : &tightBounds); 1133 1134 REPORTER_ASSERT(reporter, reducedClips.count() == testCases[i].reducedClipCount); 1135 SkASSERT(reducedClips.count() == testCases[i].reducedClipCount); 1136 REPORTER_ASSERT(reporter, reducedGenID == testCases[i].reducedGenID); 1137 SkASSERT(reducedGenID == testCases[i].reducedGenID); 1138 REPORTER_ASSERT(reporter, initial == testCases[i].initialState); 1139 SkASSERT(initial == testCases[i].initialState); 1140 if (!testCases[i].tighterBounds.isEmpty()) { 1141 REPORTER_ASSERT(reporter, tightBounds == testCases[i].tighterBounds); 1142 SkASSERT(tightBounds == testCases[i].tighterBounds); 1143 } 1144 } 1145 } 1146 } 1147 1148 static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) { 1149 SkClipStack stack; 1150 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op); 1151 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), SkRegion::kReplace_Op); 1152 SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100); 1153 1154 GrReducedClip::ElementList reducedClips; 1155 int32_t reducedGenID; 1156 GrReducedClip::InitialState initial; 1157 SkIRect tightBounds; 1158 1159 // At the time, this would crash. 1160 GrReducedClip::ReduceClipStack(stack, 1161 inflatedIBounds, 1162 &reducedClips, 1163 &reducedGenID, 1164 &initial, 1165 &tightBounds); 1166 1167 REPORTER_ASSERT(reporter, 0 == reducedClips.count()); 1168 } 1169 1170 #endif 1171 1172 DEF_TEST(ClipStack, reporter) { 1173 SkClipStack stack; 1174 1175 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount()); 1176 assert_count(reporter, stack, 0); 1177 1178 static const SkIRect gRects[] = { 1179 { 0, 0, 100, 100 }, 1180 { 25, 25, 125, 125 }, 1181 { 0, 0, 1000, 1000 }, 1182 { 0, 0, 75, 75 } 1183 }; 1184 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) { 1185 stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op); 1186 } 1187 1188 // all of the above rects should have been intersected, leaving only 1 rect 1189 SkClipStack::B2TIter iter(stack); 1190 const SkClipStack::Element* element = iter.next(); 1191 SkRect answer; 1192 answer.iset(25, 25, 75, 75); 1193 1194 REPORTER_ASSERT(reporter, element); 1195 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); 1196 REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp()); 1197 REPORTER_ASSERT(reporter, element->getRect() == answer); 1198 // now check that we only had one in our iterator 1199 REPORTER_ASSERT(reporter, !iter.next()); 1200 1201 stack.reset(); 1202 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount()); 1203 assert_count(reporter, stack, 0); 1204 1205 test_assign_and_comparison(reporter); 1206 test_iterators(reporter); 1207 test_bounds(reporter, SkClipStack::Element::kRect_Type); 1208 test_bounds(reporter, SkClipStack::Element::kRRect_Type); 1209 test_bounds(reporter, SkClipStack::Element::kPath_Type); 1210 test_isWideOpen(reporter); 1211 test_rect_merging(reporter); 1212 test_rect_replace(reporter); 1213 test_rect_inverse_fill(reporter); 1214 test_path_replace(reporter); 1215 test_quickContains(reporter); 1216 #if SK_SUPPORT_GPU 1217 test_reduced_clip_stack(reporter); 1218 test_reduced_clip_stack_genid(reporter); 1219 test_reduced_clip_stack_no_aa_crash(reporter); 1220 #endif 1221 } 1222