• 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 
10 #include "GrContext.h"
11 #include "GrDefaultGeoProcFactory.h"
12 #include "GrDrawOpTest.h"
13 #include "GrFixedClip.h"
14 #include "GrMesh.h"
15 #include "GrOpFlushState.h"
16 #include "GrPathUtils.h"
17 #include "GrPipelineBuilder.h"
18 #include "SkGeometry.h"
19 #include "SkString.h"
20 #include "SkStrokeRec.h"
21 #include "SkTLazy.h"
22 #include "SkTraceEvent.h"
23 
24 #include "ops/GrMeshDrawOp.h"
25 #include "ops/GrRectOpFactory.h"
26 
GrDefaultPathRenderer(bool separateStencilSupport,bool stencilWrapOpsSupport)27 GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
28                                              bool stencilWrapOpsSupport)
29     : fSeparateStencil(separateStencilSupport)
30     , fStencilWrapOps(stencilWrapOpsSupport) {
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 
append_countour_edge_indices(bool hairLine,uint16_t fanCenterIdx,uint16_t edgeV0Idx,uint16_t ** indices)65 static inline void append_countour_edge_indices(bool hairLine,
66                                                 uint16_t fanCenterIdx,
67                                                 uint16_t edgeV0Idx,
68                                                 uint16_t** indices) {
69     // when drawing lines we're appending line segments along
70     // the contour. When applying the other fill rules we're
71     // drawing triangle fans around fanCenterIdx.
72     if (!hairLine) {
73         *((*indices)++) = fanCenterIdx;
74     }
75     *((*indices)++) = edgeV0Idx;
76     *((*indices)++) = edgeV0Idx + 1;
77 }
78 
add_quad(SkPoint ** vert,const SkPoint * base,const SkPoint pts[],SkScalar srcSpaceTolSqd,SkScalar srcSpaceTol,bool indexed,bool isHairline,uint16_t subpathIdxStart,int offset,uint16_t ** idx)79 static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[],
80                             SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed,
81                             bool isHairline, uint16_t subpathIdxStart, int offset, uint16_t** idx) {
82     // first pt of quad is the pt we ended on in previous step
83     uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1 + offset;
84     uint16_t numPts =  (uint16_t)
85         GrPathUtils::generateQuadraticPoints(
86             pts[0], pts[1], pts[2],
87             srcSpaceTolSqd, vert,
88             GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
89     if (indexed) {
90         for (uint16_t i = 0; i < numPts; ++i) {
91             append_countour_edge_indices(isHairline, subpathIdxStart,
92                                          firstQPtIdx + i, idx);
93         }
94     }
95 }
96 
97 class DefaultPathOp final : public GrMeshDrawOp {
98 public:
99     DEFINE_OP_CLASS_ID
100 
Make(GrColor color,const SkPath & path,SkScalar tolerance,uint8_t coverage,const SkMatrix & viewMatrix,bool isHairline,const SkRect & devBounds)101     static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkPath& path, SkScalar tolerance,
102                                               uint8_t coverage, const SkMatrix& viewMatrix,
103                                               bool isHairline, const SkRect& devBounds) {
104         return std::unique_ptr<GrMeshDrawOp>(new DefaultPathOp(color, path, tolerance, coverage,
105                                                                viewMatrix, isHairline, devBounds));
106     }
107 
name() const108     const char* name() const override { return "DefaultPathOp"; }
109 
dumpInfo() const110     SkString dumpInfo() const override {
111         SkString string;
112         string.appendf("Color: 0x%08x Count: %d\n", fColor, fPaths.count());
113         for (const auto& path : fPaths) {
114             string.appendf("Tolerance: %.2f\n", path.fTolerance);
115         }
116         string.append(DumpPipelineInfo(*this->pipeline()));
117         string.append(INHERITED::dumpInfo());
118         return string;
119     }
120 
121 private:
DefaultPathOp(GrColor color,const SkPath & path,SkScalar tolerance,uint8_t coverage,const SkMatrix & viewMatrix,bool isHairline,const SkRect & devBounds)122     DefaultPathOp(GrColor color, const SkPath& path, SkScalar tolerance, uint8_t coverage,
123                   const SkMatrix& viewMatrix, bool isHairline, const SkRect& devBounds)
124             : INHERITED(ClassID())
125             , fColor(color)
126             , fCoverage(coverage)
127             , fViewMatrix(viewMatrix)
128             , fIsHairline(isHairline) {
129         fPaths.emplace_back(PathData{path, tolerance});
130 
131         this->setBounds(devBounds, HasAABloat::kNo,
132                         isHairline ? IsZeroArea::kYes : IsZeroArea::kNo);
133     }
134 
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const135     void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
136                                             GrPipelineAnalysisCoverage* coverage) const override {
137         color->setToConstant(fColor);
138         *coverage = this->coverage() == 0xff ? GrPipelineAnalysisCoverage::kNone
139                                              : GrPipelineAnalysisCoverage::kSingleChannel;
140     }
141 
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)142     void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
143         optimizations.getOverrideColorIfSet(&fColor);
144         fUsesLocalCoords = optimizations.readsLocalCoords();
145     }
146 
onPrepareDraws(Target * target) const147     void onPrepareDraws(Target* target) const override {
148         sk_sp<GrGeometryProcessor> gp;
149         {
150             using namespace GrDefaultGeoProcFactory;
151             Color color(this->color());
152             Coverage coverage(this->coverage());
153             LocalCoords localCoords(this->usesLocalCoords() ? LocalCoords::kUsePosition_Type :
154                                                               LocalCoords::kUnused_Type);
155             gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, this->viewMatrix());
156         }
157 
158         size_t vertexStride = gp->getVertexStride();
159         SkASSERT(vertexStride == sizeof(SkPoint));
160 
161         int instanceCount = fPaths.count();
162 
163         // compute number of vertices
164         int maxVertices = 0;
165 
166         // We will use index buffers if we have multiple paths or one path with multiple contours
167         bool isIndexed = instanceCount > 1;
168         for (int i = 0; i < instanceCount; i++) {
169             const PathData& args = fPaths[i];
170 
171             int contourCount;
172             maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount,
173                                                             args.fTolerance);
174 
175             isIndexed = isIndexed || contourCount > 1;
176         }
177 
178         if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) {
179             //SkDebugf("Cannot render path (%d)\n", maxVertices);
180             return;
181         }
182 
183         // determine primitiveType
184         int maxIndices = 0;
185         GrPrimitiveType primitiveType;
186         if (this->isHairline()) {
187             if (isIndexed) {
188                 maxIndices = 2 * maxVertices;
189                 primitiveType = kLines_GrPrimitiveType;
190             } else {
191                 primitiveType = kLineStrip_GrPrimitiveType;
192             }
193         } else {
194             if (isIndexed) {
195                 maxIndices = 3 * maxVertices;
196                 primitiveType = kTriangles_GrPrimitiveType;
197             } else {
198                 primitiveType = kTriangleFan_GrPrimitiveType;
199             }
200         }
201 
202         // allocate vertex / index buffers
203         const GrBuffer* vertexBuffer;
204         int firstVertex;
205 
206         void* verts = target->makeVertexSpace(vertexStride, maxVertices,
207                                               &vertexBuffer, &firstVertex);
208 
209         if (!verts) {
210             SkDebugf("Could not allocate vertices\n");
211             return;
212         }
213 
214         const GrBuffer* indexBuffer = nullptr;
215         int firstIndex = 0;
216 
217         void* indices = nullptr;
218         if (isIndexed) {
219             indices = target->makeIndexSpace(maxIndices, &indexBuffer, &firstIndex);
220 
221             if (!indices) {
222                 SkDebugf("Could not allocate indices\n");
223                 return;
224             }
225         }
226 
227         // fill buffers
228         int vertexOffset = 0;
229         int indexOffset = 0;
230         for (int i = 0; i < instanceCount; i++) {
231             const PathData& args = fPaths[i];
232 
233             int vertexCnt = 0;
234             int indexCnt = 0;
235             if (!this->createGeom(verts,
236                                   vertexOffset,
237                                   indices,
238                                   indexOffset,
239                                   &vertexCnt,
240                                   &indexCnt,
241                                   args.fPath,
242                                   args.fTolerance,
243                                   isIndexed)) {
244                 return;
245             }
246 
247             vertexOffset += vertexCnt;
248             indexOffset += indexCnt;
249             SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
250         }
251 
252         GrMesh mesh;
253         if (isIndexed) {
254             mesh.initIndexed(primitiveType, vertexBuffer, indexBuffer, firstVertex, firstIndex,
255                              vertexOffset, indexOffset);
256         } else {
257             mesh.init(primitiveType, vertexBuffer, firstVertex, vertexOffset);
258         }
259         target->draw(gp.get(), mesh);
260 
261         // put back reserves
262         target->putBackIndices((size_t)(maxIndices - indexOffset));
263         target->putBackVertices((size_t)(maxVertices - vertexOffset), (size_t)vertexStride);
264     }
265 
onCombineIfPossible(GrOp * t,const GrCaps & caps)266     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
267         DefaultPathOp* that = t->cast<DefaultPathOp>();
268         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
269                                     that->bounds(), caps)) {
270             return false;
271         }
272 
273         if (this->color() != that->color()) {
274             return false;
275         }
276 
277         if (this->coverage() != that->coverage()) {
278             return false;
279         }
280 
281         if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
282             return false;
283         }
284 
285         if (this->isHairline() != that->isHairline()) {
286             return false;
287         }
288 
289         fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
290         this->joinBounds(*that);
291         return true;
292     }
293 
createGeom(void * vertices,size_t vertexOffset,void * indices,size_t indexOffset,int * vertexCnt,int * indexCnt,const SkPath & path,SkScalar srcSpaceTol,bool isIndexed) const294     bool createGeom(void* vertices,
295                     size_t vertexOffset,
296                     void* indices,
297                     size_t indexOffset,
298                     int* vertexCnt,
299                     int* indexCnt,
300                     const SkPath& path,
301                     SkScalar srcSpaceTol,
302                     bool isIndexed) const {
303             SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
304 
305             uint16_t indexOffsetU16 = (uint16_t)indexOffset;
306             uint16_t vertexOffsetU16 = (uint16_t)vertexOffset;
307 
308             uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16;
309             uint16_t* idx = idxBase;
310             uint16_t subpathIdxStart = vertexOffsetU16;
311 
312             SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset;
313             SkPoint* vert = base;
314 
315             SkPoint pts[4];
316 
317             bool first = true;
318             int subpath = 0;
319 
320             SkPath::Iter iter(path, false);
321 
322             bool done = false;
323             while (!done) {
324                 SkPath::Verb verb = iter.next(pts);
325                 switch (verb) {
326                     case SkPath::kMove_Verb:
327                         if (!first) {
328                             uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16;
329                             subpathIdxStart = currIdx;
330                             ++subpath;
331                         }
332                         *vert = pts[0];
333                         vert++;
334                         break;
335                     case SkPath::kLine_Verb:
336                         if (isIndexed) {
337                             uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
338                             append_countour_edge_indices(this->isHairline(), subpathIdxStart,
339                                                          prevIdx, &idx);
340                         }
341                         *(vert++) = pts[1];
342                         break;
343                     case SkPath::kConic_Verb: {
344                         SkScalar weight = iter.conicWeight();
345                         SkAutoConicToQuads converter;
346                         // Converting in src-space, hance the finer tolerance (0.25)
347                         // TODO: find a way to do this in dev-space so the tolerance means something
348                         const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
349                         for (int i = 0; i < converter.countQuads(); ++i) {
350                             add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol,
351                                      isIndexed, this->isHairline(), subpathIdxStart,
352                                      (int)vertexOffset, &idx);
353                         }
354                         break;
355                     }
356                     case SkPath::kQuad_Verb:
357                         add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed,
358                                  this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx);
359                         break;
360                     case SkPath::kCubic_Verb: {
361                         // first pt of cubic is the pt we ended on in previous step
362                         uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
363                         uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
364                                         pts[0], pts[1], pts[2], pts[3],
365                                         srcSpaceTolSqd, &vert,
366                                         GrPathUtils::cubicPointCount(pts, srcSpaceTol));
367                         if (isIndexed) {
368                             for (uint16_t i = 0; i < numPts; ++i) {
369                                 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
370                                                              firstCPtIdx + i, &idx);
371                             }
372                         }
373                         break;
374                     }
375                     case SkPath::kClose_Verb:
376                         break;
377                     case SkPath::kDone_Verb:
378                         done = true;
379                 }
380                 first = false;
381             }
382 
383             *vertexCnt = static_cast<int>(vert - base);
384             *indexCnt = static_cast<int>(idx - idxBase);
385         return true;
386     }
387 
color() const388     GrColor color() const { return fColor; }
coverage() const389     uint8_t coverage() const { return fCoverage; }
usesLocalCoords() const390     bool usesLocalCoords() const { return fUsesLocalCoords; }
viewMatrix() const391     const SkMatrix& viewMatrix() const { return fViewMatrix; }
isHairline() const392     bool isHairline() const { return fIsHairline; }
393 
394     struct PathData {
395         SkPath fPath;
396         SkScalar fTolerance;
397     };
398 
399     GrColor fColor;
400     uint8_t fCoverage;
401     SkMatrix fViewMatrix;
402     bool fUsesLocalCoords;
403     bool fIsHairline;
404     SkSTArray<1, PathData, true> fPaths;
405 
406     typedef GrMeshDrawOp INHERITED;
407 };
408 
internalDrawPath(GrRenderTargetContext * renderTargetContext,GrPaint && paint,GrAAType aaType,const GrUserStencilSettings & userStencilSettings,const GrClip & clip,const SkMatrix & viewMatrix,const GrShape & shape,bool stencilOnly)409 bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
410                                              GrPaint&& paint,
411                                              GrAAType aaType,
412                                              const GrUserStencilSettings& userStencilSettings,
413                                              const GrClip& clip,
414                                              const SkMatrix& viewMatrix,
415                                              const GrShape& shape,
416                                              bool stencilOnly) {
417     SkASSERT(GrAAType::kCoverage != aaType);
418     SkPath path;
419     shape.asPath(&path);
420 
421     SkScalar hairlineCoverage;
422     uint8_t newCoverage = 0xff;
423     bool isHairline = false;
424     if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
425         newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
426         isHairline = true;
427     } else {
428         SkASSERT(shape.style().isSimpleFill());
429     }
430 
431     int                          passCount = 0;
432     const GrUserStencilSettings* passes[3];
433     GrDrawFace                   drawFace[3];
434     bool                         reverse = false;
435     bool                         lastPassIsBounds;
436 
437     if (isHairline) {
438         passCount = 1;
439         if (stencilOnly) {
440             passes[0] = &gDirectToStencil;
441         } else {
442             passes[0] = &userStencilSettings;
443         }
444         lastPassIsBounds = false;
445         drawFace[0] = GrDrawFace::kBoth;
446     } else {
447         if (single_pass_shape(shape)) {
448             passCount = 1;
449             if (stencilOnly) {
450                 passes[0] = &gDirectToStencil;
451             } else {
452                 passes[0] = &userStencilSettings;
453             }
454             drawFace[0] = GrDrawFace::kBoth;
455             lastPassIsBounds = false;
456         } else {
457             switch (path.getFillType()) {
458                 case SkPath::kInverseEvenOdd_FillType:
459                     reverse = true;
460                     // fallthrough
461                 case SkPath::kEvenOdd_FillType:
462                     passes[0] = &gEOStencilPass;
463                     if (stencilOnly) {
464                         passCount = 1;
465                         lastPassIsBounds = false;
466                     } else {
467                         passCount = 2;
468                         lastPassIsBounds = true;
469                         if (reverse) {
470                             passes[1] = &gInvEOColorPass;
471                         } else {
472                             passes[1] = &gEOColorPass;
473                         }
474                     }
475                     drawFace[0] = drawFace[1] = GrDrawFace::kBoth;
476                     break;
477 
478                 case SkPath::kInverseWinding_FillType:
479                     reverse = true;
480                     // fallthrough
481                 case SkPath::kWinding_FillType:
482                     if (fSeparateStencil) {
483                         if (fStencilWrapOps) {
484                             passes[0] = &gWindStencilSeparateWithWrap;
485                         } else {
486                             passes[0] = &gWindStencilSeparateNoWrap;
487                         }
488                         passCount = 2;
489                         drawFace[0] = GrDrawFace::kBoth;
490                     } else {
491                         if (fStencilWrapOps) {
492                             passes[0] = &gWindSingleStencilWithWrapInc;
493                             passes[1] = &gWindSingleStencilWithWrapDec;
494                         } else {
495                             passes[0] = &gWindSingleStencilNoWrapInc;
496                             passes[1] = &gWindSingleStencilNoWrapDec;
497                         }
498                         // which is cw and which is ccw is arbitrary.
499                         drawFace[0] = GrDrawFace::kCW;
500                         drawFace[1] = GrDrawFace::kCCW;
501                         passCount = 3;
502                     }
503                     if (stencilOnly) {
504                         lastPassIsBounds = false;
505                         --passCount;
506                     } else {
507                         lastPassIsBounds = true;
508                         drawFace[passCount-1] = GrDrawFace::kBoth;
509                         if (reverse) {
510                             passes[passCount-1] = &gInvWindColorPass;
511                         } else {
512                             passes[passCount-1] = &gWindColorPass;
513                         }
514                     }
515                     break;
516                 default:
517                     SkDEBUGFAIL("Unknown path fFill!");
518                     return false;
519             }
520         }
521     }
522 
523     SkScalar tol = GrPathUtils::kDefaultTolerance;
524     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
525 
526     SkRect devBounds;
527     GetPathDevBounds(path, renderTargetContext->width(), renderTargetContext->height(), viewMatrix,
528                      &devBounds);
529 
530     for (int p = 0; p < passCount; ++p) {
531         if (lastPassIsBounds && (p == passCount-1)) {
532             SkRect bounds;
533             SkMatrix localMatrix = SkMatrix::I();
534             if (reverse) {
535                 // draw over the dev bounds (which will be the whole dst surface for inv fill).
536                 bounds = devBounds;
537                 SkMatrix vmi;
538                 // mapRect through persp matrix may not be correct
539                 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
540                     vmi.mapRect(&bounds);
541                 } else {
542                     if (!viewMatrix.invert(&localMatrix)) {
543                         return false;
544                     }
545                 }
546             } else {
547                 bounds = path.getBounds();
548             }
549             const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
550                                                                                viewMatrix;
551             std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(
552                     paint.getColor(), viewM, bounds, nullptr, &localMatrix));
553 
554             SkASSERT(GrDrawFace::kBoth == drawFace[p]);
555             GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
556             pipelineBuilder.setDrawFace(drawFace[p]);
557             pipelineBuilder.setUserStencil(passes[p]);
558             renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
559         } else {
560             std::unique_ptr<GrMeshDrawOp> op =
561                     DefaultPathOp::Make(paint.getColor(), path, srcSpaceTol, newCoverage,
562                                         viewMatrix, isHairline, devBounds);
563             bool stencilPass = stencilOnly || passCount > 1;
564             GrPaint::MoveOrNew passPaint(paint, stencilPass);
565             if (stencilPass) {
566                 passPaint.paint().setXPFactory(GrDisableColorXPFactory::Get());
567             }
568             GrPipelineBuilder pipelineBuilder(std::move(passPaint), aaType);
569             pipelineBuilder.setDrawFace(drawFace[p]);
570             pipelineBuilder.setUserStencil(passes[p]);
571             renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
572         }
573     }
574     return true;
575 }
576 
onCanDrawPath(const CanDrawPathArgs & args) const577 bool GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
578     // This can draw any path with any simple fill style but doesn't do coverage-based antialiasing.
579     return GrAAType::kCoverage != args.fAAType &&
580            (args.fShape->style().isSimpleFill() ||
581             IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr));
582 }
583 
onDrawPath(const DrawPathArgs & args)584 bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
585     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
586                               "GrDefaultPathRenderer::onDrawPath");
587     return this->internalDrawPath(args.fRenderTargetContext,
588                                   std::move(args.fPaint),
589                                   args.fAAType,
590                                   *args.fUserStencilSettings,
591                                   *args.fClip,
592                                   *args.fViewMatrix,
593                                   *args.fShape,
594                                   false);
595 }
596 
onStencilPath(const StencilPathArgs & args)597 void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
598     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
599                               "GrDefaultPathRenderer::onStencilPath");
600     SkASSERT(!args.fShape->inverseFilled());
601 
602     GrPaint paint;
603     paint.setXPFactory(GrDisableColorXPFactory::Get());
604 
605     this->internalDrawPath(args.fRenderTargetContext, std::move(paint), args.fAAType,
606                            GrUserStencilSettings::kUnused, *args.fClip, *args.fViewMatrix,
607                            *args.fShape, true);
608 }
609 
610 ///////////////////////////////////////////////////////////////////////////////////////////////////
611 
612 #if GR_TEST_UTILS
613 
DRAW_OP_TEST_DEFINE(DefaultPathOp)614 DRAW_OP_TEST_DEFINE(DefaultPathOp) {
615     GrColor color = GrRandomColor(random);
616     SkMatrix viewMatrix = GrTest::TestMatrix(random);
617 
618     // For now just hairlines because the other types of draws require two ops.
619     // TODO we should figure out a way to combine the stencil and cover steps into one op.
620     GrStyle style(SkStrokeRec::kHairline_InitStyle);
621     SkPath path = GrTest::TestPath(random);
622 
623     // Compute srcSpaceTol
624     SkRect bounds = path.getBounds();
625     SkScalar tol = GrPathUtils::kDefaultTolerance;
626     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
627 
628     viewMatrix.mapRect(&bounds);
629     uint8_t coverage = GrRandomCoverage(random);
630     return DefaultPathOp::Make(color, path, srcSpaceTol, coverage, viewMatrix, true, bounds);
631 }
632 
633 #endif
634