Cleanup: Turn GrReducedClip into a class with a static function.
Clean up namespace usage.
Similar to what was done in
https://skia.googlesource.com/skia/+/a5414c4a8efc3119ee20fcee96c0bf68a04909c7
BUG=None
TEST=None
R=bsalomon@google.com
Review URL: https://codereview.chromium.org/653393003
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 1d05565..414bbab 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -28,8 +28,6 @@
typedef SkClipStack::Element Element;
-using namespace GrReducedClip;
-
////////////////////////////////////////////////////////////////////////////////
namespace {
// set up the draw state to enable the aa clipping mask. Besides setting up the
@@ -85,14 +83,14 @@
* will be used on any element. If so, it returns true to indicate that the
* entire clip should be rendered in SW and then uploaded en masse to the gpu.
*/
-bool GrClipMaskManager::useSWOnlyPath(const ElementList& elements) {
+bool GrClipMaskManager::useSWOnlyPath(const GrReducedClip::ElementList& elements) {
// TODO: generalize this function so that when
// a clip gets complex enough it can just be done in SW regardless
// of whether it would invoke the GrSoftwarePathRenderer.
SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
- for (ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
+ for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
const Element* element = iter.get();
// rects can always be drawn directly w/o using the software path
// Skip rrects once we're drawing them directly.
@@ -107,7 +105,7 @@
return false;
}
-bool GrClipMaskManager::installClipEffects(const ElementList& elements,
+bool GrClipMaskManager::installClipEffects(const GrReducedClip::ElementList& elements,
GrDrawState::AutoRestoreEffects* are,
const SkVector& clipToRTOffset,
const SkRect* drawBounds) {
@@ -121,7 +119,7 @@
are->set(drawState);
GrRenderTarget* rt = drawState->getRenderTarget();
- ElementList::Iter iter(elements);
+ GrReducedClip::ElementList::Iter iter(elements);
bool setARE = false;
bool failed = false;
@@ -217,9 +215,9 @@
const SkRect* devBounds) {
fCurrClipMaskType = kNone_ClipMaskType;
- ElementList elements(16);
+ GrReducedClip::ElementList elements(16);
int32_t genID;
- InitialState initialState;
+ GrReducedClip::InitialState initialState;
SkIRect clipSpaceIBounds;
bool requiresAA;
@@ -234,15 +232,15 @@
if (!ignoreClip) {
SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height());
clipSpaceRTIBounds.offset(clipDataIn->fOrigin);
- ReduceClipStack(*clipDataIn->fClipStack,
- clipSpaceRTIBounds,
- &elements,
- &genID,
- &initialState,
- &clipSpaceIBounds,
- &requiresAA);
+ GrReducedClip::ReduceClipStack(*clipDataIn->fClipStack,
+ clipSpaceRTIBounds,
+ &elements,
+ &genID,
+ &initialState,
+ &clipSpaceIBounds,
+ &requiresAA);
if (elements.isEmpty()) {
- if (kAllIn_InitialState == initialState) {
+ if (GrReducedClip::kAllIn_InitialState == initialState) {
ignoreClip = clipSpaceIBounds == clipSpaceRTIBounds;
} else {
return false;
@@ -534,8 +532,8 @@
////////////////////////////////////////////////////////////////////////////////
// Create a 8-bit clip mask in alpha
GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
- InitialState initialState,
- const ElementList& elements,
+ GrReducedClip::InitialState initialState,
+ const GrReducedClip::ElementList& elements,
const SkIRect& clipSpaceIBounds) {
SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
@@ -575,7 +573,7 @@
// The scratch texture that we are drawing into can be substantially larger than the mask. Only
// clear the part that we care about.
fGpu->clear(&maskSpaceIBounds,
- kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
+ GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
true,
result->asRenderTarget());
@@ -588,7 +586,7 @@
SkAutoTUnref<GrTexture> temp;
// walk through each clip element and perform its set op
- for (ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) {
+ for (GrReducedClip::ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) {
const Element* element = iter.get();
SkRegion::Op op = element->getOp();
bool invert = element->isInverseFilled();
@@ -688,8 +686,8 @@
// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device
// (as opposed to canvas) coordinates
bool GrClipMaskManager::createStencilClipMask(int32_t elementsGenID,
- InitialState initialState,
- const ElementList& elements,
+ GrReducedClip::InitialState initialState,
+ const GrReducedClip::ElementList& elements,
const SkIRect& clipSpaceIBounds,
const SkIPoint& clipSpaceToStencilOffset) {
@@ -737,11 +735,12 @@
SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers");
clipBit = (1 << (clipBit-1));
- fGpu->clearStencilClip(rt, stencilSpaceIBounds, kAllIn_InitialState == initialState);
+ fGpu->clearStencilClip(rt, stencilSpaceIBounds,
+ GrReducedClip::kAllIn_InitialState == initialState);
// walk through each clip element and perform its set op
// with the existing clip.
- for (ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
+ for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
const Element* element = iter.get();
bool fillInverted = false;
// enabled at bottom of loop
@@ -1058,11 +1057,11 @@
SkIntToScalar(-clipSpaceIBounds.fTop));
helper.init(maskSpaceIBounds, &matrix, false);
- helper.clear(kAllIn_InitialState == initialState ? 0xFF : 0x00);
+ helper.clear(GrReducedClip::kAllIn_InitialState == initialState ? 0xFF : 0x00);
SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
- for (ElementList::Iter iter(elements.headIter()) ; iter.get(); iter.next()) {
+ for (GrReducedClip::ElementList::Iter iter(elements.headIter()) ; iter.get(); iter.next()) {
const Element* element = iter.get();
SkRegion::Op op = element->getOp();
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index 2083af9..3040b46 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -1,4 +1,3 @@
-
/*
* Copyright 2012 Google Inc.
*
@@ -9,17 +8,314 @@
#include "GrReducedClip.h"
typedef SkClipStack::Element Element;
-////////////////////////////////////////////////////////////////////////////////
-namespace GrReducedClip {
+static void reduced_stack_walker(const SkClipStack& stack,
+ const SkRect& queryBounds,
+ GrReducedClip::ElementList* result,
+ int32_t* resultGenID,
+ GrReducedClip::InitialState* initialState,
+ bool* requiresAA) {
-// helper function
-void reduced_stack_walker(const SkClipStack& stack,
- const SkRect& queryBounds,
- ElementList* result,
- int32_t* resultGenID,
- 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
+ // c) a replace operation
+
+ static const GrReducedClip::InitialState kUnknown_InitialState =
+ static_cast<GrReducedClip::InitialState>(-1);
+ *initialState = kUnknown_InitialState;
+
+ // During our backwards walk, track whether we've seen ops that either grow or shrink the clip.
+ // TODO: track these per saved clip so that we can consider them on the forward pass.
+ bool embiggens = false;
+ 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) {
+ *initialState = GrReducedClip::kAllIn_InitialState;
+ break;
+ }
+ if (SkClipStack::kEmptyGenID == element->getGenID()) {
+ *initialState = GrReducedClip::kAllOut_InitialState;
+ break;
+ }
+ if (SkClipStack::kWideOpenGenID == element->getGenID()) {
+ *initialState = GrReducedClip::kAllIn_InitialState;
+ break;
+ }
+
+ bool skippable = false;
+ bool isFlip = false; // does this op just flip the in/out state of every point in the bounds
+
+ switch (element->getOp()) {
+ case SkRegion::kDifference_Op:
+ // check if the shape subtracted either contains the entire bounds (and makes
+ // the clip empty) or is outside the bounds and therefore can be skipped.
+ if (element->isInverseFilled()) {
+ if (element->contains(queryBounds)) {
+ skippable = true;
+ } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ *initialState = GrReducedClip::kAllOut_InitialState;
+ skippable = true;
+ }
+ } else {
+ if (element->contains(queryBounds)) {
+ *initialState = GrReducedClip::kAllOut_InitialState;
+ skippable = true;
+ } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ skippable = true;
+ }
+ }
+ if (!skippable) {
+ emsmallens = true;
+ }
+ break;
+ case SkRegion::kIntersect_Op:
+ // check if the shape intersected contains the entire bounds and therefore can
+ // be skipped or it is outside the entire bounds and therefore makes the clip
+ // empty.
+ if (element->isInverseFilled()) {
+ if (element->contains(queryBounds)) {
+ *initialState = GrReducedClip::kAllOut_InitialState;
+ skippable = true;
+ } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ skippable = true;
+ }
+ } else {
+ if (element->contains(queryBounds)) {
+ skippable = true;
+ } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ *initialState = GrReducedClip::kAllOut_InitialState;
+ skippable = true;
+ }
+ }
+ if (!skippable) {
+ emsmallens = true;
+ }
+ break;
+ case SkRegion::kUnion_Op:
+ // If the union-ed shape contains the entire bounds then after this element
+ // the bounds is entirely inside the clip. If the union-ed shape is outside the
+ // bounds then this op can be skipped.
+ if (element->isInverseFilled()) {
+ if (element->contains(queryBounds)) {
+ skippable = true;
+ } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ *initialState = GrReducedClip::kAllIn_InitialState;
+ skippable = true;
+ }
+ } else {
+ if (element->contains(queryBounds)) {
+ *initialState = GrReducedClip::kAllIn_InitialState;
+ skippable = true;
+ } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ skippable = true;
+ }
+ }
+ if (!skippable) {
+ embiggens = true;
+ }
+ break;
+ case SkRegion::kXOR_Op:
+ // If the bounds is entirely inside the shape being xor-ed then the effect is
+ // to flip the inside/outside state of every point in the bounds. We may be
+ // able to take advantage of this in the forward pass. If the xor-ed shape
+ // doesn't intersect the bounds then it can be skipped.
+ if (element->isInverseFilled()) {
+ if (element->contains(queryBounds)) {
+ skippable = true;
+ } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ isFlip = true;
+ }
+ } else {
+ if (element->contains(queryBounds)) {
+ isFlip = true;
+ } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ skippable = true;
+ }
+ }
+ if (!skippable) {
+ emsmallens = embiggens = true;
+ }
+ break;
+ case SkRegion::kReverseDifference_Op:
+ // When the bounds is entirely within the rev-diff shape then this behaves like xor
+ // and reverses every point inside the bounds. If the shape is completely outside
+ // the bounds then we know after this element is applied that the bounds will be
+ // all outside the current clip.B
+ if (element->isInverseFilled()) {
+ if (element->contains(queryBounds)) {
+ *initialState = GrReducedClip::kAllOut_InitialState;
+ skippable = true;
+ } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ isFlip = true;
+ }
+ } else {
+ if (element->contains(queryBounds)) {
+ isFlip = true;
+ } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ *initialState = GrReducedClip::kAllOut_InitialState;
+ skippable = true;
+ }
+ }
+ if (!skippable) {
+ emsmallens = embiggens = true;
+ }
+ break;
+ case SkRegion::kReplace_Op:
+ // Replace will always terminate our walk. We will either begin the forward walk
+ // at the replace op or detect here than the shape is either completely inside
+ // or completely outside the bounds. In this latter case it can be skipped by
+ // setting the correct value for initialState.
+ if (element->isInverseFilled()) {
+ if (element->contains(queryBounds)) {
+ *initialState = GrReducedClip::kAllOut_InitialState;
+ skippable = true;
+ } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ *initialState = GrReducedClip::kAllIn_InitialState;
+ skippable = true;
+ }
+ } else {
+ if (element->contains(queryBounds)) {
+ *initialState = GrReducedClip::kAllIn_InitialState;
+ skippable = true;
+ } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ *initialState = GrReducedClip::kAllOut_InitialState;
+ skippable = true;
+ }
+ }
+ if (!skippable) {
+ *initialState = GrReducedClip::kAllOut_InitialState;
+ embiggens = emsmallens = true;
+ }
+ break;
+ default:
+ SkDEBUGFAIL("Unexpected op.");
+ break;
+ }
+ if (!skippable) {
+ if (0 == result->count()) {
+ // This will be the last element. Record the stricter genID.
+ *resultGenID = element->getGenID();
+ }
+
+ // if it is a flip, change it to a bounds-filling rect
+ if (isFlip) {
+ SkASSERT(SkRegion::kXOR_Op == element->getOp() ||
+ SkRegion::kReverseDifference_Op == element->getOp());
+ SkNEW_INSERT_AT_LLIST_HEAD(result,
+ Element,
+ (queryBounds, SkRegion::kReverseDifference_Op, false));
+ } else {
+ Element* newElement = result->addToHead(*element);
+ if (newElement->isAA()) {
+ ++numAAElements;
+ }
+ // Intersecting an inverse shape is the same as differencing the non-inverse shape.
+ // Replacing with an inverse shape is the same as setting initialState=kAllIn and
+ // differencing the non-inverse shape.
+ bool isReplace = SkRegion::kReplace_Op == newElement->getOp();
+ if (newElement->isInverseFilled() &&
+ (SkRegion::kIntersect_Op == newElement->getOp() || isReplace)) {
+ newElement->invertShapeFillType();
+ newElement->setOp(SkRegion::kDifference_Op);
+ if (isReplace) {
+ SkASSERT(GrReducedClip::kAllOut_InitialState == *initialState);
+ *initialState = GrReducedClip::kAllIn_InitialState;
+ }
+ }
+ }
+ }
+ }
+
+ if ((GrReducedClip::kAllOut_InitialState == *initialState && !embiggens) ||
+ (GrReducedClip::kAllIn_InitialState == *initialState && !emsmallens)) {
+ result->reset();
+ } else {
+ Element* element = result->headIter().get();
+ while (element) {
+ bool skippable = false;
+ switch (element->getOp()) {
+ case SkRegion::kDifference_Op:
+ // subtracting from the empty set yields the empty set.
+ skippable = GrReducedClip::kAllOut_InitialState == *initialState;
+ break;
+ case SkRegion::kIntersect_Op:
+ // intersecting with the empty set yields the empty set
+ if (GrReducedClip::kAllOut_InitialState == *initialState) {
+ skippable = true;
+ } else {
+ // We can clear to zero and then simply draw the clip element.
+ *initialState = GrReducedClip::kAllOut_InitialState;
+ element->setOp(SkRegion::kReplace_Op);
+ }
+ break;
+ case SkRegion::kUnion_Op:
+ if (GrReducedClip::kAllIn_InitialState == *initialState) {
+ // unioning the infinite plane with anything is a no-op.
+ skippable = true;
+ } else {
+ // unioning the empty set with a shape is the shape.
+ element->setOp(SkRegion::kReplace_Op);
+ }
+ break;
+ case SkRegion::kXOR_Op:
+ if (GrReducedClip::kAllOut_InitialState == *initialState) {
+ // xor could be changed to diff in the kAllIn case, not sure it's a win.
+ element->setOp(SkRegion::kReplace_Op);
+ }
+ break;
+ case SkRegion::kReverseDifference_Op:
+ if (GrReducedClip::kAllIn_InitialState == *initialState) {
+ // subtracting the whole plane will yield the empty set.
+ skippable = true;
+ *initialState = GrReducedClip::kAllOut_InitialState;
+ } else {
+ // this picks up flips inserted in the backwards pass.
+ skippable = element->isInverseFilled() ?
+ !SkRect::Intersects(element->getBounds(), queryBounds) :
+ element->contains(queryBounds);
+ if (skippable) {
+ *initialState = GrReducedClip::kAllIn_InitialState;
+ } else {
+ element->setOp(SkRegion::kReplace_Op);
+ }
+ }
+ break;
+ case SkRegion::kReplace_Op:
+ skippable = false; // we would have skipped it in the backwards walk if we
+ // could've.
+ break;
+ default:
+ SkDEBUGFAIL("Unexpected op.");
+ break;
+ }
+ if (!skippable) {
+ break;
+ } else {
+ if (element->isAA()) {
+ --numAAElements;
+ }
+ result->popHead();
+ element = result->headIter().get();
+ }
+ }
+ }
+ if (requiresAA) {
+ *requiresAA = numAAElements > 0;
+ }
+
+ if (0 == result->count()) {
+ if (*initialState == GrReducedClip::kAllIn_InitialState) {
+ *resultGenID = SkClipStack::kWideOpenGenID;
+ } else {
+ *resultGenID = SkClipStack::kEmptyGenID;
+ }
+ }
+}
/*
There are plenty of optimizations that could be added here. Maybe flips could be folded into
@@ -28,13 +324,13 @@
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 ReduceClipStack(const SkClipStack& stack,
- const SkIRect& queryBounds,
- ElementList* result,
- int32_t* resultGenID,
- InitialState* initialState,
- SkIRect* tighterBounds,
- bool* requiresAA) {
+void GrReducedClip::ReduceClipStack(const SkClipStack& stack,
+ const SkIRect& queryBounds,
+ ElementList* result,
+ int32_t* resultGenID,
+ InitialState* initialState,
+ SkIRect* tighterBounds,
+ bool* requiresAA) {
result->reset();
// The clip established by the element list might be cached based on the last
@@ -64,7 +360,7 @@
SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
SkRect isectRect;
if (stackBounds.contains(scalarQueryBounds)) {
- *initialState = kAllIn_InitialState;
+ *initialState = GrReducedClip::kAllIn_InitialState;
if (tighterBounds) {
*tighterBounds = queryBounds;
}
@@ -82,7 +378,7 @@
if (requiresAA) {
*requiresAA = false;
}
- *initialState = kAllIn_InitialState;
+ *initialState = GrReducedClip::kAllIn_InitialState;
return;
}
}
@@ -140,311 +436,3 @@
// element.
SkASSERT(SkClipStack::kInvalidGenID != *resultGenID);
}
-
-void reduced_stack_walker(const SkClipStack& stack,
- const SkRect& queryBounds,
- ElementList* result,
- int32_t* resultGenID,
- 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
- // c) a replace operation
-
- static const InitialState kUnknown_InitialState = static_cast<InitialState>(-1);
- *initialState = kUnknown_InitialState;
-
- // During our backwards walk, track whether we've seen ops that either grow or shrink the clip.
- // TODO: track these per saved clip so that we can consider them on the forward pass.
- bool embiggens = false;
- 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) {
- *initialState = kAllIn_InitialState;
- break;
- }
- if (SkClipStack::kEmptyGenID == element->getGenID()) {
- *initialState = kAllOut_InitialState;
- break;
- }
- if (SkClipStack::kWideOpenGenID == element->getGenID()) {
- *initialState = kAllIn_InitialState;
- break;
- }
-
- bool skippable = false;
- bool isFlip = false; // does this op just flip the in/out state of every point in the bounds
-
- switch (element->getOp()) {
- case SkRegion::kDifference_Op:
- // check if the shape subtracted either contains the entire bounds (and makes
- // the clip empty) or is outside the bounds and therefore can be skipped.
- if (element->isInverseFilled()) {
- if (element->contains(queryBounds)) {
- skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- *initialState = kAllOut_InitialState;
- skippable = true;
- }
- } else {
- if (element->contains(queryBounds)) {
- *initialState = kAllOut_InitialState;
- skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- skippable = true;
- }
- }
- if (!skippable) {
- emsmallens = true;
- }
- break;
- case SkRegion::kIntersect_Op:
- // check if the shape intersected contains the entire bounds and therefore can
- // be skipped or it is outside the entire bounds and therefore makes the clip
- // empty.
- if (element->isInverseFilled()) {
- if (element->contains(queryBounds)) {
- *initialState = kAllOut_InitialState;
- skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- skippable = true;
- }
- } else {
- if (element->contains(queryBounds)) {
- skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- *initialState = kAllOut_InitialState;
- skippable = true;
- }
- }
- if (!skippable) {
- emsmallens = true;
- }
- break;
- case SkRegion::kUnion_Op:
- // If the union-ed shape contains the entire bounds then after this element
- // the bounds is entirely inside the clip. If the union-ed shape is outside the
- // bounds then this op can be skipped.
- if (element->isInverseFilled()) {
- if (element->contains(queryBounds)) {
- skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- *initialState = kAllIn_InitialState;
- skippable = true;
- }
- } else {
- if (element->contains(queryBounds)) {
- *initialState = kAllIn_InitialState;
- skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- skippable = true;
- }
- }
- if (!skippable) {
- embiggens = true;
- }
- break;
- case SkRegion::kXOR_Op:
- // If the bounds is entirely inside the shape being xor-ed then the effect is
- // to flip the inside/outside state of every point in the bounds. We may be
- // able to take advantage of this in the forward pass. If the xor-ed shape
- // doesn't intersect the bounds then it can be skipped.
- if (element->isInverseFilled()) {
- if (element->contains(queryBounds)) {
- skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- isFlip = true;
- }
- } else {
- if (element->contains(queryBounds)) {
- isFlip = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- skippable = true;
- }
- }
- if (!skippable) {
- emsmallens = embiggens = true;
- }
- break;
- case SkRegion::kReverseDifference_Op:
- // When the bounds is entirely within the rev-diff shape then this behaves like xor
- // and reverses every point inside the bounds. If the shape is completely outside
- // the bounds then we know after this element is applied that the bounds will be
- // all outside the current clip.B
- if (element->isInverseFilled()) {
- if (element->contains(queryBounds)) {
- *initialState = kAllOut_InitialState;
- skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- isFlip = true;
- }
- } else {
- if (element->contains(queryBounds)) {
- isFlip = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- *initialState = kAllOut_InitialState;
- skippable = true;
- }
- }
- if (!skippable) {
- emsmallens = embiggens = true;
- }
- break;
- case SkRegion::kReplace_Op:
- // Replace will always terminate our walk. We will either begin the forward walk
- // at the replace op or detect here than the shape is either completely inside
- // or completely outside the bounds. In this latter case it can be skipped by
- // setting the correct value for initialState.
- if (element->isInverseFilled()) {
- if (element->contains(queryBounds)) {
- *initialState = kAllOut_InitialState;
- skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- *initialState = kAllIn_InitialState;
- skippable = true;
- }
- } else {
- if (element->contains(queryBounds)) {
- *initialState = kAllIn_InitialState;
- skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- *initialState = kAllOut_InitialState;
- skippable = true;
- }
- }
- if (!skippable) {
- *initialState = kAllOut_InitialState;
- embiggens = emsmallens = true;
- }
- break;
- default:
- SkDEBUGFAIL("Unexpected op.");
- break;
- }
- if (!skippable) {
- if (0 == result->count()) {
- // This will be the last element. Record the stricter genID.
- *resultGenID = element->getGenID();
- }
-
- // if it is a flip, change it to a bounds-filling rect
- if (isFlip) {
- SkASSERT(SkRegion::kXOR_Op == element->getOp() ||
- SkRegion::kReverseDifference_Op == element->getOp());
- SkNEW_INSERT_AT_LLIST_HEAD(result,
- Element,
- (queryBounds, SkRegion::kReverseDifference_Op, false));
- } else {
- Element* newElement = result->addToHead(*element);
- if (newElement->isAA()) {
- ++numAAElements;
- }
- // Intersecting an inverse shape is the same as differencing the non-inverse shape.
- // Replacing with an inverse shape is the same as setting initialState=kAllIn and
- // differencing the non-inverse shape.
- bool isReplace = SkRegion::kReplace_Op == newElement->getOp();
- if (newElement->isInverseFilled() &&
- (SkRegion::kIntersect_Op == newElement->getOp() || isReplace)) {
- newElement->invertShapeFillType();
- newElement->setOp(SkRegion::kDifference_Op);
- if (isReplace) {
- SkASSERT(kAllOut_InitialState == *initialState);
- *initialState = kAllIn_InitialState;
- }
- }
- }
- }
- }
-
- if ((kAllOut_InitialState == *initialState && !embiggens) ||
- (kAllIn_InitialState == *initialState && !emsmallens)) {
- result->reset();
- } else {
- Element* element = result->headIter().get();
- while (element) {
- bool skippable = false;
- switch (element->getOp()) {
- case SkRegion::kDifference_Op:
- // subtracting from the empty set yields the empty set.
- skippable = kAllOut_InitialState == *initialState;
- break;
- case SkRegion::kIntersect_Op:
- // intersecting with the empty set yields the empty set
- if (kAllOut_InitialState == *initialState) {
- skippable = true;
- } else {
- // We can clear to zero and then simply draw the clip element.
- *initialState = kAllOut_InitialState;
- element->setOp(SkRegion::kReplace_Op);
- }
- break;
- case SkRegion::kUnion_Op:
- if (kAllIn_InitialState == *initialState) {
- // unioning the infinite plane with anything is a no-op.
- skippable = true;
- } else {
- // unioning the empty set with a shape is the shape.
- element->setOp(SkRegion::kReplace_Op);
- }
- break;
- case SkRegion::kXOR_Op:
- if (kAllOut_InitialState == *initialState) {
- // xor could be changed to diff in the kAllIn case, not sure it's a win.
- element->setOp(SkRegion::kReplace_Op);
- }
- break;
- case SkRegion::kReverseDifference_Op:
- if (kAllIn_InitialState == *initialState) {
- // subtracting the whole plane will yield the empty set.
- skippable = true;
- *initialState = kAllOut_InitialState;
- } else {
- // this picks up flips inserted in the backwards pass.
- skippable = element->isInverseFilled() ?
- !SkRect::Intersects(element->getBounds(), queryBounds) :
- element->contains(queryBounds);
- if (skippable) {
- *initialState = kAllIn_InitialState;
- } else {
- element->setOp(SkRegion::kReplace_Op);
- }
- }
- break;
- case SkRegion::kReplace_Op:
- skippable = false; // we would have skipped it in the backwards walk if we
- // could've.
- break;
- default:
- SkDEBUGFAIL("Unexpected op.");
- break;
- }
- if (!skippable) {
- break;
- } else {
- if (element->isAA()) {
- --numAAElements;
- }
- result->popHead();
- element = result->headIter().get();
- }
- }
- }
- if (requiresAA) {
- *requiresAA = numAAElements > 0;
- }
-
- if (0 == result->count()) {
- if (*initialState == kAllIn_InitialState) {
- *resultGenID = SkClipStack::kWideOpenGenID;
- } else {
- *resultGenID = SkClipStack::kEmptyGenID;
- }
- }
-}
-} // namespace GrReducedClip
diff --git a/src/gpu/GrReducedClip.h b/src/gpu/GrReducedClip.h
index e3a28cc..21899c7 100644
--- a/src/gpu/GrReducedClip.h
+++ b/src/gpu/GrReducedClip.h
@@ -1,4 +1,3 @@
-
/*
* Copyright 2012 Google Inc.
*
@@ -12,37 +11,39 @@
#include "SkClipStack.h"
#include "SkTLList.h"
-namespace GrReducedClip {
+class SK_API GrReducedClip {
+public:
+ typedef SkTLList<SkClipStack::Element> ElementList;
-typedef SkTLList<SkClipStack::Element> ElementList;
+ enum InitialState {
+ kAllIn_InitialState,
+ kAllOut_InitialState,
+ };
-enum InitialState {
- kAllIn_InitialState,
- kAllOut_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 clip stack generation id that represents
+ * the list of elements is returned in resultGenID. The initial state of the
+ * query rectangle before the first clip element is applied is returned via
+ * 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.
+ */
+ static void ReduceClipStack(const SkClipStack& stack,
+ const SkIRect& queryBounds,
+ ElementList* result,
+ int32_t* resultGenID,
+ InitialState* initialState,
+ SkIRect* tighterBounds = NULL,
+ bool* requiresAA = NULL);
};
-/**
- * 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 clip
- * stack generation id that represents the list of elements is returned in resultGenID. The
- * initial state of the query rectangle before the first clip element is applied is returned via
- * 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.
- * Marked SK_API so that SkLua can call this in a shared library build.
- */
-SK_API void ReduceClipStack(const SkClipStack& stack,
- const SkIRect& queryBounds,
- ElementList* result,
- int32_t* resultGenID,
- InitialState* initialState,
- SkIRect* tighterBounds = NULL,
- bool* requiresAA = NULL);
-
-} // namespace GrReducedClip
-
#endif