when it is rasterized. */
bool isAA() const { return fDoAA; }
+ //!< Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty.
+ void invertShapeFillType();
+
+ //!< Sets the set operation represented by the element.
void setOp(SkRegion::Op op) { fOp = op; }
/** The GenID can be used by clip stack clients to cache representations of the clip. The
static const int32_t kFirstUnreservedGenID = 3;
int32_t SkClipStack::gGenID = kFirstUnreservedGenID;
+void SkClipStack::Element::invertShapeFillType() {
+ switch (fType) {
+ case kRect_Type:
+ fPath.reset();
+ fPath.addRect(fRect);
+ fPath.setFillType(SkPath::kInverseWinding_FillType);
+ fType = kPath_Type;
+ break;
+ case kPath_Type:
+ fPath.toggleInverseFillType();
+ case kEmpty_Type:
+ break;
+ }
+}
+
void SkClipStack::Element::checkEmpty() const {
SkASSERT(fFiniteBound.isEmpty());
SkASSERT(kNormal_BoundsType == fFiniteBoundType);
}
}
- void addToHead(const T& t) {
+ T* addToHead(const T& t) {
this->validate();
Node* node = this->createNode();
fList.addToHead(node);
SkNEW_PLACEMENT_ARGS(node->fObj, T, (t));
this->validate();
+ return reinterpret_cast<T*>(node->fObj);
}
- void addToTail(const T& t) {
+ T* addToTail(const T& t) {
this->validate();
Node* node = this->createNode();
fList.addToTail(node);
SkNEW_PLACEMENT_ARGS(node->fObj, T, (t));
this->validate();
+ return reinterpret_cast<T*>(node->fObj);
}
/** Adds a new element to the list before the location indicated by the iterator. If the
iterator refers to a NULL location then the new element is added at the tail */
- void addBefore(const T& t, const Iter& location) {
- SkNEW_PLACEMENT_ARGS(this->internalAddBefore(location), T, (t));
+ T* addBefore(const T& t, const Iter& location) {
+ return SkNEW_PLACEMENT_ARGS(this->internalAddBefore(location), T, (t));
}
/** Adds a new element to the list after the location indicated by the iterator. If the
iterator refers to a NULL location then the new element is added at the head */
- void addAfter(const T& t, const Iter& location) {
- SkNEW_PLACEMENT_ARGS(this->internalAddAfter(location), T, (t));
+ T* addAfter(const T& t, const Iter& location) {
+ return SkNEW_PLACEMENT_ARGS(this->internalAddAfter(location), T, (t));
}
/** Convenience methods for getting an iterator initialized to the head/tail of the list. */
#include "GrSWMaskHelper.h"
#include "GrCacheID.h"
+#include "SkTLazy.h"
+
GR_DEFINE_RESOURCE_CACHE_DOMAIN(GrClipMaskManager, GetAlphaMaskDomain)
#define GR_AA_CLIP 1
bool path_needs_SW_renderer(GrContext* context,
GrGpu* gpu,
- const SkPath& path,
+ const SkPath& origPath,
const SkStroke& stroke,
bool doAA) {
+ // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer
+ SkTCopyOnFirstWrite<SkPath> path(origPath);
+ if (path->isInverseFillType()) {
+ path.writable()->toggleInverseFillType();
+ }
// last (false) parameter disallows use of the SW path renderer
- return NULL == context->getPathRenderer(path, stroke, gpu, doAA, false);
+ return NULL == context->getPathRenderer(*path, stroke, gpu, doAA, false);
}
}
}
return true;
case Element::kPath_Type: {
+ SkTCopyOnFirstWrite<SkPath> path(element->getPath());
+ if (path->isInverseFillType()) {
+ path.writable()->toggleInverseFillType();
+ }
SkStroke stroke;
stroke.setDoFill(true);
- GrPathRenderer* pr = this->getContext()->getPathRenderer(element->getPath(),
+ GrPathRenderer* pr = this->getContext()->getPathRenderer(*path,
stroke,
fGpu,
element->isAA(), false);
GrAutoScratchTexture temp;
// walk through each clip element and perform its set op
for (ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) {
-
const Element* element = iter.get();
SkRegion::Op op = element->getOp();
- if (SkRegion::kReplace_Op == op) {
- setup_boolean_blendcoeffs(drawState, op);
- this->drawClipShape(result, element);
-
- } else if (SkRegion::kReverseDifference_Op == op ||
- SkRegion::kIntersect_Op == op) {
+ if (SkRegion::kReverseDifference_Op == op || SkRegion::kIntersect_Op == op ||
+ element->isInverseFilled()) {
this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp);
if (NULL == temp.texture()) {
fAACache.reset();
elementBounds.roundOut(&maskSpaceElementIBounds);
}
- // clear the temp target & draw into it
- fGpu->clear(&maskSpaceElementIBounds, 0x00000000, temp.texture()->asRenderTarget());
+ // determines whether we're drawing white-on-black or black-on-white
+ bool invert = element->isInverseFilled();
+ // clear the temp target & draw into it
+ fGpu->clear(&maskSpaceElementIBounds,
+ invert ? 0xffffffff : 0x00000000,
+ temp.texture()->asRenderTarget());
+ drawState->setAlpha(invert ? 0x00 : 0xff);
setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
+
if (!this->drawClipShape(temp.texture(), element)) {
fAACache.reset();
return NULL;
}
- // Now draw into the accumulator using the real operation
- // and the temp buffer as a texture
+ // Now draw into the accumulator using the real operation and the temp buffer as a
+ // texture
this->mergeMask(result, temp.texture(), op, maskSpaceIBounds, maskSpaceElementIBounds);
} else {
- // all the remaining ops can just be directly draw into
- // the accumulation buffer
+ // all the remaining ops can just be directly draw into the accumulation buffer
+ drawState->setAlpha(0xff);
setup_boolean_blendcoeffs(drawState, op);
this->drawClipShape(result, element);
}
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.
-(e.g. [kIntersect, inverse path] -> [kDifference, path]). Maybe flips could be folded into
+There are plenty of optimizations that could be added here. Maybe flips could be folded into
earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps
for the case where the bounds are kInsideOut_BoundsType. We could restrict earlier operations
based on later intersect operations, and perhaps remove intersect-rects. We could optionally
Element,
(queryBounds, SkRegion::kReverseDifference_Op, false));
} else {
- result->addToHead(*element);
- if (element->isAA()) {
+ Element* newElement = result->addToHead(*element);
+ if (newElement->isAA()) {
++numAAElements;
}
+ // Intersecting an inverse shape is the same as differencing the non-inverse shape.
+ // Replacing with a inverse shape 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;
+ }
+ }
}
}
}