blob: e4f54ccd61495790a584940ebd24f92898a8b1c6 [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"
9
joshualitt332c7292015-02-23 08:44:31 -080010#include "GrBatch.h"
11#include "GrBatchTarget.h"
12#include "GrBufferAllocPool.h"
bsalomon@google.com30085192011-08-19 15:42:31 +000013#include "GrContext.h"
joshualitt5478d422014-11-14 16:00:38 -080014#include "GrDefaultGeoProcFactory.h"
bsalomon@google.com30085192011-08-19 15:42:31 +000015#include "GrPathUtils.h"
egdaniel8dd688b2015-01-22 10:16:09 -080016#include "GrPipelineBuilder.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"
bsalomon@google.com30085192011-08-19 15:42:31 +000022
bsalomon@google.com30085192011-08-19 15:42:31 +000023GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
24 bool stencilWrapOpsSupport)
25 : fSeparateStencil(separateStencilSupport)
bsalomon@google.comc2099d22012-03-02 21:26:50 +000026 , fStencilWrapOps(stencilWrapOpsSupport) {
bsalomon@google.com289533a2011-10-27 12:34:25 +000027}
28
29
bsalomon@google.com30085192011-08-19 15:42:31 +000030////////////////////////////////////////////////////////////////////////////////
31// Stencil rules for paths
32
33////// Even/Odd
34
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000035GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass,
36 kInvert_StencilOp,
37 kKeep_StencilOp,
38 kAlwaysIfInClip_StencilFunc,
39 0xffff,
40 0xffff,
41 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000042
43// ok not to check clip b/c stencil pass only wrote inside clip
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000044GR_STATIC_CONST_SAME_STENCIL(gEOColorPass,
45 kZero_StencilOp,
46 kZero_StencilOp,
47 kNotEqual_StencilFunc,
48 0xffff,
49 0x0000,
50 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000051
52// have to check clip b/c outside clip will always be zero.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000053GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass,
54 kZero_StencilOp,
55 kZero_StencilOp,
56 kEqualIfInClip_StencilFunc,
57 0xffff,
58 0x0000,
59 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000060
61////// Winding
62
63// when we have separate stencil we increment front faces / decrement back faces
64// when we don't have wrap incr and decr we use the stencil test to simulate
65// them.
66
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000067GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap,
bsalomon@google.com30085192011-08-19 15:42:31 +000068 kIncWrap_StencilOp, kDecWrap_StencilOp,
69 kKeep_StencilOp, kKeep_StencilOp,
70 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
tomhudson@google.com62b09682011-11-09 16:39:17 +000071 0xffff, 0xffff,
72 0xffff, 0xffff,
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000073 0xffff, 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000074
75// if inc'ing the max value, invert to make 0
76// if dec'ing zero invert to make all ones.
77// we can't avoid touching the stencil on both passing and
78// failing, so we can't resctrict ourselves to the clip.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000079GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap,
bsalomon@google.com30085192011-08-19 15:42:31 +000080 kInvert_StencilOp, kInvert_StencilOp,
81 kIncClamp_StencilOp, kDecClamp_StencilOp,
82 kEqual_StencilFunc, kEqual_StencilFunc,
tomhudson@google.com62b09682011-11-09 16:39:17 +000083 0xffff, 0xffff,
84 0xffff, 0x0000,
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000085 0xffff, 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000086
87// When there are no separate faces we do two passes to setup the winding rule
88// stencil. First we draw the front faces and inc, then we draw the back faces
89// and dec. These are same as the above two split into the incrementing and
90// decrementing passes.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000091GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
92 kIncWrap_StencilOp,
93 kKeep_StencilOp,
94 kAlwaysIfInClip_StencilFunc,
95 0xffff,
96 0xffff,
97 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000098
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000099GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
100 kDecWrap_StencilOp,
101 kKeep_StencilOp,
102 kAlwaysIfInClip_StencilFunc,
103 0xffff,
104 0xffff,
105 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000106
bsalomon@google.com6b2445e2011-12-15 19:47:46 +0000107GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc,
108 kInvert_StencilOp,
109 kIncClamp_StencilOp,
110 kEqual_StencilFunc,
111 0xffff,
112 0xffff,
113 0xffff);
114
115GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec,
116 kInvert_StencilOp,
117 kDecClamp_StencilOp,
118 kEqual_StencilFunc,
119 0xffff,
120 0x0000,
121 0xffff);
122
123// Color passes are the same whether we use the two-sided stencil or two passes
124
125GR_STATIC_CONST_SAME_STENCIL(gWindColorPass,
126 kZero_StencilOp,
127 kZero_StencilOp,
128 kNonZeroIfInClip_StencilFunc,
129 0xffff,
130 0x0000,
131 0xffff);
132
133GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass,
134 kZero_StencilOp,
135 kZero_StencilOp,
136 kEqualIfInClip_StencilFunc,
137 0xffff,
138 0x0000,
139 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000140
141////// Normal render to stencil
142
143// Sometimes the default path renderer can draw a path directly to the stencil
144// buffer without having to first resolve the interior / exterior.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +0000145GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil,
146 kZero_StencilOp,
147 kIncClamp_StencilOp,
148 kAlwaysIfInClip_StencilFunc,
149 0xffff,
150 0x0000,
151 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000152
153////////////////////////////////////////////////////////////////////////////////
154// Helpers for drawPath
155
bsalomon@google.com30085192011-08-19 15:42:31 +0000156#define STENCIL_OFF 0 // Always disable stencil (even when needed)
157
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000158static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000159#if STENCIL_OFF
160 return true;
161#else
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000162 if (!stroke.isHairlineStyle() && !path.isInverseFillType()) {
bsalomon@google.com7d72c452012-01-30 14:02:44 +0000163 return path.isConvex();
bsalomon@google.com30085192011-08-19 15:42:31 +0000164 }
165 return false;
166#endif
167}
168
joshualitt9853cce2014-11-17 14:22:48 -0800169GrPathRenderer::StencilSupport
170GrDefaultPathRenderer::onGetStencilSupport(const GrDrawTarget*,
egdaniel8dd688b2015-01-22 10:16:09 -0800171 const GrPipelineBuilder*,
joshualitt9853cce2014-11-17 14:22:48 -0800172 const SkPath& path,
173 const SkStrokeRec& stroke) const {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000174 if (single_pass_path(path, stroke)) {
bsalomon@google.com45a15f52012-12-10 19:10:17 +0000175 return GrPathRenderer::kNoRestriction_StencilSupport;
176 } else {
177 return GrPathRenderer::kStencilOnly_StencilSupport;
178 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000179}
180
sugoi@google.com12b4e272012-12-06 20:13:11 +0000181static inline void append_countour_edge_indices(bool hairLine,
bsalomon@google.com30085192011-08-19 15:42:31 +0000182 uint16_t fanCenterIdx,
183 uint16_t edgeV0Idx,
184 uint16_t** indices) {
185 // when drawing lines we're appending line segments along
186 // the contour. When applying the other fill rules we're
187 // drawing triangle fans around fanCenterIdx.
sugoi@google.com12b4e272012-12-06 20:13:11 +0000188 if (!hairLine) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000189 *((*indices)++) = fanCenterIdx;
190 }
191 *((*indices)++) = edgeV0Idx;
192 *((*indices)++) = edgeV0Idx + 1;
193}
194
egdanielaf18a092015-01-05 10:22:28 -0800195static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[],
196 SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed,
joshualitt332c7292015-02-23 08:44:31 -0800197 bool isHairline, uint16_t subpathIdxStart, int offset, uint16_t** idx) {
egdanielaf18a092015-01-05 10:22:28 -0800198 // first pt of quad is the pt we ended on in previous step
joshualitt332c7292015-02-23 08:44:31 -0800199 uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1 + offset;
egdanielaf18a092015-01-05 10:22:28 -0800200 uint16_t numPts = (uint16_t)
201 GrPathUtils::generateQuadraticPoints(
202 pts[0], pts[1], pts[2],
203 srcSpaceTolSqd, vert,
204 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
205 if (indexed) {
206 for (uint16_t i = 0; i < numPts; ++i) {
207 append_countour_edge_indices(isHairline, subpathIdxStart,
208 firstQPtIdx + i, idx);
209 }
210 }
211}
212
joshualitt332c7292015-02-23 08:44:31 -0800213class DefaultPathBatch : public GrBatch {
214public:
215 struct Geometry {
216 GrColor fColor;
217 SkPath fPath;
218 SkScalar fTolerance;
219 SkDEBUGCODE(SkRect fDevBounds;)
220 };
bsalomon@google.com30085192011-08-19 15:42:31 +0000221
joshualitt332c7292015-02-23 08:44:31 -0800222 static GrBatch* Create(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
223 bool isHairline) {
224 return SkNEW_ARGS(DefaultPathBatch, (geometry, coverage, viewMatrix, isHairline));
bsalomon@google.com30085192011-08-19 15:42:31 +0000225 }
226
joshualitt332c7292015-02-23 08:44:31 -0800227 const char* name() const SK_OVERRIDE { return "DefaultPathBatch"; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000228
joshualitt332c7292015-02-23 08:44:31 -0800229 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
230 // When this is called on a batch, there is only one geometry bundle
231 out->setKnownFourComponents(fGeoData[0].fColor);
232 }
233 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt233c6ce2015-02-24 08:17:49 -0800234 out->setKnownSingleComponent(this->coverage());
joshualitt332c7292015-02-23 08:44:31 -0800235 }
sugoi@google.com12b4e272012-12-06 20:13:11 +0000236
joshualitt332c7292015-02-23 08:44:31 -0800237 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
238 // Handle any color overrides
239 if (init.fColorIgnored) {
240 fGeoData[0].fColor = GrColor_ILLEGAL;
241 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
242 fGeoData[0].fColor = init.fOverrideColor;
bsalomon@google.com30085192011-08-19 15:42:31 +0000243 }
joshualitt332c7292015-02-23 08:44:31 -0800244
245 // setup batch properties
246 fBatch.fColorIgnored = init.fColorIgnored;
247 fBatch.fColor = fGeoData[0].fColor;
248 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
249 fBatch.fCoverageIgnored = init.fCoverageIgnored;
250 }
251
252 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
253 SkAutoTUnref<const GrGeometryProcessor> gp(
254 GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
255 this->color(),
256 this->viewMatrix(),
257 SkMatrix::I(),
258 false,
259 this->coverage()));
260
261 size_t vertexStride = gp->getVertexStride();
262 SkASSERT(vertexStride == sizeof(SkPoint));
263
264 batchTarget->initDraw(gp, pipeline);
265
266 // TODO this is hacky, but the only way we have to initialize the GP is to use the
267 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
268 // everywhere we can remove this nastiness
269 GrPipelineInfo init;
270 init.fColorIgnored = fBatch.fColorIgnored;
271 init.fOverrideColor = GrColor_ILLEGAL;
272 init.fCoverageIgnored = fBatch.fCoverageIgnored;
273 init.fUsesLocalCoords = this->usesLocalCoords();
274 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
275
276 int instanceCount = fGeoData.count();
277
278 // compute number of vertices
279 int maxVertices = 0;
280
281 // We will use index buffers if we have multiple paths or one path with multiple contours
282 bool isIndexed = instanceCount > 1;
283 for (int i = 0; i < instanceCount; i++) {
284 Geometry& args = fGeoData[i];
285
286 int contourCount;
287 maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount,
288 args.fTolerance);
289
290 isIndexed = isIndexed || contourCount > 1;
bsalomon@google.com30085192011-08-19 15:42:31 +0000291 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000292
joshualitt332c7292015-02-23 08:44:31 -0800293 // determine primitiveType
294 int maxIndices = 0;
295 GrPrimitiveType primitiveType;
296 if (this->isHairline()) {
297 if (isIndexed) {
298 maxIndices = 2 * maxVertices;
299 primitiveType = kLines_GrPrimitiveType;
300 } else {
301 primitiveType = kLineStrip_GrPrimitiveType;
bsalomon@google.com30085192011-08-19 15:42:31 +0000302 }
joshualitt332c7292015-02-23 08:44:31 -0800303 } else {
304 if (isIndexed) {
305 maxIndices = 3 * maxVertices;
306 primitiveType = kTriangles_GrPrimitiveType;
307 } else {
308 primitiveType = kTriangleFan_GrPrimitiveType;
309 }
310 }
311
312 // allocate vertex / index buffers
313 const GrVertexBuffer* vertexBuffer;
314 int firstVertex;
315
316 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
317 maxVertices,
318 &vertexBuffer,
319 &firstVertex);
320
321 const GrIndexBuffer* indexBuffer;
322 int firstIndex;
323
324 void* indices = NULL;
325 if (isIndexed) {
326 indices = batchTarget->indexPool()->makeSpace(maxIndices,
327 &indexBuffer,
328 &firstIndex);
329 }
330
331 // fill buffers
332 int vertexOffset = 0;
333 int indexOffset = 0;
334 for (int i = 0; i < instanceCount; i++) {
335 Geometry& args = fGeoData[i];
336
337 int vertexCnt = 0;
338 int indexCnt = 0;
339 if (!this->createGeom(vertices,
340 vertexOffset,
341 indices,
342 indexOffset,
343 &vertexCnt,
344 &indexCnt,
345 args.fPath,
346 args.fTolerance,
347 isIndexed)) {
348 return;
349 }
350
351 vertexOffset += vertexCnt;
352 indexOffset += indexCnt;
353 SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
354 }
355
356 GrDrawTarget::DrawInfo drawInfo;
357 drawInfo.setPrimitiveType(primitiveType);
358 drawInfo.setVertexBuffer(vertexBuffer);
359 drawInfo.setStartVertex(firstVertex);
360 drawInfo.setVertexCount(vertexOffset);
361 if (isIndexed) {
362 drawInfo.setIndexBuffer(indexBuffer);
363 drawInfo.setStartIndex(firstIndex);
364 drawInfo.setIndexCount(indexOffset);
365 } else {
366 drawInfo.setStartIndex(0);
367 drawInfo.setIndexCount(0);
368 }
369 batchTarget->draw(drawInfo);
370 }
371
372 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
373
374private:
375 DefaultPathBatch(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
376 bool isHairline) {
377 this->initClassID<DefaultPathBatch>();
378 fBatch.fCoverage = coverage;
379 fBatch.fIsHairline = isHairline;
380 fBatch.fViewMatrix = viewMatrix;
381 fGeoData.push_back(geometry);
382 }
383
384 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
385 DefaultPathBatch* that = t->cast<DefaultPathBatch>();
386
387 if (this->color() != that->color()) {
388 return false;
389 }
390
391 if (this->coverage() != that->coverage()) {
392 return false;
393 }
394
395 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
396 return false;
397 }
398
399 if (this->isHairline() != that->isHairline()) {
400 return false;
401 }
402
403 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
404 return true;
405 }
406
407 bool createGeom(void* vertices,
408 size_t vertexOffset,
409 void* indices,
410 size_t indexOffset,
411 int* vertexCnt,
412 int* indexCnt,
413 const SkPath& path,
414 SkScalar srcSpaceTol,
415 bool isIndexed) {
416 {
417 SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol);
418
419 uint16_t indexOffsetU16 = (uint16_t)indexOffset;
420 uint16_t vertexOffsetU16 = (uint16_t)vertexOffset;
421
422 uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16;
423 uint16_t* idx = idxBase;
424 uint16_t subpathIdxStart = vertexOffsetU16;
425
426 SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset;
427 SkPoint* vert = base;
428
429 SkPoint pts[4];
430
431 bool first = true;
432 int subpath = 0;
433
434 SkPath::Iter iter(path, false);
435
436 bool done = false;
437 while (!done) {
438 SkPath::Verb verb = iter.next(pts);
439 switch (verb) {
440 case SkPath::kMove_Verb:
441 if (!first) {
442 uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16;
443 subpathIdxStart = currIdx;
444 ++subpath;
445 }
446 *vert = pts[0];
447 vert++;
448 break;
449 case SkPath::kLine_Verb:
450 if (isIndexed) {
451 uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
452 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
453 prevIdx, &idx);
454 }
455 *(vert++) = pts[1];
456 break;
457 case SkPath::kConic_Verb: {
458 SkScalar weight = iter.conicWeight();
459 SkAutoConicToQuads converter;
460 // Converting in src-space, hance the finer tolerance (0.25)
461 // TODO: find a way to do this in dev-space so the tolerance means something
462 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
463 for (int i = 0; i < converter.countQuads(); ++i) {
464 add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol,
465 isIndexed, this->isHairline(), subpathIdxStart,
466 (int)vertexOffset, &idx);
467 }
468 break;
bsalomon@google.com30085192011-08-19 15:42:31 +0000469 }
joshualitt332c7292015-02-23 08:44:31 -0800470 case SkPath::kQuad_Verb:
471 add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed,
472 this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx);
473 break;
474 case SkPath::kCubic_Verb: {
475 // first pt of cubic is the pt we ended on in previous step
476 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
477 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
478 pts[0], pts[1], pts[2], pts[3],
479 srcSpaceTolSqd, &vert,
480 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
481 if (isIndexed) {
482 for (uint16_t i = 0; i < numPts; ++i) {
483 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
484 firstCPtIdx + i, &idx);
485 }
486 }
487 break;
488 }
489 case SkPath::kClose_Verb:
490 break;
491 case SkPath::kDone_Verb:
492 done = true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000493 }
joshualitt332c7292015-02-23 08:44:31 -0800494 first = false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000495 }
joshualitt332c7292015-02-23 08:44:31 -0800496
497 *vertexCnt = static_cast<int>(vert - base);
498 *indexCnt = static_cast<int>(idx - idxBase);
499
bsalomon@google.com30085192011-08-19 15:42:31 +0000500 }
joshualitt332c7292015-02-23 08:44:31 -0800501 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000502 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000503
joshualitt332c7292015-02-23 08:44:31 -0800504 GrColor color() const { return fBatch.fColor; }
505 uint8_t coverage() const { return fBatch.fCoverage; }
506 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
507 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
508 bool isHairline() const { return fBatch.fIsHairline; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000509
joshualitt332c7292015-02-23 08:44:31 -0800510 struct BatchTracker {
511 GrColor fColor;
512 uint8_t fCoverage;
513 SkMatrix fViewMatrix;
514 bool fUsesLocalCoords;
515 bool fColorIgnored;
516 bool fCoverageIgnored;
517 bool fIsHairline;
518 };
519
520 BatchTracker fBatch;
521 SkSTArray<1, Geometry, true> fGeoData;
522};
bsalomon@google.com30085192011-08-19 15:42:31 +0000523
joshualitt9853cce2014-11-17 14:22:48 -0800524bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800525 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800526 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800527 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800528 const SkPath& path,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000529 const SkStrokeRec& origStroke,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000530 bool stencilOnly) {
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000531 SkTCopyOnFirstWrite<SkStrokeRec> stroke(origStroke);
532
533 SkScalar hairlineCoverage;
joshualitt2e3b3e32014-12-09 13:31:14 -0800534 uint8_t newCoverage = 0xff;
joshualitt8059eb92014-12-29 15:10:07 -0800535 if (IsStrokeHairlineOrEquivalent(*stroke, viewMatrix, &hairlineCoverage)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800536 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000537
538 if (!stroke->isHairlineStyle()) {
539 stroke.writable()->setHairlineStyle();
540 }
541 }
542
joshualitt332c7292015-02-23 08:44:31 -0800543 const bool isHairline = stroke->isHairlineStyle();
bsalomon@google.com30085192011-08-19 15:42:31 +0000544
egdaniel080e6732014-12-22 07:35:52 -0800545 // Save the current xp on the draw state so we can reset it if needed
egdaniel8dd688b2015-01-22 10:16:09 -0800546 SkAutoTUnref<const GrXPFactory> backupXPFactory(SkRef(pipelineBuilder->getXPFactory()));
bsalomon@google.com30085192011-08-19 15:42:31 +0000547 // face culling doesn't make sense here
egdaniel8dd688b2015-01-22 10:16:09 -0800548 SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace());
bsalomon@google.com30085192011-08-19 15:42:31 +0000549
550 int passCount = 0;
551 const GrStencilSettings* passes[3];
joshualitt332c7292015-02-23 08:44:31 -0800552 GrPipelineBuilder::DrawFace drawFace[3];
bsalomon@google.com30085192011-08-19 15:42:31 +0000553 bool reverse = false;
554 bool lastPassIsBounds;
555
joshualitt332c7292015-02-23 08:44:31 -0800556 if (isHairline) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000557 passCount = 1;
558 if (stencilOnly) {
559 passes[0] = &gDirectToStencil;
560 } else {
561 passes[0] = NULL;
562 }
563 lastPassIsBounds = false;
egdaniel8dd688b2015-01-22 10:16:09 -0800564 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000565 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000566 if (single_pass_path(path, *stroke)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000567 passCount = 1;
568 if (stencilOnly) {
569 passes[0] = &gDirectToStencil;
570 } else {
571 passes[0] = NULL;
572 }
egdaniel8dd688b2015-01-22 10:16:09 -0800573 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000574 lastPassIsBounds = false;
575 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000576 switch (path.getFillType()) {
sugoi@google.com12b4e272012-12-06 20:13:11 +0000577 case SkPath::kInverseEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000578 reverse = true;
579 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000580 case SkPath::kEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000581 passes[0] = &gEOStencilPass;
582 if (stencilOnly) {
583 passCount = 1;
584 lastPassIsBounds = false;
585 } else {
586 passCount = 2;
587 lastPassIsBounds = true;
588 if (reverse) {
589 passes[1] = &gInvEOColorPass;
590 } else {
591 passes[1] = &gEOColorPass;
592 }
593 }
egdaniel8dd688b2015-01-22 10:16:09 -0800594 drawFace[0] = drawFace[1] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000595 break;
596
sugoi@google.com12b4e272012-12-06 20:13:11 +0000597 case SkPath::kInverseWinding_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000598 reverse = true;
599 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000600 case SkPath::kWinding_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000601 if (fSeparateStencil) {
602 if (fStencilWrapOps) {
603 passes[0] = &gWindStencilSeparateWithWrap;
604 } else {
605 passes[0] = &gWindStencilSeparateNoWrap;
606 }
607 passCount = 2;
egdaniel8dd688b2015-01-22 10:16:09 -0800608 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000609 } else {
610 if (fStencilWrapOps) {
611 passes[0] = &gWindSingleStencilWithWrapInc;
612 passes[1] = &gWindSingleStencilWithWrapDec;
613 } else {
614 passes[0] = &gWindSingleStencilNoWrapInc;
615 passes[1] = &gWindSingleStencilNoWrapDec;
616 }
617 // which is cw and which is ccw is arbitrary.
egdaniel8dd688b2015-01-22 10:16:09 -0800618 drawFace[0] = GrPipelineBuilder::kCW_DrawFace;
619 drawFace[1] = GrPipelineBuilder::kCCW_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000620 passCount = 3;
621 }
622 if (stencilOnly) {
623 lastPassIsBounds = false;
624 --passCount;
625 } else {
626 lastPassIsBounds = true;
egdaniel8dd688b2015-01-22 10:16:09 -0800627 drawFace[passCount-1] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000628 if (reverse) {
629 passes[passCount-1] = &gInvWindColorPass;
630 } else {
631 passes[passCount-1] = &gWindColorPass;
632 }
633 }
634 break;
635 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000636 SkDEBUGFAIL("Unknown path fFill!");
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000637 return false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000638 }
639 }
640 }
641
joshualitt332c7292015-02-23 08:44:31 -0800642 SkScalar tol = SK_Scalar1;
643 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
644
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000645 SkRect devBounds;
egdaniel8dd688b2015-01-22 10:16:09 -0800646 GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds);
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000647
bsalomon@google.com30085192011-08-19 15:42:31 +0000648 for (int p = 0; p < passCount; ++p) {
egdaniel8dd688b2015-01-22 10:16:09 -0800649 pipelineBuilder->setDrawFace(drawFace[p]);
bsalomon49f085d2014-09-05 13:34:00 -0700650 if (passes[p]) {
egdaniel8dd688b2015-01-22 10:16:09 -0800651 *pipelineBuilder->stencil() = *passes[p];
bsalomon@google.com30085192011-08-19 15:42:31 +0000652 }
653
654 if (lastPassIsBounds && (p == passCount-1)) {
egdaniel8dd688b2015-01-22 10:16:09 -0800655 // Reset the XP Factory on pipelineBuilder
656 pipelineBuilder->setXPFactory(backupXPFactory);
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) {
egdaniel8dd688b2015-01-22 10:16:09 -0800660 SkASSERT(pipelineBuilder->getRenderTarget());
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000661 // draw over the dev bounds (which will be the whole dst surface for inv fill).
662 bounds = devBounds;
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000663 SkMatrix vmi;
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000664 // mapRect through persp matrix may not be correct
joshualitt8059eb92014-12-29 15:10:07 -0800665 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000666 vmi.mapRect(&bounds);
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000667 } else {
joshualittd27f73e2014-12-29 07:43:36 -0800668 if (!viewMatrix.invert(&localMatrix)) {
669 return false;
670 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000671 }
672 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000673 bounds = path.getBounds();
bsalomon@google.com30085192011-08-19 15:42:31 +0000674 }
joshualitt9853cce2014-11-17 14:22:48 -0800675 GrDrawTarget::AutoGeometryPush agp(target);
joshualitt8059eb92014-12-29 15:10:07 -0800676 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
677 viewMatrix;
egdaniel8dd688b2015-01-22 10:16:09 -0800678 target->drawRect(pipelineBuilder, color, viewM, bounds, NULL, &localMatrix);
bsalomon@google.com30085192011-08-19 15:42:31 +0000679 } else {
680 if (passCount > 1) {
egdaniel8dd688b2015-01-22 10:16:09 -0800681 pipelineBuilder->setDisableColorXPFactory();
bsalomon@google.com30085192011-08-19 15:42:31 +0000682 }
joshualitt332c7292015-02-23 08:44:31 -0800683
684 DefaultPathBatch::Geometry geometry;
685 geometry.fColor = color;
686 geometry.fPath = path;
687 geometry.fTolerance = srcSpaceTol;
688 SkDEBUGCODE(geometry.fDevBounds = devBounds;)
689
690 SkAutoTUnref<GrBatch> batch(DefaultPathBatch::Create(geometry, newCoverage, viewMatrix,
691 isHairline));
692
693 target->drawBatch(pipelineBuilder, batch, &devBounds);
bsalomon@google.com30085192011-08-19 15:42:31 +0000694 }
695 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000696 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000697}
698
joshualitt9853cce2014-11-17 14:22:48 -0800699bool GrDefaultPathRenderer::canDrawPath(const GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800700 const GrPipelineBuilder* pipelineBuilder,
joshualitt8059eb92014-12-29 15:10:07 -0800701 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800702 const SkPath& path,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000703 const SkStrokeRec& stroke,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000704 bool antiAlias) const {
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000705 // this class can draw any path with any fill but doesn't do any anti-aliasing.
egdanielaf18a092015-01-05 10:22:28 -0800706 return !antiAlias && (stroke.isFillStyle() || IsStrokeHairlineOrEquivalent(stroke,
707 viewMatrix,
708 NULL));
bsalomon@google.com30085192011-08-19 15:42:31 +0000709}
710
joshualitt9853cce2014-11-17 14:22:48 -0800711bool GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800712 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800713 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800714 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800715 const SkPath& path,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000716 const SkStrokeRec& stroke,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000717 bool antiAlias) {
joshualitt9853cce2014-11-17 14:22:48 -0800718 return this->internalDrawPath(target,
egdaniel8dd688b2015-01-22 10:16:09 -0800719 pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800720 color,
joshualitt8059eb92014-12-29 15:10:07 -0800721 viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800722 path,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000723 stroke,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000724 false);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000725}
726
joshualitt9853cce2014-11-17 14:22:48 -0800727void GrDefaultPathRenderer::onStencilPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800728 GrPipelineBuilder* pipelineBuilder,
joshualitt8059eb92014-12-29 15:10:07 -0800729 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800730 const SkPath& path,
731 const SkStrokeRec& stroke) {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000732 SkASSERT(SkPath::kInverseEvenOdd_FillType != path.getFillType());
733 SkASSERT(SkPath::kInverseWinding_FillType != path.getFillType());
egdaniel8dd688b2015-01-22 10:16:09 -0800734 this->internalDrawPath(target, pipelineBuilder, GrColor_WHITE, viewMatrix, path, stroke, true);
bsalomon@google.com30085192011-08-19 15:42:31 +0000735}