OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "GrReducedClip.h" | 8 #include "GrReducedClip.h" |
9 | 9 |
10 #include "GrAppliedClip.h" | 10 #include "GrAppliedClip.h" |
(...skipping 10 matching lines...) Expand all Loading... |
21 | 21 |
22 typedef SkClipStack::Element Element; | 22 typedef SkClipStack::Element Element; |
23 | 23 |
24 /** | 24 /** |
25 * There are plenty of optimizations that could be added here. Maybe flips could
be folded into | 25 * There are plenty of optimizations that could be added here. Maybe flips could
be folded into |
26 * earlier operations. Or would inserting flips and reversing earlier ops ever b
e a win? Perhaps | 26 * earlier operations. Or would inserting flips and reversing earlier ops ever b
e a win? Perhaps |
27 * for the case where the bounds are kInsideOut_BoundsType. We could restrict ea
rlier operations | 27 * for the case where the bounds are kInsideOut_BoundsType. We could restrict ea
rlier operations |
28 * based on later intersect operations, and perhaps remove intersect-rects. We c
ould optionally | 28 * based on later intersect operations, and perhaps remove intersect-rects. We c
ould optionally |
29 * take a rect in case the caller knows a bound on what is to be drawn through t
his clip. | 29 * take a rect in case the caller knows a bound on what is to be drawn through t
his clip. |
30 */ | 30 */ |
31 GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds
) { | 31 GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds
, |
| 32 int maxWindowRectangles) { |
32 SkASSERT(!queryBounds.isEmpty()); | 33 SkASSERT(!queryBounds.isEmpty()); |
33 fHasIBounds = false; | 34 fHasIBounds = false; |
34 | 35 |
35 if (stack.isWideOpen()) { | 36 if (stack.isWideOpen()) { |
36 fInitialState = InitialState::kAllIn; | 37 fInitialState = InitialState::kAllIn; |
37 return; | 38 return; |
38 } | 39 } |
39 | 40 |
40 SkClipStack::BoundsType stackBoundsType; | 41 SkClipStack::BoundsType stackBoundsType; |
41 SkRect stackBounds; | 42 SkRect stackBounds; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
87 // clip will be enforced by the scissor through fIBounds.) | 88 // clip will be enforced by the scissor through fIBounds.) |
88 SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds
))); | 89 SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds
))); |
89 } | 90 } |
90 | 91 |
91 fIBounds = GrClip::GetPixelIBounds(tighterQuery); | 92 fIBounds = GrClip::GetPixelIBounds(tighterQuery); |
92 SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsid
eClip above. | 93 SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsid
eClip above. |
93 fHasIBounds = true; | 94 fHasIBounds = true; |
94 | 95 |
95 // Now that we have determined the bounds to use and filtered out the trivia
l cases, call the | 96 // Now that we have determined the bounds to use and filtered out the trivia
l cases, call the |
96 // helper that actually walks the stack. | 97 // helper that actually walks the stack. |
97 this->walkStack(stack, tighterQuery); | 98 this->walkStack(stack, tighterQuery, maxWindowRectangles); |
| 99 |
| 100 if (fWindowRects.count() < maxWindowRectangles) { |
| 101 this->addInteriorWindowRectangles(maxWindowRectangles); |
| 102 } |
98 } | 103 } |
99 | 104 |
100 void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
s) { | 105 void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
s, |
| 106 int maxWindowRectangles) { |
101 // walk backwards until we get to: | 107 // walk backwards until we get to: |
102 // a) the beginning | 108 // a) the beginning |
103 // b) an operation that is known to make the bounds all inside/outside | 109 // b) an operation that is known to make the bounds all inside/outside |
104 // c) a replace operation | 110 // c) a replace operation |
105 | 111 |
106 enum class InitialTriState { | 112 enum class InitialTriState { |
107 kUnknown = -1, | 113 kUnknown = -1, |
108 kAllIn = (int)GrReducedClip::InitialState::kAllIn, | 114 kAllIn = (int)GrReducedClip::InitialState::kAllIn, |
109 kAllOut = (int)GrReducedClip::InitialState::kAllOut | 115 kAllOut = (int)GrReducedClip::InitialState::kAllOut |
110 } initialTriState = InitialTriState::kUnknown; | 116 } initialTriState = InitialTriState::kUnknown; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
149 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { | 155 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { |
150 initialTriState = InitialTriState::kAllOut; | 156 initialTriState = InitialTriState::kAllOut; |
151 skippable = true; | 157 skippable = true; |
152 } | 158 } |
153 } else { | 159 } else { |
154 if (element->contains(relaxedQueryBounds)) { | 160 if (element->contains(relaxedQueryBounds)) { |
155 initialTriState = InitialTriState::kAllOut; | 161 initialTriState = InitialTriState::kAllOut; |
156 skippable = true; | 162 skippable = true; |
157 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { | 163 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { |
158 skippable = true; | 164 skippable = true; |
| 165 } else if (fWindowRects.count() < maxWindowRectangles && !em
biggens && |
| 166 !element->isAA() && Element::kRect_Type == elemen
t->getType()) { |
| 167 this->addWindowRectangle(element->getRect(), false); |
| 168 skippable = true; |
159 } | 169 } |
160 } | 170 } |
161 if (!skippable) { | 171 if (!skippable) { |
162 emsmallens = true; | 172 emsmallens = true; |
163 } | 173 } |
164 break; | 174 break; |
165 case SkRegion::kIntersect_Op: | 175 case SkRegion::kIntersect_Op: |
166 // check if the shape intersected contains the entire bounds and
therefore can | 176 // check if the shape intersected contains the entire bounds and
therefore can |
167 // be skipped or it is outside the entire bounds and therefore m
akes the clip | 177 // be skipped or it is outside the entire bounds and therefore m
akes the clip |
168 // empty. | 178 // empty. |
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
413 element = fElements.headIter().get(); | 423 element = fElements.headIter().get(); |
414 } | 424 } |
415 } | 425 } |
416 } | 426 } |
417 fRequiresAA = numAAElements > 0; | 427 fRequiresAA = numAAElements > 0; |
418 | 428 |
419 SkASSERT(InitialTriState::kUnknown != initialTriState); | 429 SkASSERT(InitialTriState::kUnknown != initialTriState); |
420 fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState); | 430 fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState); |
421 } | 431 } |
422 | 432 |
| 433 static bool element_is_pure_subtract(SkRegion::Op op) { |
| 434 SkASSERT(op >= 0); |
| 435 return op <= SkRegion::kIntersect_Op; |
| 436 |
| 437 GR_STATIC_ASSERT(0 == SkRegion::kDifference_Op); |
| 438 GR_STATIC_ASSERT(1 == SkRegion::kIntersect_Op); |
| 439 } |
| 440 |
| 441 void GrReducedClip::addInteriorWindowRectangles(int maxWindowRectangles) { |
| 442 SkASSERT(fWindowRects.count() < maxWindowRectangles); |
| 443 // Walk backwards through the element list and add window rectangles to the
interiors of |
| 444 // "difference" elements. Quit if we encounter an element that may grow the
clip. |
| 445 ElementList::Iter iter(fElements, ElementList::Iter::kTail_IterStart); |
| 446 for (; iter.get() && element_is_pure_subtract(iter.get()->getOp()); iter.pre
v()) { |
| 447 const Element* element = iter.get(); |
| 448 if (SkRegion::kDifference_Op != element->getOp()) { |
| 449 continue; |
| 450 } |
| 451 |
| 452 if (Element::kRect_Type == element->getType()) { |
| 453 SkASSERT(element->isAA()); |
| 454 this->addWindowRectangle(element->getRect(), true); |
| 455 if (fWindowRects.count() >= maxWindowRectangles) { |
| 456 return; |
| 457 } |
| 458 continue; |
| 459 } |
| 460 |
| 461 if (Element::kRRect_Type == element->getType()) { |
| 462 // For round rects we add two overlapping windows in the shape of a
plus. |
| 463 const SkRRect& clipRRect = element->getRRect(); |
| 464 SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner); |
| 465 SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner); |
| 466 if (SkRRect::kComplex_Type == clipRRect.getType()) { |
| 467 const SkVector& insetTR = clipRRect.radii(SkRRect::kUpperRight_C
orner); |
| 468 const SkVector& insetBL = clipRRect.radii(SkRRect::kLowerLeft_Co
rner); |
| 469 insetTL.fX = SkTMax(insetTL.x(), insetBL.x()); |
| 470 insetTL.fY = SkTMax(insetTL.y(), insetTR.y()); |
| 471 insetBR.fX = SkTMax(insetBR.x(), insetTR.x()); |
| 472 insetBR.fY = SkTMax(insetBR.y(), insetBL.y()); |
| 473 } |
| 474 const SkRect& bounds = clipRRect.getBounds(); |
| 475 if (insetTL.x() + insetBR.x() >= bounds.width() || |
| 476 insetTL.y() + insetBR.y() >= bounds.height()) { |
| 477 continue; // The interior "plus" is empty. |
| 478 } |
| 479 |
| 480 SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + ins
etTL.y(), |
| 481 bounds.right(), bounds.bottom() -
insetBR.y()); |
| 482 this->addWindowRectangle(horzRect, element->isAA()); |
| 483 if (fWindowRects.count() >= maxWindowRectangles) { |
| 484 return; |
| 485 } |
| 486 |
| 487 SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), boun
ds.top(), |
| 488 bounds.right() - insetBR.x(), bou
nds.bottom()); |
| 489 this->addWindowRectangle(vertRect, element->isAA()); |
| 490 if (fWindowRects.count() >= maxWindowRectangles) { |
| 491 return; |
| 492 } |
| 493 continue; |
| 494 } |
| 495 } |
| 496 } |
| 497 |
| 498 inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect,
bool elementIsAA) { |
| 499 SkIRect* window = &fWindowRects.addWindow(); |
| 500 if (!elementIsAA) { |
| 501 elementInteriorRect.round(window); |
| 502 } else { |
| 503 elementInteriorRect.roundIn(window); |
| 504 } |
| 505 } |
| 506 |
423 inline bool GrReducedClip::intersectIBounds(const SkIRect& irect) { | 507 inline bool GrReducedClip::intersectIBounds(const SkIRect& irect) { |
424 SkASSERT(fHasIBounds); | 508 SkASSERT(fHasIBounds); |
425 if (!fIBounds.intersect(irect)) { | 509 if (!fIBounds.intersect(irect)) { |
426 fHasIBounds = false; | 510 fHasIBounds = false; |
| 511 fWindowRects.reset(); |
427 fElements.reset(); | 512 fElements.reset(); |
428 fRequiresAA = false; | 513 fRequiresAA = false; |
429 fInitialState = InitialState::kAllOut; | 514 fInitialState = InitialState::kAllOut; |
430 return false; | 515 return false; |
431 } | 516 } |
432 return true; | 517 return true; |
433 } | 518 } |
434 | 519 |
435 //////////////////////////////////////////////////////////////////////////////// | 520 //////////////////////////////////////////////////////////////////////////////// |
436 // Create a 8-bit clip mask in alpha | 521 // Create a 8-bit clip mask in alpha |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
496 break; | 581 break; |
497 } | 582 } |
498 } | 583 } |
499 } | 584 } |
500 | 585 |
501 bool GrReducedClip::drawAlphaClipMask(GrDrawContext* dc) const { | 586 bool GrReducedClip::drawAlphaClipMask(GrDrawContext* dc) const { |
502 // The texture may be larger than necessary, this rect represents the part o
f the texture | 587 // The texture may be larger than necessary, this rect represents the part o
f the texture |
503 // we populate with a rasterization of the clip. | 588 // we populate with a rasterization of the clip. |
504 GrFixedClip clip(SkIRect::MakeWH(fIBounds.width(), fIBounds.height())); | 589 GrFixedClip clip(SkIRect::MakeWH(fIBounds.width(), fIBounds.height())); |
505 | 590 |
| 591 if (!fWindowRects.empty()) { |
| 592 clip.windowRectsState().setExclusive(fWindowRects, {fIBounds.left(), fIB
ounds.top()}); |
| 593 } |
| 594 |
506 // The scratch texture that we are drawing into can be substantially larger
than the mask. Only | 595 // The scratch texture that we are drawing into can be substantially larger
than the mask. Only |
507 // clear the part that we care about. | 596 // clear the part that we care about. |
508 GrColor initialCoverage = InitialState::kAllIn == this->initialState() ? -1
: 0; | 597 GrColor initialCoverage = InitialState::kAllIn == this->initialState() ? -1
: 0; |
509 dc->drawContextPriv().clear(clip, initialCoverage, true); | 598 dc->drawContextPriv().clear(clip, initialCoverage, true); |
510 | 599 |
511 // Set the matrix so that rendered clip elements are transformed to mask spa
ce from clip space. | 600 // Set the matrix so that rendered clip elements are transformed to mask spa
ce from clip space. |
512 SkMatrix translate; | 601 SkMatrix translate; |
513 translate.setTranslate(SkIntToScalar(-fIBounds.left()), SkIntToScalar(-fIBou
nds.top())); | 602 translate.setTranslate(SkIntToScalar(-fIBounds.left()), SkIntToScalar(-fIBou
nds.top())); |
514 | 603 |
515 // walk through each clip element and perform its set op | 604 // walk through each clip element and perform its set op |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
562 return true; | 651 return true; |
563 } | 652 } |
564 | 653 |
565 //////////////////////////////////////////////////////////////////////////////// | 654 //////////////////////////////////////////////////////////////////////////////// |
566 // Create a 1-bit clip mask in the stencil buffer. | 655 // Create a 1-bit clip mask in the stencil buffer. |
567 | 656 |
568 class StencilClip final : public GrClip { | 657 class StencilClip final : public GrClip { |
569 public: | 658 public: |
570 StencilClip(const SkIRect& scissorRect) : fFixedClip(scissorRect) {} | 659 StencilClip(const SkIRect& scissorRect) : fFixedClip(scissorRect) {} |
571 const GrFixedClip& fixedClip() const { return fFixedClip; } | 660 const GrFixedClip& fixedClip() const { return fFixedClip; } |
| 661 GrWindowRectsState& windowRectsState() { return fFixedClip.windowRectsState(
); } |
572 | 662 |
573 private: | 663 private: |
574 bool quickContains(const SkRect&) const final { | 664 bool quickContains(const SkRect&) const override { |
575 return false; | 665 return false; |
576 } | 666 } |
577 void getConservativeBounds(int width, int height, SkIRect* devResult, bool*
iior) const final { | 667 void getConservativeBounds(int width, int height, SkIRect* bounds, bool* iio
r) const override { |
578 fFixedClip.getConservativeBounds(width, height, devResult, iior); | 668 fFixedClip.getConservativeBounds(width, height, bounds, iior); |
579 } | 669 } |
580 bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const final { | 670 bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const override { |
581 return false; | 671 return false; |
582 } | 672 } |
583 bool apply(GrContext* context, GrDrawContext* drawContext, bool useHWAA, | 673 bool apply(GrContext* context, GrDrawContext* drawContext, bool useHWAA, |
584 bool hasUserStencilSettings, GrAppliedClip* out) const final { | 674 bool hasUserStencilSettings, GrAppliedClip* out) const override { |
585 if (!fFixedClip.apply(context, drawContext, useHWAA, hasUserStencilSetti
ngs, out)) { | 675 if (!fFixedClip.apply(context, drawContext, useHWAA, hasUserStencilSetti
ngs, out)) { |
586 return false; | 676 return false; |
587 } | 677 } |
588 out->addStencilClip(); | 678 out->addStencilClip(); |
589 return true; | 679 return true; |
590 } | 680 } |
591 | 681 |
592 GrFixedClip fFixedClip; | 682 GrFixedClip fFixedClip; |
593 | 683 |
594 typedef GrClip INHERITED; | 684 typedef GrClip INHERITED; |
595 }; | 685 }; |
596 | 686 |
597 bool GrReducedClip::drawStencilClipMask(GrContext* context, | 687 bool GrReducedClip::drawStencilClipMask(GrContext* context, |
598 GrDrawContext* drawContext, | 688 GrDrawContext* drawContext, |
599 const SkIPoint& clipOrigin) const { | 689 const SkIPoint& clipOrigin) const { |
600 // We set the current clip to the bounds so that our recursive draws are sci
ssored to them. | 690 // We set the current clip to the bounds so that our recursive draws are sci
ssored to them. |
601 StencilClip stencilClip(fIBounds.makeOffset(-clipOrigin.x(), -clipOrigin.y()
)); | 691 StencilClip stencilClip(fIBounds.makeOffset(-clipOrigin.x(), -clipOrigin.y()
)); |
602 | 692 |
| 693 if (!fWindowRects.empty()) { |
| 694 stencilClip.windowRectsState().setExclusive(fWindowRects, clipOrigin); |
| 695 } |
| 696 |
603 bool initialState = InitialState::kAllIn == this->initialState(); | 697 bool initialState = InitialState::kAllIn == this->initialState(); |
604 drawContext->drawContextPriv().clearStencilClip(stencilClip.fixedClip(), ini
tialState); | 698 drawContext->drawContextPriv().clearStencilClip(stencilClip.fixedClip(), ini
tialState); |
605 | 699 |
606 // Set the matrix so that rendered clip elements are transformed from clip t
o stencil space. | 700 // Set the matrix so that rendered clip elements are transformed from clip t
o stencil space. |
607 SkMatrix viewMatrix; | 701 SkMatrix viewMatrix; |
608 viewMatrix.setTranslate(SkIntToScalar(-clipOrigin.x()), SkIntToScalar(-clipO
rigin.y())); | 702 viewMatrix.setTranslate(SkIntToScalar(-clipOrigin.x()), SkIntToScalar(-clipO
rigin.y())); |
609 | 703 |
610 // walk through each clip element and perform its set op | 704 // walk through each clip element and perform its set op |
611 // with the existing clip. | 705 // with the existing clip. |
612 for (ElementList::Iter iter(fElements); iter.get(); iter.next()) { | 706 for (ElementList::Iter iter(fElements); iter.get(); iter.next()) { |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
737 // The view matrix is setup to do clip space -> stencil space tr
anslation, so | 831 // The view matrix is setup to do clip space -> stencil space tr
anslation, so |
738 // draw rect in clip space. | 832 // draw rect in clip space. |
739 drawContext->drawContextPriv().stencilRect(stencilClip, *pass, | 833 drawContext->drawContextPriv().stencilRect(stencilClip, *pass, |
740 false, viewMatrix, | 834 false, viewMatrix, |
741 SkRect::Make(fIBounds
)); | 835 SkRect::Make(fIBounds
)); |
742 } | 836 } |
743 } | 837 } |
744 } | 838 } |
745 return true; | 839 return true; |
746 } | 840 } |
OLD | NEW |