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