Tesselate path once for tiled offscreen AA

Review URL: http://codereview.appspot.com/4661062/


git-svn-id: http://skia.googlecode.com/svn/trunk@1777 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
bsalomon@google.com 2011-07-01 14:57:55 +00:00
parent db2a09f240
commit ee435122d7
8 changed files with 398 additions and 304 deletions

View File

@ -580,7 +580,7 @@ private:
void drawClipIntoStencil();
GrPathRenderer* getPathRenderer(const GrDrawTarget*, const GrPath&, GrPathFill);
GrPathRenderer* getPathRenderer(const GrPath&, GrPathFill);
struct OffscreenRecord;

View File

@ -18,57 +18,46 @@
#define GrPathRenderer_DEFINED
#include "GrDrawTarget.h"
#include "SkTemplates.h"
class SkPath;
struct GrPoint;
/**
* Base class for drawing paths into a GrDrawTarget.
* Paths may be drawn multiple times as when tiling for supersampling. The
* calls on GrPathRenderer to draw a path will look like this:
*
* pr->setPath(target, path, fill, translate); // sets the path to draw
* pr->drawPath(...); // draw the path
* pr->drawPath(...);
* ...
* pr->clearPath(); // finished with the path
*/
class GR_API GrPathRenderer : public GrRefCnt {
public:
GrPathRenderer(void);
/**
* Returns true if this path renderer is able to render the path.
* Returning false allows the caller to fallback to another path renderer.
*
* @param target The target to draw into
* @param path The path to draw
* @param fill The fill rule to use
*
* @return true if the path can be drawn by this object, false otherwise.
*/
virtual bool canDrawPath(const GrDrawTarget* target, const SkPath& path,
GrPathFill fill) const = 0;
/**
* Draws a path into the draw target. The target will already have its draw
* state configured for the draw.
* @param target the target to draw into.
* @param stages indicates which stages the are already
* in use. All enabled stages expect positions
* as texture coordinates. The path renderer
* use the remaining stages for its path
* filling algorithm.
* @param path the path to draw.
* @param fill the fill rule to apply.
* @param translate optional additional translation to apply to
* the path. NULL means (0,0).
*/
virtual void drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
const SkPath& path,
GrPathFill fill,
const GrPoint* translate) = 0;
virtual bool canDrawPath(const SkPath& path, GrPathFill fill) const = 0;
/**
* For complex clips Gr uses the stencil buffer. The path renderer must be
* able to render paths into the stencil buffer. However, the path renderer
* itself may require the stencil buffer to resolve the path fill rule. This
* function queries whether the path render needs its own stencil
* itself may require the stencil buffer to resolve the path fill rule.
* This function queries whether the path render needs its own stencil
* pass. If this returns false then drawPath() should not modify the
* the target's stencil settings but use those already set on target.
* the target's stencil settings but use those already set on target. The
* target is passed as a param in case the answer depends upon draw state.
* The view matrix and render target set on the draw target may change
* before setPath/drawPath is called and so shouldn't be considered.
*
* @param target target that the path will be rendered to
* @param path the path that will be drawn
@ -85,7 +74,59 @@ public:
GrPathFill fill) const { return false; }
/**
* Draws a path to the stencil buffer. Assume the writable stencil bits
* @return true if the path renderer can perform anti-aliasing (aside from
* having FSAA enabled for a render target). Target is provided to
* communicate the draw state (blend mode, stage settings, etc).
*/
virtual bool supportsAA(GrDrawTarget* target,
const SkPath& path,
GrPathFill fill) { return false; }
/**
* Sets the path to render and target to render into. All calls to drawPath
* and drawPathToStencil must occur between setPath and clearPath. The
* path cannot be modified externally between setPath and clearPath. The
* path may be drawn several times (e.g. tiled supersampler). The target's
* state may change between setPath and drawPath* calls. However, if the
* path renderer specified vertices/indices during setPath or drawPath*
* they will still be set at subsequent drawPath* calls until the next
* clearPath. The target's draw state may change between drawPath* calls
* so if the subclass does any caching of tesselation, etc. then it must
* validate that target parameters that guided the decisions still hold.
*
* @param target the target to draw into.
* @param path the path to draw.
* @param fill the fill rule to apply.
* @param translate optional additional translation to apply to
* the path. NULL means (0,0).
*/
void setPath(GrDrawTarget* target,
const SkPath* path,
GrPathFill fill,
const GrPoint* translate);
/**
* Notifies path renderer that path set in setPath is no longer in use.
*/
void clearPath();
/**
* Draws the path into the draw target. If requiresStencilBuffer returned
* false then the target may be setup for stencil rendering (since the
* path renderer didn't claim that it needs to use the stencil internally).
*
* Only called between setPath / clearPath.
*
* @param stages bitfield that indicates which stages are
* in use. All enabled stages expect positions
* as texture coordinates. The path renderer
* use the remaining stages for its path
* filling algorithm.
*/
virtual void drawPath(GrDrawTarget::StageBitfield stages) = 0;
/**
* Draws the path to the stencil buffer. Assume the writable stencil bits
* are already initialized to zero. Fill will always be either
* kWinding_PathFill or kEvenOdd_PathFill.
*
@ -95,28 +136,12 @@ public:
* The default implementation assumes the path filling algorithm doesn't
* require a separate stencil pass and so crashes.
*
*
* @param target the target to draw into.
* @param path the path to draw.
* @param fill the fill rule to apply.
* @param translate optional additional translation to apply to
* the path. NULL means (0,0).
* Only called between setPath / clearPath.
*/
virtual void drawPathToStencil(GrDrawTarget* target,
const SkPath& path,
GrPathFill fill,
const GrPoint* translate) {
virtual void drawPathToStencil() {
GrCrash("Unexpected call to drawPathToStencil.");
}
/**
* @return true if the path renderer can perform anti-aliasing (aside from
* having FSAA enabled for a render target)
*/
virtual bool supportsAA(GrDrawTarget* target,
const SkPath& path,
GrPathFill fill) { return false; }
/**
* This is called to install a custom path renderer in every GrContext at
* create time. The default implementation in GrCreatePathRenderer_none.cpp
@ -134,8 +159,56 @@ public:
fCurveTolerance = SkScalarMul(fCurveTolerance, multiplier);
}
/**
* Helper that sets a path and automatically remove it in destructor.
*/
class AutoClearPath {
public:
AutoClearPath() {
fPathRenderer = NULL;
}
AutoClearPath(GrPathRenderer* pr,
GrDrawTarget* target,
const SkPath* path,
GrPathFill fill,
const GrPoint* translate) {
GrAssert(NULL != pr);
pr->setPath(target, path, fill, translate);
fPathRenderer = pr;
}
void set(GrPathRenderer* pr,
GrDrawTarget* target,
const SkPath* path,
GrPathFill fill,
const GrPoint* translate) {
if (NULL != fPathRenderer) {
fPathRenderer->clearPath();
}
GrAssert(NULL != pr);
pr->setPath(target, path, fill, translate);
fPathRenderer = pr;
}
~AutoClearPath() {
if (NULL != fPathRenderer) {
fPathRenderer->clearPath();
}
}
private:
GrPathRenderer* fPathRenderer;
};
protected:
// subclass can override these to be notified just after a path is set
// and just before the path is cleared.
virtual void pathWasSet() {}
virtual void pathWillClear() {}
GrScalar fCurveTolerance;
const SkPath* fPath;
GrDrawTarget* fTarget;
GrPathFill fFill;
GrPoint fTranslate;
private:
@ -151,35 +224,37 @@ public:
GrDefaultPathRenderer(bool separateStencilSupport,
bool stencilWrapOpsSupport);
virtual bool canDrawPath(const GrDrawTarget* target,
const SkPath& path,
virtual bool canDrawPath(const SkPath& path,
GrPathFill fill) const { return true; }
virtual void drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
const SkPath& path,
GrPathFill fill,
const GrPoint* translate);
virtual bool requiresStencilPass(const GrDrawTarget* target,
const SkPath& path,
GrPathFill fill) const;
virtual void drawPathToStencil(GrDrawTarget* target,
const SkPath& path,
GrPathFill fill,
const GrPoint* translate);
virtual void drawPath(GrDrawTarget::StageBitfield stages);
virtual void drawPathToStencil();
protected:
virtual void pathWillClear();
private:
void onDrawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
const SkPath& path,
GrPathFill fill,
const GrPoint* translate,
bool stencilOnly);
void onDrawPath(GrDrawTarget::StageBitfield stages, bool stencilOnly);
void createGeom(GrScalar srcSpaceTolSqd,
GrDrawTarget::StageBitfield stages);
bool fSeparateStencil;
bool fStencilWrapOps;
int fSubpathCount;
SkAutoSTMalloc<8, uint16_t> fSubpathVertCount;
GrScalar fPreviousSrcTol;
GrDrawTarget::StageBitfield fPreviousStages;
typedef GrPathRenderer INHERITED;
};
#endif

