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