• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/ops/GrDefaultPathRenderer.h"
9 
10 #include "include/core/SkString.h"
11 #include "include/core/SkStrokeRec.h"
12 #include "src/core/SkGeometry.h"
13 #include "src/core/SkTLazy.h"
14 #include "src/core/SkTraceEvent.h"
15 #include "src/gpu/GrAuditTrail.h"
16 #include "src/gpu/GrCaps.h"
17 #include "src/gpu/GrDefaultGeoProcFactory.h"
18 #include "src/gpu/GrDrawOpTest.h"
19 #include "src/gpu/GrFixedClip.h"
20 #include "src/gpu/GrMesh.h"
21 #include "src/gpu/GrOpFlushState.h"
22 #include "src/gpu/GrRenderTargetContextPriv.h"
23 #include "src/gpu/GrStyle.h"
24 #include "src/gpu/GrSurfaceContextPriv.h"
25 #include "src/gpu/geometry/GrPathUtils.h"
26 #include "src/gpu/geometry/GrShape.h"
27 #include "src/gpu/ops/GrMeshDrawOp.h"
28 #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
29 
GrDefaultPathRenderer()30 GrDefaultPathRenderer::GrDefaultPathRenderer() {
31 }
32 
33 ////////////////////////////////////////////////////////////////////////////////
34 // Helpers for drawPath
35 
36 #define STENCIL_OFF     0   // Always disable stencil (even when needed)
37 
single_pass_shape(const GrShape & shape)38 static inline bool single_pass_shape(const GrShape& shape) {
39 #if STENCIL_OFF
40     return true;
41 #else
42     // Inverse fill is always two pass.
43     if (shape.inverseFilled()) {
44         return false;
45     }
46     // This path renderer only accepts simple fill paths or stroke paths that are either hairline
47     // or have a stroke width small enough to treat as hairline. Hairline paths are always single
48     // pass. Filled paths are single pass if they're convex.
49     if (shape.style().isSimpleFill()) {
50         return shape.knownToBeConvex();
51     }
52     return true;
53 #endif
54 }
55 
56 GrPathRenderer::StencilSupport
onGetStencilSupport(const GrShape & shape) const57 GrDefaultPathRenderer::onGetStencilSupport(const GrShape& shape) const {
58     if (single_pass_shape(shape)) {
59         return GrPathRenderer::kNoRestriction_StencilSupport;
60     } else {
61         return GrPathRenderer::kStencilOnly_StencilSupport;
62     }
63 }
64 
65 namespace {
66 
67 class PathGeoBuilder {
68 public:
PathGeoBuilder(GrPrimitiveType primitiveType,GrMeshDrawOp::Target * target,sk_sp<const GrGeometryProcessor> geometryProcessor)69     PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
70                    sk_sp<const GrGeometryProcessor> geometryProcessor)
71             : fPrimitiveType(primitiveType)
72             , fTarget(target)
73             , fVertexStride(sizeof(SkPoint))
74             , fGeometryProcessor(std::move(geometryProcessor))
75             , fFirstIndex(0)
76             , fIndicesInChunk(0)
77             , fIndices(nullptr) {
78         this->allocNewBuffers();
79     }
80 
~PathGeoBuilder()81     ~PathGeoBuilder() {
82         this->emitMeshAndPutBackReserve();
83     }
84 
85     /**
86      *  Path verbs
87      */
moveTo(const SkPoint & p)88     void moveTo(const SkPoint& p) {
89         needSpace(1);
90 
91         fSubpathIndexStart = this->currentIndex();
92         *(fCurVert++) = p;
93     }
94 
addLine(const SkPoint & p)95     void addLine(const SkPoint& p) {
96         needSpace(1, this->indexScale());
97 
98         if (this->isIndexed()) {
99             uint16_t prevIdx = this->currentIndex() - 1;
100             appendCountourEdgeIndices(prevIdx);
101         }
102         *(fCurVert++) = p;
103     }
104 
addQuad(const SkPoint pts[],SkScalar srcSpaceTolSqd,SkScalar srcSpaceTol)105     void addQuad(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
106         this->needSpace(GrPathUtils::kMaxPointsPerCurve,
107                         GrPathUtils::kMaxPointsPerCurve * this->indexScale());
108 
109         // First pt of quad is the pt we ended on in previous step
110         uint16_t firstQPtIdx = this->currentIndex() - 1;
111         uint16_t numPts = (uint16_t)GrPathUtils::generateQuadraticPoints(
112                 pts[0], pts[1], pts[2], srcSpaceTolSqd, &fCurVert,
113                 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
114         if (this->isIndexed()) {
115             for (uint16_t i = 0; i < numPts; ++i) {
116                 appendCountourEdgeIndices(firstQPtIdx + i);
117             }
118         }
119     }
120 
addConic(SkScalar weight,const SkPoint pts[],SkScalar srcSpaceTolSqd,SkScalar srcSpaceTol)121     void addConic(SkScalar weight, const SkPoint pts[], SkScalar srcSpaceTolSqd,
122                   SkScalar srcSpaceTol) {
123         SkAutoConicToQuads converter;
124         const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
125         for (int i = 0; i < converter.countQuads(); ++i) {
126             this->addQuad(quadPts + i * 2, srcSpaceTolSqd, srcSpaceTol);
127         }
128     }
129 
addCubic(const SkPoint pts[],SkScalar srcSpaceTolSqd,SkScalar srcSpaceTol)130     void addCubic(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
131         this->needSpace(GrPathUtils::kMaxPointsPerCurve,
132                         GrPathUtils::kMaxPointsPerCurve * this->indexScale());
133 
134         // First pt of cubic is the pt we ended on in previous step
135         uint16_t firstCPtIdx = this->currentIndex() - 1;
136         uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
137                 pts[0], pts[1], pts[2], pts[3], srcSpaceTolSqd, &fCurVert,
138                 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
139         if (this->isIndexed()) {
140             for (uint16_t i = 0; i < numPts; ++i) {
141                 appendCountourEdgeIndices(firstCPtIdx + i);
142             }
143         }
144     }
145 
addPath(const SkPath & path,SkScalar srcSpaceTol)146     void addPath(const SkPath& path, SkScalar srcSpaceTol) {
147         SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
148 
149         SkPath::Iter iter(path, false);
150         SkPoint pts[4];
151 
152         bool done = false;
153         while (!done) {
154             SkPath::Verb verb = iter.next(pts);
155             switch (verb) {
156                 case SkPath::kMove_Verb:
157                     this->moveTo(pts[0]);
158                     break;
159                 case SkPath::kLine_Verb:
160                     this->addLine(pts[1]);
161                     break;
162                 case SkPath::kConic_Verb:
163                     this->addConic(iter.conicWeight(), pts, srcSpaceTolSqd, srcSpaceTol);
164                     break;
165                 case SkPath::kQuad_Verb:
166                     this->addQuad(pts, srcSpaceTolSqd, srcSpaceTol);
167                     break;
168                 case SkPath::kCubic_Verb:
169                     this->addCubic(pts, srcSpaceTolSqd, srcSpaceTol);
170                     break;
171                 case SkPath::kClose_Verb:
172                     break;
173                 case SkPath::kDone_Verb:
174                     done = true;
175             }
176         }
177     }
178 
PathHasMultipleSubpaths(const SkPath & path)179     static bool PathHasMultipleSubpaths(const SkPath& path) {
180         bool first = true;
181 
182         SkPath::Iter iter(path, false);
183         SkPath::Verb verb;
184 
185         SkPoint pts[4];
186         while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
187             if (SkPath::kMove_Verb == verb && !first) {
188                 return true;
189             }
190             first = false;
191         }
192         return false;
193     }
194 
195 private:
196     /**
197      *  Derived properties
198      *  TODO: Cache some of these for better performance, rather than re-computing?
199      */
isIndexed() const200     bool isIndexed() const {
201         return GrPrimitiveType::kLines == fPrimitiveType ||
202                GrPrimitiveType::kTriangles == fPrimitiveType;
203     }
isHairline() const204     bool isHairline() const {
205         return GrPrimitiveType::kLines == fPrimitiveType ||
206                GrPrimitiveType::kLineStrip == fPrimitiveType;
207     }
indexScale() const208     int indexScale() const {
209         switch (fPrimitiveType) {
210             case GrPrimitiveType::kLines:
211                 return 2;
212             case GrPrimitiveType::kTriangles:
213                 return 3;
214             default:
215                 return 0;
216         }
217     }
218 
currentIndex() const219     uint16_t currentIndex() const { return fCurVert - fVertices; }
220 
221     // Allocate vertex and (possibly) index buffers
allocNewBuffers()222     void allocNewBuffers() {
223         // Ensure that we always get enough verts for a worst-case quad/cubic, plus leftover points
224         // from previous mesh piece (up to two verts to continue fanning). If we can't get that
225         // many, ask for a much larger number. This needs to be fairly big to handle  quads/cubics,
226         // which have a worst-case of 1k points.
227         static const int kMinVerticesPerChunk = GrPathUtils::kMaxPointsPerCurve + 2;
228         static const int kFallbackVerticesPerChunk = 16384;
229 
230         fVertices = static_cast<SkPoint*>(fTarget->makeVertexSpaceAtLeast(fVertexStride,
231                                                                           kMinVerticesPerChunk,
232                                                                           kFallbackVerticesPerChunk,
233                                                                           &fVertexBuffer,
234                                                                           &fFirstVertex,
235                                                                           &fVerticesInChunk));
236 
237         if (this->isIndexed()) {
238             // Similar to above: Ensure we get enough indices for one worst-case quad/cubic.
239             // No extra indices are needed for stitching, though. If we can't get that many, ask
240             // for enough to match our large vertex request.
241             const int kMinIndicesPerChunk = GrPathUtils::kMaxPointsPerCurve * this->indexScale();
242             const int kFallbackIndicesPerChunk = kFallbackVerticesPerChunk * this->indexScale();
243 
244             fIndices = fTarget->makeIndexSpaceAtLeast(kMinIndicesPerChunk, kFallbackIndicesPerChunk,
245                                                       &fIndexBuffer, &fFirstIndex,
246                                                       &fIndicesInChunk);
247         }
248 
249         fCurVert = fVertices;
250         fCurIdx = fIndices;
251         fSubpathIndexStart = 0;
252     }
253 
appendCountourEdgeIndices(uint16_t edgeV0Idx)254     void appendCountourEdgeIndices(uint16_t edgeV0Idx) {
255         // When drawing lines we're appending line segments along the countour. When applying the
256         // other fill rules we're drawing triangle fans around the start of the current (sub)path.
257         if (!this->isHairline()) {
258             *(fCurIdx++) = fSubpathIndexStart;
259         }
260         *(fCurIdx++) = edgeV0Idx;
261         *(fCurIdx++) = edgeV0Idx + 1;
262     }
263 
264     // Emits a single draw with all accumulated vertex/index data
emitMeshAndPutBackReserve()265     void emitMeshAndPutBackReserve() {
266         int vertexCount = fCurVert - fVertices;
267         int indexCount = fCurIdx - fIndices;
268         SkASSERT(vertexCount <= fVerticesInChunk);
269         SkASSERT(indexCount <= fIndicesInChunk);
270 
271         if (this->isIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
272             GrMesh* mesh = fTarget->allocMesh(fPrimitiveType);
273             if (!this->isIndexed()) {
274                 mesh->setNonIndexedNonInstanced(vertexCount);
275             } else {
276                 mesh->setIndexed(std::move(fIndexBuffer), indexCount, fFirstIndex, 0,
277                                  vertexCount - 1, GrPrimitiveRestart::kNo);
278             }
279             mesh->setVertexData(std::move(fVertexBuffer), fFirstVertex);
280             fTarget->recordDraw(fGeometryProcessor, mesh);
281         }
282 
283         fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
284         fTarget->putBackVertices((size_t)(fVerticesInChunk - vertexCount), fVertexStride);
285     }
286 
needSpace(int vertsNeeded,int indicesNeeded=0)287     void needSpace(int vertsNeeded, int indicesNeeded = 0) {
288         if (fCurVert + vertsNeeded > fVertices + fVerticesInChunk ||
289             fCurIdx + indicesNeeded > fIndices + fIndicesInChunk) {
290             // We are about to run out of space (possibly)
291 
292             // To maintain continuity, we need to remember one or two points from the current mesh.
293             // Lines only need the last point, fills need the first point from the current contour.
294             // We always grab both here, and append the ones we need at the end of this process.
295             SkPoint lastPt = *(fCurVert - 1);
296             SkASSERT(fSubpathIndexStart < fVerticesInChunk);
297             SkPoint subpathStartPt = fVertices[fSubpathIndexStart];
298 
299             // Draw the mesh we've accumulated, and put back any unused space
300             this->emitMeshAndPutBackReserve();
301 
302             // Get new buffers
303             this->allocNewBuffers();
304 
305             // Append copies of the points we saved so the two meshes will weld properly
306             if (!this->isHairline()) {
307                 *(fCurVert++) = subpathStartPt;
308             }
309             *(fCurVert++) = lastPt;
310         }
311     }
312 
313     GrPrimitiveType fPrimitiveType;
314     GrMeshDrawOp::Target* fTarget;
315     size_t fVertexStride;
316     sk_sp<const GrGeometryProcessor> fGeometryProcessor;
317 
318     sk_sp<const GrBuffer> fVertexBuffer;
319     int fFirstVertex;
320     int fVerticesInChunk;
321     SkPoint* fVertices;
322     SkPoint* fCurVert;
323 
324     sk_sp<const GrBuffer> fIndexBuffer;
325     int fFirstIndex;
326     int fIndicesInChunk;
327     uint16_t* fIndices;
328     uint16_t* fCurIdx;
329     uint16_t fSubpathIndexStart;
330 };
331 
332 class DefaultPathOp final : public GrMeshDrawOp {
333 private:
334     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
335 
336 public:
337     DEFINE_OP_CLASS_ID
338 
Make(GrRecordingContext * context,GrPaint && paint,const SkPath & path,SkScalar tolerance,uint8_t coverage,const SkMatrix & viewMatrix,bool isHairline,GrAAType aaType,const SkRect & devBounds,const GrUserStencilSettings * stencilSettings)339     static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
340                                           GrPaint&& paint,
341                                           const SkPath& path,
342                                           SkScalar tolerance,
343                                           uint8_t coverage,
344                                           const SkMatrix& viewMatrix,
345                                           bool isHairline,
346                                           GrAAType aaType,
347                                           const SkRect& devBounds,
348                                           const GrUserStencilSettings* stencilSettings) {
349         return Helper::FactoryHelper<DefaultPathOp>(context, std::move(paint), path, tolerance,
350                                                     coverage, viewMatrix, isHairline, aaType,
351                                                     devBounds, stencilSettings);
352     }
353 
name() const354     const char* name() const override { return "DefaultPathOp"; }
355 
visitProxies(const VisitProxyFunc & func) const356     void visitProxies(const VisitProxyFunc& func) const override {
357         fHelper.visitProxies(func);
358     }
359 
360 #ifdef SK_DEBUG
dumpInfo() const361     SkString dumpInfo() const override {
362         SkString string;
363         string.appendf("Color: 0x%08x Count: %d\n", fColor.toBytes_RGBA(), fPaths.count());
364         for (const auto& path : fPaths) {
365             string.appendf("Tolerance: %.2f\n", path.fTolerance);
366         }
367         string += fHelper.dumpInfo();
368         string += INHERITED::dumpInfo();
369         return string;
370     }
371 #endif
372 
DefaultPathOp(const Helper::MakeArgs & helperArgs,const SkPMColor4f & color,const SkPath & path,SkScalar tolerance,uint8_t coverage,const SkMatrix & viewMatrix,bool isHairline,GrAAType aaType,const SkRect & devBounds,const GrUserStencilSettings * stencilSettings)373     DefaultPathOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color, const SkPath& path,
374                   SkScalar tolerance, uint8_t coverage, const SkMatrix& viewMatrix, bool isHairline,
375                   GrAAType aaType, const SkRect& devBounds,
376                   const GrUserStencilSettings* stencilSettings)
377             : INHERITED(ClassID())
378             , fHelper(helperArgs, aaType, stencilSettings)
379             , fColor(color)
380             , fCoverage(coverage)
381             , fViewMatrix(viewMatrix)
382             , fIsHairline(isHairline) {
383         fPaths.emplace_back(PathData{path, tolerance});
384 
385         this->setBounds(devBounds, HasAABloat::kNo,
386                         isHairline ? IsZeroArea::kYes : IsZeroArea::kNo);
387     }
388 
fixedFunctionFlags() const389     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
390 
finalize(const GrCaps & caps,const GrAppliedClip * clip,bool hasMixedSampledCoverage,GrClampType clampType)391     GrProcessorSet::Analysis finalize(
392             const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
393             GrClampType clampType) override {
394         GrProcessorAnalysisCoverage gpCoverage =
395                 this->coverage() == 0xFF ? GrProcessorAnalysisCoverage::kNone
396                                          : GrProcessorAnalysisCoverage::kSingleChannel;
397         // This Op uses uniform (not vertex) color, so doesn't need to track wide color.
398         return fHelper.finalizeProcessors(
399                 caps, clip, hasMixedSampledCoverage, clampType, gpCoverage, &fColor, nullptr);
400     }
401 
402 private:
onPrepareDraws(Target * target)403     void onPrepareDraws(Target* target) override {
404         sk_sp<GrGeometryProcessor> gp;
405         {
406             using namespace GrDefaultGeoProcFactory;
407             Color color(this->color());
408             Coverage coverage(this->coverage());
409             LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
410                                                               : LocalCoords::kUnused_Type);
411             gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
412                                                color,
413                                                coverage,
414                                                localCoords,
415                                                this->viewMatrix());
416         }
417 
418         SkASSERT(gp->vertexStride() == sizeof(SkPoint));
419 
420         int instanceCount = fPaths.count();
421 
422         // We avoid indices when we have a single hairline contour.
423         bool isIndexed = !this->isHairline() || instanceCount > 1 ||
424                          PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
425 
426         // determine primitiveType
427         GrPrimitiveType primitiveType;
428         if (this->isHairline()) {
429             primitiveType = isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
430         } else {
431             primitiveType = GrPrimitiveType::kTriangles;
432         }
433         PathGeoBuilder pathGeoBuilder(primitiveType, target, std::move(gp));
434 
435         // fill buffers
436         for (int i = 0; i < instanceCount; i++) {
437             const PathData& args = fPaths[i];
438             pathGeoBuilder.addPath(args.fPath, args.fTolerance);
439         }
440     }
441 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)442     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
443         fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
444     }
445 
onCombineIfPossible(GrOp * t,const GrCaps & caps)446     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
447         DefaultPathOp* that = t->cast<DefaultPathOp>();
448         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
449             return CombineResult::kCannotCombine;
450         }
451 
452         if (this->color() != that->color()) {
453             return CombineResult::kCannotCombine;
454         }
455 
456         if (this->coverage() != that->coverage()) {
457             return CombineResult::kCannotCombine;
458         }
459 
460         if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
461             return CombineResult::kCannotCombine;
462         }
463 
464         if (this->isHairline() != that->isHairline()) {
465             return CombineResult::kCannotCombine;
466         }
467 
468         fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
469         return CombineResult::kMerged;
470     }
471 
color() const472     const SkPMColor4f& color() const { return fColor; }
coverage() const473     uint8_t coverage() const { return fCoverage; }
viewMatrix() const474     const SkMatrix& viewMatrix() const { return fViewMatrix; }
isHairline() const475     bool isHairline() const { return fIsHairline; }
476 
477     struct PathData {
478         SkPath fPath;
479         SkScalar fTolerance;
480     };
481 
482     SkSTArray<1, PathData, true> fPaths;
483     Helper fHelper;
484     SkPMColor4f fColor;
485     uint8_t fCoverage;
486     SkMatrix fViewMatrix;
487     bool fIsHairline;
488 
489     typedef GrMeshDrawOp INHERITED;
490 };
491 
492 }  // anonymous namespace
493 
internalDrawPath(GrRenderTargetContext * renderTargetContext,GrPaint && paint,GrAAType aaType,const GrUserStencilSettings & userStencilSettings,const GrClip & clip,const SkMatrix & viewMatrix,const GrShape & shape,bool stencilOnly)494 bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
495                                              GrPaint&& paint,
496                                              GrAAType aaType,
497                                              const GrUserStencilSettings& userStencilSettings,
498                                              const GrClip& clip,
499                                              const SkMatrix& viewMatrix,
500                                              const GrShape& shape,
501                                              bool stencilOnly) {
502     auto context = renderTargetContext->surfPriv().getContext();
503 
504     SkASSERT(GrAAType::kCoverage != aaType);
505     SkPath path;
506     shape.asPath(&path);
507 
508     SkScalar hairlineCoverage;
509     uint8_t newCoverage = 0xff;
510     bool isHairline = false;
511     if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
512         newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
513         isHairline = true;
514     } else {
515         SkASSERT(shape.style().isSimpleFill());
516     }
517 
518     int                          passCount = 0;
519     const GrUserStencilSettings* passes[2];
520     bool                         reverse = false;
521     bool                         lastPassIsBounds;
522 
523     if (isHairline) {
524         passCount = 1;
525         if (stencilOnly) {
526             passes[0] = &gDirectToStencil;
527         } else {
528             passes[0] = &userStencilSettings;
529         }
530         lastPassIsBounds = false;
531     } else {
532         if (single_pass_shape(shape)) {
533             passCount = 1;
534             if (stencilOnly) {
535                 passes[0] = &gDirectToStencil;
536             } else {
537                 passes[0] = &userStencilSettings;
538             }
539             lastPassIsBounds = false;
540         } else {
541             switch (path.getFillType()) {
542                 case SkPath::kInverseEvenOdd_FillType:
543                     reverse = true;
544                     // fallthrough
545                 case SkPath::kEvenOdd_FillType:
546                     passes[0] = &gEOStencilPass;
547                     if (stencilOnly) {
548                         passCount = 1;
549                         lastPassIsBounds = false;
550                     } else {
551                         passCount = 2;
552                         lastPassIsBounds = true;
553                         if (reverse) {
554                             passes[1] = &gInvEOColorPass;
555                         } else {
556                             passes[1] = &gEOColorPass;
557                         }
558                     }
559                     break;
560 
561                 case SkPath::kInverseWinding_FillType:
562                     reverse = true;
563                     // fallthrough
564                 case SkPath::kWinding_FillType:
565                     passes[0] = &gWindStencilPass;
566                     passCount = 2;
567                     if (stencilOnly) {
568                         lastPassIsBounds = false;
569                         --passCount;
570                     } else {
571                         lastPassIsBounds = true;
572                         if (reverse) {
573                             passes[passCount-1] = &gInvWindColorPass;
574                         } else {
575                             passes[passCount-1] = &gWindColorPass;
576                         }
577                     }
578                     break;
579                 default:
580                     SkDEBUGFAIL("Unknown path fFill!");
581                     return false;
582             }
583         }
584     }
585 
586     SkScalar tol = GrPathUtils::kDefaultTolerance;
587     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
588 
589     SkRect devBounds;
590     GetPathDevBounds(path,
591                      renderTargetContext->asRenderTargetProxy()->worstCaseWidth(),
592                      renderTargetContext->asRenderTargetProxy()->worstCaseHeight(),
593                      viewMatrix, &devBounds);
594 
595     for (int p = 0; p < passCount; ++p) {
596         if (lastPassIsBounds && (p == passCount-1)) {
597             SkRect bounds;
598             SkMatrix localMatrix = SkMatrix::I();
599             if (reverse) {
600                 // draw over the dev bounds (which will be the whole dst surface for inv fill).
601                 bounds = devBounds;
602                 SkMatrix vmi;
603                 // mapRect through persp matrix may not be correct
604                 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
605                     vmi.mapRect(&bounds);
606                 } else {
607                     if (!viewMatrix.invert(&localMatrix)) {
608                         return false;
609                     }
610                 }
611             } else {
612                 bounds = path.getBounds();
613             }
614             const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
615                                                                                viewMatrix;
616             // This is a non-coverage aa rect op since we assert aaType != kCoverage at the start
617             assert_alive(paint);
618             renderTargetContext->priv().stencilRect(clip, passes[p], std::move(paint),
619                     GrAA(aaType == GrAAType::kMSAA), viewM, bounds, &localMatrix);
620         } else {
621             bool stencilPass = stencilOnly || passCount > 1;
622             std::unique_ptr<GrDrawOp> op;
623             if (stencilPass) {
624                 GrPaint stencilPaint;
625                 stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
626                 op = DefaultPathOp::Make(context, std::move(stencilPaint), path, srcSpaceTol,
627                                          newCoverage, viewMatrix, isHairline, aaType, devBounds,
628                                          passes[p]);
629             } else {
630                 assert_alive(paint);
631                 op = DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, newCoverage,
632                                          viewMatrix, isHairline, aaType, devBounds, passes[p]);
633             }
634             renderTargetContext->addDrawOp(clip, std::move(op));
635         }
636     }
637     return true;
638 }
639 
640 GrPathRenderer::CanDrawPath
onCanDrawPath(const CanDrawPathArgs & args) const641 GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
642     bool isHairline = IsStrokeHairlineOrEquivalent(
643             args.fShape->style(), *args.fViewMatrix, nullptr);
644     // If we aren't a single_pass_shape or hairline, we require stencil buffers.
645     if (!(single_pass_shape(*args.fShape) || isHairline) &&
646         (args.fCaps->avoidStencilBuffers() || args.fTargetIsWrappedVkSecondaryCB)) {
647         return CanDrawPath::kNo;
648     }
649     // If antialiasing is required, we only support MSAA.
650     if (GrAAType::kNone != args.fAAType && GrAAType::kMSAA != args.fAAType) {
651         return CanDrawPath::kNo;
652     }
653     // This can draw any path with any simple fill style.
654     if (!args.fShape->style().isSimpleFill() && !isHairline) {
655         return CanDrawPath::kNo;
656     }
657     // This is the fallback renderer for when a path is too complicated for the others to draw.
658     return CanDrawPath::kAsBackup;
659 }
660 
onDrawPath(const DrawPathArgs & args)661 bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
662     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
663                               "GrDefaultPathRenderer::onDrawPath");
664     GrAAType aaType = (GrAAType::kNone != args.fAAType) ? GrAAType::kMSAA : GrAAType::kNone;
665 
666     return this->internalDrawPath(
667             args.fRenderTargetContext, std::move(args.fPaint), aaType, *args.fUserStencilSettings,
668             *args.fClip, *args.fViewMatrix, *args.fShape, false);
669 }
670 
onStencilPath(const StencilPathArgs & args)671 void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
672     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
673                               "GrDefaultPathRenderer::onStencilPath");
674     SkASSERT(!args.fShape->inverseFilled());
675 
676     GrPaint paint;
677     paint.setXPFactory(GrDisableColorXPFactory::Get());
678 
679     auto aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
680 
681     this->internalDrawPath(
682             args.fRenderTargetContext, std::move(paint), aaType, GrUserStencilSettings::kUnused,
683             *args.fClip, *args.fViewMatrix, *args.fShape, true);
684 }
685 
686 ///////////////////////////////////////////////////////////////////////////////////////////////////
687 
688 #if GR_TEST_UTILS
689 
GR_DRAW_OP_TEST_DEFINE(DefaultPathOp)690 GR_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
691     SkMatrix viewMatrix = GrTest::TestMatrix(random);
692 
693     // For now just hairlines because the other types of draws require two ops.
694     // TODO we should figure out a way to combine the stencil and cover steps into one op.
695     GrStyle style(SkStrokeRec::kHairline_InitStyle);
696     SkPath path = GrTest::TestPath(random);
697 
698     // Compute srcSpaceTol
699     SkRect bounds = path.getBounds();
700     SkScalar tol = GrPathUtils::kDefaultTolerance;
701     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
702 
703     viewMatrix.mapRect(&bounds);
704     uint8_t coverage = GrRandomCoverage(random);
705     GrAAType aaType = GrAAType::kNone;
706     if (numSamples > 1 && random->nextBool()) {
707         aaType = GrAAType::kMSAA;
708     }
709     return DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, coverage, viewMatrix,
710                                true, aaType, bounds, GrGetRandomStencil(random, context));
711 }
712 
713 #endif
714