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