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