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
This commit is contained in:
tfarina 2014-10-23 17:47:18 -07:00 committed by Commit bot
parent 11ed6b8140
commit bf54e49e30
3 changed files with 371 additions and 383 deletions

View File

@ -28,8 +28,6 @@
typedef SkClipStack::Element Element; typedef SkClipStack::Element Element;
using namespace GrReducedClip;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
namespace { namespace {
// set up the draw state to enable the aa clipping mask. Besides setting up the // set up the draw state to enable the aa clipping mask. Besides setting up the
@ -85,14 +83,14 @@ bool path_needs_SW_renderer(GrContext* context,
* will be used on any element. If so, it returns true to indicate that the * 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. * 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 // TODO: generalize this function so that when
// a clip gets complex enough it can just be done in SW regardless // a clip gets complex enough it can just be done in SW regardless
// of whether it would invoke the GrSoftwarePathRenderer. // of whether it would invoke the GrSoftwarePathRenderer.
SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 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(); const Element* element = iter.get();
// rects can always be drawn directly w/o using the software path // rects can always be drawn directly w/o using the software path
// Skip rrects once we're drawing them directly. // Skip rrects once we're drawing them directly.
@ -107,7 +105,7 @@ bool GrClipMaskManager::useSWOnlyPath(const ElementList& elements) {
return false; return false;
} }
bool GrClipMaskManager::installClipEffects(const ElementList& elements, bool GrClipMaskManager::installClipEffects(const GrReducedClip::ElementList& elements,
GrDrawState::AutoRestoreEffects* are, GrDrawState::AutoRestoreEffects* are,
const SkVector& clipToRTOffset, const SkVector& clipToRTOffset,
const SkRect* drawBounds) { const SkRect* drawBounds) {
@ -121,7 +119,7 @@ bool GrClipMaskManager::installClipEffects(const ElementList& elements,
are->set(drawState); are->set(drawState);
GrRenderTarget* rt = drawState->getRenderTarget(); GrRenderTarget* rt = drawState->getRenderTarget();
ElementList::Iter iter(elements); GrReducedClip::ElementList::Iter iter(elements);
bool setARE = false; bool setARE = false;
bool failed = false; bool failed = false;
@ -217,9 +215,9 @@ bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn,
const SkRect* devBounds) { const SkRect* devBounds) {
fCurrClipMaskType = kNone_ClipMaskType; fCurrClipMaskType = kNone_ClipMaskType;
ElementList elements(16); GrReducedClip::ElementList elements(16);
int32_t genID; int32_t genID;
InitialState initialState; GrReducedClip::InitialState initialState;
SkIRect clipSpaceIBounds; SkIRect clipSpaceIBounds;
bool requiresAA; bool requiresAA;
@ -234,15 +232,15 @@ bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn,
if (!ignoreClip) { if (!ignoreClip) {
SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height()); SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height());
clipSpaceRTIBounds.offset(clipDataIn->fOrigin); clipSpaceRTIBounds.offset(clipDataIn->fOrigin);
ReduceClipStack(*clipDataIn->fClipStack, GrReducedClip::ReduceClipStack(*clipDataIn->fClipStack,
clipSpaceRTIBounds, clipSpaceRTIBounds,
&elements, &elements,
&genID, &genID,
&initialState, &initialState,
&clipSpaceIBounds, &clipSpaceIBounds,
&requiresAA); &requiresAA);
if (elements.isEmpty()) { if (elements.isEmpty()) {
if (kAllIn_InitialState == initialState) { if (GrReducedClip::kAllIn_InitialState == initialState) {
ignoreClip = clipSpaceIBounds == clipSpaceRTIBounds; ignoreClip = clipSpaceIBounds == clipSpaceRTIBounds;
} else { } else {
return false; return false;
@ -534,8 +532,8 @@ GrTexture* GrClipMaskManager::allocMaskTexture(int32_t elementsGenID,
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Create a 8-bit clip mask in alpha // Create a 8-bit clip mask in alpha
GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID, GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
InitialState initialState, GrReducedClip::InitialState initialState,
const ElementList& elements, const GrReducedClip::ElementList& elements,
const SkIRect& clipSpaceIBounds) { const SkIRect& clipSpaceIBounds) {
SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
@ -575,7 +573,7 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
// The scratch texture that we are drawing into can be substantially larger than the mask. Only // The scratch texture that we are drawing into can be substantially larger than the mask. Only
// clear the part that we care about. // clear the part that we care about.
fGpu->clear(&maskSpaceIBounds, fGpu->clear(&maskSpaceIBounds,
kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000, GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
true, true,
result->asRenderTarget()); result->asRenderTarget());
@ -588,7 +586,7 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
SkAutoTUnref<GrTexture> temp; SkAutoTUnref<GrTexture> temp;
// walk through each clip element and perform its set op // 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(); const Element* element = iter.get();
SkRegion::Op op = element->getOp(); SkRegion::Op op = element->getOp();
bool invert = element->isInverseFilled(); bool invert = element->isInverseFilled();
@ -688,8 +686,8 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device
// (as opposed to canvas) coordinates // (as opposed to canvas) coordinates
bool GrClipMaskManager::createStencilClipMask(int32_t elementsGenID, bool GrClipMaskManager::createStencilClipMask(int32_t elementsGenID,
InitialState initialState, GrReducedClip::InitialState initialState,
const ElementList& elements, const GrReducedClip::ElementList& elements,
const SkIRect& clipSpaceIBounds, const SkIRect& clipSpaceIBounds,
const SkIPoint& clipSpaceToStencilOffset) { const SkIPoint& clipSpaceToStencilOffset) {
@ -737,11 +735,12 @@ bool GrClipMaskManager::createStencilClipMask(int32_t elementsGenID,
SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers");
clipBit = (1 << (clipBit-1)); 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 // walk through each clip element and perform its set op
// with the existing clip. // 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(); const Element* element = iter.get();
bool fillInverted = false; bool fillInverted = false;
// enabled at bottom of loop // enabled at bottom of loop
@ -1058,11 +1057,11 @@ GrTexture* GrClipMaskManager::createSoftwareClipMask(int32_t elementsGenID,
SkIntToScalar(-clipSpaceIBounds.fTop)); SkIntToScalar(-clipSpaceIBounds.fTop));
helper.init(maskSpaceIBounds, &matrix, false); 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); 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(); const Element* element = iter.get();
SkRegion::Op op = element->getOp(); SkRegion::Op op = element->getOp();

View File

@ -1,4 +1,3 @@
/* /*
* Copyright 2012 Google Inc. * Copyright 2012 Google Inc.
* *
@ -9,17 +8,314 @@
#include "GrReducedClip.h" #include "GrReducedClip.h"
typedef SkClipStack::Element Element; 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 // walk backwards until we get to:
void reduced_stack_walker(const SkClipStack& stack, // a) the beginning
const SkRect& queryBounds, // b) an operation that is known to make the bounds all inside/outside
ElementList* result, // c) a replace operation
int32_t* resultGenID,
InitialState* initialState, static const GrReducedClip::InitialState kUnknown_InitialState =
bool* requiresAA); 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 There are plenty of optimizations that could be added here. Maybe flips could be folded into
@ -28,13 +324,13 @@ 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 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. take a rect in case the caller knows a bound on what is to be drawn through this clip.
*/ */
void ReduceClipStack(const SkClipStack& stack, void GrReducedClip::ReduceClipStack(const SkClipStack& stack,
const SkIRect& queryBounds, const SkIRect& queryBounds,
ElementList* result, ElementList* result,
int32_t* resultGenID, int32_t* resultGenID,
InitialState* initialState, InitialState* initialState,
SkIRect* tighterBounds, SkIRect* tighterBounds,
bool* requiresAA) { bool* requiresAA) {
result->reset(); result->reset();
// The clip established by the element list might be cached based on the last // The clip established by the element list might be cached based on the last
@ -64,7 +360,7 @@ void ReduceClipStack(const SkClipStack& stack,
SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType); SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
SkRect isectRect; SkRect isectRect;
if (stackBounds.contains(scalarQueryBounds)) { if (stackBounds.contains(scalarQueryBounds)) {
*initialState = kAllIn_InitialState; *initialState = GrReducedClip::kAllIn_InitialState;
if (tighterBounds) { if (tighterBounds) {
*tighterBounds = queryBounds; *tighterBounds = queryBounds;
} }
@ -82,7 +378,7 @@ void ReduceClipStack(const SkClipStack& stack,
if (requiresAA) { if (requiresAA) {
*requiresAA = false; *requiresAA = false;
} }
*initialState = kAllIn_InitialState; *initialState = GrReducedClip::kAllIn_InitialState;
return; return;
} }
} }
@ -140,311 +436,3 @@ void ReduceClipStack(const SkClipStack& stack,
// element. // element.
SkASSERT(SkClipStack::kInvalidGenID != *resultGenID); 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

View File

@ -1,4 +1,3 @@
/* /*
* Copyright 2012 Google Inc. * Copyright 2012 Google Inc.
* *
@ -12,37 +11,39 @@
#include "SkClipStack.h" #include "SkClipStack.h"
#include "SkTLList.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, * This function takes a clip stack and a query rectangle and it produces a
kAllOut_InitialState, * 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 #endif