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