View File

@ -39,7 +39,8 @@
#define GrIntToScalar(a) SkIntToScalar(a)
#define GrScalarHalf(a) SkScalarHalf(a)
#define GrScalarAve(a,b) SkScalarAve(a,b)
#define GrMul(a,b) SkScalarMul(a,b)
#define GrMul(a,b) SkScalarMul(a,b) // deprecated, prefer GrScalarMul
#define GrScalarMul(a,b) SkScalarMul(a,b)
#define GrScalarDiv(a,b) SkScalarDiv(a, b)
#define GrScalarToFloat(a) SkScalarToFloat(a)
#define GrFloatToScalar(a) SkScalarToFloat(a)

View File

@ -23,22 +23,14 @@ class GrTesselatedPathRenderer : public GrPathRenderer {
public:
GrTesselatedPathRenderer();
virtual void drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
const GrPath& path,
GrPathFill fill,
const GrPoint* translate);
virtual bool canDrawPath(const GrDrawTarget* target,
const GrPath& path,
virtual void drawPath(GrDrawTarget::StageBitfield stages);
virtual bool canDrawPath(const GrPath& path,
GrPathFill fill) const;
virtual bool requiresStencilPass(const GrDrawTarget* target,
const GrPath& path,
GrPathFill fill) const { return false; }
virtual void drawPathToStencil(GrDrawTarget* target,
const GrPath& path,
GrPathFill fill,
const GrPoint* translate);
virtual void drawPathToStencil();
virtual bool supportsAA(GrDrawTarget* target,
const GrPath& path,
GrPathFill fill);

View File

@ -715,7 +715,7 @@ void GrContext::doOffscreenAAPass2(GrDrawTarget* target,
OffscreenRecord* record) {
SK_TRACE_EVENT0("GrContext::doOffscreenAAPass2");
GrAssert(NULL != record->fEntry0);
GrDrawTarget::AutoGeometryPush agp(target);
GrIRect tileRect;
tileRect.fLeft = boundRect.fLeft + tileX * record->fTileSizeX;
tileRect.fTop = boundRect.fTop + tileY * record->fTileSizeY,
@ -1321,7 +1321,9 @@ void GrContext::drawPath(const GrPaint& paint, const GrPath& path,
GrPathFill fill, const GrPoint* translate) {
GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
GrPathRenderer* pr = this->getPathRenderer(target, path, fill);
GrPathRenderer* pr = this->getPathRenderer(path, fill);
GrPathRenderer::AutoClearPath arp(pr, target, &path, fill, translate);
GrDrawTarget::StageBitfield stageMask = paint.getActiveStageMask();
if (!pr->supportsAA(target, path, fill) &&
this->doOffscreenAA(target, paint, kHairLine_PathFill == fill)) {
@ -1357,13 +1359,12 @@ void GrContext::drawPath(const GrPaint& paint, const GrPath& path,
for (int tx = 0; tx < record.fTileCountX; ++tx) {
for (int ty = 0; ty < record.fTileCountY; ++ty) {
this->setupOffscreenAAPass1(target, bound, tx, ty, &record);
pr->drawPath(target, 0, path, fill, translate);
pr->drawPath(0);
this->doOffscreenAAPass2(target, paint, bound, tx, ty, &record);
}
}
this->cleanupOffscreenAA(target, pr, &record);
if (IsFillInverted(fill) && bound != clipIBounds) {
int stageMask = paint.getActiveStageMask();
GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
GrRect rect;
if (clipIBounds.fTop < bound.fTop) {
@ -1390,10 +1391,7 @@ void GrContext::drawPath(const GrPaint& paint, const GrPath& path,
return;
}
}
GrDrawTarget::StageBitfield enabledStages = paint.getActiveStageMask();
pr->drawPath(target, enabledStages, path, fill, translate);
pr->drawPath(stageMask);
}
////////////////////////////////////////////////////////////////////////////////
@ -1690,14 +1688,13 @@ const GrIndexBuffer* GrContext::getQuadIndexBuffer() const {
return fGpu->getQuadIndexBuffer();
}
GrPathRenderer* GrContext::getPathRenderer(const GrDrawTarget* target,
const GrPath& path,
GrPathRenderer* GrContext::getPathRenderer(const GrPath& path,
GrPathFill fill) {
if (NULL != fCustomPathRenderer &&
fCustomPathRenderer->canDrawPath(target, path, fill)) {
fCustomPathRenderer->canDrawPath(path, fill)) {
return fCustomPathRenderer;
} else {
GrAssert(fDefaultPathRenderer.canDrawPath(target, path, fill));
GrAssert(fDefaultPathRenderer.canDrawPath(path, fill));
return &fDefaultPathRenderer;
}
}

View File

@ -24,9 +24,8 @@
#include "GrPathRenderer.h"
// probably makes no sense for this to be less than a page
static const size_t VERTEX_POOL_VB_SIZE = 1 << 12;
static const int VERTEX_POOL_VB_COUNT = 1;
static const size_t VERTEX_POOL_VB_SIZE = 1 << 18;
static const int VERTEX_POOL_VB_COUNT = 4;
////////////////////////////////////////////////////////////////////////////////
@ -454,6 +453,7 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
// with the existing clip.
for (int c = firstElement; c < count; ++c) {
GrPathFill fill;
bool fillInverted;
// enabled at bottom of loop
this->disableState(kModifyStencilClip_StateBit);
@ -465,16 +465,20 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
GrPathRenderer* pr = NULL;
const GrPath* clipPath = NULL;
GrPathRenderer::AutoClearPath arp;
if (kRect_ClipType == clip.getElementType(c)) {
canRenderDirectToStencil = true;
fill = kEvenOdd_PathFill;
fillInverted = false;
} else {
fill = clip.getPathFill(c);
fillInverted = IsFillInverted(fill);
fill = NonInvertedFill(fill);
clipPath = &clip.getPath(c);
pr = this->getClipPathRenderer(*clipPath, NonInvertedFill(fill));
pr = this->getClipPathRenderer(*clipPath, fill);
canRenderDirectToStencil =
!pr->requiresStencilPass(this, *clipPath,
NonInvertedFill(fill));
!pr->requiresStencilPass(this, *clipPath, fill);
arp.set(pr, this, clipPath, fill, NULL);
}
GrSetOp op = firstElement == c ? kReplace_SetOp : clip.getOp(c);
@ -489,7 +493,7 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
GrStencilSettings::GetClipPasses(op,
canRenderDirectToStencil,
clipBit,
IsFillInverted(fill),
fillInverted,
&passes, stencilSettings);
// draw the element to the client stencil bits if necessary
@ -509,12 +513,9 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
} else {
if (canRenderDirectToStencil) {
this->setStencil(gDrawToStencil);
pr->drawPath(this, 0, *clipPath, NonInvertedFill(fill),
NULL);
pr->drawPath(0);
} else {
pr->drawPathToStencil(this, *clipPath,
NonInvertedFill(fill),
NULL);
pr->drawPathToStencil();
}
}
}
@ -530,8 +531,7 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
this->drawSimpleRect(clip.getRect(c), NULL, 0);
} else {
SET_RANDOM_COLOR
GrAssert(!IsFillInverted(fill));
pr->drawPath(this, 0, *clipPath, fill, NULL);
pr->drawPath(0);
}
} else {
SET_RANDOM_COLOR
@ -558,7 +558,7 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
GrPathRenderer* GrGpu::getClipPathRenderer(const GrPath& path,
GrPathFill fill) {
if (NULL != fClientPathRenderer &&
fClientPathRenderer->canDrawPath(this, path, fill)) {
fClientPathRenderer->canDrawPath(path, fill)) {
return fClientPathRenderer;
} else {
if (NULL == fDefaultPathRenderer) {
@ -566,7 +566,7 @@ GrPathRenderer* GrGpu::getClipPathRenderer(const GrPath& path,
new GrDefaultPathRenderer(this->supportsTwoSidedStencil(),
this->supportsStencilWrapOps());
}
GrAssert(fDefaultPathRenderer->canDrawPath(this, path, fill));
GrAssert(fDefaultPathRenderer->canDrawPath(path, fill));
return fDefaultPathRenderer;
}
}

View File

@ -11,15 +11,49 @@
#include SK_USER_TRACE_INCLUDE_FILE
GrPathRenderer::GrPathRenderer()
: fCurveTolerance (GR_Scalar1) {
: fCurveTolerance (GR_Scalar1)
, fPath(NULL)
, fTarget(NULL) {
}
void GrPathRenderer::setPath(GrDrawTarget* target,
const SkPath* path,
GrPathFill fill,
const GrPoint* translate) {
GrAssert(NULL == fPath);
GrAssert(NULL == fTarget);
GrAssert(NULL != target);
fTarget = target;
fPath = path;
fFill = fill;
if (NULL != translate) {
fTranslate = *translate;
} else {
fTranslate.fX = fTranslate.fY = 0;
}
this->pathWasSet();
}
void GrPathRenderer::clearPath() {
this->pathWillClear();
fTarget->resetVertexSource();
fTarget = NULL;
fPath = NULL;
}
////////////////////////////////////////////////////////////////////////////////
GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
bool stencilWrapOpsSupport)
: fSeparateStencil(separateStencilSupport)
, fStencilWrapOps(stencilWrapOpsSupport) {
, fStencilWrapOps(stencilWrapOpsSupport)
, fSubpathCount(0)
, fSubpathVertCount(0)
, fPreviousSrcTol(-GR_Scalar1)
, fPreviousStages(-1) {
fTarget = NULL;
}
////////////////////////////////////////////////////////////////////////////////
@ -189,21 +223,105 @@ bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target,
return !single_pass_path(*target, path, fill);
}
void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
const GrPath& path,
GrPathFill fill,
const GrPoint* translate,
void GrDefaultPathRenderer::pathWillClear() {
fSubpathVertCount.realloc(0);
fTarget->resetVertexSource();
fPreviousSrcTol = -GR_Scalar1;
fPreviousStages = -1;
}
void GrDefaultPathRenderer::createGeom(GrScalar srcSpaceTol,
GrDrawTarget::StageBitfield stages) {
{
SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom");
fPreviousSrcTol = srcSpaceTol;
fPreviousStages = stages;
GrScalar srcSpaceTolSqd = GrMul(srcSpaceTol, srcSpaceTol);
int maxPts = GrPathUtils::worstCasePointCount(*fPath, &fSubpathCount,
srcSpaceTol);
GrVertexLayout layout = 0;
for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
if ((1 << s) & stages) {
layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
}
}
// add 4 to hold the bounding rect
GrPoint* base;
fTarget->reserveVertexSpace(layout, maxPts + 4, (void**)&base);
GrPoint* vert = base;
GrPoint* subpathBase = base;
fSubpathVertCount.realloc(fSubpathCount);
GrPoint pts[4];
bool first = true;
int subpath = 0;
SkPath::Iter iter(*fPath, false);
for (;;) {
GrPathCmd cmd = (GrPathCmd)iter.next(pts);
switch (cmd) {
case kMove_PathCmd:
if (!first) {
fSubpathVertCount[subpath] = vert-subpathBase;
subpathBase = vert;
++subpath;
}
*vert = pts[0];
vert++;
break;
case kLine_PathCmd:
*vert = pts[1];
vert++;
break;
case kQuadratic_PathCmd: {
GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
srcSpaceTolSqd, &vert,
GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
break;
}
case kCubic_PathCmd: {
GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
srcSpaceTolSqd, &vert,
GrPathUtils::cubicPointCount(pts, srcSpaceTol));
break;
}
case kClose_PathCmd:
break;
case kEnd_PathCmd:
fSubpathVertCount[subpath] = vert-subpathBase;
++subpath; // this could be only in debug
goto FINISHED;
}
first = false;
}
FINISHED:
GrAssert(subpath == fSubpathCount);
GrAssert((vert - base) <= maxPts);
if (fTranslate.fX || fTranslate.fY) {
int count = vert - base;
for (int i = 0; i < count; i++) {
base[i].offset(fTranslate.fX, fTranslate.fY);
}
}
}
}
void GrDefaultPathRenderer::onDrawPath(GrDrawTarget::StageBitfield stages,
bool stencilOnly) {
SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath",
"points", SkStringPrintf("%i", path.countPoints()).c_str());
GrDrawTarget::AutoStateRestore asr(target);
bool colorWritesWereDisabled = target->isColorWriteDisabled();
// face culling doesn't make sense here
GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
GrMatrix viewM = target->getViewMatrix();
GrMatrix viewM = fTarget->getViewMatrix();
// In order to tesselate the path we get a bound on how much the matrix can
// stretch when mapping to screen coordinates.
GrScalar stretch = viewM.getMaxStretch();
@ -216,28 +334,25 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
} else {
tol = GrScalarDiv(tol, stretch);
}
GrScalar tolSqd = GrMul(tol, tol);
int subpathCnt;
int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
GrVertexLayout layout = 0;
for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
if ((1 << s) & stages) {
layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
}
// FIXME: It's really dumb that we recreate the verts for a new vertex
// layout. We only do that because the GrDrawTarget API doesn't allow
// us to change the vertex layout after reserveVertexSpace(). We won't
// actually change the vertex data when the layout changes since all the
// stages reference the positions (rather than having separate tex coords)
// and we don't ever have per-vert colors. In practice our call sites
// won't change the stages in use inside a setPath / removePath pair. But
// it is a silly limitation of the GrDrawTarget design that should be fixed.
if (tol != fPreviousSrcTol ||
stages != fPreviousStages) {
this->createGeom(tol, stages);
}
// add 4 to hold the bounding rect
GrDrawTarget::AutoReleaseGeometry arg(target, layout, maxPts + 4, 0);
GrAssert(NULL != fTarget);
GrDrawTarget::AutoStateRestore asr(fTarget);
bool colorWritesWereDisabled = fTarget->isColorWriteDisabled();
// face culling doesn't make sense here
GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
GrPoint* base = (GrPoint*) arg.vertices();
GrPoint* vert = base;
GrPoint* subpathBase = base;
SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
// TODO: use primitve restart if available rather than multiple draws
GrPrimitiveType type;
int passCount = 0;
const GrStencilSettings* passes[3];
@ -245,7 +360,7 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
bool reverse = false;
bool lastPassIsBounds;
if (kHairLine_PathFill == fill) {
if (kHairLine_PathFill == fFill) {
type = kLineStrip_PrimitiveType;
passCount = 1;
if (stencilOnly) {
@ -257,7 +372,7 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
drawFace[0] = GrDrawTarget::kBoth_DrawFace;
} else {
type = kTriangleFan_PrimitiveType;
if (single_pass_path(*target, path, fill)) {
if (single_pass_path(*fTarget, *fPath, fFill)) {
passCount = 1;
if (stencilOnly) {
passes[0] = &gDirectToStencil;
@ -267,7 +382,7 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
drawFace[0] = GrDrawTarget::kBoth_DrawFace;
lastPassIsBounds = false;
} else {
switch (fill) {
switch (fFill) {
case kInverseEvenOdd_PathFill:
reverse = true;
// fallthrough
@ -327,140 +442,62 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
}
break;
default:
GrAssert(!"Unknown path fill!");
GrAssert(!"Unknown path fFill!");
return;
}
}
}
GrPoint pts[4];
bool first = true;
int subpath = 0;
SkPath::Iter iter(path, false);
{
SK_TRACE_EVENT0("GrDefaultPathRenderer::onDrawPath::assembleVerts");
for (;;) {
GrPathCmd cmd = (GrPathCmd)iter.next(pts);
switch (cmd) {
case kMove_PathCmd:
if (!first) {
subpathVertCount[subpath] = vert-subpathBase;
subpathBase = vert;
++subpath;
}
*vert = pts[0];
vert++;
break;
case kLine_PathCmd:
*vert = pts[1];
vert++;
break;
case kQuadratic_PathCmd: {
GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
tolSqd, &vert,
GrPathUtils::quadraticPointCount(pts, tol));
break;
}
case kCubic_PathCmd: {
GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
tolSqd, &vert,
GrPathUtils::cubicPointCount(pts, tol));
break;
}
case kClose_PathCmd:
break;
case kEnd_PathCmd:
subpathVertCount[subpath] = vert-subpathBase;
++subpath; // this could be only in debug
goto FINISHED;
}
first = false;
}
}
FINISHED:
GrAssert(subpath == subpathCnt);
GrAssert((vert - base) <= maxPts);
if (translate) {
int count = vert - base;
for (int i = 0; i < count; i++) {
base[i].offset(translate->fX, translate->fY);
}
}
// if we're stenciling we will follow with a pass that draws
// a bounding rect to set the color. We're stenciling when
// passCount > 1.
const int& boundVertexStart = maxPts;
GrPoint* boundsVerts = base + boundVertexStart;
if (lastPassIsBounds) {
GrRect bounds;
if (reverse) {
GrAssert(NULL != target->getRenderTarget());
// draw over the whole world.
bounds.setLTRB(0, 0,
GrIntToScalar(target->getRenderTarget()->width()),
GrIntToScalar(target->getRenderTarget()->height()));
GrMatrix vmi;
if (target->getViewInverse(&vmi)) {
vmi.mapRect(&bounds);
}
} else {
bounds.setBounds((GrPoint*)base, vert - base);
}
boundsVerts[0].setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight,
bounds.fBottom);
}
{
SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath::renderPasses",
"verts", SkStringPrintf("%i", vert - base).c_str());
for (int p = 0; p < passCount; ++p) {
target->setDrawFace(drawFace[p]);
fTarget->setDrawFace(drawFace[p]);
if (NULL != passes[p]) {
target->setStencil(*passes[p]);
fTarget->setStencil(*passes[p]);
}
if (lastPassIsBounds && (p == passCount-1)) {
if (!colorWritesWereDisabled) {
target->disableState(GrDrawTarget::kNoColorWrites_StateBit);
fTarget->disableState(GrDrawTarget::kNoColorWrites_StateBit);
}
target->drawNonIndexed(kTriangleFan_PrimitiveType,
boundVertexStart, 4);
GrRect bounds;
if (reverse) {
GrAssert(NULL != fTarget->getRenderTarget());
// draw over the whole world.
bounds.setLTRB(0, 0,
GrIntToScalar(fTarget->getRenderTarget()->width()),
GrIntToScalar(fTarget->getRenderTarget()->height()));
GrMatrix vmi;
if (fTarget->getViewInverse(&vmi)) {
vmi.mapRect(&bounds);
}
} else {
bounds = fPath->getBounds();
}
GrDrawTarget::AutoGeometryPush agp(fTarget);
fTarget->drawSimpleRect(bounds, NULL, stages);
} else {
if (passCount > 1) {
target->enableState(GrDrawTarget::kNoColorWrites_StateBit);
fTarget->enableState(GrDrawTarget::kNoColorWrites_StateBit);
}
int baseVertex = 0;
for (int sp = 0; sp < subpathCnt; ++sp) {
target->drawNonIndexed(type,
baseVertex,
subpathVertCount[sp]);
baseVertex += subpathVertCount[sp];
for (int sp = 0; sp < fSubpathCount; ++sp) {
fTarget->drawNonIndexed(type, baseVertex,
fSubpathVertCount[sp]);
baseVertex += fSubpathVertCount[sp];
}
}
}
}
}
void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
const GrPath& path,
GrPathFill fill,
const GrPoint* translate) {
this->onDrawPath(target, stages, path, fill, translate, false);
void GrDefaultPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
this->onDrawPath(stages, false);
}
void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target,
const GrPath& path,
GrPathFill fill,
const GrPoint* translate) {
GrAssert(kInverseEvenOdd_PathFill != fill);
GrAssert(kInverseWinding_PathFill != fill);
this->onDrawPath(target, 0, path, fill, translate, true);
void GrDefaultPathRenderer::drawPathToStencil() {
GrAssert(kInverseEvenOdd_PathFill != fFill);
GrAssert(kInverseWinding_PathFill != fFill);
this->onDrawPath(0, true);
}

View File

@ -33,7 +33,7 @@ typedef void (*TESSCB)();
// limit the allowable vertex range to approximately half of the representable
// IEEE exponent in order to avoid overflow when doing multiplies between
// vertex components,
const float kMaxVertexValue = 1e18;
const float kMaxVertexValue = 1e18f;
static inline GrDrawTarget::Edge computeEdge(const GrPoint& p,
const GrPoint& q,
@ -352,16 +352,12 @@ static size_t computeEdgesAndIntersect(const GrMatrix& matrix,
return edges->count();
}
void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
const GrPath& path,
GrPathFill fill,
const GrPoint* translate) {
GrDrawTarget::AutoStateRestore asr(target);
void GrTesselatedPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
GrDrawTarget::AutoStateRestore asr(fTarget);
// face culling doesn't make sense here
GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
GrMatrix viewM = target->getViewMatrix();
GrMatrix viewM = fTarget->getViewMatrix();
// In order to tesselate the path we get a bound on how much the matrix can
// stretch when mapping to screen coordinates.
GrScalar stretch = viewM.getMaxStretch();
@ -377,7 +373,7 @@ void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
GrScalar tolSqd = GrMul(tol, tol);
int subpathCnt;
int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
int maxPts = GrPathUtils::worstCasePointCount(*fPath, &subpathCnt, tol);
GrVertexLayout layout = 0;
for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
@ -386,7 +382,7 @@ void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
}
}
bool inverted = IsFillInverted(fill);
bool inverted = IsFillInverted(fFill);
if (inverted) {
maxPts += 4;
subpathCnt++;
@ -402,7 +398,7 @@ void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
GrPoint pts[4];
SkPath::Iter iter(path, false);
SkPath::Iter iter(*fPath, false);
bool first = true;
int subpath = 0;
@ -444,20 +440,20 @@ void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
first = false;
}
FINISHED:
if (translate) {
if (0 != fTranslate.fX || 0 != fTranslate.fY) {
for (int i = 0; i < vert - base; i++) {
base[i].offset(translate->fX, translate->fY);
base[i].offset(fTranslate.fX, fTranslate.fY);
}
}
if (inverted) {
GrRect bounds;
GrAssert(NULL != target->getRenderTarget());
GrAssert(NULL != fTarget->getRenderTarget());
bounds.setLTRB(0, 0,
GrIntToScalar(target->getRenderTarget()->width()),
GrIntToScalar(target->getRenderTarget()->height()));
GrIntToScalar(fTarget->getRenderTarget()->width()),
GrIntToScalar(fTarget->getRenderTarget()->height()));
GrMatrix vmi;
if (target->getViewInverse(&vmi)) {
if (fTarget->getViewInverse(&vmi)) {
vmi.mapRect(&bounds);
}
*vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
@ -476,22 +472,22 @@ FINISHED:
return;
}
if (subpathCnt == 1 && !inverted && path.isConvex()) {
if (target->isAntialiasState()) {
if (subpathCnt == 1 && !inverted && fPath->isConvex()) {
if (fTarget->isAntialiasState()) {
GrEdgeArray edges;
GrMatrix inverse, matrix = target->getViewMatrix();
target->getViewInverse(&inverse);
GrMatrix inverse, matrix = fTarget->getViewMatrix();
fTarget->getViewInverse(&inverse);
count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
size_t maxEdges = target->getMaxEdges();
size_t maxEdges = fTarget->getMaxEdges();
if (count == 0) {
return;
}
if (count <= maxEdges) {
// All edges fit; upload all edges and draw all verts as a fan
target->setVertexSourceToArray(layout, base, count);
target->setEdgeAAData(&edges[0], count);
target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
fTarget->setVertexSourceToArray(layout, base, count);
fTarget->setEdgeAAData(&edges[0], count);
fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
} else {
// Upload "maxEdges" edges and verts at a time, and draw as
// separate fans
@ -499,26 +495,26 @@ FINISHED:
edges[i] = edges[0];
base[i] = base[0];
int size = GR_CT_MIN(count - i, maxEdges);
target->setVertexSourceToArray(layout, &base[i], size);
target->setEdgeAAData(&edges[i], size);
target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
fTarget->setVertexSourceToArray(layout, &base[i], size);
fTarget->setEdgeAAData(&edges[i], size);
fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
}
}
target->setEdgeAAData(NULL, 0);
fTarget->setEdgeAAData(NULL, 0);
} else {
target->setVertexSourceToArray(layout, base, count);
target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
fTarget->setVertexSourceToArray(layout, base, count);
fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
}
return;
}
if (target->isAntialiasState()) {
if (fTarget->isAntialiasState()) {
// Run the tesselator once to get the boundaries.
GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fill));
GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fFill));
btess.addVertices(base, subpathVertCount, subpathCnt);
GrMatrix inverse, matrix = target->getViewMatrix();
if (!target->getViewInverse(&inverse)) {
GrMatrix inverse, matrix = fTarget->getViewMatrix();
if (!fTarget->getViewInverse(&inverse)) {
return;
}
@ -553,7 +549,7 @@ FINISHED:
}
// Draw the resulting polys and upload their edge data.
target->enableState(GrDrawTarget::kEdgeAAConcave_StateBit);
fTarget->enableState(GrDrawTarget::kEdgeAAConcave_StateBit);
const GrPointArray& vertices = ptess.vertices();
const GrIndexArray& indices = ptess.indices();
const GrDrawTarget::Edge* edges = ptess.edges();
@ -586,23 +582,23 @@ FINISHED:
tri_edges[t++] = edge4;
tri_edges[t++] = edge5;
}
target->setEdgeAAData(&tri_edges[0], t);
target->setVertexSourceToArray(layout, &tri_verts[0], 3);
target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
fTarget->setEdgeAAData(&tri_edges[0], t);
fTarget->setVertexSourceToArray(layout, &tri_verts[0], 3);
fTarget->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
}
target->setEdgeAAData(NULL, 0);
target->disableState(GrDrawTarget::kEdgeAAConcave_StateBit);
fTarget->setEdgeAAData(NULL, 0);
fTarget->disableState(GrDrawTarget::kEdgeAAConcave_StateBit);
return;
}
GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fill));
GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fFill));
ptess.addVertices(base, subpathVertCount, subpathCnt);
const GrPointArray& vertices = ptess.vertices();
const GrIndexArray& indices = ptess.indices();
if (indices.count() > 0) {
target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
target->setIndexSourceToArray(indices.begin(), indices.count());
target->drawIndexed(kTriangles_PrimitiveType,
fTarget->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
fTarget->setIndexSourceToArray(indices.begin(), indices.count());
fTarget->drawIndexed(kTriangles_PrimitiveType,
0,
0,
vertices.count(),
@ -610,16 +606,12 @@ FINISHED:
}
}
bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
const SkPath& path,
bool GrTesselatedPathRenderer::canDrawPath(const SkPath& path,
GrPathFill fill) const {
return kHairLine_PathFill != fill;
}
void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
const SkPath& path,
GrPathFill fill,
const GrPoint* translate) {
void GrTesselatedPathRenderer::drawPathToStencil() {
GrAlwaysAssert(!"multipass stencil should not be needed");
}