blob: b8b7f6253cbe5a2134571a8ebc5ae8c4f7bf34b6 [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"
13#include "SkTrace.h"
14
15
16GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
17 bool stencilWrapOpsSupport)
18 : fSeparateStencil(separateStencilSupport)
19 , fStencilWrapOps(stencilWrapOpsSupport)
20 , fSubpathCount(0)
21 , fSubpathVertCount(0)
22 , fPreviousSrcTol(-GR_Scalar1)
23 , fPreviousStages(-1) {
24 fTarget = NULL;
25}
26
27////////////////////////////////////////////////////////////////////////////////
28// Stencil rules for paths
29
30////// Even/Odd
31
32static const GrStencilSettings gEOStencilPass = {
33 kInvert_StencilOp, kInvert_StencilOp,
34 kKeep_StencilOp, kKeep_StencilOp,
35 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
36 0xffffffff, 0xffffffff,
37 0xffffffff, 0xffffffff,
38 0xffffffff, 0xffffffff
39};
40
41// ok not to check clip b/c stencil pass only wrote inside clip
42static const GrStencilSettings gEOColorPass = {
43 kZero_StencilOp, kZero_StencilOp,
44 kZero_StencilOp, kZero_StencilOp,
45 kNotEqual_StencilFunc, kNotEqual_StencilFunc,
46 0xffffffff, 0xffffffff,
47 0x0, 0x0,
48 0xffffffff, 0xffffffff
49};
50
51// have to check clip b/c outside clip will always be zero.
52static const GrStencilSettings gInvEOColorPass = {
53 kZero_StencilOp, kZero_StencilOp,
54 kZero_StencilOp, kZero_StencilOp,
55 kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
56 0xffffffff, 0xffffffff,
57 0x0, 0x0,
58 0xffffffff, 0xffffffff
59};
60
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
67static const GrStencilSettings gWindStencilSeparateWithWrap = {
68 kIncWrap_StencilOp, kDecWrap_StencilOp,
69 kKeep_StencilOp, kKeep_StencilOp,
70 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
71 0xffffffff, 0xffffffff,
72 0xffffffff, 0xffffffff,
73 0xffffffff, 0xffffffff
74};
75
76// if inc'ing the max value, invert to make 0
77// if dec'ing zero invert to make all ones.
78// we can't avoid touching the stencil on both passing and
79// failing, so we can't resctrict ourselves to the clip.
80static const GrStencilSettings gWindStencilSeparateNoWrap = {
81 kInvert_StencilOp, kInvert_StencilOp,
82 kIncClamp_StencilOp, kDecClamp_StencilOp,
83 kEqual_StencilFunc, kEqual_StencilFunc,
84 0xffffffff, 0xffffffff,
85 0xffffffff, 0x0,
86 0xffffffff, 0xffffffff
87};
88
89// When there are no separate faces we do two passes to setup the winding rule
90// stencil. First we draw the front faces and inc, then we draw the back faces
91// and dec. These are same as the above two split into the incrementing and
92// decrementing passes.
93static const GrStencilSettings gWindSingleStencilWithWrapInc = {
94 kIncWrap_StencilOp, kIncWrap_StencilOp,
95 kKeep_StencilOp, kKeep_StencilOp,
96 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
97 0xffffffff, 0xffffffff,
98 0xffffffff, 0xffffffff,
99 0xffffffff, 0xffffffff
100};
101static const GrStencilSettings gWindSingleStencilWithWrapDec = {
102 kDecWrap_StencilOp, kDecWrap_StencilOp,
103 kKeep_StencilOp, kKeep_StencilOp,
104 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
105 0xffffffff, 0xffffffff,
106 0xffffffff, 0xffffffff,
107 0xffffffff, 0xffffffff
108};
109static const GrStencilSettings gWindSingleStencilNoWrapInc = {
110 kInvert_StencilOp, kInvert_StencilOp,
111 kIncClamp_StencilOp, kIncClamp_StencilOp,
112 kEqual_StencilFunc, kEqual_StencilFunc,
113 0xffffffff, 0xffffffff,
114 0xffffffff, 0xffffffff,
115 0xffffffff, 0xffffffff
116};
117static const GrStencilSettings gWindSingleStencilNoWrapDec = {
118 kInvert_StencilOp, kInvert_StencilOp,
119 kDecClamp_StencilOp, kDecClamp_StencilOp,
120 kEqual_StencilFunc, kEqual_StencilFunc,
121 0xffffffff, 0xffffffff,
122 0x0, 0x0,
123 0xffffffff, 0xffffffff
124};
125
126static const GrStencilSettings gWindColorPass = {
127 kZero_StencilOp, kZero_StencilOp,
128 kZero_StencilOp, kZero_StencilOp,
129 kNonZeroIfInClip_StencilFunc, kNonZeroIfInClip_StencilFunc,
130 0xffffffff, 0xffffffff,
131 0x0, 0x0,
132 0xffffffff, 0xffffffff
133};
134
135static const GrStencilSettings gInvWindColorPass = {
136 kZero_StencilOp, kZero_StencilOp,
137 kZero_StencilOp, kZero_StencilOp,
138 kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
139 0xffffffff, 0xffffffff,
140 0x0, 0x0,
141 0xffffffff, 0xffffffff
142};
143
144////// Normal render to stencil
145
146// Sometimes the default path renderer can draw a path directly to the stencil
147// buffer without having to first resolve the interior / exterior.
148static const GrStencilSettings gDirectToStencil = {
149 kZero_StencilOp, kZero_StencilOp,
150 kIncClamp_StencilOp, kIncClamp_StencilOp,
151 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
152 0xffffffff, 0xffffffff,
153 0x0, 0x0,
154 0xffffffff, 0xffffffff
155};
156
157////////////////////////////////////////////////////////////////////////////////
158// Helpers for drawPath
159
160static GrConvexHint getConvexHint(const SkPath& path) {
161 return path.isConvex() ? kConvex_ConvexHint : kConcave_ConvexHint;
162}
163
164#define STENCIL_OFF 0 // Always disable stencil (even when needed)
165
166static inline bool single_pass_path(const GrDrawTarget& target,
167 const GrPath& path,
168 GrPathFill fill) {
169#if STENCIL_OFF
170 return true;
171#else
172 if (kEvenOdd_PathFill == fill) {
173 GrConvexHint hint = getConvexHint(path);
174 return hint == kConvex_ConvexHint ||
175 hint == kNonOverlappingConvexPieces_ConvexHint;
176 } else if (kWinding_PathFill == fill) {
177 GrConvexHint hint = getConvexHint(path);
178 return hint == kConvex_ConvexHint ||
179 hint == kNonOverlappingConvexPieces_ConvexHint ||
180 (hint == kSameWindingConvexPieces_ConvexHint &&
181 target.canDisableBlend() && !target.isDitherState());
182
183 }
184 return false;
185#endif
186}
187
188bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target,
189 const GrPath& path,
190 GrPathFill fill) const {
191 return !single_pass_path(*target, path, fill);
192}
193
194void GrDefaultPathRenderer::pathWillClear() {
195 fSubpathVertCount.realloc(0);
196 fTarget->resetVertexSource();
197 if (fUseIndexedDraw) {
198 fTarget->resetIndexSource();
199 }
200 fPreviousSrcTol = -GR_Scalar1;
201 fPreviousStages = -1;
202}
203
204static inline void append_countour_edge_indices(GrPathFill fillType,
205 uint16_t fanCenterIdx,
206 uint16_t edgeV0Idx,
207 uint16_t** indices) {
208 // when drawing lines we're appending line segments along
209 // the contour. When applying the other fill rules we're
210 // drawing triangle fans around fanCenterIdx.
211 if (kHairLine_PathFill != fillType) {
212 *((*indices)++) = fanCenterIdx;
213 }
214 *((*indices)++) = edgeV0Idx;
215 *((*indices)++) = edgeV0Idx + 1;
216}
217
218bool GrDefaultPathRenderer::createGeom(GrScalar srcSpaceTol,
219 GrDrawTarget::StageBitfield stages) {
220 {
221 SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom");
222
223 GrScalar srcSpaceTolSqd = GrMul(srcSpaceTol, srcSpaceTol);
224 int maxPts = GrPathUtils::worstCasePointCount(*fPath, &fSubpathCount,
225 srcSpaceTol);
226
227 if (maxPts <= 0) {
228 return false;
229 }
230 if (maxPts > ((int)SK_MaxU16 + 1)) {
231 GrPrintf("Path not rendered, too many verts (%d)\n", maxPts);
232 return false;
233 }
234
235 GrVertexLayout layout = 0;
236 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
237 if ((1 << s) & stages) {
238 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
239 }
240 }
241
242 fUseIndexedDraw = fSubpathCount > 1;
243
244 int maxIdxs = 0;
245 if (kHairLine_PathFill == fFill) {
246 if (fUseIndexedDraw) {
247 maxIdxs = 2 * maxPts;
248 fPrimitiveType = kLines_PrimitiveType;
249 } else {
250 fPrimitiveType = kLineStrip_PrimitiveType;
251 }
252 } else {
253 if (fUseIndexedDraw) {
254 maxIdxs = 3 * maxPts;
255 fPrimitiveType = kTriangles_PrimitiveType;
256 } else {
257 fPrimitiveType = kTriangleFan_PrimitiveType;
258 }
259 }
260
261 GrPoint* base;
262 if (!fTarget->reserveVertexSpace(layout, maxPts, (void**)&base)) {
263 return false;
264 }
265 GrAssert(NULL != base);
266 GrPoint* vert = base;
267
268 uint16_t* idxBase = NULL;
269 uint16_t* idx = NULL;
270 uint16_t subpathIdxStart = 0;
271 if (fUseIndexedDraw) {
272 if (!fTarget->reserveIndexSpace(maxIdxs, (void**)&idxBase)) {
273 fTarget->resetVertexSource();
274 return false;
275 }
276 GrAssert(NULL != idxBase);
277 idx = idxBase;
278 }
279
280 fSubpathVertCount.realloc(fSubpathCount);
281
282 GrPoint pts[4];
283
284 bool first = true;
285 int subpath = 0;
286
287 SkPath::Iter iter(*fPath, false);
288
289 for (;;) {
290 GrPathCmd cmd = (GrPathCmd)iter.next(pts);
291 switch (cmd) {
292 case kMove_PathCmd:
293 if (!first) {
294 uint16_t currIdx = (uint16_t) (vert - base);
295 fSubpathVertCount[subpath] = currIdx - subpathIdxStart;
296 subpathIdxStart = currIdx;
297 ++subpath;
298 }
299 *vert = pts[0];
300 vert++;
301 break;
302 case kLine_PathCmd:
303 if (fUseIndexedDraw) {
304 uint16_t prevIdx = (uint16_t)(vert - base) - 1;
305 append_countour_edge_indices(fFill, subpathIdxStart,
306 prevIdx, &idx);
307 }
308 *(vert++) = pts[1];
309 break;
310 case kQuadratic_PathCmd: {
311 // first pt of quad is the pt we ended on in previous step
312 uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1;
313 uint16_t numPts = (uint16_t)
314 GrPathUtils::generateQuadraticPoints(
315 pts[0], pts[1], pts[2],
316 srcSpaceTolSqd, &vert,
317 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
318 if (fUseIndexedDraw) {
319 for (uint16_t i = 0; i < numPts; ++i) {
320 append_countour_edge_indices(fFill, subpathIdxStart,
321 firstQPtIdx + i, &idx);
322 }
323 }
324 break;
325 }
326 case kCubic_PathCmd: {
327 // first pt of cubic is the pt we ended on in previous step
328 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1;
329 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
330 pts[0], pts[1], pts[2], pts[3],
331 srcSpaceTolSqd, &vert,
332 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
333 if (fUseIndexedDraw) {
334 for (uint16_t i = 0; i < numPts; ++i) {
335 append_countour_edge_indices(fFill, subpathIdxStart,
336 firstCPtIdx + i, &idx);
337 }
338 }
339 break;
340 }
341 case kClose_PathCmd:
342 break;
343 case kEnd_PathCmd:
344 uint16_t currIdx = (uint16_t) (vert - base);
345 fSubpathVertCount[subpath] = currIdx - subpathIdxStart;
346 goto FINISHED;
347 }
348 first = false;
349 }
350FINISHED:
351 GrAssert((vert - base) <= maxPts);
352 GrAssert((idx - idxBase) <= maxIdxs);
353
354 fVertexCnt = vert - base;
355 fIndexCnt = idx - idxBase;
356
357 if (fTranslate.fX || fTranslate.fY) {
358 int count = vert - base;
359 for (int i = 0; i < count; i++) {
360 base[i].offset(fTranslate.fX, fTranslate.fY);
361 }
362 }
363 }
364 // set these at the end so if we failed on first drawPath inside a
365 // setPath/clearPath block we won't assume geom was created on a subsequent
366 // drawPath in the same block.
367 fPreviousSrcTol = srcSpaceTol;
368 fPreviousStages = stages;
369 return true;
370}
371
372void GrDefaultPathRenderer::onDrawPath(GrDrawTarget::StageBitfield stages,
373 bool stencilOnly) {
374
375 SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath",
376 "points", SkStringPrintf("%i", path.countPoints()).c_str());
377
378 GrMatrix viewM = fTarget->getViewMatrix();
379 // In order to tesselate the path we get a bound on how much the matrix can
380 // stretch when mapping to screen coordinates.
381 GrScalar stretch = viewM.getMaxStretch();
382 bool useStretch = stretch > 0;
383 GrScalar tol = fCurveTolerance;
384
385 if (!useStretch) {
386 // TODO: deal with perspective in some better way.
387 tol /= 10;
388 } else {
389 tol = GrScalarDiv(tol, stretch);
390 }
391 // 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
410 GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
411
412 int passCount = 0;
413 const GrStencilSettings* passes[3];
414 GrDrawTarget::DrawFace drawFace[3];
415 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;
426 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
427 } 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 }
435 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
436 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 }
456 drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace;
457 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;
470 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
471 } 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.
480 drawFace[0] = GrDrawTarget::kCW_DrawFace;
481 drawFace[1] = GrDrawTarget::kCCW_DrawFace;
482 passCount = 3;
483 }
484 if (stencilOnly) {
485 lastPassIsBounds = false;
486 --passCount;
487 } else {
488 lastPassIsBounds = true;
489 drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace;
490 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 {
505 SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath::renderPasses",
506 "verts", SkStringPrintf("%i", vert - base).c_str());
507 for (int p = 0; p < passCount; ++p) {
508 fTarget->setDrawFace(drawFace[p]);
509 if (NULL != passes[p]) {
510 fTarget->setStencil(*passes[p]);
511 }
512
513 if (lastPassIsBounds && (p == passCount-1)) {
514 if (!colorWritesWereDisabled) {
515 fTarget->disableState(GrDrawTarget::kNoColorWrites_StateBit);
516 }
517 GrRect bounds;
518 if (reverse) {
519 GrAssert(NULL != fTarget->getRenderTarget());
520 // draw over the whole world.
521 bounds.setLTRB(0, 0,
522 GrIntToScalar(fTarget->getRenderTarget()->width()),
523 GrIntToScalar(fTarget->getRenderTarget()->height()));
524 GrMatrix vmi;
525 if (fTarget->getViewInverse(&vmi)) {
526 vmi.mapRect(&bounds);
527 }
528 } else {
529 bounds = fPath->getBounds();
530 bounds.offset(fTranslate);
531 }
532 GrDrawTarget::AutoGeometryPush agp(fTarget);
533 fTarget->drawSimpleRect(bounds, NULL, stages);
534 } else {
535 if (passCount > 1) {
536 fTarget->enableState(GrDrawTarget::kNoColorWrites_StateBit);
537 }
538 if (fUseIndexedDraw) {
539 fTarget->drawIndexed(fPrimitiveType, 0, 0,
540 fVertexCnt, fIndexCnt);
541 } else {
542 int baseVertex = 0;
543 for (int sp = 0; sp < fSubpathCount; ++sp) {
544 fTarget->drawNonIndexed(fPrimitiveType, baseVertex,
545 fSubpathVertCount[sp]);
546 baseVertex += fSubpathVertCount[sp];
547 }
548 }
549 }
550 }
551 }
552}
553
554void GrDefaultPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
555 this->onDrawPath(stages, false);
556}
557
558void GrDefaultPathRenderer::drawPathToStencil() {
559 GrAssert(kInverseEvenOdd_PathFill != fFill);
560 GrAssert(kInverseWinding_PathFill != fFill);
561 this->onDrawPath(0, true);
562}