blob: 40631a74ff1b1df0c2515ae7eedad90bbe193ace [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 Salomonee3e0ba2017-07-13 16:40:46 -040016#include "GrSimpleMeshDrawOpHelper.h"
egdanielaf18a092015-01-05 10:22:28 -080017#include "SkGeometry.h"
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000018#include "SkString.h"
sugoi@google.com5f74cf82012-12-17 21:16:45 +000019#include "SkStrokeRec.h"
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +000020#include "SkTLazy.h"
commit-bot@chromium.org933e65d2014-03-20 20:00:24 +000021#include "SkTraceEvent.h"
Brian Salomon89527432016-12-16 09:52:16 -050022#include "ops/GrMeshDrawOp.h"
Brian Salomonbaaf4392017-06-15 09:59:23 -040023#include "ops/GrRectOpFactory.h"
joshualitt74417822015-08-07 11:42:16 -070024
Brian Salomon15b25092017-05-08 11:10:53 -040025GrDefaultPathRenderer::GrDefaultPathRenderer() {
bsalomon@google.com289533a2011-10-27 12:34:25 +000026}
27
bsalomon@google.com30085192011-08-19 15:42:31 +000028////////////////////////////////////////////////////////////////////////////////
29// Helpers for drawPath
30
bsalomon@google.com30085192011-08-19 15:42:31 +000031#define STENCIL_OFF 0 // Always disable stencil (even when needed)
32
bsalomon8acedde2016-06-24 10:42:16 -070033static inline bool single_pass_shape(const GrShape& shape) {
bsalomon@google.com30085192011-08-19 15:42:31 +000034#if STENCIL_OFF
35 return true;
36#else
bsalomonee432412016-06-27 07:18:18 -070037 // 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()) {
bsalomon8acedde2016-06-24 10:42:16 -070045 return shape.knownToBeConvex();
bsalomon@google.com30085192011-08-19 15:42:31 +000046 }
bsalomonee432412016-06-27 07:18:18 -070047 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +000048#endif
49}
50
joshualitt9853cce2014-11-17 14:22:48 -080051GrPathRenderer::StencilSupport
bsalomon8acedde2016-06-24 10:42:16 -070052GrDefaultPathRenderer::onGetStencilSupport(const GrShape& shape) const {
53 if (single_pass_shape(shape)) {
bsalomon@google.com45a15f52012-12-10 19:10:17 +000054 return GrPathRenderer::kNoRestriction_StencilSupport;
55 } else {
56 return GrPathRenderer::kStencilOnly_StencilSupport;
57 }
bsalomon@google.com30085192011-08-19 15:42:31 +000058}
59
Brian Salomonee3e0ba2017-07-13 16:40:46 -040060namespace {
61
Brian Osman49b7b6f2017-06-20 14:43:58 -040062class PathGeoBuilder {
63public:
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();
Brian Osmaneb86b702017-06-07 11:38:31 -040076 }
77
Brian Osman49b7b6f2017-06-20 14:43:58 -040078 ~PathGeoBuilder() {
Brian Osman3902d402017-06-20 15:36:31 -040079 this->emitMeshAndPutBackReserve();
Brian Osman49b7b6f2017-06-20 14:43:58 -040080 }
81
82 /**
83 * Path verbs
84 */
85 void moveTo(const SkPoint& p) {
86 needSpace(1);
87
88 fSubpathIndexStart = this->currentIndex();
89 *(fCurVert++) = p;
90 }
91
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
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 }
egdanielaf18a092015-01-05 10:22:28 -0800115 }
116 }
Brian Osman49b7b6f2017-06-20 14:43:58 -0400117
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
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
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) {
Stephen White2cee2832017-08-23 09:37:16 -0400151 SkPath::Verb verb = iter.next(pts, false);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400152 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
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
192private:
193 /**
194 * Derived properties
195 * TODO: Cache some of these for better performance, rather than re-computing?
196 */
197 bool isIndexed() const {
198 return GrPrimitiveType::kLines == fMesh.primitiveType() ||
199 GrPrimitiveType::kTriangles == fMesh.primitiveType();
200 }
201 bool isHairline() const {
202 return GrPrimitiveType::kLines == fMesh.primitiveType() ||
203 GrPrimitiveType::kLineStrip == fMesh.primitiveType();
204 }
205 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
216 uint16_t currentIndex() const { return fCurVert - fVertices; }
217
Brian Osman49b7b6f2017-06-20 14:43:58 -0400218 // Allocate vertex and (possibly) index buffers
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 }
Brian Osman3902d402017-06-20 15:36:31 -0400245
246 fCurVert = fVertices;
247 fCurIdx = fIndices;
248 fSubpathIndexStart = 0;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400249 }
250
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
Brian Osman3902d402017-06-20 15:36:31 -0400262 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) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400269 if (!this->isIndexed()) {
Brian Osman3902d402017-06-20 15:36:31 -0400270 fMesh.setNonIndexedNonInstanced(vertexCount);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400271 } else {
Brian Osman3902d402017-06-20 15:36:31 -0400272 fMesh.setIndexed(fIndexBuffer, indexCount, fFirstIndex, 0, vertexCount - 1);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400273 }
274 fMesh.setVertexData(fVertexBuffer, fFirstVertex);
275 fTarget->draw(fGeometryProcessor, fPipeline, fMesh);
276 }
Brian Osman3902d402017-06-20 15:36:31 -0400277
278 fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
279 fTarget->putBackVertices((size_t)(fVerticesInChunk - vertexCount), fVertexStride);
Brian Osman49b7b6f2017-06-20 14:43:58 -0400280 }
281
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);
Brian Osman3902d402017-06-20 15:36:31 -0400291 SkASSERT(fSubpathIndexStart < fVerticesInChunk);
292 SkPoint subpathStartPt = fVertices[fSubpathIndexStart];
Brian Osman49b7b6f2017-06-20 14:43:58 -0400293
Brian Osman3902d402017-06-20 15:36:31 -0400294 // Draw the mesh we've accumulated, and put back any unused space
295 this->emitMeshAndPutBackReserve();
Brian Osman49b7b6f2017-06-20 14:43:58 -0400296
Brian Osman3902d402017-06-20 15:36:31 -0400297 // Get new buffers
Brian Osman49b7b6f2017-06-20 14:43:58 -0400298 this->allocNewBuffers();
299
Brian Osman49b7b6f2017-06-20 14:43:58 -0400300 // 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;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400319
320 const GrBuffer* fIndexBuffer;
321 int fFirstIndex;
322 int fIndicesInChunk;
323 uint16_t* fIndices;
324 uint16_t* fCurIdx;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400325 uint16_t fSubpathIndexStart;
326};
egdanielaf18a092015-01-05 10:22:28 -0800327
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400328class DefaultPathOp final : public GrMeshDrawOp {
329private:
330 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
331
joshualitt332c7292015-02-23 08:44:31 -0800332public:
Brian Salomon25a88092016-12-01 09:36:50 -0500333 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700334
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400335 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);
bsalomon@google.com30085192011-08-19 15:42:31 +0000342 }
343
Brian Salomon780dad12016-12-15 18:08:40 -0500344 const char* name() const override { return "DefaultPathOp"; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000345
Robert Phillipsf1748f52017-09-14 14:11:24 -0400346 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400347 fHelper.visitProxies(func);
348 }
349
Brian Salomon7c3e7182016-12-01 09:35:30 -0500350 SkString dumpInfo() const override {
351 SkString string;
Brian Salomon780dad12016-12-15 18:08:40 -0500352 string.appendf("Color: 0x%08x Count: %d\n", fColor, fPaths.count());
353 for (const auto& path : fPaths) {
354 string.appendf("Tolerance: %.2f\n", path.fTolerance);
Brian Salomon7c3e7182016-12-01 09:35:30 -0500355 }
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400356 string += fHelper.dumpInfo();
357 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -0500358 return string;
359 }
360
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400361 DefaultPathOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkPath& path,
362 SkScalar tolerance, uint8_t coverage, const SkMatrix& viewMatrix, bool isHairline,
363 GrAAType aaType, const SkRect& devBounds,
364 const GrUserStencilSettings* stencilSettings)
Brian Salomon780dad12016-12-15 18:08:40 -0500365 : INHERITED(ClassID())
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400366 , fHelper(helperArgs, aaType, stencilSettings)
Brian Salomon780dad12016-12-15 18:08:40 -0500367 , fColor(color)
368 , fCoverage(coverage)
369 , fViewMatrix(viewMatrix)
370 , fIsHairline(isHairline) {
371 fPaths.emplace_back(PathData{path, tolerance});
joshualitt332c7292015-02-23 08:44:31 -0800372
Brian Salomon780dad12016-12-15 18:08:40 -0500373 this->setBounds(devBounds, HasAABloat::kNo,
374 isHairline ? IsZeroArea::kYes : IsZeroArea::kNo);
375 }
376
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400377 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
378
Brian Osman9a725dd2017-09-20 09:53:22 -0400379 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
380 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400381 GrProcessorAnalysisCoverage gpCoverage =
382 this->coverage() == 0xFF ? GrProcessorAnalysisCoverage::kNone
383 : GrProcessorAnalysisCoverage::kSingleChannel;
Brian Osman9a725dd2017-09-20 09:53:22 -0400384 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, gpCoverage, &fColor);
Brian Salomon92aee3d2016-12-21 09:20:25 -0500385 }
386
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400387private:
Brian Salomon91326c32017-08-09 16:02:19 -0400388 void onPrepareDraws(Target* target) override {
bungeman06ca8ec2016-06-09 08:01:03 -0700389 sk_sp<GrGeometryProcessor> gp;
joshualittdf0c5572015-08-03 11:35:28 -0700390 {
391 using namespace GrDefaultGeoProcFactory;
392 Color color(this->color());
393 Coverage coverage(this->coverage());
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400394 LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
395 : LocalCoords::kUnused_Type);
bungeman06ca8ec2016-06-09 08:01:03 -0700396 gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, this->viewMatrix());
joshualittdf0c5572015-08-03 11:35:28 -0700397 }
joshualitt332c7292015-02-23 08:44:31 -0800398
Brian Osman49b7b6f2017-06-20 14:43:58 -0400399 SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
joshualitt332c7292015-02-23 08:44:31 -0800400
Brian Salomon780dad12016-12-15 18:08:40 -0500401 int instanceCount = fPaths.count();
joshualitt332c7292015-02-23 08:44:31 -0800402
joshualitt332c7292015-02-23 08:44:31 -0800403 // We will use index buffers if we have multiple paths or one path with multiple contours
404 bool isIndexed = instanceCount > 1;
Brian Osman49b7b6f2017-06-20 14:43:58 -0400405 for (int i = 0; !isIndexed && i < instanceCount; i++) {
Brian Osmanfd7819c2017-06-07 14:43:17 +0000406 const PathData& args = fPaths[i];
Brian Osman49b7b6f2017-06-20 14:43:58 -0400407 isIndexed = isIndexed || PathGeoBuilder::PathHasMultipleSubpaths(args.fPath);
Brian Osman7f95dfc2017-06-09 14:43:49 +0000408 }
409
410 // determine primitiveType
Brian Osman7f95dfc2017-06-09 14:43:49 +0000411 GrPrimitiveType primitiveType;
412 if (this->isHairline()) {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400413 primitiveType = isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
Brian Osman7f95dfc2017-06-09 14:43:49 +0000414 } else {
Brian Osman49b7b6f2017-06-20 14:43:58 -0400415 primitiveType = isIndexed ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleFan;
Brian Osman7f95dfc2017-06-09 14:43:49 +0000416 }
417
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400418 PathGeoBuilder pathGeoBuilder(primitiveType, target, gp.get(),
419 fHelper.makePipeline(target));
Brian Osman7f95dfc2017-06-09 14:43:49 +0000420
421 // fill buffers
Brian Osman7f95dfc2017-06-09 14:43:49 +0000422 for (int i = 0; i < instanceCount; i++) {
423 const PathData& args = fPaths[i];
Brian Osman49b7b6f2017-06-20 14:43:58 -0400424 pathGeoBuilder.addPath(args.fPath, args.fTolerance);
Brian Osman7f95dfc2017-06-09 14:43:49 +0000425 }
joshualitt332c7292015-02-23 08:44:31 -0800426 }
427
Brian Salomon25a88092016-12-01 09:36:50 -0500428 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon780dad12016-12-15 18:08:40 -0500429 DefaultPathOp* that = t->cast<DefaultPathOp>();
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400430 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -0700431 return false;
432 }
433
joshualitt332c7292015-02-23 08:44:31 -0800434 if (this->color() != that->color()) {
435 return false;
436 }
437
438 if (this->coverage() != that->coverage()) {
439 return false;
440 }
441
442 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
443 return false;
444 }
445
446 if (this->isHairline() != that->isHairline()) {
447 return false;
448 }
449
Brian Salomon780dad12016-12-15 18:08:40 -0500450 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700451 this->joinBounds(*that);
joshualitt332c7292015-02-23 08:44:31 -0800452 return true;
453 }
454
Brian Salomon780dad12016-12-15 18:08:40 -0500455 GrColor color() const { return fColor; }
456 uint8_t coverage() const { return fCoverage; }
Brian Salomon780dad12016-12-15 18:08:40 -0500457 const SkMatrix& viewMatrix() const { return fViewMatrix; }
458 bool isHairline() const { return fIsHairline; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000459
Brian Salomon780dad12016-12-15 18:08:40 -0500460 struct PathData {
bsalomon0432dd62016-06-30 07:19:27 -0700461 SkPath fPath;
462 SkScalar fTolerance;
463 };
464
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400465 SkSTArray<1, PathData, true> fPaths;
466 Helper fHelper;
Brian Salomon780dad12016-12-15 18:08:40 -0500467 GrColor fColor;
468 uint8_t fCoverage;
469 SkMatrix fViewMatrix;
Brian Salomon780dad12016-12-15 18:08:40 -0500470 bool fIsHairline;
reed1b55a962015-09-17 20:16:13 -0700471
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400472 typedef GrMeshDrawOp INHERITED;
joshualitt332c7292015-02-23 08:44:31 -0800473};
bsalomon@google.com30085192011-08-19 15:42:31 +0000474
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400475} // anonymous namespace
476
Brian Osman11052242016-10-27 14:47:55 -0400477bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
Brian Salomon82f44312017-01-11 13:42:54 -0500478 GrPaint&& paint,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500479 GrAAType aaType,
robertphillipsd2b6d642016-07-21 08:55:08 -0700480 const GrUserStencilSettings& userStencilSettings,
cdalton862cff32016-05-12 15:09:48 -0700481 const GrClip& clip,
joshualitt8059eb92014-12-29 15:10:07 -0800482 const SkMatrix& viewMatrix,
bsalomon8acedde2016-06-24 10:42:16 -0700483 const GrShape& shape,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000484 bool stencilOnly) {
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500485 SkASSERT(GrAAType::kCoverage != aaType);
bsalomon8acedde2016-06-24 10:42:16 -0700486 SkPath path;
487 shape.asPath(&path);
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000488
489 SkScalar hairlineCoverage;
joshualitt2e3b3e32014-12-09 13:31:14 -0800490 uint8_t newCoverage = 0xff;
bsalomon6663acf2016-05-10 09:14:17 -0700491 bool isHairline = false;
bsalomon8acedde2016-06-24 10:42:16 -0700492 if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800493 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
bsalomon6663acf2016-05-10 09:14:17 -0700494 isHairline = true;
495 } else {
bsalomon8acedde2016-06-24 10:42:16 -0700496 SkASSERT(shape.style().isSimpleFill());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000497 }
498
cdalton93a379b2016-05-11 13:58:08 -0700499 int passCount = 0;
Brian Salomonf0861672017-05-08 11:10:10 -0400500 const GrUserStencilSettings* passes[2];
cdalton93a379b2016-05-11 13:58:08 -0700501 bool reverse = false;
502 bool lastPassIsBounds;
bsalomon@google.com30085192011-08-19 15:42:31 +0000503
joshualitt332c7292015-02-23 08:44:31 -0800504 if (isHairline) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000505 passCount = 1;
506 if (stencilOnly) {
507 passes[0] = &gDirectToStencil;
508 } else {
robertphillipsd2b6d642016-07-21 08:55:08 -0700509 passes[0] = &userStencilSettings;
bsalomon@google.com30085192011-08-19 15:42:31 +0000510 }
511 lastPassIsBounds = false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000512 } else {
bsalomon8acedde2016-06-24 10:42:16 -0700513 if (single_pass_shape(shape)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000514 passCount = 1;
515 if (stencilOnly) {
516 passes[0] = &gDirectToStencil;
517 } else {
robertphillipsd2b6d642016-07-21 08:55:08 -0700518 passes[0] = &userStencilSettings;
bsalomon@google.com30085192011-08-19 15:42:31 +0000519 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000520 lastPassIsBounds = false;
521 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000522 switch (path.getFillType()) {
sugoi@google.com12b4e272012-12-06 20:13:11 +0000523 case SkPath::kInverseEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000524 reverse = true;
525 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000526 case SkPath::kEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000527 passes[0] = &gEOStencilPass;
528 if (stencilOnly) {
529 passCount = 1;
530 lastPassIsBounds = false;
531 } else {
532 passCount = 2;
533 lastPassIsBounds = true;
534 if (reverse) {
535 passes[1] = &gInvEOColorPass;
536 } else {
537 passes[1] = &gEOColorPass;
538 }
539 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000540 break;
541
sugoi@google.com12b4e272012-12-06 20:13:11 +0000542 case SkPath::kInverseWinding_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000543 reverse = true;
544 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000545 case SkPath::kWinding_FillType:
Brian Salomon15b25092017-05-08 11:10:53 -0400546 passes[0] = &gWindStencilPass;
Brian Salomonf0861672017-05-08 11:10:10 -0400547 passCount = 2;
bsalomon@google.com30085192011-08-19 15:42:31 +0000548 if (stencilOnly) {
549 lastPassIsBounds = false;
550 --passCount;
551 } else {
552 lastPassIsBounds = true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000553 if (reverse) {
554 passes[passCount-1] = &gInvWindColorPass;
555 } else {
556 passes[passCount-1] = &gWindColorPass;
557 }
558 }
559 break;
560 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000561 SkDEBUGFAIL("Unknown path fFill!");
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000562 return false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000563 }
564 }
565 }
566
senorblanco2b4bb072015-04-22 13:45:18 -0700567 SkScalar tol = GrPathUtils::kDefaultTolerance;
joshualitt332c7292015-02-23 08:44:31 -0800568 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
569
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000570 SkRect devBounds;
Robert Phillipsfc281382017-10-30 15:55:56 +0000571 GetPathDevBounds(path, renderTargetContext->width(), renderTargetContext->height(), viewMatrix,
572 &devBounds);
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000573
bsalomon@google.com30085192011-08-19 15:42:31 +0000574 for (int p = 0; p < passCount; ++p) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000575 if (lastPassIsBounds && (p == passCount-1)) {
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000576 SkRect bounds;
joshualittd27f73e2014-12-29 07:43:36 -0800577 SkMatrix localMatrix = SkMatrix::I();
bsalomon@google.com30085192011-08-19 15:42:31 +0000578 if (reverse) {
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000579 // draw over the dev bounds (which will be the whole dst surface for inv fill).
580 bounds = devBounds;
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000581 SkMatrix vmi;
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000582 // mapRect through persp matrix may not be correct
joshualitt8059eb92014-12-29 15:10:07 -0800583 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000584 vmi.mapRect(&bounds);
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000585 } else {
joshualittd27f73e2014-12-29 07:43:36 -0800586 if (!viewMatrix.invert(&localMatrix)) {
587 return false;
588 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000589 }
590 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000591 bounds = path.getBounds();
bsalomon@google.com30085192011-08-19 15:42:31 +0000592 }
joshualitt8059eb92014-12-29 15:10:07 -0800593 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
594 viewMatrix;
Brian Salomonac70f842017-05-08 10:43:33 -0400595 renderTargetContext->addDrawOp(
596 clip,
Brian Salomonbaaf4392017-06-15 09:59:23 -0400597 GrRectOpFactory::MakeNonAAFillWithLocalMatrix(
598 std::move(paint), viewM, localMatrix, bounds, aaType, passes[p]));
robertphillips976f5f02016-06-03 10:59:20 -0700599 } else {
Brian Salomond4652ca2017-01-13 12:11:36 -0500600 bool stencilPass = stencilOnly || passCount > 1;
Brian Salomonb74ef032017-08-10 12:46:01 -0400601 std::unique_ptr<GrDrawOp> op;
Brian Salomond4652ca2017-01-13 12:11:36 -0500602 if (stencilPass) {
Brian Salomonb74ef032017-08-10 12:46:01 -0400603 GrPaint stencilPaint;
604 stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
605 op = DefaultPathOp::Make(std::move(stencilPaint), path, srcSpaceTol, newCoverage,
606 viewMatrix, isHairline, aaType, devBounds, passes[p]);
607 } else {
608 op = DefaultPathOp::Make(std::move(paint), path, srcSpaceTol, newCoverage,
609 viewMatrix, isHairline, aaType, devBounds, passes[p]);
Brian Salomond4652ca2017-01-13 12:11:36 -0500610 }
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400611 renderTargetContext->addDrawOp(clip, std::move(op));
bsalomon@google.com30085192011-08-19 15:42:31 +0000612 }
613 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000614 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000615}
616
Chris Dalton5ed44232017-09-07 13:22:46 -0600617GrPathRenderer::CanDrawPath
618GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
Eric Karl5c779752017-05-08 12:02:07 -0700619 bool isHairline = IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr);
620 // If we aren't a single_pass_shape or hairline, we require stencil buffers.
621 if (!(single_pass_shape(*args.fShape) || isHairline) && args.fCaps->avoidStencilBuffers()) {
Chris Dalton5ed44232017-09-07 13:22:46 -0600622 return CanDrawPath::kNo;
Eric Karl5c779752017-05-08 12:02:07 -0700623 }
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500624 // This can draw any path with any simple fill style but doesn't do coverage-based antialiasing.
Chris Dalton5ed44232017-09-07 13:22:46 -0600625 if (GrAAType::kCoverage == args.fAAType ||
626 (!args.fShape->style().isSimpleFill() && !isHairline)) {
627 return CanDrawPath::kNo;
628 }
629 // This is the fallback renderer for when a path is too complicated for the others to draw.
630 return CanDrawPath::kAsBackup;
bsalomon@google.com30085192011-08-19 15:42:31 +0000631}
632
bsalomon0aff2fa2015-07-31 06:48:27 -0700633bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400634 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700635 "GrDefaultPathRenderer::onDrawPath");
Brian Osman11052242016-10-27 14:47:55 -0400636 return this->internalDrawPath(args.fRenderTargetContext,
Brian Salomon82f44312017-01-11 13:42:54 -0500637 std::move(args.fPaint),
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500638 args.fAAType,
robertphillipsd2b6d642016-07-21 08:55:08 -0700639 *args.fUserStencilSettings,
cdalton862cff32016-05-12 15:09:48 -0700640 *args.fClip,
bsalomon0aff2fa2015-07-31 06:48:27 -0700641 *args.fViewMatrix,
bsalomon8acedde2016-06-24 10:42:16 -0700642 *args.fShape,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000643 false);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000644}
645
bsalomon0aff2fa2015-07-31 06:48:27 -0700646void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400647 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700648 "GrDefaultPathRenderer::onStencilPath");
bsalomon8acedde2016-06-24 10:42:16 -0700649 SkASSERT(!args.fShape->inverseFilled());
robertphillips976f5f02016-06-03 10:59:20 -0700650
651 GrPaint paint;
Brian Salomona1633922017-01-09 11:46:10 -0500652 paint.setXPFactory(GrDisableColorXPFactory::Get());
robertphillips976f5f02016-06-03 10:59:20 -0700653
Brian Salomon82f44312017-01-11 13:42:54 -0500654 this->internalDrawPath(args.fRenderTargetContext, std::move(paint), args.fAAType,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500655 GrUserStencilSettings::kUnused, *args.fClip, *args.fViewMatrix,
656 *args.fShape, true);
bsalomon@google.com30085192011-08-19 15:42:31 +0000657}
joshualitt622d3ad2015-05-07 08:13:11 -0700658
659///////////////////////////////////////////////////////////////////////////////////////////////////
660
Hal Canary6f6961e2017-01-31 13:50:44 -0500661#if GR_TEST_UTILS
joshualitt622d3ad2015-05-07 08:13:11 -0700662
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400663GR_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
joshualitt622d3ad2015-05-07 08:13:11 -0700664 SkMatrix viewMatrix = GrTest::TestMatrix(random);
665
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500666 // For now just hairlines because the other types of draws require two ops.
667 // TODO we should figure out a way to combine the stencil and cover steps into one op.
bsalomon6663acf2016-05-10 09:14:17 -0700668 GrStyle style(SkStrokeRec::kHairline_InitStyle);
joshualitt622d3ad2015-05-07 08:13:11 -0700669 SkPath path = GrTest::TestPath(random);
670
671 // Compute srcSpaceTol
672 SkRect bounds = path.getBounds();
673 SkScalar tol = GrPathUtils::kDefaultTolerance;
674 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
675
joshualitt622d3ad2015-05-07 08:13:11 -0700676 viewMatrix.mapRect(&bounds);
677 uint8_t coverage = GrRandomCoverage(random);
Brian Salomonee3e0ba2017-07-13 16:40:46 -0400678 GrAAType aaType = GrAAType::kNone;
679 if (GrFSAAType::kUnifiedMSAA == fsaaType && random->nextBool()) {
680 aaType = GrAAType::kMSAA;
681 }
682 return DefaultPathOp::Make(std::move(paint), path, srcSpaceTol, coverage, viewMatrix, true,
683 aaType, bounds, GrGetRandomStencil(random, context));
joshualitt622d3ad2015-05-07 08:13:11 -0700684}
685
686#endif