blob: bbd9e4036ce8003fb8e208d5e76948a1ddee4b41 [file] [log] [blame]
bsalomon@google.com30085192011-08-19 15:42:31 +00001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/ops/GrDefaultPathRenderer.h"
Robert Phillipsbe9aff22019-02-15 11:33:22 -05009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#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"
Greg Danielf91aeb22019-06-18 09:58:02 -040015#include "src/gpu/GrAuditTrail.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#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"
Robert Phillips9028bac2020-03-10 16:19:27 -040022#include "src/gpu/GrProgramInfo.h"
Michael Ludwigaa1b6b32019-05-29 14:43:13 -040023#include "src/gpu/GrRenderTargetContextPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/gpu/GrStyle.h"
25#include "src/gpu/GrSurfaceContextPriv.h"
Michael Ludwig663afe52019-06-03 16:46:19 -040026#include "src/gpu/geometry/GrPathUtils.h"
27#include "src/gpu/geometry/GrShape.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050028#include "src/gpu/ops/GrMeshDrawOp.h"
Robert Phillips55f681f2020-02-28 08:58:15 -050029#include "src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h"
joshualitt74417822015-08-07 11:42:16 -070030
Brian Salomon15b25092017-05-08 11:10:53 -040031GrDefaultPathRenderer::GrDefaultPathRenderer() {
bsalomon@google.com289533a2011-10-27 12:34:25 +000032}
33
bsalomon@google.com30085192011-08-19 15:42:31 +000034////////////////////////////////////////////////////////////////////////////////
35// Helpers for drawPath
36
bsalomon@google.com30085192011-08-19 15:42:31 +000037#define STENCIL_OFF 0 // Always disable stencil (even when needed)
38
bsalomon8acedde2016-06-24 10:42:16 -070039static inline bool single_pass_shape(const GrShape& shape) {
bsalomon@google.com30085192011-08-19 15:42:31 +000040#if STENCIL_OFF
41 return true;
42#else
bsalomonee432412016-06-27 07:18:18 -070043 // Inverse fill is always two pass.
44 if (shape.inverseFilled()) {
45 return false;
46 }
47 // This path renderer only accepts simple fill paths or stroke paths that are either hairline
48 // or have a stroke width small enough to treat as hairline. Hairline paths are always single
49 // pass. Filled paths are single pass if they're convex.
50 if (shape.style().isSimpleFill()) {
bsalomon8acedde2016-06-24 10:42:16 -070051 return shape.knownToBeConvex();
bsalomon@google.com30085192011-08-19 15:42:31 +000052 }
bsalomonee432412016-06-27 07:18:18 -070053 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +000054#endif
55}
56
joshualitt9853cce2014-11-17 14:22:48 -080057GrPathRenderer::StencilSupport
bsalomon8acedde2016-06-24 10:42:16 -070058GrDefaultPathRenderer::onGetStencilSupport(const GrShape& shape) const {
59 if (single_pass_shape(shape)) {
bsalomon@google.com45a15f52012-12-10 19:10:17 +000060 return GrPathRenderer::kNoRestriction_StencilSupport;
61 } else {
62 return GrPathRenderer::kStencilOnly_StencilSupport;
63 }
bsalomon@google.com30085192011-08-19 15:42:31 +000064}
65
Brian Salomonee3e0ba2017-07-13 16:40:46 -040066namespace {
67
Brian Osman49b7b6f2017-06-20 14:43:58 -040068class PathGeoBuilder {
69public:
Robert Phillips9028bac2020-03-10 16:19:27 -040070 PathGeoBuilder(GrPrimitiveType primitiveType,
71 GrMeshDrawOp::Target* target,
72 SkTDArray<GrMesh*>* meshes)
Brian Salomon7eae3e02018-08-07 14:02:38 +000073 : fPrimitiveType(primitiveType)
Brian Osman49b7b6f2017-06-20 14:43:58 -040074 , fTarget(target)
75 , fVertexStride(sizeof(SkPoint))
Brian Osman49b7b6f2017-06-20 14:43:58 -040076 , fFirstIndex(0)
77 , fIndicesInChunk(0)
Robert Phillips9028bac2020-03-10 16:19:27 -040078 , fIndices(nullptr)
79 , fMeshes(meshes) {
Brian Osman49b7b6f2017-06-20 14:43:58 -040080 this->allocNewBuffers();
Brian Osmaneb86b702017-06-07 11:38:31 -040081 }
82
Brian Osman49b7b6f2017-06-20 14:43:58 -040083 ~PathGeoBuilder() {
Robert Phillips9028bac2020-03-10 16:19:27 -040084 this->createMeshAndPutBackReserve();
Brian Osman49b7b6f2017-06-20 14:43:58 -040085 }
86
87 /**
88 * Path verbs
89 */
90 void moveTo(const SkPoint& p) {
91 needSpace(1);
92
93 fSubpathIndexStart = this->currentIndex();
94 *(fCurVert++) = p;
95 }
96
97 void addLine(const SkPoint& p) {
98 needSpace(1, this->indexScale());
99
Brian Salomon763abf02018-05-01 18:49:38 +0000100 if (this->isIndexed()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400101 uint16_t prevIdx = this->currentIndex() - 1;
102 appendCountourEdgeIndices(prevIdx);
103 }
104 *(fCurVert++) = p;
105 }
106
107 void addQuad(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
108 this->needSpace(GrPathUtils::kMaxPointsPerCurve,
109 GrPathUtils::kMaxPointsPerCurve * this->indexScale());
110
111 // First pt of quad is the pt we ended on in previous step
112 uint16_t firstQPtIdx = this->currentIndex() - 1;
113 uint16_t numPts = (uint16_t)GrPathUtils::generateQuadraticPoints(
114 pts[0], pts[1], pts[2], srcSpaceTolSqd, &fCurVert,
115 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
Brian Salomon763abf02018-05-01 18:49:38 +0000116 if (this->isIndexed()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400117 for (uint16_t i = 0; i < numPts; ++i) {
118 appendCountourEdgeIndices(firstQPtIdx + i);
119 }
egdanielaf18a092015-01-05 10:22:28 -0800120 }
121 }
Brian Osman49b7b6f2017-06-20 14:43:58 -0400122
123 void addConic(SkScalar weight, const SkPoint pts[], SkScalar srcSpaceTolSqd,
124 SkScalar srcSpaceTol) {
125 SkAutoConicToQuads converter;
126 const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
127 for (int i = 0; i < converter.countQuads(); ++i) {
128 this->addQuad(quadPts + i * 2, srcSpaceTolSqd, srcSpaceTol);
129 }
130 }
131
132 void addCubic(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
133 this->needSpace(GrPathUtils::kMaxPointsPerCurve,
134 GrPathUtils::kMaxPointsPerCurve * this->indexScale());
135
136 // First pt of cubic is the pt we ended on in previous step
137 uint16_t firstCPtIdx = this->currentIndex() - 1;
138 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
139 pts[0], pts[1], pts[2], pts[3], srcSpaceTolSqd, &fCurVert,
140 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
Brian Salomon763abf02018-05-01 18:49:38 +0000141 if (this->isIndexed()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400142 for (uint16_t i = 0; i < numPts; ++i) {
143 appendCountourEdgeIndices(firstCPtIdx + i);
144 }
145 }
146 }
147
148 void addPath(const SkPath& path, SkScalar srcSpaceTol) {
149 SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
150
151 SkPath::Iter iter(path, false);
152 SkPoint pts[4];
153
154 bool done = false;
155 while (!done) {
Mike Reedba7e9a62019-08-16 13:30:34 -0400156 SkPath::Verb verb = iter.next(pts);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400157 switch (verb) {
158 case SkPath::kMove_Verb:
159 this->moveTo(pts[0]);
160 break;
161 case SkPath::kLine_Verb:
162 this->addLine(pts[1]);
163 break;
164 case SkPath::kConic_Verb:
165 this->addConic(iter.conicWeight(), pts, srcSpaceTolSqd, srcSpaceTol);
166 break;
167 case SkPath::kQuad_Verb:
168 this->addQuad(pts, srcSpaceTolSqd, srcSpaceTol);
169 break;
170 case SkPath::kCubic_Verb:
171 this->addCubic(pts, srcSpaceTolSqd, srcSpaceTol);
172 break;
173 case SkPath::kClose_Verb:
174 break;
175 case SkPath::kDone_Verb:
176 done = true;
177 }
178 }
179 }
180
181 static bool PathHasMultipleSubpaths(const SkPath& path) {
182 bool first = true;
183
184 SkPath::Iter iter(path, false);
185 SkPath::Verb verb;
186
187 SkPoint pts[4];
Mike Reedba7e9a62019-08-16 13:30:34 -0400188 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400189 if (SkPath::kMove_Verb == verb && !first) {
190 return true;
191 }
192 first = false;
193 }
194 return false;
195 }
196
197private:
198 /**
199 * Derived properties
200 * TODO: Cache some of these for better performance, rather than re-computing?
201 */
Brian Salomon763abf02018-05-01 18:49:38 +0000202 bool isIndexed() const {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000203 return GrPrimitiveType::kLines == fPrimitiveType ||
204 GrPrimitiveType::kTriangles == fPrimitiveType;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400205 }
206 bool isHairline() const {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000207 return GrPrimitiveType::kLines == fPrimitiveType ||
208 GrPrimitiveType::kLineStrip == fPrimitiveType;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400209 }
210 int indexScale() const {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000211 switch (fPrimitiveType) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400212 case GrPrimitiveType::kLines:
213 return 2;
214 case GrPrimitiveType::kTriangles:
215 return 3;
216 default:
217 return 0;
218 }
219 }
220
221 uint16_t currentIndex() const { return fCurVert - fVertices; }
222
Brian Osman49b7b6f2017-06-20 14:43:58 -0400223 // Allocate vertex and (possibly) index buffers
224 void allocNewBuffers() {
225 // Ensure that we always get enough verts for a worst-case quad/cubic, plus leftover points
226 // from previous mesh piece (up to two verts to continue fanning). If we can't get that
227 // many, ask for a much larger number. This needs to be fairly big to handle quads/cubics,
228 // which have a worst-case of 1k points.
229 static const int kMinVerticesPerChunk = GrPathUtils::kMaxPointsPerCurve + 2;
230 static const int kFallbackVerticesPerChunk = 16384;
231
232 fVertices = static_cast<SkPoint*>(fTarget->makeVertexSpaceAtLeast(fVertexStride,
233 kMinVerticesPerChunk,
234 kFallbackVerticesPerChunk,
235 &fVertexBuffer,
236 &fFirstVertex,
237 &fVerticesInChunk));
238
Brian Salomon763abf02018-05-01 18:49:38 +0000239 if (this->isIndexed()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400240 // Similar to above: Ensure we get enough indices for one worst-case quad/cubic.
241 // No extra indices are needed for stitching, though. If we can't get that many, ask
242 // for enough to match our large vertex request.
243 const int kMinIndicesPerChunk = GrPathUtils::kMaxPointsPerCurve * this->indexScale();
244 const int kFallbackIndicesPerChunk = kFallbackVerticesPerChunk * this->indexScale();
245
246 fIndices = fTarget->makeIndexSpaceAtLeast(kMinIndicesPerChunk, kFallbackIndicesPerChunk,
247 &fIndexBuffer, &fFirstIndex,
248 &fIndicesInChunk);
249 }
Brian Osman3902d402017-06-20 15:36:31 -0400250
251 fCurVert = fVertices;
252 fCurIdx = fIndices;
253 fSubpathIndexStart = 0;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400254 }
255
256 void appendCountourEdgeIndices(uint16_t edgeV0Idx) {
257 // When drawing lines we're appending line segments along the countour. When applying the
258 // other fill rules we're drawing triangle fans around the start of the current (sub)path.
259 if (!this->isHairline()) {
260 *(fCurIdx++) = fSubpathIndexStart;
261 }
262 *(fCurIdx++) = edgeV0Idx;
263 *(fCurIdx++) = edgeV0Idx + 1;
264 }
265
266 // Emits a single draw with all accumulated vertex/index data
Robert Phillips9028bac2020-03-10 16:19:27 -0400267 void createMeshAndPutBackReserve() {
Brian Osman3902d402017-06-20 15:36:31 -0400268 int vertexCount = fCurVert - fVertices;
269 int indexCount = fCurIdx - fIndices;
270 SkASSERT(vertexCount <= fVerticesInChunk);
271 SkASSERT(indexCount <= fIndicesInChunk);
272
Robert Phillips9028bac2020-03-10 16:19:27 -0400273 GrMesh* mesh = nullptr;
Brian Salomon763abf02018-05-01 18:49:38 +0000274 if (this->isIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
Robert Phillips9028bac2020-03-10 16:19:27 -0400275 mesh = fTarget->allocMesh();
Brian Salomon763abf02018-05-01 18:49:38 +0000276 if (!this->isIndexed()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000277 mesh->setNonIndexedNonInstanced(vertexCount);
Brian Salomon763abf02018-05-01 18:49:38 +0000278 } else {
Brian Salomon12d22642019-01-29 14:38:50 -0500279 mesh->setIndexed(std::move(fIndexBuffer), indexCount, fFirstIndex, 0,
280 vertexCount - 1, GrPrimitiveRestart::kNo);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400281 }
Brian Salomon12d22642019-01-29 14:38:50 -0500282 mesh->setVertexData(std::move(fVertexBuffer), fFirstVertex);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400283 }
Brian Osman3902d402017-06-20 15:36:31 -0400284
285 fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
286 fTarget->putBackVertices((size_t)(fVerticesInChunk - vertexCount), fVertexStride);
Robert Phillips9028bac2020-03-10 16:19:27 -0400287
288 if (mesh) {
289 fMeshes->push_back(mesh);
290 }
Brian Osman49b7b6f2017-06-20 14:43:58 -0400291 }
292
293 void needSpace(int vertsNeeded, int indicesNeeded = 0) {
294 if (fCurVert + vertsNeeded > fVertices + fVerticesInChunk ||
295 fCurIdx + indicesNeeded > fIndices + fIndicesInChunk) {
296 // We are about to run out of space (possibly)
297
298 // To maintain continuity, we need to remember one or two points from the current mesh.
299 // Lines only need the last point, fills need the first point from the current contour.
300 // We always grab both here, and append the ones we need at the end of this process.
301 SkPoint lastPt = *(fCurVert - 1);
Brian Osman3902d402017-06-20 15:36:31 -0400302 SkASSERT(fSubpathIndexStart < fVerticesInChunk);
303 SkPoint subpathStartPt = fVertices[fSubpathIndexStart];
Brian Osman49b7b6f2017-06-20 14:43:58 -0400304
Brian Osman3902d402017-06-20 15:36:31 -0400305 // Draw the mesh we've accumulated, and put back any unused space
Robert Phillips9028bac2020-03-10 16:19:27 -0400306 this->createMeshAndPutBackReserve();
Brian Osman49b7b6f2017-06-20 14:43:58 -0400307
Brian Osman3902d402017-06-20 15:36:31 -0400308 // Get new buffers
Brian Osman49b7b6f2017-06-20 14:43:58 -0400309 this->allocNewBuffers();
310
Brian Osman49b7b6f2017-06-20 14:43:58 -0400311 // Append copies of the points we saved so the two meshes will weld properly
312 if (!this->isHairline()) {
313 *(fCurVert++) = subpathStartPt;
314 }
315 *(fCurVert++) = lastPt;
316 }
317 }
318
Brian Salomon7eae3e02018-08-07 14:02:38 +0000319 GrPrimitiveType fPrimitiveType;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400320 GrMeshDrawOp::Target* fTarget;
321 size_t fVertexStride;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400322
Brian Salomon12d22642019-01-29 14:38:50 -0500323 sk_sp<const GrBuffer> fVertexBuffer;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400324 int fFirstVertex;
325 int fVerticesInChunk;
326 SkPoint* fVertices;
327 SkPoint* fCurVert;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400328
Brian Salomon12d22642019-01-29 14:38:50 -0500329 sk_sp<const GrBuffer> fIndexBuffer;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400330 int fFirstIndex;
331 int fIndicesInChunk;
332 uint16_t* fIndices;
333 uint16_t* fCurIdx;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400334 uint16_t fSubpathIndexStart;
Robert Phillips9028bac2020-03-10 16:19:27 -0400335
336 SkTDArray<GrMesh*>* fMeshes;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400337};
egdanielaf18a092015-01-05 10:22:28 -0800338
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400339class DefaultPathOp final : public GrMeshDrawOp {
340private:
341 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
342
joshualitt332c7292015-02-23 08:44:31 -0800343public:
Brian Salomon25a88092016-12-01 09:36:50 -0500344 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700345
Robert Phillipsb97da532019-02-12 15:24:12 -0500346 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400347 GrPaint&& paint,
348 const SkPath& path,
349 SkScalar tolerance,
350 uint8_t coverage,
351 const SkMatrix& viewMatrix,
352 bool isHairline,
353 GrAAType aaType,
354 const SkRect& devBounds,
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400355 const GrUserStencilSettings* stencilSettings) {
Robert Phillips7c525e62018-06-12 10:11:12 -0400356 return Helper::FactoryHelper<DefaultPathOp>(context, std::move(paint), path, tolerance,
357 coverage, viewMatrix, isHairline, aaType,
358 devBounds, stencilSettings);
bsalomon@google.com30085192011-08-19 15:42:31 +0000359 }
360
Brian Salomon780dad12016-12-15 18:08:40 -0500361 const char* name() const override { return "DefaultPathOp"; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000362
Chris Dalton1706cbf2019-05-21 19:35:29 -0600363 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips9028bac2020-03-10 16:19:27 -0400364 if (fProgramInfo) {
365 fProgramInfo->visitProxies(func);
366 } else {
367 fHelper.visitProxies(func);
368 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400369 }
370
Brian Osman9a390ac2018-11-12 09:47:48 -0500371#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -0500372 SkString dumpInfo() const override {
373 SkString string;
Brian Osmancf860852018-10-31 14:04:39 -0400374 string.appendf("Color: 0x%08x Count: %d\n", fColor.toBytes_RGBA(), fPaths.count());
Brian Salomon780dad12016-12-15 18:08:40 -0500375 for (const auto& path : fPaths) {
376 string.appendf("Tolerance: %.2f\n", path.fTolerance);
Brian Salomon7c3e7182016-12-01 09:35:30 -0500377 }
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400378 string += fHelper.dumpInfo();
379 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -0500380 return string;
381 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500382#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -0500383
Brian Osmancf860852018-10-31 14:04:39 -0400384 DefaultPathOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color, const SkPath& path,
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400385 SkScalar tolerance, uint8_t coverage, const SkMatrix& viewMatrix, bool isHairline,
386 GrAAType aaType, const SkRect& devBounds,
387 const GrUserStencilSettings* stencilSettings)
Brian Salomon780dad12016-12-15 18:08:40 -0500388 : INHERITED(ClassID())
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400389 , fHelper(helperArgs, aaType, stencilSettings)
Brian Salomon780dad12016-12-15 18:08:40 -0500390 , fColor(color)
391 , fCoverage(coverage)
392 , fViewMatrix(viewMatrix)
393 , fIsHairline(isHairline) {
394 fPaths.emplace_back(PathData{path, tolerance});
joshualitt332c7292015-02-23 08:44:31 -0800395
Greg Daniel5faf4742019-10-01 15:14:44 -0400396 HasAABloat aaBloat = (aaType == GrAAType::kNone) ? HasAABloat ::kNo : HasAABloat::kYes;
397 this->setBounds(devBounds, aaBloat,
398 isHairline ? IsHairline::kYes : IsHairline::kNo);
Brian Salomon780dad12016-12-15 18:08:40 -0500399 }
400
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400401 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
402
Chris Dalton6ce447a2019-06-23 18:07:38 -0600403 GrProcessorSet::Analysis finalize(
404 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
405 GrClampType clampType) override {
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400406 GrProcessorAnalysisCoverage gpCoverage =
407 this->coverage() == 0xFF ? GrProcessorAnalysisCoverage::kNone
408 : GrProcessorAnalysisCoverage::kSingleChannel;
Brian Osman8fa7ab42019-03-18 10:22:42 -0400409 // This Op uses uniform (not vertex) color, so doesn't need to track wide color.
410 return fHelper.finalizeProcessors(
Chris Dalton6ce447a2019-06-23 18:07:38 -0600411 caps, clip, hasMixedSampledCoverage, clampType, gpCoverage, &fColor, nullptr);
Brian Salomon92aee3d2016-12-21 09:20:25 -0500412 }
413
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400414private:
Robert Phillips9028bac2020-03-10 16:19:27 -0400415 GrPrimitiveType primType() const {
416 if (this->isHairline()) {
417 int instanceCount = fPaths.count();
418
419 // We avoid indices when we have a single hairline contour.
420 bool isIndexed = instanceCount > 1 ||
421 PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
422
423 return isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
424 }
425
426 return GrPrimitiveType::kTriangles;
427 }
428
429 GrProgramInfo* createProgramInfo(const GrCaps* caps,
430 SkArenaAlloc* arena,
431 const GrSurfaceProxyView* outputView,
432 GrAppliedClip&& appliedClip,
433 const GrXferProcessor::DstProxyView& dstProxyView) {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500434 GrGeometryProcessor* gp;
joshualittdf0c5572015-08-03 11:35:28 -0700435 {
436 using namespace GrDefaultGeoProcFactory;
437 Color color(this->color());
438 Coverage coverage(this->coverage());
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400439 LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
440 : LocalCoords::kUnused_Type);
Robert Phillips9028bac2020-03-10 16:19:27 -0400441 gp = GrDefaultGeoProcFactory::Make(arena,
442 caps->shaderCaps(),
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400443 color,
444 coverage,
445 localCoords,
446 this->viewMatrix());
joshualittdf0c5572015-08-03 11:35:28 -0700447 }
joshualitt332c7292015-02-23 08:44:31 -0800448
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500449 SkASSERT(gp->vertexStride() == sizeof(SkPoint));
joshualitt332c7292015-02-23 08:44:31 -0800450
Robert Phillips9028bac2020-03-10 16:19:27 -0400451 return fHelper.createProgramInfoWithStencil(caps, arena, outputView, std::move(appliedClip),
452 dstProxyView, gp, this->primType());
Brian Salomon763abf02018-05-01 18:49:38 +0000453
Robert Phillips9028bac2020-03-10 16:19:27 -0400454 }
Brian Osman7f95dfc2017-06-09 14:43:49 +0000455
Robert Phillips9028bac2020-03-10 16:19:27 -0400456 GrProgramInfo* createProgramInfo(Target* target) {
457 return this->createProgramInfo(&target->caps(),
458 target->allocator(),
459 target->outputView(),
460 target->detachAppliedClip(),
461 target->dstProxyView());
462 }
463
464 void onPrePrepareDraws(GrRecordingContext* context,
465 const GrSurfaceProxyView* outputView,
466 GrAppliedClip* clip,
467 const GrXferProcessor::DstProxyView& dstProxyView) override {
468 SkArenaAlloc* arena = context->priv().recordTimeAllocator();
469
470 // This is equivalent to a GrOpFlushState::detachAppliedClip
471 GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip();
472
473 fProgramInfo = this->createProgramInfo(context->priv().caps(), arena, outputView,
474 std::move(appliedClip), dstProxyView);
475
476 context->priv().recordProgramInfo(fProgramInfo);
477 }
478
479 void onPrepareDraws(Target* target) override {
480 PathGeoBuilder pathGeoBuilder(this->primType(), target, &fMeshes);
Brian Osman7f95dfc2017-06-09 14:43:49 +0000481
482 // fill buffers
Robert Phillips9028bac2020-03-10 16:19:27 -0400483 for (int i = 0; i < fPaths.count(); i++) {
Brian Salomon763abf02018-05-01 18:49:38 +0000484 const PathData& args = fPaths[i];
485 pathGeoBuilder.addPath(args.fPath, args.fTolerance);
Brian Osman7f95dfc2017-06-09 14:43:49 +0000486 }
joshualitt332c7292015-02-23 08:44:31 -0800487 }
488
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700489 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips9028bac2020-03-10 16:19:27 -0400490 if (!fProgramInfo) {
491 fProgramInfo = this->createProgramInfo(flushState);
492 }
Robert Phillips3968fcb2019-12-05 16:40:31 -0500493
Robert Phillips9028bac2020-03-10 16:19:27 -0400494 if (!fProgramInfo || !fMeshes.count()) {
495 return;
496 }
497
498 flushState->opsRenderPass()->bindPipeline(*fProgramInfo, chainBounds);
499 for (int i = 0; i < fMeshes.count(); ++i) {
500 flushState->opsRenderPass()->drawMeshes(*fProgramInfo, fMeshes[i], 1);
501 }
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700502 }
503
Michael Ludwig28b0c5d2019-12-19 14:51:00 -0500504 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
505 const GrCaps& caps) override {
Brian Salomon780dad12016-12-15 18:08:40 -0500506 DefaultPathOp* that = t->cast<DefaultPathOp>();
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400507 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000508 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -0700509 }
510
joshualitt332c7292015-02-23 08:44:31 -0800511 if (this->color() != that->color()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000512 return CombineResult::kCannotCombine;
joshualitt332c7292015-02-23 08:44:31 -0800513 }
514
515 if (this->coverage() != that->coverage()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000516 return CombineResult::kCannotCombine;
joshualitt332c7292015-02-23 08:44:31 -0800517 }
518
Mike Reed2c383152019-12-18 16:47:47 -0500519 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000520 return CombineResult::kCannotCombine;
joshualitt332c7292015-02-23 08:44:31 -0800521 }
522
523 if (this->isHairline() != that->isHairline()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000524 return CombineResult::kCannotCombine;
joshualitt332c7292015-02-23 08:44:31 -0800525 }
526
Brian Salomon780dad12016-12-15 18:08:40 -0500527 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
Brian Salomon7eae3e02018-08-07 14:02:38 +0000528 return CombineResult::kMerged;
joshualitt332c7292015-02-23 08:44:31 -0800529 }
530
Brian Osmancf860852018-10-31 14:04:39 -0400531 const SkPMColor4f& color() const { return fColor; }
Brian Salomon780dad12016-12-15 18:08:40 -0500532 uint8_t coverage() const { return fCoverage; }
Brian Salomon780dad12016-12-15 18:08:40 -0500533 const SkMatrix& viewMatrix() const { return fViewMatrix; }
534 bool isHairline() const { return fIsHairline; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000535
Brian Salomon780dad12016-12-15 18:08:40 -0500536 struct PathData {
bsalomon0432dd62016-06-30 07:19:27 -0700537 SkPath fPath;
538 SkScalar fTolerance;
539 };
540
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400541 SkSTArray<1, PathData, true> fPaths;
542 Helper fHelper;
Brian Osmancf860852018-10-31 14:04:39 -0400543 SkPMColor4f fColor;
Brian Salomon780dad12016-12-15 18:08:40 -0500544 uint8_t fCoverage;
545 SkMatrix fViewMatrix;
Brian Salomon780dad12016-12-15 18:08:40 -0500546 bool fIsHairline;
reed1b55a962015-09-17 20:16:13 -0700547
Robert Phillips9028bac2020-03-10 16:19:27 -0400548 SkTDArray<GrMesh*> fMeshes;
549 GrProgramInfo* fProgramInfo = nullptr;
550
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400551 typedef GrMeshDrawOp INHERITED;
joshualitt332c7292015-02-23 08:44:31 -0800552};
bsalomon@google.com30085192011-08-19 15:42:31 +0000553
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400554} // anonymous namespace
555
Brian Osman11052242016-10-27 14:47:55 -0400556bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
Brian Salomon82f44312017-01-11 13:42:54 -0500557 GrPaint&& paint,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500558 GrAAType aaType,
robertphillipsd2b6d642016-07-21 08:55:08 -0700559 const GrUserStencilSettings& userStencilSettings,
cdalton862cff32016-05-12 15:09:48 -0700560 const GrClip& clip,
joshualitt8059eb92014-12-29 15:10:07 -0800561 const SkMatrix& viewMatrix,
bsalomon8acedde2016-06-24 10:42:16 -0700562 const GrShape& shape,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000563 bool stencilOnly) {
Robert Phillipsb97da532019-02-12 15:24:12 -0500564 auto context = renderTargetContext->surfPriv().getContext();
Robert Phillips7c525e62018-06-12 10:11:12 -0400565
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500566 SkASSERT(GrAAType::kCoverage != aaType);
bsalomon8acedde2016-06-24 10:42:16 -0700567 SkPath path;
568 shape.asPath(&path);
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000569
570 SkScalar hairlineCoverage;
joshualitt2e3b3e32014-12-09 13:31:14 -0800571 uint8_t newCoverage = 0xff;
bsalomon6663acf2016-05-10 09:14:17 -0700572 bool isHairline = false;
bsalomon8acedde2016-06-24 10:42:16 -0700573 if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800574 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
bsalomon6663acf2016-05-10 09:14:17 -0700575 isHairline = true;
576 } else {
bsalomon8acedde2016-06-24 10:42:16 -0700577 SkASSERT(shape.style().isSimpleFill());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000578 }
579
cdalton93a379b2016-05-11 13:58:08 -0700580 int passCount = 0;
Brian Salomonf0861672017-05-08 11:10:10 -0400581 const GrUserStencilSettings* passes[2];
cdalton93a379b2016-05-11 13:58:08 -0700582 bool reverse = false;
583 bool lastPassIsBounds;
bsalomon@google.com30085192011-08-19 15:42:31 +0000584
joshualitt332c7292015-02-23 08:44:31 -0800585 if (isHairline) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000586 passCount = 1;
587 if (stencilOnly) {
588 passes[0] = &gDirectToStencil;
589 } else {
robertphillipsd2b6d642016-07-21 08:55:08 -0700590 passes[0] = &userStencilSettings;
bsalomon@google.com30085192011-08-19 15:42:31 +0000591 }
592 lastPassIsBounds = false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000593 } else {
bsalomon8acedde2016-06-24 10:42:16 -0700594 if (single_pass_shape(shape)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000595 passCount = 1;
596 if (stencilOnly) {
597 passes[0] = &gDirectToStencil;
598 } else {
robertphillipsd2b6d642016-07-21 08:55:08 -0700599 passes[0] = &userStencilSettings;
bsalomon@google.com30085192011-08-19 15:42:31 +0000600 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000601 lastPassIsBounds = false;
602 } else {
Mike Reedcf0e3c62019-12-03 16:26:15 -0500603 switch (path.getFillType()) {
Mike Reed7d34dc72019-11-26 12:17:17 -0500604 case SkPathFillType::kInverseEvenOdd:
bsalomon@google.com30085192011-08-19 15:42:31 +0000605 reverse = true;
606 // fallthrough
Mike Reed7d34dc72019-11-26 12:17:17 -0500607 case SkPathFillType::kEvenOdd:
bsalomon@google.com30085192011-08-19 15:42:31 +0000608 passes[0] = &gEOStencilPass;
609 if (stencilOnly) {
610 passCount = 1;
611 lastPassIsBounds = false;
612 } else {
613 passCount = 2;
614 lastPassIsBounds = true;
615 if (reverse) {
616 passes[1] = &gInvEOColorPass;
617 } else {
618 passes[1] = &gEOColorPass;
619 }
620 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000621 break;
622
Mike Reed7d34dc72019-11-26 12:17:17 -0500623 case SkPathFillType::kInverseWinding:
bsalomon@google.com30085192011-08-19 15:42:31 +0000624 reverse = true;
625 // fallthrough
Mike Reed7d34dc72019-11-26 12:17:17 -0500626 case SkPathFillType::kWinding:
Brian Salomon15b25092017-05-08 11:10:53 -0400627 passes[0] = &gWindStencilPass;
Brian Salomonf0861672017-05-08 11:10:10 -0400628 passCount = 2;
bsalomon@google.com30085192011-08-19 15:42:31 +0000629 if (stencilOnly) {
630 lastPassIsBounds = false;
631 --passCount;
632 } else {
633 lastPassIsBounds = true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000634 if (reverse) {
635 passes[passCount-1] = &gInvWindColorPass;
636 } else {
637 passes[passCount-1] = &gWindColorPass;
638 }
639 }
640 break;
641 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000642 SkDEBUGFAIL("Unknown path fFill!");
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000643 return false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000644 }
645 }
646 }
647
senorblanco2b4bb072015-04-22 13:45:18 -0700648 SkScalar tol = GrPathUtils::kDefaultTolerance;
joshualitt332c7292015-02-23 08:44:31 -0800649 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
650
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000651 SkRect devBounds;
Robert Phillipse9462dd2019-10-23 12:41:29 -0400652 GetPathDevBounds(path, renderTargetContext->asRenderTargetProxy()->backingStoreDimensions(),
Robert Phillipsec325342017-10-30 18:02:48 +0000653 viewMatrix, &devBounds);
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000654
bsalomon@google.com30085192011-08-19 15:42:31 +0000655 for (int p = 0; p < passCount; ++p) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000656 if (lastPassIsBounds && (p == passCount-1)) {
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000657 SkRect bounds;
joshualittd27f73e2014-12-29 07:43:36 -0800658 SkMatrix localMatrix = SkMatrix::I();
bsalomon@google.com30085192011-08-19 15:42:31 +0000659 if (reverse) {
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000660 // draw over the dev bounds (which will be the whole dst surface for inv fill).
661 bounds = devBounds;
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000662 SkMatrix vmi;
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000663 // mapRect through persp matrix may not be correct
joshualitt8059eb92014-12-29 15:10:07 -0800664 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000665 vmi.mapRect(&bounds);
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000666 } else {
joshualittd27f73e2014-12-29 07:43:36 -0800667 if (!viewMatrix.invert(&localMatrix)) {
668 return false;
669 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000670 }
671 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000672 bounds = path.getBounds();
bsalomon@google.com30085192011-08-19 15:42:31 +0000673 }
joshualitt8059eb92014-12-29 15:10:07 -0800674 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
675 viewMatrix;
Michael Ludwig72ab3462018-12-10 12:43:36 -0500676 // This is a non-coverage aa rect op since we assert aaType != kCoverage at the start
Mike Klein16885072018-12-11 09:54:31 -0500677 assert_alive(paint);
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400678 renderTargetContext->priv().stencilRect(clip, passes[p], std::move(paint),
679 GrAA(aaType == GrAAType::kMSAA), viewM, bounds, &localMatrix);
robertphillips976f5f02016-06-03 10:59:20 -0700680 } else {
Brian Salomond4652ca2017-01-13 12:11:36 -0500681 bool stencilPass = stencilOnly || passCount > 1;
Brian Salomonb74ef032017-08-10 12:46:01 -0400682 std::unique_ptr<GrDrawOp> op;
Brian Salomond4652ca2017-01-13 12:11:36 -0500683 if (stencilPass) {
Brian Salomonb74ef032017-08-10 12:46:01 -0400684 GrPaint stencilPaint;
685 stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
Robert Phillips7c525e62018-06-12 10:11:12 -0400686 op = DefaultPathOp::Make(context, std::move(stencilPaint), path, srcSpaceTol,
687 newCoverage, viewMatrix, isHairline, aaType, devBounds,
688 passes[p]);
Brian Salomonb74ef032017-08-10 12:46:01 -0400689 } else {
Mike Klein16885072018-12-11 09:54:31 -0500690 assert_alive(paint);
Robert Phillips7c525e62018-06-12 10:11:12 -0400691 op = DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, newCoverage,
Brian Salomonb74ef032017-08-10 12:46:01 -0400692 viewMatrix, isHairline, aaType, devBounds, passes[p]);
Brian Salomond4652ca2017-01-13 12:11:36 -0500693 }
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400694 renderTargetContext->addDrawOp(clip, std::move(op));
bsalomon@google.com30085192011-08-19 15:42:31 +0000695 }
696 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000697 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000698}
699
Chris Dalton5ed44232017-09-07 13:22:46 -0600700GrPathRenderer::CanDrawPath
701GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
Chris Dalton09e56892019-03-13 00:22:01 -0600702 bool isHairline = IsStrokeHairlineOrEquivalent(
703 args.fShape->style(), *args.fViewMatrix, nullptr);
Eric Karl5c779752017-05-08 12:02:07 -0700704 // If we aren't a single_pass_shape or hairline, we require stencil buffers.
Greg Danielbe7fc462019-01-03 16:40:42 -0500705 if (!(single_pass_shape(*args.fShape) || isHairline) &&
706 (args.fCaps->avoidStencilBuffers() || args.fTargetIsWrappedVkSecondaryCB)) {
Chris Dalton5ed44232017-09-07 13:22:46 -0600707 return CanDrawPath::kNo;
Eric Karl5c779752017-05-08 12:02:07 -0700708 }
Chris Dalton09e56892019-03-13 00:22:01 -0600709 // If antialiasing is required, we only support MSAA.
Chris Dalton6ce447a2019-06-23 18:07:38 -0600710 if (GrAAType::kNone != args.fAAType && GrAAType::kMSAA != args.fAAType) {
Chris Dalton09e56892019-03-13 00:22:01 -0600711 return CanDrawPath::kNo;
712 }
713 // This can draw any path with any simple fill style.
714 if (!args.fShape->style().isSimpleFill() && !isHairline) {
Chris Dalton5ed44232017-09-07 13:22:46 -0600715 return CanDrawPath::kNo;
716 }
717 // This is the fallback renderer for when a path is too complicated for the others to draw.
718 return CanDrawPath::kAsBackup;
bsalomon@google.com30085192011-08-19 15:42:31 +0000719}
720
bsalomon0aff2fa2015-07-31 06:48:27 -0700721bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400722 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700723 "GrDefaultPathRenderer::onDrawPath");
Chris Dalton6ce447a2019-06-23 18:07:38 -0600724 GrAAType aaType = (GrAAType::kNone != args.fAAType) ? GrAAType::kMSAA : GrAAType::kNone;
Chris Dalton09e56892019-03-13 00:22:01 -0600725
726 return this->internalDrawPath(
727 args.fRenderTargetContext, std::move(args.fPaint), aaType, *args.fUserStencilSettings,
728 *args.fClip, *args.fViewMatrix, *args.fShape, false);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000729}
730
bsalomon0aff2fa2015-07-31 06:48:27 -0700731void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400732 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700733 "GrDefaultPathRenderer::onStencilPath");
bsalomon8acedde2016-06-24 10:42:16 -0700734 SkASSERT(!args.fShape->inverseFilled());
robertphillips976f5f02016-06-03 10:59:20 -0700735
736 GrPaint paint;
Brian Salomona1633922017-01-09 11:46:10 -0500737 paint.setXPFactory(GrDisableColorXPFactory::Get());
robertphillips976f5f02016-06-03 10:59:20 -0700738
Chris Dalton09e56892019-03-13 00:22:01 -0600739 auto aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
740
741 this->internalDrawPath(
742 args.fRenderTargetContext, std::move(paint), aaType, GrUserStencilSettings::kUnused,
743 *args.fClip, *args.fViewMatrix, *args.fShape, true);
bsalomon@google.com30085192011-08-19 15:42:31 +0000744}
joshualitt622d3ad2015-05-07 08:13:11 -0700745
746///////////////////////////////////////////////////////////////////////////////////////////////////
747
Hal Canary6f6961e2017-01-31 13:50:44 -0500748#if GR_TEST_UTILS
joshualitt622d3ad2015-05-07 08:13:11 -0700749
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400750GR_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
joshualitt622d3ad2015-05-07 08:13:11 -0700751 SkMatrix viewMatrix = GrTest::TestMatrix(random);
752
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500753 // For now just hairlines because the other types of draws require two ops.
754 // TODO we should figure out a way to combine the stencil and cover steps into one op.
bsalomon6663acf2016-05-10 09:14:17 -0700755 GrStyle style(SkStrokeRec::kHairline_InitStyle);
joshualitt622d3ad2015-05-07 08:13:11 -0700756 SkPath path = GrTest::TestPath(random);
757
758 // Compute srcSpaceTol
759 SkRect bounds = path.getBounds();
760 SkScalar tol = GrPathUtils::kDefaultTolerance;
761 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
762
joshualitt622d3ad2015-05-07 08:13:11 -0700763 viewMatrix.mapRect(&bounds);
764 uint8_t coverage = GrRandomCoverage(random);
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400765 GrAAType aaType = GrAAType::kNone;
Chris Dalton6ce447a2019-06-23 18:07:38 -0600766 if (numSamples > 1 && random->nextBool()) {
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400767 aaType = GrAAType::kMSAA;
768 }
Robert Phillips7c525e62018-06-12 10:11:12 -0400769 return DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, coverage, viewMatrix,
770 true, aaType, bounds, GrGetRandomStencil(random, context));
joshualitt622d3ad2015-05-07 08:13:11 -0700771}
772
773#endif