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