aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/GrClipMaskManager.cpp
diff options
context:
space:
mode:
authorGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-12-14 13:36:53 +0000
committerGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-12-14 13:36:53 +0000
commitb68addd39f3e7183facc70374a133e7a6fb603c5 (patch)
tree11fa4fed0770880d95ff5d543b819a4c186c8616 /src/gpu/GrClipMaskManager.cpp
parentea033606a06d05d2d42aa7118409fee798e53167 (diff)
Use the stencil buffer to avoid temporary alpha clip masks.
R=robertphillips@google.com Review URL: https://codereview.appspot.com/6935048 git-svn-id: http://skia.googlecode.com/svn/trunk@6818 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/gpu/GrClipMaskManager.cpp')
-rw-r--r--src/gpu/GrClipMaskManager.cpp150
1 files changed, 110 insertions, 40 deletions
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 4e12e35dfc..f14a37489e 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -293,9 +293,10 @@ bool draw_path_in_software(GrContext* context,
}
////////////////////////////////////////////////////////////////////////////////
-bool GrClipMaskManager::drawClipShape(GrTexture* target, const SkClipStack::Element* element) {
+bool GrClipMaskManager::drawElement(GrTexture* target,
+ const SkClipStack::Element* element,
+ GrPathRenderer* pr) {
GrDrawState* drawState = fGpu->drawState();
- GrAssert(NULL != drawState);
drawState->setRenderTarget(target->asRenderTarget());
@@ -319,14 +320,12 @@ bool GrClipMaskManager::drawClipShape(GrTexture* target, const SkClipStack::Elem
}
SkStroke stroke;
stroke.setDoFill(true);
- GrPathRendererChain::DrawType type = element->isAA() ?
- GrPathRendererChain::kColorAntiAlias_DrawType :
- GrPathRendererChain::kColor_DrawType;
- GrPathRenderer* pr = this->getContext()->getPathRenderer(*path,
- stroke,
- fGpu,
- false,
- type);
+ if (NULL == pr) {
+ GrPathRendererChain::DrawType type;
+ type = element->isAA() ? GrPathRendererChain::kColorAntiAlias_DrawType :
+ GrPathRendererChain::kColor_DrawType;
+ pr = this->getContext()->getPathRenderer(*path, stroke, fGpu, false, type);
+ }
if (NULL == pr) {
return false;
}
@@ -341,13 +340,41 @@ bool GrClipMaskManager::drawClipShape(GrTexture* target, const SkClipStack::Elem
return true;
}
+bool GrClipMaskManager::canStencilAndDrawElement(GrTexture* target,
+ const SkClipStack::Element* element,
+ GrPathRenderer** pr) {
+ GrDrawState* drawState = fGpu->drawState();
+ drawState->setRenderTarget(target->asRenderTarget());
+
+ switch (element->getType()) {
+ case Element::kRect_Type:
+ return true;
+ case Element::kPath_Type: {
+ SkTCopyOnFirstWrite<SkPath> path(element->getPath());
+ if (path->isInverseFillType()) {
+ path.writable()->toggleInverseFillType();
+ }
+ SkStroke stroke;
+ stroke.setDoFill(true);
+ GrPathRendererChain::DrawType type = element->isAA() ?
+ GrPathRendererChain::kStencilAndColorAntiAlias_DrawType :
+ GrPathRendererChain::kStencilAndColor_DrawType;
+ *pr = this->getContext()->getPathRenderer(*path, stroke, fGpu, false, type);
+ return NULL != *pr;
+ }
+ default:
+ // something is wrong if we're trying to draw an empty element.
+ GrCrash("Unexpected element type");
+ return false;
+ }
+}
+
void GrClipMaskManager::mergeMask(GrTexture* dstMask,
GrTexture* srcMask,
SkRegion::Op op,
const GrIRect& dstBound,
const GrIRect& srcBound) {
GrDrawState* drawState = fGpu->drawState();
- GrAssert(NULL != drawState);
SkMatrix oldMatrix = drawState->getViewMatrix();
drawState->viewMatrix()->reset();
@@ -401,7 +428,7 @@ bool GrClipMaskManager::getMaskTexture(int32_t clipStackGenID,
fAACache.reset();
GrTextureDesc desc;
- desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit;
+ desc.fFlags = kRenderTarget_GrTextureFlagBit;
desc.fWidth = clipSpaceIBounds.width();
desc.fHeight = clipSpaceIBounds.height();
desc.fConfig = kAlpha_8_GrPixelConfig;
@@ -458,56 +485,100 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t clipStackGenID,
kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
result->asRenderTarget());
+ // When we use the stencil in the below loop it is important to have this clip installed.
+ // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first
+ // pass must not set values outside of this bounds or stencil values outside the rect won't be
+ // cleared.
+ GrDrawTarget::AutoClipRestore acr(fGpu, maskSpaceIBounds);
+ drawState->enableState(GrDrawState::kClip_StateBit);
+
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();
+ bool invert = element->isInverseFilled();
- if (SkRegion::kReverseDifference_Op == op || SkRegion::kIntersect_Op == op ||
- element->isInverseFilled()) {
- this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp);
- if (NULL == temp.texture()) {
- fAACache.reset();
- return NULL;
- }
-
+ if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
+ GrPathRenderer* pr = NULL;
+ bool useTemp = !this->canStencilAndDrawElement(result, element, &pr);
+ GrTexture* dst;
// This is the bounds of the clip element in the space of the alpha-mask. The temporary
// mask buffer can be substantially larger than the actually clip stack element. We
// touch the minimum number of pixels necessary and use decal mode to combine it with
- // the accumulator
+ // the accumulator.
GrIRect maskSpaceElementIBounds;
- if (element->isInverseFilled()) {
- maskSpaceElementIBounds = maskSpaceIBounds;
+
+ if (useTemp) {
+ if (invert) {
+ maskSpaceElementIBounds = maskSpaceIBounds;
+ } else {
+ GrRect elementBounds = element->getBounds();
+ elementBounds.offset(clipToMaskOffset);
+ elementBounds.roundOut(&maskSpaceElementIBounds);
+ }
+
+ this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp);
+ if (NULL == temp.texture()) {
+ fAACache.reset();
+ return NULL;
+ }
+ dst = temp.texture();
+ // clear the temp target and set blend to replace
+ fGpu->clear(&maskSpaceElementIBounds,
+ invert ? 0xffffffff : 0x00000000,
+ dst->asRenderTarget());
+ setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
+
} else {
- GrRect elementBounds = element->getBounds();
- elementBounds.offset(clipToMaskOffset);
- elementBounds.roundOut(&maskSpaceElementIBounds);
+ // draw directly into the result with the stencil set to make the pixels affected
+ // by the clip shape be non-zero.
+ dst = result;
+ GR_STATIC_CONST_SAME_STENCIL(kStencilInElement,
+ kReplace_StencilOp,
+ kReplace_StencilOp,
+ kAlways_StencilFunc,
+ 0xffff,
+ 0xffff,
+ 0xffff);
+ drawState->setStencil(kStencilInElement);
+ setup_boolean_blendcoeffs(drawState, op);
}
- // 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)) {
+ if (!this->drawElement(dst, element, pr)) {
fAACache.reset();
return NULL;
}
- // Now draw into the accumulator using the real operation and the temp buffer as a
- // texture
- this->mergeMask(result, temp.texture(), op, maskSpaceIBounds, maskSpaceElementIBounds);
+ if (useTemp) {
+ // 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 {
+ // Draw to the exterior pixels (those with a zero stencil value).
+ drawState->setAlpha(invert ? 0xff : 0x00);
+ GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement,
+ kZero_StencilOp,
+ kZero_StencilOp,
+ kEqual_StencilFunc,
+ 0xffff,
+ 0x0000,
+ 0xffff);
+ drawState->setStencil(kDrawOutsideElement);
+ fGpu->drawSimpleRect(clipSpaceIBounds);
+ drawState->disableStencil();
+ }
} else {
// 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);
+ this->drawElement(result, element);
}
}
@@ -542,7 +613,6 @@ bool GrClipMaskManager::createStencilClipMask(InitialState initialState,
stencilBuffer->setLastClip(genID, clipSpaceIBounds, clipSpaceToStencilOffset);
-
GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
drawState = fGpu->drawState();
drawState->setRenderTarget(rt);