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         HasAABloat aaBloat = (aaType == GrAAType::kNone) ? HasAABloat ::kNo : HasAABloat::kYes;
386         this->setBounds(devBounds, aaBloat,
387                         isHairline ? IsHairline::kYes : IsHairline::kNo);
388     }
389 
fixedFunctionFlags() const390     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
391 
finalize(const GrCaps & caps,const GrAppliedClip * clip,bool hasMixedSampledCoverage,GrClampType clampType)392     GrProcessorSet::Analysis finalize(
393             const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
394             GrClampType clampType) override {
395         GrProcessorAnalysisCoverage gpCoverage =
396                 this->coverage() == 0xFF ? GrProcessorAnalysisCoverage::kNone
397                                          : GrProcessorAnalysisCoverage::kSingleChannel;
398         // This Op uses uniform (not vertex) color, so doesn't need to track wide color.
399         return fHelper.finalizeProcessors(
400                 caps, clip, hasMixedSampledCoverage, clampType, gpCoverage, &fColor, nullptr);
401     }
402 
403 private:
onPrepareDraws(Target * target)404     void onPrepareDraws(Target* target) override {
405         sk_sp<GrGeometryProcessor> gp;
406         {
407             using namespace GrDefaultGeoProcFactory;
408             Color color(this->color());
409             Coverage coverage(this->coverage());
410             LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
411                                                               : LocalCoords::kUnused_Type);
412             gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
413                                                color,
414                                                coverage,
415                                                localCoords,
416                                                this->viewMatrix());
417         }
418 
419         SkASSERT(gp->vertexStride() == sizeof(SkPoint));
420 
421         int instanceCount = fPaths.count();
422 
423         // We avoid indices when we have a single hairline contour.
424         bool isIndexed = !this->isHairline() || instanceCount > 1 ||
425                          PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
426 
427         // determine primitiveType
428         GrPrimitiveType primitiveType;
429         if (this->isHairline()) {
430             primitiveType = isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
431         } else {
432             primitiveType = GrPrimitiveType::kTriangles;
433         }
434         PathGeoBuilder pathGeoBuilder(primitiveType, target, std::move(gp));
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 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)443     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
444         fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
445     }
446 
onCombineIfPossible(GrOp * t,const GrCaps & caps)447     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
448         DefaultPathOp* that = t->cast<DefaultPathOp>();
449         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
450             return CombineResult::kCannotCombine;
451         }
452 
453         if (this->color() != that->color()) {
454             return CombineResult::kCannotCombine;
455         }
456 
457         if (this->coverage() != that->coverage()) {
458             return CombineResult::kCannotCombine;
459         }
460 
461         if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
462             return CombineResult::kCannotCombine;
463         }
464 
465         if (this->isHairline() != that->isHairline()) {
466             return CombineResult::kCannotCombine;
467         }
468 
469         fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
470         return CombineResult::kMerged;
471     }
472 
color() const473     const SkPMColor4f& color() const { return fColor; }
coverage() const474     uint8_t coverage() const { return fCoverage; }
viewMatrix() const475     const SkMatrix& viewMatrix() const { return fViewMatrix; }
isHairline() const476     bool isHairline() const { return fIsHairline; }
477 
478     struct PathData {
479         SkPath fPath;
480         SkScalar fTolerance;
481     };
482 
483     SkSTArray<1, PathData, true> fPaths;
484     Helper fHelper;
485     SkPMColor4f fColor;
486     uint8_t fCoverage;
487     SkMatrix fViewMatrix;
488     bool fIsHairline;
489 
490     typedef GrMeshDrawOp INHERITED;
491 };
492 
493 }  // anonymous namespace
494 
internalDrawPath(GrRenderTargetContext * renderTargetContext,GrPaint && paint,GrAAType aaType,const GrUserStencilSettings & userStencilSettings,const GrClip & clip,const SkMatrix & viewMatrix,const GrShape & shape,bool stencilOnly)495 bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
496                                              GrPaint&& paint,
497                                              GrAAType aaType,
498                                              const GrUserStencilSettings& userStencilSettings,
499                                              const GrClip& clip,
500                                              const SkMatrix& viewMatrix,
501                                              const GrShape& shape,
502                                              bool stencilOnly) {
503     auto context = renderTargetContext->surfPriv().getContext();
504 
505     SkASSERT(GrAAType::kCoverage != aaType);
506     SkPath path;
507     shape.asPath(&path);
508 
509     SkScalar hairlineCoverage;
510     uint8_t newCoverage = 0xff;
511     bool isHairline = false;
512     if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
513         newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
514         isHairline = true;
515     } else {
516         SkASSERT(shape.style().isSimpleFill());
517     }
518 
519     int                          passCount = 0;
520     const GrUserStencilSettings* passes[2];
521     bool                         reverse = false;
522     bool                         lastPassIsBounds;
523 
524     if (isHairline) {
525         passCount = 1;
526         if (stencilOnly) {
527             passes[0] = &gDirectToStencil;
528         } else {
529             passes[0] = &userStencilSettings;
530         }
531         lastPassIsBounds = false;
532     } else {
533         if (single_pass_shape(shape)) {
534             passCount = 1;
535             if (stencilOnly) {
536                 passes[0] = &gDirectToStencil;
537             } else {
538                 passes[0] = &userStencilSettings;
539             }
540             lastPassIsBounds = false;
541         } else {
542             switch (path.getFillType()) {
543                 case SkPath::kInverseEvenOdd_FillType:
544                     reverse = true;
545                     // fallthrough
546                 case SkPath::kEvenOdd_FillType:
547                     passes[0] = &gEOStencilPass;
548                     if (stencilOnly) {
549                         passCount = 1;
550                         lastPassIsBounds = false;
551                     } else {
552                         passCount = 2;
553                         lastPassIsBounds = true;
554                         if (reverse) {
555                             passes[1] = &gInvEOColorPass;
556                         } else {
557                             passes[1] = &gEOColorPass;
558                         }
559                     }
560                     break;
561 
562                 case SkPath::kInverseWinding_FillType:
563                     reverse = true;
564                     // fallthrough
565                 case SkPath::kWinding_FillType:
566                     passes[0] = &gWindStencilPass;
567                     passCount = 2;
568                     if (stencilOnly) {
569                         lastPassIsBounds = false;
570                         --passCount;
571                     } else {
572                         lastPassIsBounds = true;
573                         if (reverse) {
574                             passes[passCount-1] = &gInvWindColorPass;
575                         } else {
576                             passes[passCount-1] = &gWindColorPass;
577                         }
578                     }
579                     break;
580                 default:
581                     SkDEBUGFAIL("Unknown path fFill!");
582                     return false;
583             }
584         }
585     }
586 
587     SkScalar tol = GrPathUtils::kDefaultTolerance;
588     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
589 
590     SkRect devBounds;
591     GetPathDevBounds(path,
592                      renderTargetContext->asRenderTargetProxy()->worstCaseWidth(),
593                      renderTargetContext->asRenderTargetProxy()->worstCaseHeight(),
594                      viewMatrix, &devBounds);
595 
596     for (int p = 0; p < passCount; ++p) {
597         if (lastPassIsBounds && (p == passCount-1)) {
598             SkRect bounds;
599             SkMatrix localMatrix = SkMatrix::I();
600             if (reverse) {
601                 // draw over the dev bounds (which will be the whole dst surface for inv fill).
602                 bounds = devBounds;
603                 SkMatrix vmi;
604                 // mapRect through persp matrix may not be correct
605                 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
606                     vmi.mapRect(&bounds);
607                 } else {
608                     if (!viewMatrix.invert(&localMatrix)) {
609                         return false;
610                     }
611                 }
612             } else {
613                 bounds = path.getBounds();
614             }
615             const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
616                                                                                viewMatrix;
617             // This is a non-coverage aa rect op since we assert aaType != kCoverage at the start
618             assert_alive(paint);
619             renderTargetContext->priv().stencilRect(clip, passes[p], std::move(paint),
620                     GrAA(aaType == GrAAType::kMSAA), viewM, bounds, &localMatrix);
621         } else {
622             bool stencilPass = stencilOnly || passCount > 1;
623             std::unique_ptr<GrDrawOp> op;
624             if (stencilPass) {
625                 GrPaint stencilPaint;
626                 stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
627                 op = DefaultPathOp::Make(context, std::move(stencilPaint), path, srcSpaceTol,
628                                          newCoverage, viewMatrix, isHairline, aaType, devBounds,
629                                          passes[p]);
630             } else {
631                 assert_alive(paint);
632                 op = DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, newCoverage,
633                                          viewMatrix, isHairline, aaType, devBounds, passes[p]);
634             }
635             renderTargetContext->addDrawOp(clip, std::move(op));
636         }
637     }
638     return true;
639 }
640 
641 GrPathRenderer::CanDrawPath
onCanDrawPath(const CanDrawPathArgs & args) const642 GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
643     bool isHairline = IsStrokeHairlineOrEquivalent(
644             args.fShape->style(), *args.fViewMatrix, nullptr);
645     // If we aren't a single_pass_shape or hairline, we require stencil buffers.
646     if (!(single_pass_shape(*args.fShape) || isHairline) &&
647         (args.fCaps->avoidStencilBuffers() || args.fTargetIsWrappedVkSecondaryCB)) {
648         return CanDrawPath::kNo;
649     }
650     // If antialiasing is required, we only support MSAA.
651     if (GrAAType::kNone != args.fAAType && GrAAType::kMSAA != args.fAAType) {
652         return CanDrawPath::kNo;
653     }
654     // This can draw any path with any simple fill style.
655     if (!args.fShape->style().isSimpleFill() && !isHairline) {
656         return CanDrawPath::kNo;
657     }
658     // This is the fallback renderer for when a path is too complicated for the others to draw.
659     return CanDrawPath::kAsBackup;
660 }
661 
onDrawPath(const DrawPathArgs & args)662 bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
663     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
664                               "GrDefaultPathRenderer::onDrawPath");
665     GrAAType aaType = (GrAAType::kNone != args.fAAType) ? GrAAType::kMSAA : GrAAType::kNone;
666 
667     return this->internalDrawPath(
668             args.fRenderTargetContext, std::move(args.fPaint), aaType, *args.fUserStencilSettings,
669             *args.fClip, *args.fViewMatrix, *args.fShape, false);
670 }
671 
onStencilPath(const StencilPathArgs & args)672 void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
673     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
674                               "GrDefaultPathRenderer::onStencilPath");
675     SkASSERT(!args.fShape->inverseFilled());
676 
677     GrPaint paint;
678     paint.setXPFactory(GrDisableColorXPFactory::Get());
679 
680     auto aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
681 
682     this->internalDrawPath(
683             args.fRenderTargetContext, std::move(paint), aaType, GrUserStencilSettings::kUnused,
684             *args.fClip, *args.fViewMatrix, *args.fShape, true);
685 }
686 
687 ///////////////////////////////////////////////////////////////////////////////////////////////////
688 
689 #if GR_TEST_UTILS
690 
GR_DRAW_OP_TEST_DEFINE(DefaultPathOp)691 GR_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
692     SkMatrix viewMatrix = GrTest::TestMatrix(random);
693 
694     // For now just hairlines because the other types of draws require two ops.
695     // TODO we should figure out a way to combine the stencil and cover steps into one op.
696     GrStyle style(SkStrokeRec::kHairline_InitStyle);
697     SkPath path = GrTest::TestPath(random);
698 
699     // Compute srcSpaceTol
700     SkRect bounds = path.getBounds();
701     SkScalar tol = GrPathUtils::kDefaultTolerance;
702     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
703 
704     viewMatrix.mapRect(&bounds);
705     uint8_t coverage = GrRandomCoverage(random);
706     GrAAType aaType = GrAAType::kNone;
707     if (numSamples > 1 && random->nextBool()) {
708         aaType = GrAAType::kMSAA;
709     }
710     return DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, coverage, viewMatrix,
711                                true, aaType, bounds, GrGetRandomStencil(random, context));
712 }
713 
714 #endif
715