diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp index 8ee28ddfb..47457920b 100644 --- a/src/gpu/GrReducedClip.cpp +++ b/src/gpu/GrReducedClip.cpp @@ -13,6 +13,13 @@ typedef SkClipStack::Element Element; namespace GrReducedClip { +// helper function +void reduced_stack_walker(const SkClipStack& stack, + const SkRect& queryBounds, + ElementList* result, + InitialState* initialState, + bool* requiresAA); + /* There are plenty of optimizations that could be added here. For example we could consider checking for cases where an inverse path can be changed to a regular fill with a different op. @@ -22,10 +29,12 @@ for the case where the bounds are kInsideOut_BoundsType. We could restrict earli based on later intersect operations, and perhaps remove intersect-rects. We could optionally take a rect in case the caller knows a bound on what is to be drawn through this clip. */ -void GrReduceClipStack(const SkClipStack& stack, - const SkRect& queryBounds, - ElementList* result, - InitialState* initialState) { +void ReduceClipStack(const SkClipStack& stack, + const SkIRect& queryBounds, + ElementList* result, + InitialState* initialState, + SkIRect* tighterBounds, + bool* requiresAA) { result->reset(); if (stack.isWideOpen()) { @@ -33,39 +42,98 @@ void GrReduceClipStack(const SkClipStack& stack, return; } + + // We initially look at whether the bounds alone is sufficient. We also use the stack bounds to + // attempt to compute the tighterBounds. + SkClipStack::BoundsType stackBoundsType; SkRect stackBounds; bool iior; stack.getBounds(&stackBounds, &stackBoundsType, &iior); + const SkIRect* bounds = &queryBounds; + + SkRect scalarQueryBounds = SkRect::MakeFromIRect(queryBounds); + if (iior) { SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType); SkRect isectRect; - if (stackBounds.contains(queryBounds)) { + if (stackBounds.contains(scalarQueryBounds)) { *initialState = kAllIn_InitialState; - } else if (isectRect.intersect(stackBounds, queryBounds)) { - // iior should only be true if aa/non-aa status matches among all elements. - SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); - bool doAA = iter.prev()->isAA(); - SkNEW_INSERT_AT_LLIST_HEAD(result, Element, (isectRect, SkRegion::kReplace_Op, doAA)); + if (NULL != tighterBounds) { + *tighterBounds = queryBounds; + } + if (NULL != requiresAA) { + *requiresAA = false; + } + } else if (isectRect.intersect(stackBounds, scalarQueryBounds)) { + if (NULL != tighterBounds) { + isectRect.roundOut(tighterBounds); + SkRect scalarTighterBounds = SkRect::MakeFromIRect(*tighterBounds); + if (scalarTighterBounds == isectRect) { + // the round-out didn't add any area outside the clip rect. + *requiresAA = false; + *initialState = kAllIn_InitialState; + return; + } + *initialState = kAllOut_InitialState; + // iior should only be true if aa/non-aa status matches among all elements. + SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); + bool doAA = iter.prev()->isAA(); + SkNEW_INSERT_AT_LLIST_HEAD(result, Element, (isectRect, SkRegion::kReplace_Op, doAA)); + if (NULL != requiresAA) { + *requiresAA = doAA; + } + } } else { *initialState = kAllOut_InitialState; + if (NULL != requiresAA) { + *requiresAA = false; + } } return; } else { if (SkClipStack::kNormal_BoundsType == stackBoundsType) { - if (!SkRect::Intersects(stackBounds, queryBounds)) { + if (!SkRect::Intersects(stackBounds, scalarQueryBounds)) { *initialState = kAllOut_InitialState; + if (NULL != requiresAA) { + *requiresAA = false; + } return; } + if (NULL != tighterBounds) { + SkIRect stackIBounds; + stackBounds.roundOut(&stackIBounds); + tighterBounds->intersect(queryBounds, stackIBounds); + bounds = tighterBounds; + } } else { - if (stackBounds.contains(queryBounds)) { + if (stackBounds.contains(scalarQueryBounds)) { *initialState = kAllOut_InitialState; + if (NULL != requiresAA) { + *requiresAA = false; + } return; } + if (NULL != tighterBounds) { + *tighterBounds = queryBounds; + } } } + SkRect scalarBounds = SkRect::MakeFromIRect(*bounds); + + // Now that we have determined the bounds to use and filtered out the trivial cases, call the + // helper that actually walks the stack. + reduced_stack_walker(stack, scalarBounds, result, initialState, requiresAA); +} + +void reduced_stack_walker(const SkClipStack& stack, + const SkRect& queryBounds, + ElementList* result, + InitialState* initialState, + bool* requiresAA) { + // walk backwards until we get to: // a) the beginning // b) an operation that is known to make the bounds all inside/outside @@ -80,6 +148,7 @@ void GrReduceClipStack(const SkClipStack& stack, bool emsmallens = false; SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); + int numAAElements = 0; while ((kUnknown_InitialState == *initialState)) { const Element* element = iter.prev(); if (NULL == element) { @@ -254,6 +323,9 @@ void GrReduceClipStack(const SkClipStack& stack, (queryBounds, SkRegion::kReverseDifference_Op, false)); } else { result->addToHead(*element); + if (element->isAA()) { + ++numAAElements; + } } } } @@ -262,7 +334,6 @@ void GrReduceClipStack(const SkClipStack& stack, (kAllIn_InitialState == *initialState && !emsmallens)) { result->reset(); } else { - int clipsToSkip = 0; Element* element = result->headIter().get(); while (NULL != element) { bool skippable = false; @@ -308,7 +379,6 @@ void GrReduceClipStack(const SkClipStack& stack, } break; case SkRegion::kReplace_Op: - SkASSERT(!clipsToSkip); // replace should always be the first op skippable = false; // we would have skipped it in the backwards walk if we // could've. break; @@ -319,10 +389,16 @@ void GrReduceClipStack(const SkClipStack& stack, if (!skippable) { break; } else { + if (element->isAA()) { + --numAAElements; + } result->popHead(); element = result->headIter().get(); } } } + if (NULL != requiresAA) { + *requiresAA = numAAElements > 0; + } } } // namespace GrReducedClip diff --git a/src/gpu/GrReducedClip.h b/src/gpu/GrReducedClip.h index 6b6725f56..abfc244f2 100644 --- a/src/gpu/GrReducedClip.h +++ b/src/gpu/GrReducedClip.h @@ -22,12 +22,19 @@ enum InitialState { * This function takes a clip stack and a query rectangle and it produces a reduced set of * SkClipStack::Elements that are equivalent to applying the full stack to the rectangle. The * initial state of the query rectangle before the first clip element is applied is returned via - * initialState. This function is declared here so that it can be unit-tested. It may become a - * member function of SkClipStack when its interface is determined to be stable. + * initialState. Optionally, the caller can request a tighter bounds on the clip be returned via + * tighterBounds. If not NULL, tighterBounds will always be contained by queryBounds after return. + * If tighterBounds is specified then it is assumed that the caller will implicitly clip against it. + * If the caller specifies non-NULL for requiresAA then it will indicate whether anti-aliasing is + * required to process any of the elements in the result. + * + * This may become a member function of SkClipStack when its interface is determined to be stable. */ -void GrReduceClipStack(const SkClipStack& stack, - const SkRect& queryBounds, - ElementList* result, - InitialState* initialState); +void ReduceClipStack(const SkClipStack& stack, + const SkIRect& queryBounds, + ElementList* result, + InitialState* initialState, + SkIRect* tighterBounds = NULL, + bool* requiresAA = NULL); } // namespace GrReducedClip diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp index 3127a7105..9b4a68f69 100644 --- a/tests/ClipStackTest.cpp +++ b/tests/ClipStackTest.cpp @@ -669,7 +669,13 @@ static void test_reduced_clip_stack(skiatest::Reporter* reporter) { ElementList reducedClips; GrReducedClip::InitialState initial; - GrReducedClip::GrReduceClipStack(stack, inflatedBounds, &reducedClips, &initial); + SkIRect tBounds; + SkIRect* tightBounds = r.nextBool() ? &tBounds : NULL; + GrReducedClip::ReduceClipStack(stack, + inflatedIBounds, + &reducedClips, + &initial, + tightBounds); // Build a new clip stack based on the reduced clip elements SkClipStack reducedStack; @@ -681,6 +687,11 @@ static void test_reduced_clip_stack(skiatest::Reporter* reporter) { add_elem_to_stack(*iter.get(), &reducedStack); } + // GrReducedClipStack assumes that the final result is clipped to the returned bounds + if (NULL != tightBounds) { + reducedStack.clipDevRect(*tightBounds, SkRegion::kIntersect_Op); + } + // convert both the original stack and reduced stack to SkRegions and see if they're equal SkRegion region; SkRegion reducedRegion;