blob: 07e40f045452275c718192b6625bb5e1dd14a2f5 [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
8#include "GrDefaultPathRenderer.h"
bsalomon@google.com30085192011-08-19 15:42:31 +00009#include "GrContext.h"
joshualitt5478d422014-11-14 16:00:38 -080010#include "GrDefaultGeoProcFactory.h"
Brian Salomon5ec9def2016-12-20 15:34:05 -050011#include "GrDrawOpTest.h"
csmartdalton02fa32c2016-08-19 13:29:27 -070012#include "GrFixedClip.h"
egdaniel0e1853c2016-03-17 11:35:45 -070013#include "GrMesh.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050014#include "GrOpFlushState.h"
bsalomon@google.com30085192011-08-19 15:42:31 +000015#include "GrPathUtils.h"
Brian Salomon653f42f2018-07-10 10:07:31 -040016#include "GrShape.h"
Brian Salomonee3e0ba2017-07-13 16:40:46 -040017#include "GrSimpleMeshDrawOpHelper.h"
Brian Salomon653f42f2018-07-10 10:07:31 -040018#include "GrStyle.h"
Robert Phillips7c525e62018-06-12 10:11:12 -040019#include "GrSurfaceContextPriv.h"
egdanielaf18a092015-01-05 10:22:28 -080020#include "SkGeometry.h"
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000021#include "SkString.h"
sugoi@google.com5f74cf82012-12-17 21:16:45 +000022#include "SkStrokeRec.h"
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +000023#include "SkTLazy.h"
commit-bot@chromium.org933e65d2014-03-20 20:00:24 +000024#include "SkTraceEvent.h"
Brian Salomon89527432016-12-16 09:52:16 -050025#include "ops/GrMeshDrawOp.h"
Brian Salomonbaaf4392017-06-15 09:59:23 -040026#include "ops/GrRectOpFactory.h"
joshualitt74417822015-08-07 11:42:16 -070027
Brian Salomon15b25092017-05-08 11:10:53 -040028GrDefaultPathRenderer::GrDefaultPathRenderer() {
bsalomon@google.com289533a2011-10-27 12:34:25 +000029}
30
bsalomon@google.com30085192011-08-19 15:42:31 +000031////////////////////////////////////////////////////////////////////////////////
32// Helpers for drawPath
33
bsalomon@google.com30085192011-08-19 15:42:31 +000034#define STENCIL_OFF 0 // Always disable stencil (even when needed)
35
bsalomon8acedde2016-06-24 10:42:16 -070036static inline bool single_pass_shape(const GrShape& shape) {
bsalomon@google.com30085192011-08-19 15:42:31 +000037#if STENCIL_OFF
38 return true;
39#else
bsalomonee432412016-06-27 07:18:18 -070040 // 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()) {
bsalomon8acedde2016-06-24 10:42:16 -070048 return shape.knownToBeConvex();
bsalomon@google.com30085192011-08-19 15:42:31 +000049 }
bsalomonee432412016-06-27 07:18:18 -070050 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +000051#endif
52}
53
joshualitt9853cce2014-11-17 14:22:48 -080054GrPathRenderer::StencilSupport
bsalomon8acedde2016-06-24 10:42:16 -070055GrDefaultPathRenderer::onGetStencilSupport(const GrShape& shape) const {
56 if (single_pass_shape(shape)) {
bsalomon@google.com45a15f52012-12-10 19:10:17 +000057 return GrPathRenderer::kNoRestriction_StencilSupport;
58 } else {
59 return GrPathRenderer::kStencilOnly_StencilSupport;
60 }
bsalomon@google.com30085192011-08-19 15:42:31 +000061}
62
Brian Salomonee3e0ba2017-07-13 16:40:46 -040063namespace {
64
Brian Osman49b7b6f2017-06-20 14:43:58 -040065class PathGeoBuilder {
66public:
Brian Salomon763abf02018-05-01 18:49:38 +000067 PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
Brian Salomon49348902018-06-26 09:12:38 -040068 GrGeometryProcessor* geometryProcessor, const GrPipeline* pipeline,
69 const GrPipeline::FixedDynamicState* fixedDynamicState)
Brian Osman49b7b6f2017-06-20 14:43:58 -040070 : fMesh(primitiveType)
71 , fTarget(target)
72 , fVertexStride(sizeof(SkPoint))
73 , fGeometryProcessor(geometryProcessor)
74 , fPipeline(pipeline)
Brian Salomon49348902018-06-26 09:12:38 -040075 , fFixedDynamicState(fixedDynamicState)
Brian Osman49b7b6f2017-06-20 14:43:58 -040076 , fIndexBuffer(nullptr)
77 , fFirstIndex(0)
78 , fIndicesInChunk(0)
79 , fIndices(nullptr) {
80 this->allocNewBuffers();
Brian Osmaneb86b702017-06-07 11:38:31 -040081 }
82
Brian Osman49b7b6f2017-06-20 14:43:58 -040083 ~PathGeoBuilder() {
Brian Osman3902d402017-06-20 15:36:31 -040084 this->emitMeshAndPutBackReserve();
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) {
Stephen White2cee2832017-08-23 09:37:16 -0400156 SkPath::Verb verb = iter.next(pts, false);
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];
Brian Salomon29f9ed42017-11-29 10:52:00 -0500188 while ((verb = iter.next(pts, false)) != 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 {
203 return GrPrimitiveType::kLines == fMesh.primitiveType() ||
204 GrPrimitiveType::kTriangles == fMesh.primitiveType();
Brian Osman49b7b6f2017-06-20 14:43:58 -0400205 }
206 bool isHairline() const {
207 return GrPrimitiveType::kLines == fMesh.primitiveType() ||
208 GrPrimitiveType::kLineStrip == fMesh.primitiveType();
209 }
210 int indexScale() const {
211 switch (fMesh.primitiveType()) {
212 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
Brian Osman3902d402017-06-20 15:36:31 -0400267 void emitMeshAndPutBackReserve() {
268 int vertexCount = fCurVert - fVertices;
269 int indexCount = fCurIdx - fIndices;
270 SkASSERT(vertexCount <= fVerticesInChunk);
271 SkASSERT(indexCount <= fIndicesInChunk);
272
Brian Salomon763abf02018-05-01 18:49:38 +0000273 if (this->isIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
274 if (!this->isIndexed()) {
275 fMesh.setNonIndexedNonInstanced(vertexCount);
276 } else {
Brian Salomon802cb312018-06-08 18:05:20 -0400277 fMesh.setIndexed(fIndexBuffer, indexCount, fFirstIndex, 0, vertexCount - 1,
278 GrPrimitiveRestart::kNo);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400279 }
Brian Salomon763abf02018-05-01 18:49:38 +0000280 fMesh.setVertexData(fVertexBuffer, fFirstVertex);
Brian Salomon49348902018-06-26 09:12:38 -0400281 fTarget->draw(fGeometryProcessor, fPipeline, fFixedDynamicState, fMesh);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400282 }
Brian Osman3902d402017-06-20 15:36:31 -0400283
284 fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
285 fTarget->putBackVertices((size_t)(fVerticesInChunk - vertexCount), fVertexStride);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400286 }
287
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);
Brian Osman3902d402017-06-20 15:36:31 -0400297 SkASSERT(fSubpathIndexStart < fVerticesInChunk);
298 SkPoint subpathStartPt = fVertices[fSubpathIndexStart];
Brian Osman49b7b6f2017-06-20 14:43:58 -0400299
Brian Osman3902d402017-06-20 15:36:31 -0400300 // Draw the mesh we've accumulated, and put back any unused space
301 this->emitMeshAndPutBackReserve();
Brian Osman49b7b6f2017-06-20 14:43:58 -0400302
Brian Osman3902d402017-06-20 15:36:31 -0400303 // Get new buffers
Brian Osman49b7b6f2017-06-20 14:43:58 -0400304 this->allocNewBuffers();
305
Brian Osman49b7b6f2017-06-20 14:43:58 -0400306 // 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 GrMesh fMesh;
315 GrMeshDrawOp::Target* fTarget;
316 size_t fVertexStride;
317 GrGeometryProcessor* fGeometryProcessor;
318 const GrPipeline* fPipeline;
Brian Salomon49348902018-06-26 09:12:38 -0400319 const GrPipeline::FixedDynamicState* fFixedDynamicState;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400320
321 const GrBuffer* fVertexBuffer;
322 int fFirstVertex;
323 int fVerticesInChunk;
324 SkPoint* fVertices;
325 SkPoint* fCurVert;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400326
327 const GrBuffer* fIndexBuffer;
328 int fFirstIndex;
329 int fIndicesInChunk;
330 uint16_t* fIndices;
331 uint16_t* fCurIdx;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400332 uint16_t fSubpathIndexStart;
333};
egdanielaf18a092015-01-05 10:22:28 -0800334
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400335class DefaultPathOp final : public GrMeshDrawOp {
336private:
337 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
338
joshualitt332c7292015-02-23 08:44:31 -0800339public:
Brian Salomon25a88092016-12-01 09:36:50 -0500340 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700341
Robert Phillips7c525e62018-06-12 10:11:12 -0400342 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,
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400351 const GrUserStencilSettings* stencilSettings) {
Robert Phillips7c525e62018-06-12 10:11:12 -0400352 return Helper::FactoryHelper<DefaultPathOp>(context, std::move(paint), path, tolerance,
353 coverage, viewMatrix, isHairline, aaType,
354 devBounds, stencilSettings);
bsalomon@google.com30085192011-08-19 15:42:31 +0000355 }
356
Brian Salomon780dad12016-12-15 18:08:40 -0500357 const char* name() const override { return "DefaultPathOp"; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000358
Robert Phillipsf1748f52017-09-14 14:11:24 -0400359 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400360 fHelper.visitProxies(func);
361 }
362
Brian Salomon7c3e7182016-12-01 09:35:30 -0500363 SkString dumpInfo() const override {
364 SkString string;
Brian Salomon780dad12016-12-15 18:08:40 -0500365 string.appendf("Color: 0x%08x Count: %d\n", fColor, fPaths.count());
366 for (const auto& path : fPaths) {
367 string.appendf("Tolerance: %.2f\n", path.fTolerance);
Brian Salomon7c3e7182016-12-01 09:35:30 -0500368 }
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400369 string += fHelper.dumpInfo();
370 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -0500371 return string;
372 }
373
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400374 DefaultPathOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkPath& path,
375 SkScalar tolerance, uint8_t coverage, const SkMatrix& viewMatrix, bool isHairline,
376 GrAAType aaType, const SkRect& devBounds,
377 const GrUserStencilSettings* stencilSettings)
Brian Salomon780dad12016-12-15 18:08:40 -0500378 : INHERITED(ClassID())
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400379 , fHelper(helperArgs, aaType, stencilSettings)
Brian Salomon780dad12016-12-15 18:08:40 -0500380 , fColor(color)
381 , fCoverage(coverage)
382 , fViewMatrix(viewMatrix)
383 , fIsHairline(isHairline) {
384 fPaths.emplace_back(PathData{path, tolerance});
joshualitt332c7292015-02-23 08:44:31 -0800385
Brian Salomon780dad12016-12-15 18:08:40 -0500386 this->setBounds(devBounds, HasAABloat::kNo,
387 isHairline ? IsZeroArea::kYes : IsZeroArea::kNo);
388 }
389
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400390 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
391
Brian Osman532b3f92018-07-11 10:02:07 -0400392 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400393 GrProcessorAnalysisCoverage gpCoverage =
394 this->coverage() == 0xFF ? GrProcessorAnalysisCoverage::kNone
395 : GrProcessorAnalysisCoverage::kSingleChannel;
Brian Osman532b3f92018-07-11 10:02:07 -0400396 return fHelper.xpRequiresDstTexture(caps, clip, gpCoverage, &fColor);
Brian Salomon92aee3d2016-12-21 09:20:25 -0500397 }
398
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400399private:
Brian Salomon91326c32017-08-09 16:02:19 -0400400 void onPrepareDraws(Target* target) override {
bungeman06ca8ec2016-06-09 08:01:03 -0700401 sk_sp<GrGeometryProcessor> gp;
joshualittdf0c5572015-08-03 11:35:28 -0700402 {
403 using namespace GrDefaultGeoProcFactory;
404 Color color(this->color());
405 Coverage coverage(this->coverage());
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400406 LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
407 : LocalCoords::kUnused_Type);
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400408 gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
409 color,
410 coverage,
411 localCoords,
412 this->viewMatrix());
joshualittdf0c5572015-08-03 11:35:28 -0700413 }
joshualitt332c7292015-02-23 08:44:31 -0800414
Brian Salomon92be2f72018-06-19 14:33:47 -0400415 SkASSERT(gp->debugOnly_vertexStride() == sizeof(SkPoint));
joshualitt332c7292015-02-23 08:44:31 -0800416
Brian Salomon763abf02018-05-01 18:49:38 +0000417 int instanceCount = fPaths.count();
418
419 // We avoid indices when we have a single hairline contour.
420 bool isIndexed = !this->isHairline() || instanceCount > 1 ||
421 PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
Brian Osman7f95dfc2017-06-09 14:43:49 +0000422
423 // determine primitiveType
Brian Osman7f95dfc2017-06-09 14:43:49 +0000424 GrPrimitiveType primitiveType;
425 if (this->isHairline()) {
Brian Salomon763abf02018-05-01 18:49:38 +0000426 primitiveType = isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
Brian Osman7f95dfc2017-06-09 14:43:49 +0000427 } else {
Brian Salomon06c33042018-04-25 15:45:27 -0400428 primitiveType = GrPrimitiveType::kTriangles;
Brian Osman7f95dfc2017-06-09 14:43:49 +0000429 }
Brian Salomon49348902018-06-26 09:12:38 -0400430 auto pipe = fHelper.makePipeline(target);
431 PathGeoBuilder pathGeoBuilder(primitiveType, target, gp.get(), pipe.fPipeline,
432 pipe.fFixedDynamicState);
Brian Osman7f95dfc2017-06-09 14:43:49 +0000433
434 // fill buffers
Brian Salomon763abf02018-05-01 18:49:38 +0000435 for (int i = 0; i < instanceCount; i++) {
436 const PathData& args = fPaths[i];
437 pathGeoBuilder.addPath(args.fPath, args.fTolerance);
Brian Osman7f95dfc2017-06-09 14:43:49 +0000438 }
joshualitt332c7292015-02-23 08:44:31 -0800439 }
440
Brian Salomon25a88092016-12-01 09:36:50 -0500441 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon780dad12016-12-15 18:08:40 -0500442 DefaultPathOp* that = t->cast<DefaultPathOp>();
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400443 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -0700444 return false;
445 }
446
joshualitt332c7292015-02-23 08:44:31 -0800447 if (this->color() != that->color()) {
448 return false;
449 }
450
451 if (this->coverage() != that->coverage()) {
452 return false;
453 }
454
455 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
456 return false;
457 }
458
459 if (this->isHairline() != that->isHairline()) {
460 return false;
461 }
462
Brian Salomon780dad12016-12-15 18:08:40 -0500463 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700464 this->joinBounds(*that);
joshualitt332c7292015-02-23 08:44:31 -0800465 return true;
466 }
467
Brian Salomon780dad12016-12-15 18:08:40 -0500468 GrColor color() const { return fColor; }
469 uint8_t coverage() const { return fCoverage; }
Brian Salomon780dad12016-12-15 18:08:40 -0500470 const SkMatrix& viewMatrix() const { return fViewMatrix; }
471 bool isHairline() const { return fIsHairline; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000472
Brian Salomon780dad12016-12-15 18:08:40 -0500473 struct PathData {
bsalomon0432dd62016-06-30 07:19:27 -0700474 SkPath fPath;
475 SkScalar fTolerance;
476 };
477
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400478 SkSTArray<1, PathData, true> fPaths;
479 Helper fHelper;
Brian Salomon780dad12016-12-15 18:08:40 -0500480 GrColor fColor;
481 uint8_t fCoverage;
482 SkMatrix fViewMatrix;
Brian Salomon780dad12016-12-15 18:08:40 -0500483 bool fIsHairline;
reed1b55a962015-09-17 20:16:13 -0700484
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400485 typedef GrMeshDrawOp INHERITED;
joshualitt332c7292015-02-23 08:44:31 -0800486};
bsalomon@google.com30085192011-08-19 15:42:31 +0000487
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400488} // anonymous namespace
489
Brian Osman11052242016-10-27 14:47:55 -0400490bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
Brian Salomon82f44312017-01-11 13:42:54 -0500491 GrPaint&& paint,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500492 GrAAType aaType,
robertphillipsd2b6d642016-07-21 08:55:08 -0700493 const GrUserStencilSettings& userStencilSettings,
cdalton862cff32016-05-12 15:09:48 -0700494 const GrClip& clip,
joshualitt8059eb92014-12-29 15:10:07 -0800495 const SkMatrix& viewMatrix,
bsalomon8acedde2016-06-24 10:42:16 -0700496 const GrShape& shape,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000497 bool stencilOnly) {
Robert Phillips7c525e62018-06-12 10:11:12 -0400498 GrContext* context = renderTargetContext->surfPriv().getContext();
499
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500500 SkASSERT(GrAAType::kCoverage != aaType);
bsalomon8acedde2016-06-24 10:42:16 -0700501 SkPath path;
502 shape.asPath(&path);
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000503
504 SkScalar hairlineCoverage;
joshualitt2e3b3e32014-12-09 13:31:14 -0800505 uint8_t newCoverage = 0xff;
bsalomon6663acf2016-05-10 09:14:17 -0700506 bool isHairline = false;
bsalomon8acedde2016-06-24 10:42:16 -0700507 if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800508 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
bsalomon6663acf2016-05-10 09:14:17 -0700509 isHairline = true;
510 } else {
bsalomon8acedde2016-06-24 10:42:16 -0700511 SkASSERT(shape.style().isSimpleFill());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000512 }
513
cdalton93a379b2016-05-11 13:58:08 -0700514 int passCount = 0;
Brian Salomonf0861672017-05-08 11:10:10 -0400515 const GrUserStencilSettings* passes[2];
cdalton93a379b2016-05-11 13:58:08 -0700516 bool reverse = false;
517 bool lastPassIsBounds;
bsalomon@google.com30085192011-08-19 15:42:31 +0000518
joshualitt332c7292015-02-23 08:44:31 -0800519 if (isHairline) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000520 passCount = 1;
521 if (stencilOnly) {
522 passes[0] = &gDirectToStencil;
523 } else {
robertphillipsd2b6d642016-07-21 08:55:08 -0700524 passes[0] = &userStencilSettings;
bsalomon@google.com30085192011-08-19 15:42:31 +0000525 }
526 lastPassIsBounds = false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000527 } else {
bsalomon8acedde2016-06-24 10:42:16 -0700528 if (single_pass_shape(shape)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000529 passCount = 1;
530 if (stencilOnly) {
531 passes[0] = &gDirectToStencil;
532 } else {
robertphillipsd2b6d642016-07-21 08:55:08 -0700533 passes[0] = &userStencilSettings;
bsalomon@google.com30085192011-08-19 15:42:31 +0000534 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000535 lastPassIsBounds = false;
536 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000537 switch (path.getFillType()) {
sugoi@google.com12b4e272012-12-06 20:13:11 +0000538 case SkPath::kInverseEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000539 reverse = true;
540 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000541 case SkPath::kEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000542 passes[0] = &gEOStencilPass;
543 if (stencilOnly) {
544 passCount = 1;
545 lastPassIsBounds = false;
546 } else {
547 passCount = 2;
548 lastPassIsBounds = true;
549 if (reverse) {
550 passes[1] = &gInvEOColorPass;
551 } else {
552 passes[1] = &gEOColorPass;
553 }
554 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000555 break;
556
sugoi@google.com12b4e272012-12-06 20:13:11 +0000557 case SkPath::kInverseWinding_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000558 reverse = true;
559 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000560 case SkPath::kWinding_FillType:
Brian Salomon15b25092017-05-08 11:10:53 -0400561 passes[0] = &gWindStencilPass;
Brian Salomonf0861672017-05-08 11:10:10 -0400562 passCount = 2;
bsalomon@google.com30085192011-08-19 15:42:31 +0000563 if (stencilOnly) {
564 lastPassIsBounds = false;
565 --passCount;
566 } else {
567 lastPassIsBounds = true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000568 if (reverse) {
569 passes[passCount-1] = &gInvWindColorPass;
570 } else {
571 passes[passCount-1] = &gWindColorPass;
572 }
573 }
574 break;
575 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000576 SkDEBUGFAIL("Unknown path fFill!");
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000577 return false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000578 }
579 }
580 }
581
senorblanco2b4bb072015-04-22 13:45:18 -0700582 SkScalar tol = GrPathUtils::kDefaultTolerance;
joshualitt332c7292015-02-23 08:44:31 -0800583 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
584
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000585 SkRect devBounds;
Robert Phillipsec325342017-10-30 18:02:48 +0000586 GetPathDevBounds(path,
587 renderTargetContext->asRenderTargetProxy()->worstCaseWidth(),
588 renderTargetContext->asRenderTargetProxy()->worstCaseHeight(),
589 viewMatrix, &devBounds);
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000590
bsalomon@google.com30085192011-08-19 15:42:31 +0000591 for (int p = 0; p < passCount; ++p) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000592 if (lastPassIsBounds && (p == passCount-1)) {
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000593 SkRect bounds;
joshualittd27f73e2014-12-29 07:43:36 -0800594 SkMatrix localMatrix = SkMatrix::I();
bsalomon@google.com30085192011-08-19 15:42:31 +0000595 if (reverse) {
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000596 // draw over the dev bounds (which will be the whole dst surface for inv fill).
597 bounds = devBounds;
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000598 SkMatrix vmi;
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000599 // mapRect through persp matrix may not be correct
joshualitt8059eb92014-12-29 15:10:07 -0800600 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000601 vmi.mapRect(&bounds);
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000602 } else {
joshualittd27f73e2014-12-29 07:43:36 -0800603 if (!viewMatrix.invert(&localMatrix)) {
604 return false;
605 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000606 }
607 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000608 bounds = path.getBounds();
bsalomon@google.com30085192011-08-19 15:42:31 +0000609 }
joshualitt8059eb92014-12-29 15:10:07 -0800610 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
611 viewMatrix;
Brian Salomonac70f842017-05-08 10:43:33 -0400612 renderTargetContext->addDrawOp(
613 clip,
Brian Salomonbaaf4392017-06-15 09:59:23 -0400614 GrRectOpFactory::MakeNonAAFillWithLocalMatrix(
Robert Phillips7c525e62018-06-12 10:11:12 -0400615 context, std::move(paint), viewM, localMatrix,
616 bounds, aaType, passes[p]));
robertphillips976f5f02016-06-03 10:59:20 -0700617 } else {
Brian Salomond4652ca2017-01-13 12:11:36 -0500618 bool stencilPass = stencilOnly || passCount > 1;
Brian Salomonb74ef032017-08-10 12:46:01 -0400619 std::unique_ptr<GrDrawOp> op;
Brian Salomond4652ca2017-01-13 12:11:36 -0500620 if (stencilPass) {
Brian Salomonb74ef032017-08-10 12:46:01 -0400621 GrPaint stencilPaint;
622 stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
Robert Phillips7c525e62018-06-12 10:11:12 -0400623 op = DefaultPathOp::Make(context, std::move(stencilPaint), path, srcSpaceTol,
624 newCoverage, viewMatrix, isHairline, aaType, devBounds,
625 passes[p]);
Brian Salomonb74ef032017-08-10 12:46:01 -0400626 } else {
Robert Phillips7c525e62018-06-12 10:11:12 -0400627 op = DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, newCoverage,
Brian Salomonb74ef032017-08-10 12:46:01 -0400628 viewMatrix, isHairline, aaType, devBounds, passes[p]);
Brian Salomond4652ca2017-01-13 12:11:36 -0500629 }
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400630 renderTargetContext->addDrawOp(clip, std::move(op));
bsalomon@google.com30085192011-08-19 15:42:31 +0000631 }
632 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000633 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000634}
635
Chris Dalton5ed44232017-09-07 13:22:46 -0600636GrPathRenderer::CanDrawPath
637GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
Eric Karl5c779752017-05-08 12:02:07 -0700638 bool isHairline = IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr);
639 // If we aren't a single_pass_shape or hairline, we require stencil buffers.
640 if (!(single_pass_shape(*args.fShape) || isHairline) && args.fCaps->avoidStencilBuffers()) {
Chris Dalton5ed44232017-09-07 13:22:46 -0600641 return CanDrawPath::kNo;
Eric Karl5c779752017-05-08 12:02:07 -0700642 }
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500643 // This can draw any path with any simple fill style but doesn't do coverage-based antialiasing.
Chris Dalton5ed44232017-09-07 13:22:46 -0600644 if (GrAAType::kCoverage == args.fAAType ||
645 (!args.fShape->style().isSimpleFill() && !isHairline)) {
646 return CanDrawPath::kNo;
647 }
648 // This is the fallback renderer for when a path is too complicated for the others to draw.
649 return CanDrawPath::kAsBackup;
bsalomon@google.com30085192011-08-19 15:42:31 +0000650}
651
bsalomon0aff2fa2015-07-31 06:48:27 -0700652bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400653 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700654 "GrDefaultPathRenderer::onDrawPath");
Brian Osman11052242016-10-27 14:47:55 -0400655 return this->internalDrawPath(args.fRenderTargetContext,
Brian Salomon82f44312017-01-11 13:42:54 -0500656 std::move(args.fPaint),
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500657 args.fAAType,
robertphillipsd2b6d642016-07-21 08:55:08 -0700658 *args.fUserStencilSettings,
cdalton862cff32016-05-12 15:09:48 -0700659 *args.fClip,
bsalomon0aff2fa2015-07-31 06:48:27 -0700660 *args.fViewMatrix,
bsalomon8acedde2016-06-24 10:42:16 -0700661 *args.fShape,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000662 false);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000663}
664
bsalomon0aff2fa2015-07-31 06:48:27 -0700665void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400666 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700667 "GrDefaultPathRenderer::onStencilPath");
bsalomon8acedde2016-06-24 10:42:16 -0700668 SkASSERT(!args.fShape->inverseFilled());
robertphillips976f5f02016-06-03 10:59:20 -0700669
670 GrPaint paint;
Brian Salomona1633922017-01-09 11:46:10 -0500671 paint.setXPFactory(GrDisableColorXPFactory::Get());
robertphillips976f5f02016-06-03 10:59:20 -0700672
Brian Salomon82f44312017-01-11 13:42:54 -0500673 this->internalDrawPath(args.fRenderTargetContext, std::move(paint), args.fAAType,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500674 GrUserStencilSettings::kUnused, *args.fClip, *args.fViewMatrix,
675 *args.fShape, true);
bsalomon@google.com30085192011-08-19 15:42:31 +0000676}
joshualitt622d3ad2015-05-07 08:13:11 -0700677
678///////////////////////////////////////////////////////////////////////////////////////////////////
679
Hal Canary6f6961e2017-01-31 13:50:44 -0500680#if GR_TEST_UTILS
joshualitt622d3ad2015-05-07 08:13:11 -0700681
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400682GR_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
joshualitt622d3ad2015-05-07 08:13:11 -0700683 SkMatrix viewMatrix = GrTest::TestMatrix(random);
684
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500685 // For now just hairlines because the other types of draws require two ops.
686 // TODO we should figure out a way to combine the stencil and cover steps into one op.
bsalomon6663acf2016-05-10 09:14:17 -0700687 GrStyle style(SkStrokeRec::kHairline_InitStyle);
joshualitt622d3ad2015-05-07 08:13:11 -0700688 SkPath path = GrTest::TestPath(random);
689
690 // Compute srcSpaceTol
691 SkRect bounds = path.getBounds();
692 SkScalar tol = GrPathUtils::kDefaultTolerance;
693 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
694
joshualitt622d3ad2015-05-07 08:13:11 -0700695 viewMatrix.mapRect(&bounds);
696 uint8_t coverage = GrRandomCoverage(random);
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400697 GrAAType aaType = GrAAType::kNone;
698 if (GrFSAAType::kUnifiedMSAA == fsaaType && random->nextBool()) {
699 aaType = GrAAType::kMSAA;
700 }
Robert Phillips7c525e62018-06-12 10:11:12 -0400701 return DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, coverage, viewMatrix,
702 true, aaType, bounds, GrGetRandomStencil(random, context));
joshualitt622d3ad2015-05-07 08:13:11 -0700703}
704
705#endif