Improve handling of inverse clip paths in GrClipMaskManager.

Will require rebaselining of complexclip_aa and complexclip_aa_layer on GPU.

R=robertphillips@google.com
Review URL: https://codereview.appspot.com/6907052

git-svn-id: http://skia.googlecode.com/svn/trunk@6712 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
bsalomon@google.com 2012-12-07 20:43:52 +00:00
Родитель a2a3192847
Коммит c6b3e48cb3
5 изменённых файлов: 71 добавлений и 27 удалений

Просмотреть файл

@ -98,6 +98,10 @@ public:
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

Просмотреть файл

@ -16,6 +16,21 @@
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);

Просмотреть файл

@ -66,32 +66,34 @@ public:
}
}
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. */

Просмотреть файл

@ -20,6 +20,8 @@
#include "GrSWMaskHelper.h"
#include "GrCacheID.h"
#include "SkTLazy.h"
GR_DEFINE_RESOURCE_CACHE_DOMAIN(GrClipMaskManager, GetAlphaMaskDomain)
#define GR_AA_CLIP 1
@ -59,11 +61,16 @@ void setup_drawstate_aaclip(GrGpu* gpu,
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);
}
}
@ -300,9 +307,13 @@ bool GrClipMaskManager::drawClipShape(GrTexture* target, const SkClipStack::Elem
}
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);
@ -437,16 +448,11 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t clipStackGenID,
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();
@ -466,21 +472,27 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t clipStackGenID,
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);
}

Просмотреть файл

@ -21,9 +21,7 @@ void reduced_stack_walker(const SkClipStack& stack,
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
@ -322,10 +320,23 @@ void reduced_stack_walker(const SkClipStack& stack,
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;
}
}
}
}
}