2 * Copyright 2011 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "GrDefaultPathRenderer.h"
11 #include "GrBatchTarget.h"
12 #include "GrBufferAllocPool.h"
13 #include "GrContext.h"
14 #include "GrDefaultGeoProcFactory.h"
15 #include "GrPathUtils.h"
16 #include "GrPipelineBuilder.h"
17 #include "SkGeometry.h"
19 #include "SkStrokeRec.h"
21 #include "SkTraceEvent.h"
23 GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
24 bool stencilWrapOpsSupport)
25 : fSeparateStencil(separateStencilSupport)
26 , fStencilWrapOps(stencilWrapOpsSupport) {
30 ////////////////////////////////////////////////////////////////////////////////
31 // Stencil rules for paths
35 GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass,
38 kAlwaysIfInClip_StencilFunc,
43 // ok not to check clip b/c stencil pass only wrote inside clip
44 GR_STATIC_CONST_SAME_STENCIL(gEOColorPass,
47 kNotEqual_StencilFunc,
52 // have to check clip b/c outside clip will always be zero.
53 GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass,
56 kEqualIfInClip_StencilFunc,
63 // when we have separate stencil we increment front faces / decrement back faces
64 // when we don't have wrap incr and decr we use the stencil test to simulate
67 GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap,
68 kIncWrap_StencilOp, kDecWrap_StencilOp,
69 kKeep_StencilOp, kKeep_StencilOp,
70 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
75 // if inc'ing the max value, invert to make 0
76 // if dec'ing zero invert to make all ones.
77 // we can't avoid touching the stencil on both passing and
78 // failing, so we can't resctrict ourselves to the clip.
79 GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap,
80 kInvert_StencilOp, kInvert_StencilOp,
81 kIncClamp_StencilOp, kDecClamp_StencilOp,
82 kEqual_StencilFunc, kEqual_StencilFunc,
87 // When there are no separate faces we do two passes to setup the winding rule
88 // stencil. First we draw the front faces and inc, then we draw the back faces
89 // and dec. These are same as the above two split into the incrementing and
90 // decrementing passes.
91 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
94 kAlwaysIfInClip_StencilFunc,
99 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
102 kAlwaysIfInClip_StencilFunc,
107 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc,
115 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec,
123 // Color passes are the same whether we use the two-sided stencil or two passes
125 GR_STATIC_CONST_SAME_STENCIL(gWindColorPass,
128 kNonZeroIfInClip_StencilFunc,
133 GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass,
136 kEqualIfInClip_StencilFunc,
141 ////// Normal render to stencil
143 // Sometimes the default path renderer can draw a path directly to the stencil
144 // buffer without having to first resolve the interior / exterior.
145 GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil,
148 kAlwaysIfInClip_StencilFunc,
153 ////////////////////////////////////////////////////////////////////////////////
154 // Helpers for drawPath
156 #define STENCIL_OFF 0 // Always disable stencil (even when needed)
158 static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) {
162 if (!stroke.isHairlineStyle() && !path.isInverseFillType()) {
163 return path.isConvex();
169 GrPathRenderer::StencilSupport
170 GrDefaultPathRenderer::onGetStencilSupport(const GrDrawTarget*,
171 const GrPipelineBuilder*,
173 const SkStrokeRec& stroke) const {
174 if (single_pass_path(path, stroke)) {
175 return GrPathRenderer::kNoRestriction_StencilSupport;
177 return GrPathRenderer::kStencilOnly_StencilSupport;
181 static inline void append_countour_edge_indices(bool hairLine,
182 uint16_t fanCenterIdx,
184 uint16_t** indices) {
185 // when drawing lines we're appending line segments along
186 // the contour. When applying the other fill rules we're
187 // drawing triangle fans around fanCenterIdx.
189 *((*indices)++) = fanCenterIdx;
191 *((*indices)++) = edgeV0Idx;
192 *((*indices)++) = edgeV0Idx + 1;
195 static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[],
196 SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed,
197 bool isHairline, uint16_t subpathIdxStart, int offset, uint16_t** idx) {
198 // first pt of quad is the pt we ended on in previous step
199 uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1 + offset;
200 uint16_t numPts = (uint16_t)
201 GrPathUtils::generateQuadraticPoints(
202 pts[0], pts[1], pts[2],
203 srcSpaceTolSqd, vert,
204 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
206 for (uint16_t i = 0; i < numPts; ++i) {
207 append_countour_edge_indices(isHairline, subpathIdxStart,
208 firstQPtIdx + i, idx);
213 class DefaultPathBatch : public GrBatch {
219 SkDEBUGCODE(SkRect fDevBounds;)
222 static GrBatch* Create(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
224 return SkNEW_ARGS(DefaultPathBatch, (geometry, coverage, viewMatrix, isHairline));
227 const char* name() const SK_OVERRIDE { return "DefaultPathBatch"; }
229 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
230 // When this is called on a batch, there is only one geometry bundle
231 out->setKnownFourComponents(fGeoData[0].fColor);
233 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
234 out->setKnownSingleComponent(this->coverage());
237 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
238 // Handle any color overrides
239 if (init.fColorIgnored) {
240 fGeoData[0].fColor = GrColor_ILLEGAL;
241 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
242 fGeoData[0].fColor = init.fOverrideColor;
245 // setup batch properties
246 fBatch.fColorIgnored = init.fColorIgnored;
247 fBatch.fColor = fGeoData[0].fColor;
248 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
249 fBatch.fCoverageIgnored = init.fCoverageIgnored;
252 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
253 SkAutoTUnref<const GrGeometryProcessor> gp(
254 GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
261 size_t vertexStride = gp->getVertexStride();
262 SkASSERT(vertexStride == sizeof(SkPoint));
264 batchTarget->initDraw(gp, pipeline);
266 // TODO this is hacky, but the only way we have to initialize the GP is to use the
267 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
268 // everywhere we can remove this nastiness
270 init.fColorIgnored = fBatch.fColorIgnored;
271 init.fOverrideColor = GrColor_ILLEGAL;
272 init.fCoverageIgnored = fBatch.fCoverageIgnored;
273 init.fUsesLocalCoords = this->usesLocalCoords();
274 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
276 int instanceCount = fGeoData.count();
278 // compute number of vertices
281 // We will use index buffers if we have multiple paths or one path with multiple contours
282 bool isIndexed = instanceCount > 1;
283 for (int i = 0; i < instanceCount; i++) {
284 Geometry& args = fGeoData[i];
287 maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount,
290 isIndexed = isIndexed || contourCount > 1;
293 if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) {
294 SkDebugf("Cannot render path (%d)\n", maxVertices);
298 // determine primitiveType
300 GrPrimitiveType primitiveType;
301 if (this->isHairline()) {
303 maxIndices = 2 * maxVertices;
304 primitiveType = kLines_GrPrimitiveType;
306 primitiveType = kLineStrip_GrPrimitiveType;
310 maxIndices = 3 * maxVertices;
311 primitiveType = kTriangles_GrPrimitiveType;
313 primitiveType = kTriangleFan_GrPrimitiveType;
317 // allocate vertex / index buffers
318 const GrVertexBuffer* vertexBuffer;
321 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
327 SkDebugf("Could not allocate vertices\n");
331 const GrIndexBuffer* indexBuffer;
334 void* indices = NULL;
336 indices = batchTarget->indexPool()->makeSpace(maxIndices,
341 SkDebugf("Could not allocate indices\n");
347 int vertexOffset = 0;
349 for (int i = 0; i < instanceCount; i++) {
350 Geometry& args = fGeoData[i];
354 if (!this->createGeom(vertices,
366 vertexOffset += vertexCnt;
367 indexOffset += indexCnt;
368 SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
371 GrDrawTarget::DrawInfo drawInfo;
372 drawInfo.setPrimitiveType(primitiveType);
373 drawInfo.setVertexBuffer(vertexBuffer);
374 drawInfo.setStartVertex(firstVertex);
375 drawInfo.setVertexCount(vertexOffset);
377 drawInfo.setIndexBuffer(indexBuffer);
378 drawInfo.setStartIndex(firstIndex);
379 drawInfo.setIndexCount(indexOffset);
381 drawInfo.setStartIndex(0);
382 drawInfo.setIndexCount(0);
384 batchTarget->draw(drawInfo);
387 batchTarget->putBackIndices((size_t)(maxIndices - indexOffset));
388 batchTarget->putBackVertices((size_t)(maxVertices - vertexOffset), (size_t)vertexStride);
391 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
394 DefaultPathBatch(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
396 this->initClassID<DefaultPathBatch>();
397 fBatch.fCoverage = coverage;
398 fBatch.fIsHairline = isHairline;
399 fBatch.fViewMatrix = viewMatrix;
400 fGeoData.push_back(geometry);
403 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
404 DefaultPathBatch* that = t->cast<DefaultPathBatch>();
406 if (this->color() != that->color()) {
410 if (this->coverage() != that->coverage()) {
414 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
418 if (this->isHairline() != that->isHairline()) {
422 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
426 bool createGeom(void* vertices,
433 SkScalar srcSpaceTol,
436 SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol);
438 uint16_t indexOffsetU16 = (uint16_t)indexOffset;
439 uint16_t vertexOffsetU16 = (uint16_t)vertexOffset;
441 uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16;
442 uint16_t* idx = idxBase;
443 uint16_t subpathIdxStart = vertexOffsetU16;
445 SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset;
446 SkPoint* vert = base;
453 SkPath::Iter iter(path, false);
457 SkPath::Verb verb = iter.next(pts);
459 case SkPath::kMove_Verb:
461 uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16;
462 subpathIdxStart = currIdx;
468 case SkPath::kLine_Verb:
470 uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
471 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
476 case SkPath::kConic_Verb: {
477 SkScalar weight = iter.conicWeight();
478 SkAutoConicToQuads converter;
479 // Converting in src-space, hance the finer tolerance (0.25)
480 // TODO: find a way to do this in dev-space so the tolerance means something
481 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
482 for (int i = 0; i < converter.countQuads(); ++i) {
483 add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol,
484 isIndexed, this->isHairline(), subpathIdxStart,
485 (int)vertexOffset, &idx);
489 case SkPath::kQuad_Verb:
490 add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed,
491 this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx);
493 case SkPath::kCubic_Verb: {
494 // first pt of cubic is the pt we ended on in previous step
495 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
496 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
497 pts[0], pts[1], pts[2], pts[3],
498 srcSpaceTolSqd, &vert,
499 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
501 for (uint16_t i = 0; i < numPts; ++i) {
502 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
503 firstCPtIdx + i, &idx);
508 case SkPath::kClose_Verb:
510 case SkPath::kDone_Verb:
516 *vertexCnt = static_cast<int>(vert - base);
517 *indexCnt = static_cast<int>(idx - idxBase);
523 GrColor color() const { return fBatch.fColor; }
524 uint8_t coverage() const { return fBatch.fCoverage; }
525 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
526 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
527 bool isHairline() const { return fBatch.fIsHairline; }
529 struct BatchTracker {
532 SkMatrix fViewMatrix;
533 bool fUsesLocalCoords;
535 bool fCoverageIgnored;
540 SkSTArray<1, Geometry, true> fGeoData;
543 bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
544 GrPipelineBuilder* pipelineBuilder,
546 const SkMatrix& viewMatrix,
548 const SkStrokeRec& origStroke,
550 SkTCopyOnFirstWrite<SkStrokeRec> stroke(origStroke);
552 SkScalar hairlineCoverage;
553 uint8_t newCoverage = 0xff;
554 if (IsStrokeHairlineOrEquivalent(*stroke, viewMatrix, &hairlineCoverage)) {
555 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
557 if (!stroke->isHairlineStyle()) {
558 stroke.writable()->setHairlineStyle();
562 const bool isHairline = stroke->isHairlineStyle();
564 // Save the current xp on the draw state so we can reset it if needed
565 SkAutoTUnref<const GrXPFactory> backupXPFactory(SkRef(pipelineBuilder->getXPFactory()));
566 // face culling doesn't make sense here
567 SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace());
570 const GrStencilSettings* passes[3];
571 GrPipelineBuilder::DrawFace drawFace[3];
572 bool reverse = false;
573 bool lastPassIsBounds;
578 passes[0] = &gDirectToStencil;
582 lastPassIsBounds = false;
583 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
585 if (single_pass_path(path, *stroke)) {
588 passes[0] = &gDirectToStencil;
592 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
593 lastPassIsBounds = false;
595 switch (path.getFillType()) {
596 case SkPath::kInverseEvenOdd_FillType:
599 case SkPath::kEvenOdd_FillType:
600 passes[0] = &gEOStencilPass;
603 lastPassIsBounds = false;
606 lastPassIsBounds = true;
608 passes[1] = &gInvEOColorPass;
610 passes[1] = &gEOColorPass;
613 drawFace[0] = drawFace[1] = GrPipelineBuilder::kBoth_DrawFace;
616 case SkPath::kInverseWinding_FillType:
619 case SkPath::kWinding_FillType:
620 if (fSeparateStencil) {
621 if (fStencilWrapOps) {
622 passes[0] = &gWindStencilSeparateWithWrap;
624 passes[0] = &gWindStencilSeparateNoWrap;
627 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
629 if (fStencilWrapOps) {
630 passes[0] = &gWindSingleStencilWithWrapInc;
631 passes[1] = &gWindSingleStencilWithWrapDec;
633 passes[0] = &gWindSingleStencilNoWrapInc;
634 passes[1] = &gWindSingleStencilNoWrapDec;
636 // which is cw and which is ccw is arbitrary.
637 drawFace[0] = GrPipelineBuilder::kCW_DrawFace;
638 drawFace[1] = GrPipelineBuilder::kCCW_DrawFace;
642 lastPassIsBounds = false;
645 lastPassIsBounds = true;
646 drawFace[passCount-1] = GrPipelineBuilder::kBoth_DrawFace;
648 passes[passCount-1] = &gInvWindColorPass;
650 passes[passCount-1] = &gWindColorPass;
655 SkDEBUGFAIL("Unknown path fFill!");
661 SkScalar tol = SK_Scalar1;
662 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
665 GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds);
667 for (int p = 0; p < passCount; ++p) {
668 pipelineBuilder->setDrawFace(drawFace[p]);
670 *pipelineBuilder->stencil() = *passes[p];
673 if (lastPassIsBounds && (p == passCount-1)) {
674 // Reset the XP Factory on pipelineBuilder
675 pipelineBuilder->setXPFactory(backupXPFactory);
677 SkMatrix localMatrix = SkMatrix::I();
679 SkASSERT(pipelineBuilder->getRenderTarget());
680 // draw over the dev bounds (which will be the whole dst surface for inv fill).
683 // mapRect through persp matrix may not be correct
684 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
685 vmi.mapRect(&bounds);
687 if (!viewMatrix.invert(&localMatrix)) {
692 bounds = path.getBounds();
694 GrDrawTarget::AutoGeometryPush agp(target);
695 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
697 target->drawRect(pipelineBuilder, color, viewM, bounds, NULL, &localMatrix);
700 pipelineBuilder->setDisableColorXPFactory();
703 DefaultPathBatch::Geometry geometry;
704 geometry.fColor = color;
705 geometry.fPath = path;
706 geometry.fTolerance = srcSpaceTol;
707 SkDEBUGCODE(geometry.fDevBounds = devBounds;)
709 SkAutoTUnref<GrBatch> batch(DefaultPathBatch::Create(geometry, newCoverage, viewMatrix,
712 target->drawBatch(pipelineBuilder, batch, &devBounds);
718 bool GrDefaultPathRenderer::canDrawPath(const GrDrawTarget* target,
719 const GrPipelineBuilder* pipelineBuilder,
720 const SkMatrix& viewMatrix,
722 const SkStrokeRec& stroke,
723 bool antiAlias) const {
724 // this class can draw any path with any fill but doesn't do any anti-aliasing.
725 return !antiAlias && (stroke.isFillStyle() || IsStrokeHairlineOrEquivalent(stroke,
730 bool GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
731 GrPipelineBuilder* pipelineBuilder,
733 const SkMatrix& viewMatrix,
735 const SkStrokeRec& stroke,
737 return this->internalDrawPath(target,
746 void GrDefaultPathRenderer::onStencilPath(GrDrawTarget* target,
747 GrPipelineBuilder* pipelineBuilder,
748 const SkMatrix& viewMatrix,
750 const SkStrokeRec& stroke) {
751 SkASSERT(SkPath::kInverseEvenOdd_FillType != path.getFillType());
752 SkASSERT(SkPath::kInverseWinding_FillType != path.getFillType());
753 this->internalDrawPath(target, pipelineBuilder, GrColor_WHITE, viewMatrix, path, stroke, true);