Use a prioritized list of path renderers in Gr.
http://codereview.appspot.com/4867058
git-svn-id: http://skia.googlecode.com/svn/trunk@2143 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index 2f64996..ee16321 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -16,7 +16,6 @@
// remove.
#include "GrRenderTarget.h"
-class GrDefaultPathRenderer;
class GrDrawTarget;
class GrFontCache;
class GrGpu;
@@ -25,6 +24,7 @@
class GrIndexBufferAllocPool;
class GrInOrderDrawBuffer;
class GrPathRenderer;
+class GrPathRendererChain;
class GrResourceEntry;
class GrResourceCache;
class GrStencilBuffer;
@@ -556,8 +556,7 @@
GrResourceCache* fTextureCache;
GrFontCache* fFontCache;
- GrPathRenderer* fCustomPathRenderer;
- GrDefaultPathRenderer* fDefaultPathRenderer;
+ GrPathRendererChain* fPathRendererChain;
GrVertexBufferAllocPool* fDrawBufferVBAllocPool;
GrIndexBufferAllocPool* fDrawBufferIBAllocPool;
@@ -592,9 +591,9 @@
GrDrawTarget* prepareToDraw(const GrPaint& paint, DrawCategory drawType);
- void drawClipIntoStencil();
-
- GrPathRenderer* getPathRenderer(const GrPath&, GrPathFill);
+ GrPathRenderer* getPathRenderer(const GrDrawTarget* target,
+ const GrPath& path,
+ GrPathFill fill);
struct OffscreenRecord;
diff --git a/gpu/src/GrAddPathRenderers_none.cpp b/gpu/src/GrAddPathRenderers_none.cpp
new file mode 100644
index 0000000..46855db
--- /dev/null
+++ b/gpu/src/GrAddPathRenderers_none.cpp
@@ -0,0 +1,15 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrPathRenderer.h"
+
+
+void GrPathRenderer::AddPathRenderers(GrContext*,
+ GrPathRendererChain::UsageFlags,
+ GrPathRendererChain*) {}
diff --git a/gpu/src/GrAddPathRenderers_tesselated.cpp b/gpu/src/GrAddPathRenderers_tesselated.cpp
new file mode 100644
index 0000000..a1cde13
--- /dev/null
+++ b/gpu/src/GrAddPathRenderers_tesselated.cpp
@@ -0,0 +1,17 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrTesselatedPathRenderer.h"
+
+
+void GrPathRenderer::AddPathRenderers(GrContext*,
+ GrPathRendererChain::UsageFlags flags,
+ GrPathRendererChain* chain) {
+ chain->addPathRenderer(new GrTesselatedPathRenderer())->unref();
+}
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index b3e902d..506cb9f 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -61,11 +61,11 @@
delete fDrawBuffer;
delete fDrawBufferVBAllocPool;
delete fDrawBufferIBAllocPool;
- GrSafeUnref(fDefaultPathRenderer);
- GrSafeUnref(fCustomPathRenderer);
+
GrSafeUnref(fAAFillRectIndexBuffer);
GrSafeUnref(fAAStrokeRectIndexBuffer);
fGpu->unref();
+ GrSafeUnref(fPathRendererChain);
}
void GrContext::contextLost() {
@@ -78,6 +78,10 @@
// don't try to free the resources in the API.
fGpu->abandonResources();
+ // a path renderer may be holding onto resources that
+ // are now unusable
+ GrSafeSetNull(fPathRendererChain);
+
delete fDrawBuffer;
fDrawBuffer = NULL;
@@ -103,6 +107,8 @@
this->flush();
fTextureCache->removeAll();
fFontCache->freeAll();
+ // a path renderer may be holding onto resources
+ GrSafeSetNull(fPathRendererChain);
}
////////////////////////////////////////////////////////////////////////////////
@@ -1411,7 +1417,12 @@
GrPathFill fill, const GrPoint* translate) {
GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
- GrPathRenderer* pr = this->getPathRenderer(path, fill);
+ GrPathRenderer* pr = this->getPathRenderer(target, path, fill);
+ if (NULL == pr) {
+ GrPrintf("Unable to find path renderer compatible with path.\n");
+ return;
+ }
+
GrPathRenderer::AutoClearPath arp(pr, target, &path, fill, translate);
GrDrawTarget::StageBitfield stageMask = paint.getActiveStageMask();
@@ -1658,6 +1669,16 @@
return target;
}
+GrPathRenderer* GrContext::getPathRenderer(const GrDrawTarget* target,
+ const GrPath& path,
+ GrPathFill fill) {
+ if (NULL == fPathRendererChain) {
+ fPathRendererChain =
+ new GrPathRendererChain(this, GrPathRendererChain::kNone_UsageFlag);
+ }
+ return fPathRendererChain->getPathRenderer(target, path, fill);
+}
+
////////////////////////////////////////////////////////////////////////////////
void GrContext::setRenderTarget(GrRenderTarget* target) {
@@ -1712,11 +1733,7 @@
fGpu->ref();
fGpu->setContext(this);
- fDefaultPathRenderer =
- new GrDefaultPathRenderer(gpu->supportsTwoSidedStencil(),
- gpu->supportsStencilWrapOps());
- fCustomPathRenderer = GrPathRenderer::CreatePathRenderer();
- fGpu->setClipPathRenderer(fCustomPathRenderer);
+ fPathRendererChain = NULL;
fTextureCache = new GrResourceCache(MAX_TEXTURE_CACHE_COUNT,
MAX_TEXTURE_CACHE_BYTES);
@@ -1780,17 +1797,6 @@
return fGpu->getQuadIndexBuffer();
}
-GrPathRenderer* GrContext::getPathRenderer(const GrPath& path,
- GrPathFill fill) {
- if (NULL != fCustomPathRenderer &&
- fCustomPathRenderer->canDrawPath(path, fill)) {
- return fCustomPathRenderer;
- } else {
- GrAssert(fDefaultPathRenderer->canDrawPath(path, fill));
- return fDefaultPathRenderer;
- }
-}
-
void GrContext::convolveInX(GrTexture* texture,
const SkRect& rect,
const float* kernel,
diff --git a/gpu/src/GrCreatePathRenderer_none.cpp b/gpu/src/GrCreatePathRenderer_none.cpp
deleted file mode 100644
index 5072afa..0000000
--- a/gpu/src/GrCreatePathRenderer_none.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#include "GrPathRenderer.h"
-
-
-GrPathRenderer* GrPathRenderer::CreatePathRenderer() { return NULL; }
diff --git a/gpu/src/GrCreatePathRenderer_tesselated.cpp b/gpu/src/GrCreatePathRenderer_tesselated.cpp
deleted file mode 100644
index 38d7aa2..0000000
--- a/gpu/src/GrCreatePathRenderer_tesselated.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#include "GrTesselatedPathRenderer.h"
-
-
-GrPathRenderer* GrPathRenderer::CreatePathRenderer() { return new GrTesselatedPathRenderer(); }
diff --git a/gpu/src/GrDefaultPathRenderer.cpp b/gpu/src/GrDefaultPathRenderer.cpp
new file mode 100644
index 0000000..b8b7f62
--- /dev/null
+++ b/gpu/src/GrDefaultPathRenderer.cpp
@@ -0,0 +1,562 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrDefaultPathRenderer.h"
+
+#include "GrContext.h"
+#include "GrPathUtils.h"
+#include "SkTrace.h"
+
+
+GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
+ bool stencilWrapOpsSupport)
+ : fSeparateStencil(separateStencilSupport)
+ , fStencilWrapOps(stencilWrapOpsSupport)
+ , fSubpathCount(0)
+ , fSubpathVertCount(0)
+ , fPreviousSrcTol(-GR_Scalar1)
+ , fPreviousStages(-1) {
+ fTarget = NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Stencil rules for paths
+
+////// Even/Odd
+
+static const GrStencilSettings gEOStencilPass = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff
+};
+
+// ok not to check clip b/c stencil pass only wrote inside clip
+static const GrStencilSettings gEOColorPass = {
+ kZero_StencilOp, kZero_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kNotEqual_StencilFunc, kNotEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+// have to check clip b/c outside clip will always be zero.
+static const GrStencilSettings gInvEOColorPass = {
+ kZero_StencilOp, kZero_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+////// Winding
+
+// when we have separate stencil we increment front faces / decrement back faces
+// when we don't have wrap incr and decr we use the stencil test to simulate
+// them.
+
+static const GrStencilSettings gWindStencilSeparateWithWrap = {
+ kIncWrap_StencilOp, kDecWrap_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff
+};
+
+// if inc'ing the max value, invert to make 0
+// if dec'ing zero invert to make all ones.
+// we can't avoid touching the stencil on both passing and
+// failing, so we can't resctrict ourselves to the clip.
+static const GrStencilSettings gWindStencilSeparateNoWrap = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kIncClamp_StencilOp, kDecClamp_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+// When there are no separate faces we do two passes to setup the winding rule
+// stencil. First we draw the front faces and inc, then we draw the back faces
+// and dec. These are same as the above two split into the incrementing and
+// decrementing passes.
+static const GrStencilSettings gWindSingleStencilWithWrapInc = {
+ kIncWrap_StencilOp, kIncWrap_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff
+};
+static const GrStencilSettings gWindSingleStencilWithWrapDec = {
+ kDecWrap_StencilOp, kDecWrap_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff
+};
+static const GrStencilSettings gWindSingleStencilNoWrapInc = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kIncClamp_StencilOp, kIncClamp_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff
+};
+static const GrStencilSettings gWindSingleStencilNoWrapDec = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kDecClamp_StencilOp, kDecClamp_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gWindColorPass = {
+ kZero_StencilOp, kZero_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kNonZeroIfInClip_StencilFunc, kNonZeroIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gInvWindColorPass = {
+ kZero_StencilOp, kZero_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+////// Normal render to stencil
+
+// Sometimes the default path renderer can draw a path directly to the stencil
+// buffer without having to first resolve the interior / exterior.
+static const GrStencilSettings gDirectToStencil = {
+ kZero_StencilOp, kZero_StencilOp,
+ kIncClamp_StencilOp, kIncClamp_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Helpers for drawPath
+
+static GrConvexHint getConvexHint(const SkPath& path) {
+ return path.isConvex() ? kConvex_ConvexHint : kConcave_ConvexHint;
+}
+
+#define STENCIL_OFF 0 // Always disable stencil (even when needed)
+
+static inline bool single_pass_path(const GrDrawTarget& target,
+ const GrPath& path,
+ GrPathFill fill) {
+#if STENCIL_OFF
+ return true;
+#else
+ if (kEvenOdd_PathFill == fill) {
+ GrConvexHint hint = getConvexHint(path);
+ return hint == kConvex_ConvexHint ||
+ hint == kNonOverlappingConvexPieces_ConvexHint;
+ } else if (kWinding_PathFill == fill) {
+ GrConvexHint hint = getConvexHint(path);
+ return hint == kConvex_ConvexHint ||
+ hint == kNonOverlappingConvexPieces_ConvexHint ||
+ (hint == kSameWindingConvexPieces_ConvexHint &&
+ target.canDisableBlend() && !target.isDitherState());
+
+ }
+ return false;
+#endif
+}
+
+bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target,
+ const GrPath& path,
+ GrPathFill fill) const {
+ return !single_pass_path(*target, path, fill);
+}
+
+void GrDefaultPathRenderer::pathWillClear() {
+ fSubpathVertCount.realloc(0);
+ fTarget->resetVertexSource();
+ if (fUseIndexedDraw) {
+ fTarget->resetIndexSource();
+ }
+ fPreviousSrcTol = -GR_Scalar1;
+ fPreviousStages = -1;
+}
+
+static inline void append_countour_edge_indices(GrPathFill fillType,
+ uint16_t fanCenterIdx,
+ uint16_t edgeV0Idx,
+ uint16_t** indices) {
+ // when drawing lines we're appending line segments along
+ // the contour. When applying the other fill rules we're
+ // drawing triangle fans around fanCenterIdx.
+ if (kHairLine_PathFill != fillType) {
+ *((*indices)++) = fanCenterIdx;
+ }
+ *((*indices)++) = edgeV0Idx;
+ *((*indices)++) = edgeV0Idx + 1;
+}
+
+bool GrDefaultPathRenderer::createGeom(GrScalar srcSpaceTol,
+ GrDrawTarget::StageBitfield stages) {
+ {
+ SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom");
+
+ GrScalar srcSpaceTolSqd = GrMul(srcSpaceTol, srcSpaceTol);
+ int maxPts = GrPathUtils::worstCasePointCount(*fPath, &fSubpathCount,
+ srcSpaceTol);
+
+ if (maxPts <= 0) {
+ return false;
+ }
+ if (maxPts > ((int)SK_MaxU16 + 1)) {
+ GrPrintf("Path not rendered, too many verts (%d)\n", maxPts);
+ return false;
+ }
+
+ GrVertexLayout layout = 0;
+ for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
+ if ((1 << s) & stages) {
+ layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
+ }
+ }
+
+ fUseIndexedDraw = fSubpathCount > 1;
+
+ int maxIdxs = 0;
+ if (kHairLine_PathFill == fFill) {
+ if (fUseIndexedDraw) {
+ maxIdxs = 2 * maxPts;
+ fPrimitiveType = kLines_PrimitiveType;
+ } else {
+ fPrimitiveType = kLineStrip_PrimitiveType;
+ }
+ } else {
+ if (fUseIndexedDraw) {
+ maxIdxs = 3 * maxPts;
+ fPrimitiveType = kTriangles_PrimitiveType;
+ } else {
+ fPrimitiveType = kTriangleFan_PrimitiveType;
+ }
+ }
+
+ GrPoint* base;
+ if (!fTarget->reserveVertexSpace(layout, maxPts, (void**)&base)) {
+ return false;
+ }
+ GrAssert(NULL != base);
+ GrPoint* vert = base;
+
+ uint16_t* idxBase = NULL;
+ uint16_t* idx = NULL;
+ uint16_t subpathIdxStart = 0;
+ if (fUseIndexedDraw) {
+ if (!fTarget->reserveIndexSpace(maxIdxs, (void**)&idxBase)) {
+ fTarget->resetVertexSource();
+ return false;
+ }
+ GrAssert(NULL != idxBase);
+ idx = idxBase;
+ }
+
+ 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) {
+ uint16_t currIdx = (uint16_t) (vert - base);
+ fSubpathVertCount[subpath] = currIdx - subpathIdxStart;
+ subpathIdxStart = currIdx;
+ ++subpath;
+ }
+ *vert = pts[0];
+ vert++;
+ break;
+ case kLine_PathCmd:
+ if (fUseIndexedDraw) {
+ uint16_t prevIdx = (uint16_t)(vert - base) - 1;
+ append_countour_edge_indices(fFill, subpathIdxStart,
+ prevIdx, &idx);
+ }
+ *(vert++) = pts[1];
+ break;
+ case kQuadratic_PathCmd: {
+ // first pt of quad is the pt we ended on in previous step
+ uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1;
+ uint16_t numPts = (uint16_t)
+ GrPathUtils::generateQuadraticPoints(
+ pts[0], pts[1], pts[2],
+ srcSpaceTolSqd, &vert,
+ GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
+ if (fUseIndexedDraw) {
+ for (uint16_t i = 0; i < numPts; ++i) {
+ append_countour_edge_indices(fFill, subpathIdxStart,
+ firstQPtIdx + i, &idx);
+ }
+ }
+ break;
+ }
+ case kCubic_PathCmd: {
+ // first pt of cubic is the pt we ended on in previous step
+ uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1;
+ uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
+ pts[0], pts[1], pts[2], pts[3],
+ srcSpaceTolSqd, &vert,
+ GrPathUtils::cubicPointCount(pts, srcSpaceTol));
+ if (fUseIndexedDraw) {
+ for (uint16_t i = 0; i < numPts; ++i) {
+ append_countour_edge_indices(fFill, subpathIdxStart,
+ firstCPtIdx + i, &idx);
+ }
+ }
+ break;
+ }
+ case kClose_PathCmd:
+ break;
+ case kEnd_PathCmd:
+ uint16_t currIdx = (uint16_t) (vert - base);
+ fSubpathVertCount[subpath] = currIdx - subpathIdxStart;
+ goto FINISHED;
+ }
+ first = false;
+ }
+FINISHED:
+ GrAssert((vert - base) <= maxPts);
+ GrAssert((idx - idxBase) <= maxIdxs);
+
+ fVertexCnt = vert - base;
+ fIndexCnt = idx - idxBase;
+
+ if (fTranslate.fX || fTranslate.fY) {
+ int count = vert - base;
+ for (int i = 0; i < count; i++) {
+ base[i].offset(fTranslate.fX, fTranslate.fY);
+ }
+ }
+ }
+ // set these at the end so if we failed on first drawPath inside a
+ // setPath/clearPath block we won't assume geom was created on a subsequent
+ // drawPath in the same block.
+ fPreviousSrcTol = srcSpaceTol;
+ fPreviousStages = stages;
+ return true;
+}
+
+void GrDefaultPathRenderer::onDrawPath(GrDrawTarget::StageBitfield stages,
+ bool stencilOnly) {
+
+ SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath",
+ "points", SkStringPrintf("%i", path.countPoints()).c_str());
+
+ 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();
+ bool useStretch = stretch > 0;
+ GrScalar tol = fCurveTolerance;
+
+ if (!useStretch) {
+ // TODO: deal with perspective in some better way.
+ tol /= 10;
+ } else {
+ tol = GrScalarDiv(tol, stretch);
+ }
+ // 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) {
+ if (!this->createGeom(tol, stages)) {
+ return;
+ }
+ }
+
+ GrAssert(NULL != fTarget);
+ GrDrawTarget::AutoStateRestore asr(fTarget);
+ bool colorWritesWereDisabled = fTarget->isColorWriteDisabled();
+ // face culling doesn't make sense here
+ GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
+
+ int passCount = 0;
+ const GrStencilSettings* passes[3];
+ GrDrawTarget::DrawFace drawFace[3];
+ bool reverse = false;
+ bool lastPassIsBounds;
+
+ if (kHairLine_PathFill == fFill) {
+ passCount = 1;
+ if (stencilOnly) {
+ passes[0] = &gDirectToStencil;
+ } else {
+ passes[0] = NULL;
+ }
+ lastPassIsBounds = false;
+ drawFace[0] = GrDrawTarget::kBoth_DrawFace;
+ } else {
+ if (single_pass_path(*fTarget, *fPath, fFill)) {
+ passCount = 1;
+ if (stencilOnly) {
+ passes[0] = &gDirectToStencil;
+ } else {
+ passes[0] = NULL;
+ }
+ drawFace[0] = GrDrawTarget::kBoth_DrawFace;
+ lastPassIsBounds = false;
+ } else {
+ switch (fFill) {
+ case kInverseEvenOdd_PathFill:
+ reverse = true;
+ // fallthrough
+ case kEvenOdd_PathFill:
+ passes[0] = &gEOStencilPass;
+ if (stencilOnly) {
+ passCount = 1;
+ lastPassIsBounds = false;
+ } else {
+ passCount = 2;
+ lastPassIsBounds = true;
+ if (reverse) {
+ passes[1] = &gInvEOColorPass;
+ } else {
+ passes[1] = &gEOColorPass;
+ }
+ }
+ drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace;
+ break;
+
+ case kInverseWinding_PathFill:
+ reverse = true;
+ // fallthrough
+ case kWinding_PathFill:
+ if (fSeparateStencil) {
+ if (fStencilWrapOps) {
+ passes[0] = &gWindStencilSeparateWithWrap;
+ } else {
+ passes[0] = &gWindStencilSeparateNoWrap;
+ }
+ passCount = 2;
+ drawFace[0] = GrDrawTarget::kBoth_DrawFace;
+ } else {
+ if (fStencilWrapOps) {
+ passes[0] = &gWindSingleStencilWithWrapInc;
+ passes[1] = &gWindSingleStencilWithWrapDec;
+ } else {
+ passes[0] = &gWindSingleStencilNoWrapInc;
+ passes[1] = &gWindSingleStencilNoWrapDec;
+ }
+ // which is cw and which is ccw is arbitrary.
+ drawFace[0] = GrDrawTarget::kCW_DrawFace;
+ drawFace[1] = GrDrawTarget::kCCW_DrawFace;
+ passCount = 3;
+ }
+ if (stencilOnly) {
+ lastPassIsBounds = false;
+ --passCount;
+ } else {
+ lastPassIsBounds = true;
+ drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace;
+ if (reverse) {
+ passes[passCount-1] = &gInvWindColorPass;
+ } else {
+ passes[passCount-1] = &gWindColorPass;
+ }
+ }
+ break;
+ default:
+ GrAssert(!"Unknown path fFill!");
+ return;
+ }
+ }
+ }
+
+ {
+ SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath::renderPasses",
+ "verts", SkStringPrintf("%i", vert - base).c_str());
+ for (int p = 0; p < passCount; ++p) {
+ fTarget->setDrawFace(drawFace[p]);
+ if (NULL != passes[p]) {
+ fTarget->setStencil(*passes[p]);
+ }
+
+ if (lastPassIsBounds && (p == passCount-1)) {
+ if (!colorWritesWereDisabled) {
+ fTarget->disableState(GrDrawTarget::kNoColorWrites_StateBit);
+ }
+ 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();
+ bounds.offset(fTranslate);
+ }
+ GrDrawTarget::AutoGeometryPush agp(fTarget);
+ fTarget->drawSimpleRect(bounds, NULL, stages);
+ } else {
+ if (passCount > 1) {
+ fTarget->enableState(GrDrawTarget::kNoColorWrites_StateBit);
+ }
+ if (fUseIndexedDraw) {
+ fTarget->drawIndexed(fPrimitiveType, 0, 0,
+ fVertexCnt, fIndexCnt);
+ } else {
+ int baseVertex = 0;
+ for (int sp = 0; sp < fSubpathCount; ++sp) {
+ fTarget->drawNonIndexed(fPrimitiveType, baseVertex,
+ fSubpathVertCount[sp]);
+ baseVertex += fSubpathVertCount[sp];
+ }
+ }
+ }
+ }
+ }
+}
+
+void GrDefaultPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
+ this->onDrawPath(stages, false);
+}
+
+void GrDefaultPathRenderer::drawPathToStencil() {
+ GrAssert(kInverseEvenOdd_PathFill != fFill);
+ GrAssert(kInverseWinding_PathFill != fFill);
+ this->onDrawPath(0, true);
+}
diff --git a/gpu/src/GrDefaultPathRenderer.h b/gpu/src/GrDefaultPathRenderer.h
new file mode 100644
index 0000000..e11716e
--- /dev/null
+++ b/gpu/src/GrDefaultPathRenderer.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrDefaultPathRenderer_DEFINED
+#define GrDefaultPathRenderer_DEFINED
+
+#include "GrPathRenderer.h"
+#include "SkTemplates.h"
+
+/**
+ * Subclass that renders the path using the stencil buffer to resolve fill
+ * rules (e.g. winding, even-odd)
+ */
+class GR_API GrDefaultPathRenderer : public GrPathRenderer {
+public:
+ GrDefaultPathRenderer(bool separateStencilSupport,
+ bool stencilWrapOpsSupport);
+
+ virtual bool canDrawPath(const SkPath& path,
+ GrPathFill fill) const { return true; }
+
+ virtual bool requiresStencilPass(const GrDrawTarget* target,
+ const SkPath& path,
+ GrPathFill fill) const;
+
+ virtual void drawPath(GrDrawTarget::StageBitfield stages);
+ virtual void drawPathToStencil();
+
+protected:
+ virtual void pathWillClear();
+
+private:
+
+ void onDrawPath(GrDrawTarget::StageBitfield stages, bool stencilOnly);
+
+ bool createGeom(GrScalar srcSpaceTol,
+ GrDrawTarget::StageBitfield stages);
+
+ bool fSeparateStencil;
+ bool fStencilWrapOps;
+
+ int fSubpathCount;
+ SkAutoSTMalloc<8, uint16_t> fSubpathVertCount;
+ int fIndexCnt;
+ int fVertexCnt;
+ GrScalar fPreviousSrcTol;
+ GrDrawTarget::StageBitfield fPreviousStages;
+ GrPrimitiveType fPrimitiveType;
+ bool fUseIndexedDraw;
+
+ typedef GrPathRenderer INHERITED;
+};
+
+#endif
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index cde851d..7744e6a 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -40,8 +40,7 @@
, fGeomPoolStateStack(&fGeoSrcStateStackStorage)
, fQuadIndexBuffer(NULL)
, fUnitSquareVertexBuffer(NULL)
- , fDefaultPathRenderer(NULL)
- , fClientPathRenderer(NULL)
+ , fPathRendererChain(NULL)
, fContextIsDirty(true)
, fResourceHead(NULL) {
@@ -62,8 +61,6 @@
GrGpu::~GrGpu() {
this->releaseResources();
- GrSafeUnref(fDefaultPathRenderer);
- GrSafeUnref(fClientPathRenderer);
}
void GrGpu::abandonResources() {
@@ -81,6 +78,8 @@
fVertexPool = NULL;
delete fIndexPool;
fIndexPool = NULL;
+ // in case path renderer has any GrResources, start from scratch
+ GrSafeSetNull(fPathRendererChain);
}
void GrGpu::releaseResources() {
@@ -98,6 +97,8 @@
fVertexPool = NULL;
delete fIndexPool;
fIndexPool = NULL;
+ // in case path renderer has any GrResources, start from scratch
+ GrSafeSetNull(fPathRendererChain);
}
void GrGpu::insertResource(GrResource* resource) {
@@ -521,6 +522,11 @@
fill = NonInvertedFill(fill);
clipPath = &clip.getPath(c);
pr = this->getClipPathRenderer(*clipPath, fill);
+ if (NULL == pr) {
+ fClipInStencil = false;
+ fClip = clip;
+ return false;
+ }
canRenderDirectToStencil =
!pr->requiresStencilPass(this, *clipPath, fill);
arp.set(pr, this, clipPath, fill, NULL);
@@ -602,18 +608,12 @@
GrPathRenderer* GrGpu::getClipPathRenderer(const GrPath& path,
GrPathFill fill) {
- if (NULL != fClientPathRenderer &&
- fClientPathRenderer->canDrawPath(path, fill)) {
- return fClientPathRenderer;
- } else {
- if (NULL == fDefaultPathRenderer) {
- fDefaultPathRenderer =
- new GrDefaultPathRenderer(this->supportsTwoSidedStencil(),
- this->supportsStencilWrapOps());
- }
- GrAssert(fDefaultPathRenderer->canDrawPath(path, fill));
- return fDefaultPathRenderer;
+ if (NULL == fPathRendererChain) {
+ fPathRendererChain =
+ new GrPathRendererChain(this->getContext(),
+ GrPathRendererChain::kNonAAOnly_UsageFlag);
}
+ return fPathRendererChain->getPathRenderer(this, path, fill);
}
diff --git a/gpu/src/GrGpu.h b/gpu/src/GrGpu.h
index d3c6c9d..e1e85f7 100644
--- a/gpu/src/GrGpu.h
+++ b/gpu/src/GrGpu.h
@@ -11,13 +11,14 @@
#define GrGpu_DEFINED
#include "GrDrawTarget.h"
-#include "GrPathRenderer.h"
#include "GrRect.h"
#include "GrRefCnt.h"
#include "GrTexture.h"
class GrContext;
class GrIndexBufferAllocPool;
+class GrPathRenderer;
+class GrPathRendererChain;
class GrResource;
class GrStencilBuffer;
class GrVertexBufferAllocPool;
@@ -259,14 +260,6 @@
virtual void clear(const GrIRect* rect, GrColor color);
/**
- * Installs a path renderer that will be used to draw paths that are
- * part of the clip.
- */
- void setClipPathRenderer(GrPathRenderer* pathRenderer) {
- GrSafeAssign(fClientPathRenderer, pathRenderer);
- }
-
- /**
* Returns an index buffer that can be used to render quads.
* Six indices per quad: 0, 1, 2, 0, 2, 3, etc.
* The max number of quads can be queried using GrIndexBuffer::maxQuads().
@@ -520,8 +513,9 @@
mutable GrVertexBuffer* fUnitSquareVertexBuffer; // mutable so it can be
// created on-demand
- GrDefaultPathRenderer* fDefaultPathRenderer;
- GrPathRenderer* fClientPathRenderer;
+ // must be instantiated after GrGpu object has been given its owning
+ // GrContext ptr. (GrGpu is constructed first then handed off to GrContext).
+ GrPathRendererChain* fPathRendererChain;
bool fContextIsDirty;
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index 26c92b8..06a00e4 100644
--- a/gpu/src/GrPathRenderer.cpp
+++ b/gpu/src/GrPathRenderer.cpp
@@ -5,24 +5,15 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+
#include "GrPathRenderer.h"
-#include "GrPoint.h"
-#include "GrDrawTarget.h"
-#include "GrPathUtils.h"
-#include "GrTexture.h"
-
-#include "SkString.h"
-#include "SkTemplates.h"
-#include "SkTrace.h"
-
GrPathRenderer::GrPathRenderer()
: fCurveTolerance (GR_Scalar1)
, fPath(NULL)
, fTarget(NULL) {
}
-
void GrPathRenderer::setPath(GrDrawTarget* target,
const SkPath* path,
GrPathFill fill,
@@ -49,553 +40,3 @@
fTarget = NULL;
fPath = NULL;
}
-
-////////////////////////////////////////////////////////////////////////////////
-
-GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
- bool stencilWrapOpsSupport)
- : fSeparateStencil(separateStencilSupport)
- , fStencilWrapOps(stencilWrapOpsSupport)
- , fSubpathCount(0)
- , fSubpathVertCount(0)
- , fPreviousSrcTol(-GR_Scalar1)
- , fPreviousStages(-1) {
- fTarget = NULL;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Stencil rules for paths
-
-////// Even/Odd
-
-static const GrStencilSettings gEOStencilPass = {
- kInvert_StencilOp, kInvert_StencilOp,
- kKeep_StencilOp, kKeep_StencilOp,
- kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
- 0xffffffff, 0xffffffff,
- 0xffffffff, 0xffffffff,
- 0xffffffff, 0xffffffff
-};
-
-// ok not to check clip b/c stencil pass only wrote inside clip
-static const GrStencilSettings gEOColorPass = {
- kZero_StencilOp, kZero_StencilOp,
- kZero_StencilOp, kZero_StencilOp,
- kNotEqual_StencilFunc, kNotEqual_StencilFunc,
- 0xffffffff, 0xffffffff,
- 0x0, 0x0,
- 0xffffffff, 0xffffffff
-};
-
-// have to check clip b/c outside clip will always be zero.
-static const GrStencilSettings gInvEOColorPass = {
- kZero_StencilOp, kZero_StencilOp,
- kZero_StencilOp, kZero_StencilOp,
- kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
- 0xffffffff, 0xffffffff,
- 0x0, 0x0,
- 0xffffffff, 0xffffffff
-};
-
-////// Winding
-
-// when we have separate stencil we increment front faces / decrement back faces
-// when we don't have wrap incr and decr we use the stencil test to simulate
-// them.
-
-static const GrStencilSettings gWindStencilSeparateWithWrap = {
- kIncWrap_StencilOp, kDecWrap_StencilOp,
- kKeep_StencilOp, kKeep_StencilOp,
- kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
- 0xffffffff, 0xffffffff,
- 0xffffffff, 0xffffffff,
- 0xffffffff, 0xffffffff
-};
-
-// if inc'ing the max value, invert to make 0
-// if dec'ing zero invert to make all ones.
-// we can't avoid touching the stencil on both passing and
-// failing, so we can't resctrict ourselves to the clip.
-static const GrStencilSettings gWindStencilSeparateNoWrap = {
- kInvert_StencilOp, kInvert_StencilOp,
- kIncClamp_StencilOp, kDecClamp_StencilOp,
- kEqual_StencilFunc, kEqual_StencilFunc,
- 0xffffffff, 0xffffffff,
- 0xffffffff, 0x0,
- 0xffffffff, 0xffffffff
-};
-
-// When there are no separate faces we do two passes to setup the winding rule
-// stencil. First we draw the front faces and inc, then we draw the back faces
-// and dec. These are same as the above two split into the incrementing and
-// decrementing passes.
-static const GrStencilSettings gWindSingleStencilWithWrapInc = {
- kIncWrap_StencilOp, kIncWrap_StencilOp,
- kKeep_StencilOp, kKeep_StencilOp,
- kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
- 0xffffffff, 0xffffffff,
- 0xffffffff, 0xffffffff,
- 0xffffffff, 0xffffffff
-};
-static const GrStencilSettings gWindSingleStencilWithWrapDec = {
- kDecWrap_StencilOp, kDecWrap_StencilOp,
- kKeep_StencilOp, kKeep_StencilOp,
- kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
- 0xffffffff, 0xffffffff,
- 0xffffffff, 0xffffffff,
- 0xffffffff, 0xffffffff
-};
-static const GrStencilSettings gWindSingleStencilNoWrapInc = {
- kInvert_StencilOp, kInvert_StencilOp,
- kIncClamp_StencilOp, kIncClamp_StencilOp,
- kEqual_StencilFunc, kEqual_StencilFunc,
- 0xffffffff, 0xffffffff,
- 0xffffffff, 0xffffffff,
- 0xffffffff, 0xffffffff
-};
-static const GrStencilSettings gWindSingleStencilNoWrapDec = {
- kInvert_StencilOp, kInvert_StencilOp,
- kDecClamp_StencilOp, kDecClamp_StencilOp,
- kEqual_StencilFunc, kEqual_StencilFunc,
- 0xffffffff, 0xffffffff,
- 0x0, 0x0,
- 0xffffffff, 0xffffffff
-};
-
-static const GrStencilSettings gWindColorPass = {
- kZero_StencilOp, kZero_StencilOp,
- kZero_StencilOp, kZero_StencilOp,
- kNonZeroIfInClip_StencilFunc, kNonZeroIfInClip_StencilFunc,
- 0xffffffff, 0xffffffff,
- 0x0, 0x0,
- 0xffffffff, 0xffffffff
-};
-
-static const GrStencilSettings gInvWindColorPass = {
- kZero_StencilOp, kZero_StencilOp,
- kZero_StencilOp, kZero_StencilOp,
- kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
- 0xffffffff, 0xffffffff,
- 0x0, 0x0,
- 0xffffffff, 0xffffffff
-};
-
-////// Normal render to stencil
-
-// Sometimes the default path renderer can draw a path directly to the stencil
-// buffer without having to first resolve the interior / exterior.
-static const GrStencilSettings gDirectToStencil = {
- kZero_StencilOp, kZero_StencilOp,
- kIncClamp_StencilOp, kIncClamp_StencilOp,
- kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
- 0xffffffff, 0xffffffff,
- 0x0, 0x0,
- 0xffffffff, 0xffffffff
-};
-
-////////////////////////////////////////////////////////////////////////////////
-// Helpers for drawPath
-
-static GrConvexHint getConvexHint(const SkPath& path) {
- return path.isConvex() ? kConvex_ConvexHint : kConcave_ConvexHint;
-}
-
-#define STENCIL_OFF 0 // Always disable stencil (even when needed)
-
-static inline bool single_pass_path(const GrDrawTarget& target,
- const GrPath& path,
- GrPathFill fill) {
-#if STENCIL_OFF
- return true;
-#else
- if (kEvenOdd_PathFill == fill) {
- GrConvexHint hint = getConvexHint(path);
- return hint == kConvex_ConvexHint ||
- hint == kNonOverlappingConvexPieces_ConvexHint;
- } else if (kWinding_PathFill == fill) {
- GrConvexHint hint = getConvexHint(path);
- return hint == kConvex_ConvexHint ||
- hint == kNonOverlappingConvexPieces_ConvexHint ||
- (hint == kSameWindingConvexPieces_ConvexHint &&
- target.canDisableBlend() && !target.isDitherState());
-
- }
- return false;
-#endif
-}
-
-bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target,
- const GrPath& path,
- GrPathFill fill) const {
- return !single_pass_path(*target, path, fill);
-}
-
-void GrDefaultPathRenderer::pathWillClear() {
- fSubpathVertCount.realloc(0);
- fTarget->resetVertexSource();
- if (fUseIndexedDraw) {
- fTarget->resetIndexSource();
- }
- fPreviousSrcTol = -GR_Scalar1;
- fPreviousStages = -1;
-}
-
-static inline void append_countour_edge_indices(GrPathFill fillType,
- uint16_t fanCenterIdx,
- uint16_t edgeV0Idx,
- uint16_t** indices) {
- // when drawing lines we're appending line segments along
- // the contour. When applying the other fill rules we're
- // drawing triangle fans around fanCenterIdx.
- if (kHairLine_PathFill != fillType) {
- *((*indices)++) = fanCenterIdx;
- }
- *((*indices)++) = edgeV0Idx;
- *((*indices)++) = edgeV0Idx + 1;
-}
-
-bool GrDefaultPathRenderer::createGeom(GrScalar srcSpaceTol,
- GrDrawTarget::StageBitfield stages) {
- {
- SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom");
-
- GrScalar srcSpaceTolSqd = GrMul(srcSpaceTol, srcSpaceTol);
- int maxPts = GrPathUtils::worstCasePointCount(*fPath, &fSubpathCount,
- srcSpaceTol);
-
- if (maxPts <= 0) {
- return false;
- }
- if (maxPts > ((int)SK_MaxU16 + 1)) {
- GrPrintf("Path not rendered, too many verts (%d)\n", maxPts);
- return false;
- }
-
- GrVertexLayout layout = 0;
- for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
- if ((1 << s) & stages) {
- layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
- }
- }
-
- fUseIndexedDraw = fSubpathCount > 1;
-
- int maxIdxs = 0;
- if (kHairLine_PathFill == fFill) {
- if (fUseIndexedDraw) {
- maxIdxs = 2 * maxPts;
- fPrimitiveType = kLines_PrimitiveType;
- } else {
- fPrimitiveType = kLineStrip_PrimitiveType;
- }
- } else {
- if (fUseIndexedDraw) {
- maxIdxs = 3 * maxPts;
- fPrimitiveType = kTriangles_PrimitiveType;
- } else {
- fPrimitiveType = kTriangleFan_PrimitiveType;
- }
- }
-
- GrPoint* base;
- if (!fTarget->reserveVertexSpace(layout, maxPts, (void**)&base)) {
- return false;
- }
- GrAssert(NULL != base);
- GrPoint* vert = base;
-
- uint16_t* idxBase = NULL;
- uint16_t* idx = NULL;
- uint16_t subpathIdxStart = 0;
- if (fUseIndexedDraw) {
- if (!fTarget->reserveIndexSpace(maxIdxs, (void**)&idxBase)) {
- fTarget->resetVertexSource();
- return false;
- }
- GrAssert(NULL != idxBase);
- idx = idxBase;
- }
-
- 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) {
- uint16_t currIdx = (uint16_t) (vert - base);
- fSubpathVertCount[subpath] = currIdx - subpathIdxStart;
- subpathIdxStart = currIdx;
- ++subpath;
- }
- *vert = pts[0];
- vert++;
- break;
- case kLine_PathCmd:
- if (fUseIndexedDraw) {
- uint16_t prevIdx = (uint16_t)(vert - base) - 1;
- append_countour_edge_indices(fFill, subpathIdxStart,
- prevIdx, &idx);
- }
- *(vert++) = pts[1];
- break;
- case kQuadratic_PathCmd: {
- // first pt of quad is the pt we ended on in previous step
- uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1;
- uint16_t numPts = (uint16_t)
- GrPathUtils::generateQuadraticPoints(
- pts[0], pts[1], pts[2],
- srcSpaceTolSqd, &vert,
- GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
- if (fUseIndexedDraw) {
- for (uint16_t i = 0; i < numPts; ++i) {
- append_countour_edge_indices(fFill, subpathIdxStart,
- firstQPtIdx + i, &idx);
- }
- }
- break;
- }
- case kCubic_PathCmd: {
- // first pt of cubic is the pt we ended on in previous step
- uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1;
- uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
- pts[0], pts[1], pts[2], pts[3],
- srcSpaceTolSqd, &vert,
- GrPathUtils::cubicPointCount(pts, srcSpaceTol));
- if (fUseIndexedDraw) {
- for (uint16_t i = 0; i < numPts; ++i) {
- append_countour_edge_indices(fFill, subpathIdxStart,
- firstCPtIdx + i, &idx);
- }
- }
- break;
- }
- case kClose_PathCmd:
- break;
- case kEnd_PathCmd:
- uint16_t currIdx = (uint16_t) (vert - base);
- fSubpathVertCount[subpath] = currIdx - subpathIdxStart;
- goto FINISHED;
- }
- first = false;
- }
-FINISHED:
- GrAssert((vert - base) <= maxPts);
- GrAssert((idx - idxBase) <= maxIdxs);
-
- fVertexCnt = vert - base;
- fIndexCnt = idx - idxBase;
-
- if (fTranslate.fX || fTranslate.fY) {
- int count = vert - base;
- for (int i = 0; i < count; i++) {
- base[i].offset(fTranslate.fX, fTranslate.fY);
- }
- }
- }
- // set these at the end so if we failed on first drawPath inside a
- // setPath/clearPath block we won't assume geom was created on a subsequent
- // drawPath in the same block.
- fPreviousSrcTol = srcSpaceTol;
- fPreviousStages = stages;
- return true;
-}
-
-void GrDefaultPathRenderer::onDrawPath(GrDrawTarget::StageBitfield stages,
- bool stencilOnly) {
-
- SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath",
- "points", SkStringPrintf("%i", path.countPoints()).c_str());
-
- 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();
- bool useStretch = stretch > 0;
- GrScalar tol = fCurveTolerance;
-
- if (!useStretch) {
- // TODO: deal with perspective in some better way.
- tol /= 10;
- } else {
- tol = GrScalarDiv(tol, stretch);
- }
- // 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) {
- if (!this->createGeom(tol, stages)) {
- return;
- }
- }
-
- GrAssert(NULL != fTarget);
- GrDrawTarget::AutoStateRestore asr(fTarget);
- bool colorWritesWereDisabled = fTarget->isColorWriteDisabled();
- // face culling doesn't make sense here
- GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
-
- int passCount = 0;
- const GrStencilSettings* passes[3];
- GrDrawTarget::DrawFace drawFace[3];
- bool reverse = false;
- bool lastPassIsBounds;
-
- if (kHairLine_PathFill == fFill) {
- passCount = 1;
- if (stencilOnly) {
- passes[0] = &gDirectToStencil;
- } else {
- passes[0] = NULL;
- }
- lastPassIsBounds = false;
- drawFace[0] = GrDrawTarget::kBoth_DrawFace;
- } else {
- if (single_pass_path(*fTarget, *fPath, fFill)) {
- passCount = 1;
- if (stencilOnly) {
- passes[0] = &gDirectToStencil;
- } else {
- passes[0] = NULL;
- }
- drawFace[0] = GrDrawTarget::kBoth_DrawFace;
- lastPassIsBounds = false;
- } else {
- switch (fFill) {
- case kInverseEvenOdd_PathFill:
- reverse = true;
- // fallthrough
- case kEvenOdd_PathFill:
- passes[0] = &gEOStencilPass;
- if (stencilOnly) {
- passCount = 1;
- lastPassIsBounds = false;
- } else {
- passCount = 2;
- lastPassIsBounds = true;
- if (reverse) {
- passes[1] = &gInvEOColorPass;
- } else {
- passes[1] = &gEOColorPass;
- }
- }
- drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace;
- break;
-
- case kInverseWinding_PathFill:
- reverse = true;
- // fallthrough
- case kWinding_PathFill:
- if (fSeparateStencil) {
- if (fStencilWrapOps) {
- passes[0] = &gWindStencilSeparateWithWrap;
- } else {
- passes[0] = &gWindStencilSeparateNoWrap;
- }
- passCount = 2;
- drawFace[0] = GrDrawTarget::kBoth_DrawFace;
- } else {
- if (fStencilWrapOps) {
- passes[0] = &gWindSingleStencilWithWrapInc;
- passes[1] = &gWindSingleStencilWithWrapDec;
- } else {
- passes[0] = &gWindSingleStencilNoWrapInc;
- passes[1] = &gWindSingleStencilNoWrapDec;
- }
- // which is cw and which is ccw is arbitrary.
- drawFace[0] = GrDrawTarget::kCW_DrawFace;
- drawFace[1] = GrDrawTarget::kCCW_DrawFace;
- passCount = 3;
- }
- if (stencilOnly) {
- lastPassIsBounds = false;
- --passCount;
- } else {
- lastPassIsBounds = true;
- drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace;
- if (reverse) {
- passes[passCount-1] = &gInvWindColorPass;
- } else {
- passes[passCount-1] = &gWindColorPass;
- }
- }
- break;
- default:
- GrAssert(!"Unknown path fFill!");
- return;
- }
- }
- }
-
- {
- SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath::renderPasses",
- "verts", SkStringPrintf("%i", vert - base).c_str());
- for (int p = 0; p < passCount; ++p) {
- fTarget->setDrawFace(drawFace[p]);
- if (NULL != passes[p]) {
- fTarget->setStencil(*passes[p]);
- }
-
- if (lastPassIsBounds && (p == passCount-1)) {
- if (!colorWritesWereDisabled) {
- fTarget->disableState(GrDrawTarget::kNoColorWrites_StateBit);
- }
- 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();
- bounds.offset(fTranslate);
- }
- GrDrawTarget::AutoGeometryPush agp(fTarget);
- fTarget->drawSimpleRect(bounds, NULL, stages);
- } else {
- if (passCount > 1) {
- fTarget->enableState(GrDrawTarget::kNoColorWrites_StateBit);
- }
- if (fUseIndexedDraw) {
- fTarget->drawIndexed(fPrimitiveType, 0, 0,
- fVertexCnt, fIndexCnt);
- } else {
- int baseVertex = 0;
- for (int sp = 0; sp < fSubpathCount; ++sp) {
- fTarget->drawNonIndexed(fPrimitiveType, baseVertex,
- fSubpathVertCount[sp]);
- baseVertex += fSubpathVertCount[sp];
- }
- }
- }
- }
- }
-}
-
-void GrDefaultPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
- this->onDrawPath(stages, false);
-}
-
-void GrDefaultPathRenderer::drawPathToStencil() {
- GrAssert(kInverseEvenOdd_PathFill != fFill);
- GrAssert(kInverseWinding_PathFill != fFill);
- this->onDrawPath(0, true);
-}
diff --git a/gpu/src/GrPathRenderer.h b/gpu/src/GrPathRenderer.h
index 67c3a93..25dfafb 100644
--- a/gpu/src/GrPathRenderer.h
+++ b/gpu/src/GrPathRenderer.h
@@ -11,9 +11,11 @@
#define GrPathRenderer_DEFINED
#include "GrDrawTarget.h"
-#include "SkTemplates.h"
+#include "GrTArray.h"
+#include "GrPathRendererChain.h"
class SkPath;
+
struct GrPoint;
/**
@@ -29,6 +31,23 @@
*/
class GR_API GrPathRenderer : public GrRefCnt {
public:
+
+ /**
+ * This is called to install custom path renderers in every GrContext at
+ * create time. The default implementation in GrCreatePathRenderer_none.cpp
+ * does not add any additional renderers. Link against another
+ * implementation to install your own. The most recently added is the
+ * most preferred path renderer.
+ *
+ * @param context the context that will use the path renderer
+ * @param flags flags indicating how path renderers will be used
+ * @param prChain the chain to add path renderers to.
+ */
+ static void AddPathRenderers(GrContext* context,
+ GrPathRendererChain::UsageFlags flags,
+ GrPathRendererChain* prChain);
+
+
GrPathRenderer(void);
/**
* Returns true if this path renderer is able to render the path.
@@ -71,7 +90,7 @@
* 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,
+ virtual bool supportsAA(const GrDrawTarget* target,
const SkPath& path,
GrPathFill fill) { return false; }
@@ -136,13 +155,6 @@
}
/**
- * This is called to install a custom path renderer in every GrContext at
- * create time. The default implementation in GrCreatePathRenderer_none.cpp
- * returns NULL. Link against another implementation to install your own.
- */
- static GrPathRenderer* CreatePathRenderer();
-
- /**
* Multiply curve tolerance by the given value, increasing or decreasing
* the maximum error permitted in tesselating curves with short straight
* line segments.
@@ -208,49 +220,5 @@
typedef GrRefCnt INHERITED;
};
-/**
- * Subclass that renders the path using the stencil buffer to resolve fill
- * rules (e.g. winding, even-odd)
- */
-class GR_API GrDefaultPathRenderer : public GrPathRenderer {
-public:
- GrDefaultPathRenderer(bool separateStencilSupport,
- bool stencilWrapOpsSupport);
-
- virtual bool canDrawPath(const SkPath& path,
- GrPathFill fill) const { return true; }
-
- virtual bool requiresStencilPass(const GrDrawTarget* target,
- const SkPath& path,
- GrPathFill fill) const;
-
- virtual void drawPath(GrDrawTarget::StageBitfield stages);
- virtual void drawPathToStencil();
-
-protected:
- virtual void pathWillClear();
-
-private:
-
- void onDrawPath(GrDrawTarget::StageBitfield stages, bool stencilOnly);
-
- bool createGeom(GrScalar srcSpaceTol,
- GrDrawTarget::StageBitfield stages);
-
- bool fSeparateStencil;
- bool fStencilWrapOps;
-
- int fSubpathCount;
- SkAutoSTMalloc<8, uint16_t> fSubpathVertCount;
- int fIndexCnt;
- int fVertexCnt;
- GrScalar fPreviousSrcTol;
- GrDrawTarget::StageBitfield fPreviousStages;
- GrPrimitiveType fPrimitiveType;
- bool fUseIndexedDraw;
-
- typedef GrPathRenderer INHERITED;
-};
-
#endif
diff --git a/gpu/src/GrPathRendererChain.cpp b/gpu/src/GrPathRendererChain.cpp
new file mode 100644
index 0000000..afae912
--- /dev/null
+++ b/gpu/src/GrPathRendererChain.cpp
@@ -0,0 +1,65 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrPathRendererChain.h"
+
+#include "GrContext.h"
+#include "GrDefaultPathRenderer.h"
+#include "GrGpu.h"
+
+GrPathRendererChain::GrPathRendererChain(GrContext* context, UsageFlags flags)
+ : fInit(false)
+ , fFlags(flags)
+ , fOwner(context)
+ , fChain(fStorage.get(), kPreAllocCount) {
+ fInit = false;
+}
+
+GrPathRendererChain::~GrPathRendererChain() {
+ for (int i = 0; i < fChain.count(); ++i) {
+ fChain[i]->unref();
+ }
+}
+
+GrPathRenderer* GrPathRendererChain::addPathRenderer(GrPathRenderer* pr) {
+ fChain.push_back() = pr;
+ pr->ref();
+ return pr;
+}
+
+GrPathRenderer* GrPathRendererChain::getPathRenderer(const GrDrawTarget* target,
+ const GrPath& path,
+ GrPathFill fill) {
+ if (!fInit) {
+ this->init();
+ }
+ bool preferAA = target->isAntialiasState() &&
+ !target->getRenderTarget()->isMultisampled();
+ GrPathRenderer* nonAAPR = NULL;
+ for (int i = 0; i < fChain.count(); ++i) {
+ if (fChain[i]->canDrawPath(path, fill)) {
+ if (!preferAA || fChain[i]->supportsAA(target, path, fill)) {
+ return fChain[i];
+ } else {
+ nonAAPR = fChain[i];
+ }
+ }
+ }
+ return nonAAPR;
+}
+
+void GrPathRendererChain::init() {
+ GrAssert(!fInit);
+ GrGpu* gpu = fOwner->getGpu();
+ this->addPathRenderer(
+ new GrDefaultPathRenderer(gpu->supportsTwoSidedStencil(),
+ gpu->supportsStencilWrapOps()))->unref();
+ GrPathRenderer::AddPathRenderers(fOwner, fFlags, this);
+ fInit = true;
+}
diff --git a/gpu/src/GrPathRendererChain.h b/gpu/src/GrPathRendererChain.h
new file mode 100644
index 0000000..5fa8c2e
--- /dev/null
+++ b/gpu/src/GrPathRendererChain.h
@@ -0,0 +1,64 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrPathRendererChain_DEFINED
+#define GrPathRendererChain_DEFINED
+
+#include "GrRefCnt.h"
+#include "GrTArray.h"
+
+class GrContext;
+class GrDrawTarget;
+class SkPath;
+class GrPathRenderer;
+
+/**
+ * Keeps track of a ordered list of path renderers. When a path needs to be
+ * drawn this list is scanned to find the most preferred renderer. To add your
+ * path renderer to the list implement the GrPathRenderer::AddPathRenderers
+ * function.
+ */
+class GrPathRendererChain : public SkRefCnt {
+public:
+
+ enum UsageFlags {
+ kNone_UsageFlag = 0,
+ kNonAAOnly_UsageFlag = 1,
+ };
+
+ GrPathRendererChain(GrContext* context, UsageFlags flags);
+
+ ~GrPathRendererChain();
+
+ // takes a ref and unrefs in destructor
+ GrPathRenderer* addPathRenderer(GrPathRenderer* pr);
+
+ GrPathRenderer* getPathRenderer(const GrDrawTarget* target,
+ const SkPath& path,
+ GrPathFill fill);
+
+private:
+
+ GrPathRendererChain();
+
+ void init();
+
+ enum {
+ kPreAllocCount = 8,
+ };
+ bool fInit;
+ GrContext* fOwner;
+ UsageFlags fFlags;
+ GrAlignedSTStorage<kPreAllocCount, GrPathRenderer*> fStorage;
+ GrTArray<GrPathRenderer*, true> fChain;
+};
+
+GR_MAKE_BITFIELD_OPS(GrPathRendererChain::UsageFlags)
+
+#endif
\ No newline at end of file
diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp
index 9a72101..aca6ec2 100644
--- a/gpu/src/GrTesselatedPathRenderer.cpp
+++ b/gpu/src/GrTesselatedPathRenderer.cpp
@@ -608,8 +608,8 @@
GrAlwaysAssert(!"multipass stencil should not be needed");
}
-bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
- const SkPath& path,
- GrPathFill fill) {
+bool GrTesselatedPathRenderer::supportsAA(const GrDrawTarget* target,
+ const SkPath& path,
+ GrPathFill fill) {
return true;
}
diff --git a/gpu/src/GrTesselatedPathRenderer.h b/gpu/src/GrTesselatedPathRenderer.h
index 5791be6..c815f50 100644
--- a/gpu/src/GrTesselatedPathRenderer.h
+++ b/gpu/src/GrTesselatedPathRenderer.h
@@ -24,7 +24,7 @@
const GrPath& path,
GrPathFill fill) const { return false; }
virtual void drawPathToStencil();
- virtual bool supportsAA(GrDrawTarget* target,
+ virtual bool supportsAA(const GrDrawTarget* target,
const GrPath& path,
GrPathFill fill);
};