blob: 52decb42185ffad6e332a5ee148c04e071b25cae [file] [log] [blame]
bsalomon@google.com30085192011-08-19 15:42:31 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "GrDefaultPathRenderer.h"
10
11#include "GrContext.h"
12#include "GrPathUtils.h"
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000013#include "SkString.h"
bsalomon@google.com30085192011-08-19 15:42:31 +000014#include "SkTrace.h"
15
16
17GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
18 bool stencilWrapOpsSupport)
19 : fSeparateStencil(separateStencilSupport)
20 , fStencilWrapOps(stencilWrapOpsSupport)
21 , fSubpathCount(0)
22 , fSubpathVertCount(0)
23 , fPreviousSrcTol(-GR_Scalar1)
24 , fPreviousStages(-1) {
25 fTarget = NULL;
26}
27
bsalomon@google.com289533a2011-10-27 12:34:25 +000028bool GrDefaultPathRenderer::canDrawPath(const GrDrawTarget::Caps& targetCaps,
29 const SkPath& path,
30 GrPathFill fill,
31 bool antiAlias) const {
32 // this class can draw any path with any fill but doesn't do any
33 // anti-aliasing.
34 return !antiAlias;
35}
36
37
bsalomon@google.com30085192011-08-19 15:42:31 +000038////////////////////////////////////////////////////////////////////////////////
39// Stencil rules for paths
40
41////// Even/Odd
42
43static const GrStencilSettings gEOStencilPass = {
44 kInvert_StencilOp, kInvert_StencilOp,
45 kKeep_StencilOp, kKeep_StencilOp,
46 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
47 0xffffffff, 0xffffffff,
48 0xffffffff, 0xffffffff,
49 0xffffffff, 0xffffffff
50};
51
52// ok not to check clip b/c stencil pass only wrote inside clip
53static const GrStencilSettings gEOColorPass = {
54 kZero_StencilOp, kZero_StencilOp,
55 kZero_StencilOp, kZero_StencilOp,
56 kNotEqual_StencilFunc, kNotEqual_StencilFunc,
57 0xffffffff, 0xffffffff,
58 0x0, 0x0,
59 0xffffffff, 0xffffffff
60};
61
62// have to check clip b/c outside clip will always be zero.
63static const GrStencilSettings gInvEOColorPass = {
64 kZero_StencilOp, kZero_StencilOp,
65 kZero_StencilOp, kZero_StencilOp,
66 kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
67 0xffffffff, 0xffffffff,
68 0x0, 0x0,
69 0xffffffff, 0xffffffff
70};
71
72////// Winding
73
74// when we have separate stencil we increment front faces / decrement back faces
75// when we don't have wrap incr and decr we use the stencil test to simulate
76// them.
77
78static const GrStencilSettings gWindStencilSeparateWithWrap = {
79 kIncWrap_StencilOp, kDecWrap_StencilOp,
80 kKeep_StencilOp, kKeep_StencilOp,
81 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
82 0xffffffff, 0xffffffff,
83 0xffffffff, 0xffffffff,
84 0xffffffff, 0xffffffff
85};
86
87// if inc'ing the max value, invert to make 0
88// if dec'ing zero invert to make all ones.
89// we can't avoid touching the stencil on both passing and
90// failing, so we can't resctrict ourselves to the clip.
91static const GrStencilSettings gWindStencilSeparateNoWrap = {
92 kInvert_StencilOp, kInvert_StencilOp,
93 kIncClamp_StencilOp, kDecClamp_StencilOp,
94 kEqual_StencilFunc, kEqual_StencilFunc,
95 0xffffffff, 0xffffffff,
96 0xffffffff, 0x0,
97 0xffffffff, 0xffffffff
98};
99
100// When there are no separate faces we do two passes to setup the winding rule
101// stencil. First we draw the front faces and inc, then we draw the back faces
102// and dec. These are same as the above two split into the incrementing and
103// decrementing passes.
104static const GrStencilSettings gWindSingleStencilWithWrapInc = {
105 kIncWrap_StencilOp, kIncWrap_StencilOp,
106 kKeep_StencilOp, kKeep_StencilOp,
107 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
108 0xffffffff, 0xffffffff,
109 0xffffffff, 0xffffffff,
110 0xffffffff, 0xffffffff
111};
112static const GrStencilSettings gWindSingleStencilWithWrapDec = {
113 kDecWrap_StencilOp, kDecWrap_StencilOp,
114 kKeep_StencilOp, kKeep_StencilOp,
115 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
116 0xffffffff, 0xffffffff,
117 0xffffffff, 0xffffffff,
118 0xffffffff, 0xffffffff
119};
120static const GrStencilSettings gWindSingleStencilNoWrapInc = {
121 kInvert_StencilOp, kInvert_StencilOp,
122 kIncClamp_StencilOp, kIncClamp_StencilOp,
123 kEqual_StencilFunc, kEqual_StencilFunc,
124 0xffffffff, 0xffffffff,
125 0xffffffff, 0xffffffff,
126 0xffffffff, 0xffffffff
127};
128static const GrStencilSettings gWindSingleStencilNoWrapDec = {
129 kInvert_StencilOp, kInvert_StencilOp,
130 kDecClamp_StencilOp, kDecClamp_StencilOp,
131 kEqual_StencilFunc, kEqual_StencilFunc,
132 0xffffffff, 0xffffffff,
133 0x0, 0x0,
134 0xffffffff, 0xffffffff
135};
136
137static const GrStencilSettings gWindColorPass = {
138 kZero_StencilOp, kZero_StencilOp,
139 kZero_StencilOp, kZero_StencilOp,
140 kNonZeroIfInClip_StencilFunc, kNonZeroIfInClip_StencilFunc,
141 0xffffffff, 0xffffffff,
142 0x0, 0x0,
143 0xffffffff, 0xffffffff
144};
145
146static const GrStencilSettings gInvWindColorPass = {
147 kZero_StencilOp, kZero_StencilOp,
148 kZero_StencilOp, kZero_StencilOp,
149 kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
150 0xffffffff, 0xffffffff,
151 0x0, 0x0,
152 0xffffffff, 0xffffffff
153};
154
155////// Normal render to stencil
156
157// Sometimes the default path renderer can draw a path directly to the stencil
158// buffer without having to first resolve the interior / exterior.
159static const GrStencilSettings gDirectToStencil = {
160 kZero_StencilOp, kZero_StencilOp,
161 kIncClamp_StencilOp, kIncClamp_StencilOp,
162 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
163 0xffffffff, 0xffffffff,
164 0x0, 0x0,
165 0xffffffff, 0xffffffff
166};
167
168////////////////////////////////////////////////////////////////////////////////
169// Helpers for drawPath
170
171static GrConvexHint getConvexHint(const SkPath& path) {
172 return path.isConvex() ? kConvex_ConvexHint : kConcave_ConvexHint;
173}
174
175#define STENCIL_OFF 0 // Always disable stencil (even when needed)
176
177static inline bool single_pass_path(const GrDrawTarget& target,
178 const GrPath& path,
179 GrPathFill fill) {
180#if STENCIL_OFF
181 return true;
182#else
183 if (kEvenOdd_PathFill == fill) {
184 GrConvexHint hint = getConvexHint(path);
185 return hint == kConvex_ConvexHint ||
186 hint == kNonOverlappingConvexPieces_ConvexHint;
187 } else if (kWinding_PathFill == fill) {
188 GrConvexHint hint = getConvexHint(path);
189 return hint == kConvex_ConvexHint ||
190 hint == kNonOverlappingConvexPieces_ConvexHint ||
191 (hint == kSameWindingConvexPieces_ConvexHint &&
bsalomon@google.com86c1f712011-10-12 14:54:26 +0000192 !target.drawWillReadDst() && !target.isDitherState());
bsalomon@google.com30085192011-08-19 15:42:31 +0000193
194 }
195 return false;
196#endif
197}
198
199bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target,
bsalomon@google.com289533a2011-10-27 12:34:25 +0000200 const GrPath& path,
bsalomon@google.com30085192011-08-19 15:42:31 +0000201 GrPathFill fill) const {
202 return !single_pass_path(*target, path, fill);
203}
204
205void GrDefaultPathRenderer::pathWillClear() {
bsalomon@google.com7d4679a2011-09-02 22:06:24 +0000206 fSubpathVertCount.reset(0);
bsalomon@google.com30085192011-08-19 15:42:31 +0000207 fTarget->resetVertexSource();
208 if (fUseIndexedDraw) {
209 fTarget->resetIndexSource();
210 }
211 fPreviousSrcTol = -GR_Scalar1;
212 fPreviousStages = -1;
213}
214
215static inline void append_countour_edge_indices(GrPathFill fillType,
216 uint16_t fanCenterIdx,
217 uint16_t edgeV0Idx,
218 uint16_t** indices) {
219 // when drawing lines we're appending line segments along
220 // the contour. When applying the other fill rules we're
221 // drawing triangle fans around fanCenterIdx.
222 if (kHairLine_PathFill != fillType) {
223 *((*indices)++) = fanCenterIdx;
224 }
225 *((*indices)++) = edgeV0Idx;
226 *((*indices)++) = edgeV0Idx + 1;
227}
228
229bool GrDefaultPathRenderer::createGeom(GrScalar srcSpaceTol,
230 GrDrawTarget::StageBitfield stages) {
231 {
232 SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom");
233
234 GrScalar srcSpaceTolSqd = GrMul(srcSpaceTol, srcSpaceTol);
235 int maxPts = GrPathUtils::worstCasePointCount(*fPath, &fSubpathCount,
236 srcSpaceTol);
237
238 if (maxPts <= 0) {
239 return false;
240 }
241 if (maxPts > ((int)SK_MaxU16 + 1)) {
242 GrPrintf("Path not rendered, too many verts (%d)\n", maxPts);
243 return false;
244 }
245
246 GrVertexLayout layout = 0;
247 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
248 if ((1 << s) & stages) {
249 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
250 }
251 }
252
253 fUseIndexedDraw = fSubpathCount > 1;
254
255 int maxIdxs = 0;
256 if (kHairLine_PathFill == fFill) {
257 if (fUseIndexedDraw) {
258 maxIdxs = 2 * maxPts;
259 fPrimitiveType = kLines_PrimitiveType;
260 } else {
261 fPrimitiveType = kLineStrip_PrimitiveType;
262 }
263 } else {
264 if (fUseIndexedDraw) {
265 maxIdxs = 3 * maxPts;
266 fPrimitiveType = kTriangles_PrimitiveType;
267 } else {
268 fPrimitiveType = kTriangleFan_PrimitiveType;
269 }
270 }
271
272 GrPoint* base;
273 if (!fTarget->reserveVertexSpace(layout, maxPts, (void**)&base)) {
274 return false;
275 }
276 GrAssert(NULL != base);
277 GrPoint* vert = base;
278
279 uint16_t* idxBase = NULL;
280 uint16_t* idx = NULL;
281 uint16_t subpathIdxStart = 0;
282 if (fUseIndexedDraw) {
283 if (!fTarget->reserveIndexSpace(maxIdxs, (void**)&idxBase)) {
284 fTarget->resetVertexSource();
285 return false;
286 }
287 GrAssert(NULL != idxBase);
288 idx = idxBase;
289 }
290
bsalomon@google.com7d4679a2011-09-02 22:06:24 +0000291 fSubpathVertCount.reset(fSubpathCount);
bsalomon@google.com30085192011-08-19 15:42:31 +0000292
293 GrPoint pts[4];
294
295 bool first = true;
296 int subpath = 0;
297
298 SkPath::Iter iter(*fPath, false);
299
300 for (;;) {
301 GrPathCmd cmd = (GrPathCmd)iter.next(pts);
302 switch (cmd) {
303 case kMove_PathCmd:
304 if (!first) {
305 uint16_t currIdx = (uint16_t) (vert - base);
306 fSubpathVertCount[subpath] = currIdx - subpathIdxStart;
307 subpathIdxStart = currIdx;
308 ++subpath;
309 }
310 *vert = pts[0];
311 vert++;
312 break;
313 case kLine_PathCmd:
314 if (fUseIndexedDraw) {
315 uint16_t prevIdx = (uint16_t)(vert - base) - 1;
316 append_countour_edge_indices(fFill, subpathIdxStart,
317 prevIdx, &idx);
318 }
319 *(vert++) = pts[1];
320 break;
321 case kQuadratic_PathCmd: {
322 // first pt of quad is the pt we ended on in previous step
323 uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1;
324 uint16_t numPts = (uint16_t)
325 GrPathUtils::generateQuadraticPoints(
326 pts[0], pts[1], pts[2],
327 srcSpaceTolSqd, &vert,
328 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
329 if (fUseIndexedDraw) {
330 for (uint16_t i = 0; i < numPts; ++i) {
331 append_countour_edge_indices(fFill, subpathIdxStart,
332 firstQPtIdx + i, &idx);
333 }
334 }
335 break;
336 }
337 case kCubic_PathCmd: {
338 // first pt of cubic is the pt we ended on in previous step
339 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1;
340 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
341 pts[0], pts[1], pts[2], pts[3],
342 srcSpaceTolSqd, &vert,
343 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
344 if (fUseIndexedDraw) {
345 for (uint16_t i = 0; i < numPts; ++i) {
346 append_countour_edge_indices(fFill, subpathIdxStart,
347 firstCPtIdx + i, &idx);
348 }
349 }
350 break;
351 }
352 case kClose_PathCmd:
353 break;
354 case kEnd_PathCmd:
355 uint16_t currIdx = (uint16_t) (vert - base);
356 fSubpathVertCount[subpath] = currIdx - subpathIdxStart;
357 goto FINISHED;
358 }
359 first = false;
360 }
361FINISHED:
362 GrAssert((vert - base) <= maxPts);
363 GrAssert((idx - idxBase) <= maxIdxs);
364
365 fVertexCnt = vert - base;
366 fIndexCnt = idx - idxBase;
367
368 if (fTranslate.fX || fTranslate.fY) {
369 int count = vert - base;
370 for (int i = 0; i < count; i++) {
371 base[i].offset(fTranslate.fX, fTranslate.fY);
372 }
373 }
374 }
375 // set these at the end so if we failed on first drawPath inside a
376 // setPath/clearPath block we won't assume geom was created on a subsequent
377 // drawPath in the same block.
378 fPreviousSrcTol = srcSpaceTol;
379 fPreviousStages = stages;
380 return true;
381}
382
383void GrDefaultPathRenderer::onDrawPath(GrDrawTarget::StageBitfield stages,
384 bool stencilOnly) {
385
bsalomon@google.com30085192011-08-19 15:42:31 +0000386 GrMatrix viewM = fTarget->getViewMatrix();
bsalomon@google.com181e9bd2011-09-07 18:42:30 +0000387 GrScalar tol = GR_Scalar1;
bsalomon@google.com38396322011-09-09 19:32:04 +0000388 tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, fPath->getBounds());
bsalomon@google.com30085192011-08-19 15:42:31 +0000389
bsalomon@google.com30085192011-08-19 15:42:31 +0000390 // FIXME: It's really dumb that we recreate the verts for a new vertex
391 // layout. We only do that because the GrDrawTarget API doesn't allow
392 // us to change the vertex layout after reserveVertexSpace(). We won't
393 // actually change the vertex data when the layout changes since all the
394 // stages reference the positions (rather than having separate tex coords)
395 // and we don't ever have per-vert colors. In practice our call sites
396 // won't change the stages in use inside a setPath / removePath pair. But
397 // it is a silly limitation of the GrDrawTarget design that should be fixed.
398 if (tol != fPreviousSrcTol ||
399 stages != fPreviousStages) {
400 if (!this->createGeom(tol, stages)) {
401 return;
402 }
403 }
404
405 GrAssert(NULL != fTarget);
406 GrDrawTarget::AutoStateRestore asr(fTarget);
407 bool colorWritesWereDisabled = fTarget->isColorWriteDisabled();
408 // face culling doesn't make sense here
409 GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
410
411 int passCount = 0;
412 const GrStencilSettings* passes[3];
413 GrDrawTarget::DrawFace drawFace[3];
414 bool reverse = false;
415 bool lastPassIsBounds;
416
417 if (kHairLine_PathFill == fFill) {
418 passCount = 1;
419 if (stencilOnly) {
420 passes[0] = &gDirectToStencil;
421 } else {
422 passes[0] = NULL;
423 }
424 lastPassIsBounds = false;
425 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
426 } else {
427 if (single_pass_path(*fTarget, *fPath, fFill)) {
428 passCount = 1;
429 if (stencilOnly) {
430 passes[0] = &gDirectToStencil;
431 } else {
432 passes[0] = NULL;
433 }
434 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
435 lastPassIsBounds = false;
436 } else {
437 switch (fFill) {
438 case kInverseEvenOdd_PathFill:
439 reverse = true;
440 // fallthrough
441 case kEvenOdd_PathFill:
442 passes[0] = &gEOStencilPass;
443 if (stencilOnly) {
444 passCount = 1;
445 lastPassIsBounds = false;
446 } else {
447 passCount = 2;
448 lastPassIsBounds = true;
449 if (reverse) {
450 passes[1] = &gInvEOColorPass;
451 } else {
452 passes[1] = &gEOColorPass;
453 }
454 }
455 drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace;
456 break;
457
458 case kInverseWinding_PathFill:
459 reverse = true;
460 // fallthrough
461 case kWinding_PathFill:
462 if (fSeparateStencil) {
463 if (fStencilWrapOps) {
464 passes[0] = &gWindStencilSeparateWithWrap;
465 } else {
466 passes[0] = &gWindStencilSeparateNoWrap;
467 }
468 passCount = 2;
469 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
470 } else {
471 if (fStencilWrapOps) {
472 passes[0] = &gWindSingleStencilWithWrapInc;
473 passes[1] = &gWindSingleStencilWithWrapDec;
474 } else {
475 passes[0] = &gWindSingleStencilNoWrapInc;
476 passes[1] = &gWindSingleStencilNoWrapDec;
477 }
478 // which is cw and which is ccw is arbitrary.
479 drawFace[0] = GrDrawTarget::kCW_DrawFace;
480 drawFace[1] = GrDrawTarget::kCCW_DrawFace;
481 passCount = 3;
482 }
483 if (stencilOnly) {
484 lastPassIsBounds = false;
485 --passCount;
486 } else {
487 lastPassIsBounds = true;
488 drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace;
489 if (reverse) {
490 passes[passCount-1] = &gInvWindColorPass;
491 } else {
492 passes[passCount-1] = &gWindColorPass;
493 }
494 }
495 break;
496 default:
497 GrAssert(!"Unknown path fFill!");
498 return;
499 }
500 }
501 }
502
503 {
bsalomon@google.com30085192011-08-19 15:42:31 +0000504 for (int p = 0; p < passCount; ++p) {
505 fTarget->setDrawFace(drawFace[p]);
506 if (NULL != passes[p]) {
507 fTarget->setStencil(*passes[p]);
508 }
509
510 if (lastPassIsBounds && (p == passCount-1)) {
511 if (!colorWritesWereDisabled) {
512 fTarget->disableState(GrDrawTarget::kNoColorWrites_StateBit);
513 }
514 GrRect bounds;
515 if (reverse) {
516 GrAssert(NULL != fTarget->getRenderTarget());
517 // draw over the whole world.
518 bounds.setLTRB(0, 0,
519 GrIntToScalar(fTarget->getRenderTarget()->width()),
520 GrIntToScalar(fTarget->getRenderTarget()->height()));
521 GrMatrix vmi;
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000522 // mapRect through persp matrix may not be correct
523 if (!fTarget->getViewMatrix().hasPerspective() &&
524 fTarget->getViewInverse(&vmi)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000525 vmi.mapRect(&bounds);
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000526 } else {
527 if (stages) {
528 if (!fTarget->getViewInverse(&vmi)) {
529 GrPrintf("Could not invert matrix.");
530 return;
531 }
532 fTarget->preConcatSamplerMatrices(stages, vmi);
533 }
534 fTarget->setViewMatrix(GrMatrix::I());
bsalomon@google.com30085192011-08-19 15:42:31 +0000535 }
536 } else {
537 bounds = fPath->getBounds();
538 bounds.offset(fTranslate);
539 }
540 GrDrawTarget::AutoGeometryPush agp(fTarget);
541 fTarget->drawSimpleRect(bounds, NULL, stages);
542 } else {
543 if (passCount > 1) {
544 fTarget->enableState(GrDrawTarget::kNoColorWrites_StateBit);
545 }
546 if (fUseIndexedDraw) {
547 fTarget->drawIndexed(fPrimitiveType, 0, 0,
548 fVertexCnt, fIndexCnt);
549 } else {
550 int baseVertex = 0;
551 for (int sp = 0; sp < fSubpathCount; ++sp) {
552 fTarget->drawNonIndexed(fPrimitiveType, baseVertex,
553 fSubpathVertCount[sp]);
554 baseVertex += fSubpathVertCount[sp];
555 }
556 }
557 }
558 }
559 }
560}
561
562void GrDefaultPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
563 this->onDrawPath(stages, false);
564}
565
566void GrDefaultPathRenderer::drawPathToStencil() {
567 GrAssert(kInverseEvenOdd_PathFill != fFill);
568 GrAssert(kInverseWinding_PathFill != fFill);
569 this->onDrawPath(0, true);
570}