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