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