Initial version of AA clip A8/R8 mask creation

http://codereview.appspot.com/6104043/



git-svn-id: http://skia.googlecode.com/svn/trunk@3778 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
robertphillips@google.com 2012-04-27 14:29:26 +00:00
parent f6b070da5e
commit f294b773f0
2 changed files with 329 additions and 8 deletions

View File

@ -12,6 +12,7 @@
#include "GrStencilBuffer.h"
#include "GrPathRenderer.h"
////////////////////////////////////////////////////////////////////////////////
void ScissoringSettings::setupScissoring(GrGpu* gpu) {
if (!fEnableScissoring) {
gpu->disableScissor();
@ -21,7 +22,9 @@ void ScissoringSettings::setupScissoring(GrGpu* gpu) {
gpu->enableScissoring(fScissorRect);
}
// sort out what kind of clip mask needs to be created: A8/R8, stencil or scissor
////////////////////////////////////////////////////////////////////////////////
// sort out what kind of clip mask needs to be created: alpha, stencil
// or scissor
bool GrClipMaskManager::createClipMask(GrGpu* gpu,
const GrClip& clipIn,
ScissoringSettings* scissorSettings) {
@ -30,6 +33,7 @@ bool GrClipMaskManager::createClipMask(GrGpu* gpu,
scissorSettings->fEnableScissoring = false;
fClipMaskInStencil = false;
fClipMaskInAlpha = false;
GrDrawState* drawState = gpu->drawState();
if (!drawState->isClipState()) {
@ -41,6 +45,24 @@ bool GrClipMaskManager::createClipMask(GrGpu* gpu,
// GrDrawTarget should have filtered this for us
GrAssert(NULL != rt);
#if GR_AA_CLIP
// If MSAA is enabled use the (faster) stencil path for AA clipping
// otherwise the alpha clip mask is our only option
if (clipIn.requiresAA() && 0 == rt->numSamples()) {
// Since we are going to create a destination texture of the correct
// size for the mask (rather than being bound by the size of the
// render target) we aren't going to use scissoring like the stencil
// path does (see scissorSettings below)
if (this->createAlphaClipMask(gpu, clipIn)) {
fClipMaskInAlpha = true;
return true;
}
// if alpha clip mask creation fails fall through to the stencil
// buffer method
}
#endif // GR_AA_CLIP
GrRect bounds;
GrRect rtRect;
rtRect.setLTRB(0, 0,
@ -85,6 +107,7 @@ bool GrClipMaskManager::createClipMask(GrGpu* gpu,
#endif
namespace {
////////////////////////////////////////////////////////////////////////////////
// determines how many elements at the head of the clip can be skipped and
// whether the initial clear should be to the inside- or outside-the-clip value,
// and what op should be used to draw the first element that isn't skipped.
@ -172,8 +195,279 @@ int process_initial_clip_elements(const GrClip& clip,
}
return done ? curr-1 : count;
}
}
namespace {
////////////////////////////////////////////////////////////////////////////////
// set up the OpenGL blend function to perform the specified
// boolean operation for alpha clip mask creation
void setUpBooleanBlendCoeffs(GrDrawState* drawState, SkRegion::Op op) {
switch (op) {
case SkRegion::kReplace_Op:
drawState->setBlendFunc(kOne_BlendCoeff, kZero_BlendCoeff);
break;
case SkRegion::kIntersect_Op:
drawState->setBlendFunc(kDC_BlendCoeff, kZero_BlendCoeff);
break;
case SkRegion::kUnion_Op:
drawState->setBlendFunc(kOne_BlendCoeff, kISC_BlendCoeff);
break;
case SkRegion::kXOR_Op:
drawState->setBlendFunc(kIDC_BlendCoeff, kISC_BlendCoeff);
break;
case SkRegion::kDifference_Op:
drawState->setBlendFunc(kZero_BlendCoeff, kISC_BlendCoeff);
break;
case SkRegion::kReverseDifference_Op:
drawState->setBlendFunc(kIDC_BlendCoeff, kZero_BlendCoeff);
break;
default:
GrAssert(false);
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////
bool GrClipMaskManager::drawPath(GrGpu* gpu,
const GrPath& path,
GrPathFill fill,
bool doAA) {
GrPathRenderer* pr = this->getClipPathRenderer(gpu, path, fill, doAA);
if (NULL == pr) {
return false;
}
pr->drawPath(path, fill, NULL, gpu, 0, doAA);
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool GrClipMaskManager::drawClipShape(GrGpu* gpu,
GrTexture* target,
const GrClip& clipIn,
int index) {
GrDrawState* drawState = gpu->drawState();
GrAssert(NULL != drawState);
drawState->setRenderTarget(target->asRenderTarget());
if (kRect_ClipType == clipIn.getElementType(index)) {
if (clipIn.getDoAA(index)) {
// convert the rect to a path for AA
SkPath temp;
temp.addRect(clipIn.getRect(index));
return this->drawPath(gpu, temp,
kEvenOdd_PathFill, clipIn.getDoAA(index));
} else {
gpu->drawSimpleRect(clipIn.getRect(index), NULL, 0);
}
} else {
return this->drawPath(gpu,
clipIn.getPath(index),
clipIn.getPathFill(index),
clipIn.getDoAA(index));
}
return true;
}
void GrClipMaskManager::drawTexture(GrGpu* gpu,
GrTexture* target,
const GrRect& rect,
GrTexture* texture) {
GrDrawState* drawState = gpu->drawState();
GrAssert(NULL != drawState);
// no AA here since it is encoded in the texture
drawState->setRenderTarget(target->asRenderTarget());
GrMatrix sampleM;
sampleM.setIDiv(texture->width(), texture->height());
drawState->setTexture(0, texture);
drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
GrSamplerState::kNearest_Filter,
sampleM);
gpu->drawSimpleRect(rect, NULL, 1 << 0);
drawState->setTexture(0, NULL);
}
namespace {
void clear(GrGpu* gpu,
GrTexture* target,
const GrRect& bounds,
GrColor color) {
GrDrawState* drawState = gpu->drawState();
GrAssert(NULL != drawState);
// zap entire target to specified color
drawState->setRenderTarget(target->asRenderTarget());
gpu->clear(NULL, color);
}
}
////////////////////////////////////////////////////////////////////////////////
// Create a 8-bit clip mask in alpha
bool GrClipMaskManager::createAlphaClipMask(GrGpu* gpu, const GrClip& clipIn) {
GrDrawState* origDrawState = gpu->drawState();
GrAssert(origDrawState->isClipState());
GrRenderTarget* rt = origDrawState->getRenderTarget();
GrAssert(NULL != rt);
GrRect rtRect;
rtRect.setLTRB(0, 0,
GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
// unlike the stencil path the alpha path is not bound to the size of the
// render target - determine the minimum size required for the mask
GrRect bounds;
if (clipIn.hasConservativeBounds()) {
bounds = clipIn.getConservativeBounds();
if (!bounds.intersect(rtRect)) {
// the mask will be empty in this case
GrAssert(false);
bounds.setEmpty();
}
} else {
// still locked to the size of the render target
bounds = rtRect;
}
bounds.roundOut();
// need to outset a pixel since the standard bounding box computation
// path doesn't leave any room for antialiasing (esp. w.r.t. rects)
bounds.outset(SkIntToScalar(1), SkIntToScalar(1));
GrAssert(SkScalarIsInt(bounds.width()));
GrAssert(SkScalarIsInt(bounds.height()));
GrTextureDesc desc = {
kRenderTarget_GrTextureFlagBit,
SkScalarCeilToInt(bounds.width()),
SkScalarCeilToInt(bounds.height()),
kAlpha_8_GrPixelConfig,
0 // samples
};
GrRect newRTBounds;
newRTBounds.setLTRB(0, 0, bounds.width(), bounds.height());
GrTexture* accum = gpu->createTexture(desc, NULL, 0);
GrTexture* temp = gpu->createTexture(desc, NULL, 0);
if (NULL == accum || NULL == temp) {
// TODO: free up accum & temp here!
fClipMaskInAlpha = false;
return false;
}
GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
GrDrawState* drawState = gpu->drawState();
GrDrawTarget::AutoGeometryPush agp(gpu);
int count = clipIn.getElementCount();
if (0 != bounds.fTop || 0 != bounds.fLeft) {
// if we were able to trim down the size of the mask we need to
// offset the paths & rects that will be used to compute it
GrMatrix m;
m.setTranslate(-bounds.fLeft, -bounds.fTop);
drawState->preConcatViewMatrix(m);
}
bool clearToInside;
SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
int start = process_initial_clip_elements(clipIn,
bounds,
&clearToInside,
&startOp);
clear(gpu, accum, newRTBounds, clearToInside ? 0xffffffff : 0x00000000);
// walk through each clip element and perform its set op
for (int c = start; c < count; ++c) {
SkRegion::Op op = (c == start) ? startOp : clipIn.getOp(c);
if (SkRegion::kReplace_Op == op) {
// TODO: replace is actually a lot faster then intersection
// for this path - refactor the stencil path so it can handle
// replace ops and alter GrClip to allow them through
// clear the accumulator and draw the new object directly into it
clear(gpu, accum, newRTBounds, 0x00000000);
setUpBooleanBlendCoeffs(drawState, op);
this->drawClipShape(gpu, accum, clipIn, c);
} else if (SkRegion::kReverseDifference_Op == op ||
SkRegion::kIntersect_Op == op) {
// there is no point in intersecting a screen filling rectangle.
if (SkRegion::kIntersect_Op == op &&
kRect_ClipType == clipIn.getElementType(c) &&
clipIn.getRect(c).contains(bounds)) {
continue;
}
// clear the temp target & draw into it
clear(gpu, temp, newRTBounds, 0x00000000);
setUpBooleanBlendCoeffs(drawState, SkRegion::kReplace_Op);
this->drawClipShape(gpu, temp, clipIn, c);
// TODO: rather than adding these two translations here
// compute the bounding box needed to render the texture
// into temp
if (0 != bounds.fTop || 0 != bounds.fLeft) {
GrMatrix m;
m.setTranslate(bounds.fLeft, bounds.fTop);
drawState->preConcatViewMatrix(m);
}
// Now draw into the accumulator using the real operation
// and the temp buffer as a texture
setUpBooleanBlendCoeffs(drawState, op);
this->drawTexture(gpu, accum, newRTBounds, temp);
if (0 != bounds.fTop || 0 != bounds.fLeft) {
GrMatrix m;
m.setTranslate(-bounds.fLeft, -bounds.fTop);
drawState->preConcatViewMatrix(m);
}
} else {
// all the remaining ops can just be directly draw into
// the accumulation buffer
setUpBooleanBlendCoeffs(drawState, op);
this->drawClipShape(gpu, accum, clipIn, c);
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// Create a 1-bit clip mask in the stencil buffer
bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
const GrClip& clipIn,
@ -248,6 +542,8 @@ bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
// without extra passes to
// resolve in/out status.
SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c);
GrPathRenderer* pr = NULL;
const GrPath* clipPath = NULL;
if (kRect_ClipType == clipCopy.getElementType(c)) {
@ -256,7 +552,7 @@ bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
fillInverted = false;
// there is no point in intersecting a screen filling
// rectangle.
if (SkRegion::kIntersect_Op == clipCopy.getOp(c) &&
if (SkRegion::kIntersect_Op == op &&
clipCopy.getRect(c).contains(rtRect)) {
continue;
}
@ -265,7 +561,7 @@ bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
fillInverted = GrIsFillInverted(fill);
fill = GrNonInvertedFill(fill);
clipPath = &clipCopy.getPath(c);
pr = this->getClipPathRenderer(gpu, *clipPath, fill);
pr = this->getClipPathRenderer(gpu, *clipPath, fill, false);
if (NULL == pr) {
fClipMaskInStencil = false;
gpu->setClip(clipCopy); // restore to the original
@ -275,7 +571,6 @@ bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
!pr->requiresStencilPass(*clipPath, fill, gpu);
}
SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c);
int passes;
GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
@ -342,17 +637,20 @@ bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
return true;
}
////////////////////////////////////////////////////////////////////////////////
GrPathRenderer* GrClipMaskManager::getClipPathRenderer(GrGpu* gpu,
const GrPath& path,
GrPathFill fill) {
GrPathFill fill,
bool antiAlias) {
if (NULL == fPathRendererChain) {
fPathRendererChain =
new GrPathRendererChain(gpu->getContext(),
GrPathRendererChain::kNonAAOnly_UsageFlag);
GrPathRendererChain::kNone_UsageFlag);
}
return fPathRendererChain->getPathRenderer(path, fill, gpu, false);
return fPathRendererChain->getPathRenderer(path, fill, gpu, antiAlias);
}
////////////////////////////////////////////////////////////////////////////////
void GrClipMaskManager::freeResources() {
// in case path renderer has any GrResources, start from scratch
GrSafeSetNull(fPathRendererChain);

View File

@ -10,12 +10,15 @@
#define GrClipMaskManager_DEFINED
#include "GrRect.h"
#include "GrPath.h"
class GrGpu;
class GrClip;
class GrPathRenderer;
class GrPathRendererChain;
class SkPath;
class GrTexture;
class GrDrawState;
/**
* Scissoring needs special handling during stencil clip mask creation
@ -43,6 +46,7 @@ class GrClipMaskManager {
public:
GrClipMaskManager()
: fClipMaskInStencil(false)
, fClipMaskInAlpha(false)
, fPathRendererChain(NULL) {
}
@ -53,6 +57,7 @@ public:
void freeResources();
bool isClipInStencil() const { return fClipMaskInStencil; }
bool isClipInAlpha() const { return fClipMaskInAlpha; }
void resetMask() {
fClipMaskInStencil = false;
@ -61,6 +66,7 @@ public:
protected:
private:
bool fClipMaskInStencil; // is the clip mask in the stencil buffer?
bool fClipMaskInAlpha; // is the clip mask in an alpha texture?
// must be instantiated after GrGpu object has been given its owning
// GrContext ptr. (GrGpu is constructed first then handed off to GrContext).
@ -70,11 +76,28 @@ private:
const GrClip& clip,
const GrRect& bounds,
ScissoringSettings* scissorSettings);
bool createAlphaClipMask(GrGpu* gpu, const GrClip& clipIn);
bool drawPath(GrGpu* gpu,
const GrPath& path,
GrPathFill fill,
bool doAA);
bool drawClipShape(GrGpu* gpu,
GrTexture* target,
const GrClip& clipIn,
int index);
void drawTexture(GrGpu* gpu,
GrTexture* target,
const GrRect& rect,
GrTexture* texture);
// determines the path renderer used to draw a clip path element.
GrPathRenderer* getClipPathRenderer(GrGpu* gpu,
const SkPath& path,
GrPathFill fill);
GrPathFill fill,
bool antiAlias);
};