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