зеркало из https://github.com/mozilla/moz-skia.git
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:
Родитель
a2a3192847
Коммит
c6b3e48cb3
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче