blob: 1795bd4daecba9078fb2455baeb66b808ed40bb7 [file] [log] [blame]
bsalomon@google.comffca4002011-02-22 20:34:01 +00001#include "GrPathRenderer.h"
2
3#include "GrPoint.h"
4#include "GrDrawTarget.h"
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +00005#include "GrPathUtils.h"
bsalomon@google.comffca4002011-02-22 20:34:01 +00006#include "GrMemory.h"
7#include "GrTexture.h"
8
tomhudson@google.comd22b6e42011-06-24 15:53:40 +00009GrPathRenderer::GrPathRenderer()
10 : fCurveTolerance (GR_Scalar1) {
11
12}
13
bsalomon@google.comd302f142011-03-03 13:54:13 +000014GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
15 bool stencilWrapOpsSupport)
tomhudson@google.comd22b6e42011-06-24 15:53:40 +000016 : fSeparateStencil(separateStencilSupport)
17 , fStencilWrapOps(stencilWrapOpsSupport) {
bsalomon@google.comffca4002011-02-22 20:34:01 +000018
19}
20
21////////////////////////////////////////////////////////////////////////////////
bsalomon@google.comd302f142011-03-03 13:54:13 +000022// Stencil rules for paths
23
24////// Even/Odd
25
26static const GrStencilSettings gEOStencilPass = {
27 kInvert_StencilOp, kInvert_StencilOp,
28 kKeep_StencilOp, kKeep_StencilOp,
29 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
30 0xffffffff, 0xffffffff,
31 0xffffffff, 0xffffffff,
32 0xffffffff, 0xffffffff
33};
34
35// ok not to check clip b/c stencil pass only wrote inside clip
36static const GrStencilSettings gEOColorPass = {
37 kZero_StencilOp, kZero_StencilOp,
38 kZero_StencilOp, kZero_StencilOp,
39 kNotEqual_StencilFunc, kNotEqual_StencilFunc,
40 0xffffffff, 0xffffffff,
41 0x0, 0x0,
42 0xffffffff, 0xffffffff
43};
44
45// have to check clip b/c outside clip will always be zero.
46static const GrStencilSettings gInvEOColorPass = {
47 kZero_StencilOp, kZero_StencilOp,
48 kZero_StencilOp, kZero_StencilOp,
49 kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
50 0xffffffff, 0xffffffff,
51 0x0, 0x0,
52 0xffffffff, 0xffffffff
53};
54
55////// Winding
56
57// when we have separate stencil we increment front faces / decrement back faces
58// when we don't have wrap incr and decr we use the stencil test to simulate
59// them.
60
61static const GrStencilSettings gWindStencilSeparateWithWrap = {
62 kIncWrap_StencilOp, kDecWrap_StencilOp,
63 kKeep_StencilOp, kKeep_StencilOp,
64 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
65 0xffffffff, 0xffffffff,
66 0xffffffff, 0xffffffff,
67 0xffffffff, 0xffffffff
68};
69
70// if inc'ing the max value, invert to make 0
71// if dec'ing zero invert to make all ones.
72// we can't avoid touching the stencil on both passing and
73// failing, so we can't resctrict ourselves to the clip.
74static const GrStencilSettings gWindStencilSeparateNoWrap = {
75 kInvert_StencilOp, kInvert_StencilOp,
76 kIncClamp_StencilOp, kDecClamp_StencilOp,
77 kEqual_StencilFunc, kEqual_StencilFunc,
78 0xffffffff, 0xffffffff,
79 0xffffffff, 0x0,
80 0xffffffff, 0xffffffff
81};
82
83// When there are no separate faces we do two passes to setup the winding rule
84// stencil. First we draw the front faces and inc, then we draw the back faces
85// and dec. These are same as the above two split into the incrementing and
86// decrementing passes.
87static const GrStencilSettings gWindSingleStencilWithWrapInc = {
88 kIncWrap_StencilOp, kIncWrap_StencilOp,
89 kKeep_StencilOp, kKeep_StencilOp,
90 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
91 0xffffffff, 0xffffffff,
92 0xffffffff, 0xffffffff,
93 0xffffffff, 0xffffffff
94};
95static const GrStencilSettings gWindSingleStencilWithWrapDec = {
96 kDecWrap_StencilOp, kDecWrap_StencilOp,
97 kKeep_StencilOp, kKeep_StencilOp,
98 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
99 0xffffffff, 0xffffffff,
100 0xffffffff, 0xffffffff,
101 0xffffffff, 0xffffffff
102};
103static const GrStencilSettings gWindSingleStencilNoWrapInc = {
104 kInvert_StencilOp, kInvert_StencilOp,
105 kIncClamp_StencilOp, kIncClamp_StencilOp,
106 kEqual_StencilFunc, kEqual_StencilFunc,
107 0xffffffff, 0xffffffff,
108 0xffffffff, 0xffffffff,
109 0xffffffff, 0xffffffff
110};
111static const GrStencilSettings gWindSingleStencilNoWrapDec = {
112 kInvert_StencilOp, kInvert_StencilOp,
113 kDecClamp_StencilOp, kDecClamp_StencilOp,
114 kEqual_StencilFunc, kEqual_StencilFunc,
115 0xffffffff, 0xffffffff,
116 0x0, 0x0,
117 0xffffffff, 0xffffffff
118};
119
120static const GrStencilSettings gWindColorPass = {
121 kZero_StencilOp, kZero_StencilOp,
122 kZero_StencilOp, kZero_StencilOp,
123 kNonZeroIfInClip_StencilFunc, kNonZeroIfInClip_StencilFunc,
124 0xffffffff, 0xffffffff,
125 0x0, 0x0,
126 0xffffffff, 0xffffffff
127};
128
129static const GrStencilSettings gInvWindColorPass = {
130 kZero_StencilOp, kZero_StencilOp,
131 kZero_StencilOp, kZero_StencilOp,
132 kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
133 0xffffffff, 0xffffffff,
134 0x0, 0x0,
135 0xffffffff, 0xffffffff
136};
137
138////// Normal render to stencil
139
140// Sometimes the default path renderer can draw a path directly to the stencil
141// buffer without having to first resolve the interior / exterior.
142static const GrStencilSettings gDirectToStencil = {
143 kZero_StencilOp, kZero_StencilOp,
144 kIncClamp_StencilOp, kIncClamp_StencilOp,
145 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
146 0xffffffff, 0xffffffff,
147 0x0, 0x0,
148 0xffffffff, 0xffffffff
149};
150
151////////////////////////////////////////////////////////////////////////////////
152// Helpers for drawPath
bsalomon@google.comffca4002011-02-22 20:34:01 +0000153
reed@google.com07f3ee12011-05-16 17:21:57 +0000154static GrConvexHint getConvexHint(const SkPath& path) {
155 return path.isConvex() ? kConvex_ConvexHint : kConcave_ConvexHint;
156}
157
bsalomon@google.comffca4002011-02-22 20:34:01 +0000158#define STENCIL_OFF 0 // Always disable stencil (even when needed)
bsalomon@google.comffca4002011-02-22 20:34:01 +0000159
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000160static inline bool single_pass_path(const GrDrawTarget& target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000161 const GrPath& path,
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000162 GrPathFill fill) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000163#if STENCIL_OFF
164 return true;
165#else
166 if (kEvenOdd_PathFill == fill) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000167 GrConvexHint hint = getConvexHint(path);
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000168 return hint == kConvex_ConvexHint ||
169 hint == kNonOverlappingConvexPieces_ConvexHint;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000170 } else if (kWinding_PathFill == fill) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000171 GrConvexHint hint = getConvexHint(path);
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000172 return hint == kConvex_ConvexHint ||
173 hint == kNonOverlappingConvexPieces_ConvexHint ||
174 (hint == kSameWindingConvexPieces_ConvexHint &&
bsalomon@google.comffca4002011-02-22 20:34:01 +0000175 target.canDisableBlend() && !target.isDitherState());
176
177 }
178 return false;
179#endif
180}
181
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000182bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000183 const GrPath& path,
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000184 GrPathFill fill) const {
reed@google.com07f3ee12011-05-16 17:21:57 +0000185 return !single_pass_path(*target, path, fill);
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000186}
187
bsalomon@google.combcdbbe62011-04-12 15:40:00 +0000188void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
189 GrDrawTarget::StageBitfield stages,
reed@google.com07f3ee12011-05-16 17:21:57 +0000190 const GrPath& path,
bsalomon@google.combcdbbe62011-04-12 15:40:00 +0000191 GrPathFill fill,
192 const GrPoint* translate,
193 bool stencilOnly) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000194
195 GrDrawTarget::AutoStateRestore asr(target);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000196 bool colorWritesWereDisabled = target->isColorWriteDisabled();
197 // face culling doesn't make sense here
198 GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
bsalomon@google.comffca4002011-02-22 20:34:01 +0000199
200 GrMatrix viewM = target->getViewMatrix();
201 // In order to tesselate the path we get a bound on how much the matrix can
202 // stretch when mapping to screen coordinates.
203 GrScalar stretch = viewM.getMaxStretch();
204 bool useStretch = stretch > 0;
tomhudson@google.comd22b6e42011-06-24 15:53:40 +0000205 GrScalar tol = fCurveTolerance;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000206
207 if (!useStretch) {
208 // TODO: deal with perspective in some better way.
209 tol /= 10;
210 } else {
bungeman@google.com8c5753e2011-05-20 19:11:50 +0000211 tol = GrScalarDiv(tol, stretch);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000212 }
213 GrScalar tolSqd = GrMul(tol, tol);
214
215 int subpathCnt;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000216 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000217
218 GrVertexLayout layout = 0;
219 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
220 if ((1 << s) & stages) {
221 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
222 }
223 }
224
225 // add 4 to hold the bounding rect
226 GrDrawTarget::AutoReleaseGeometry arg(target, layout, maxPts + 4, 0);
227
228 GrPoint* base = (GrPoint*) arg.vertices();
229 GrPoint* vert = base;
230 GrPoint* subpathBase = base;
231
232 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
233
bsalomon@google.comffca4002011-02-22 20:34:01 +0000234 // TODO: use primitve restart if available rather than multiple draws
235 GrPrimitiveType type;
236 int passCount = 0;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000237 const GrStencilSettings* passes[3];
238 GrDrawTarget::DrawFace drawFace[3];
bsalomon@google.comffca4002011-02-22 20:34:01 +0000239 bool reverse = false;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000240 bool lastPassIsBounds;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000241
242 if (kHairLine_PathFill == fill) {
243 type = kLineStrip_PrimitiveType;
244 passCount = 1;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000245 if (stencilOnly) {
246 passes[0] = &gDirectToStencil;
247 } else {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000248 passes[0] = NULL;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000249 }
250 lastPassIsBounds = false;
251 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000252 } else {
253 type = kTriangleFan_PrimitiveType;
reed@google.com07f3ee12011-05-16 17:21:57 +0000254 if (single_pass_path(*target, path, fill)) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000255 passCount = 1;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000256 if (stencilOnly) {
257 passes[0] = &gDirectToStencil;
258 } else {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000259 passes[0] = NULL;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000260 }
261 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
262 lastPassIsBounds = false;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000263 } else {
264 switch (fill) {
265 case kInverseEvenOdd_PathFill:
266 reverse = true;
267 // fallthrough
268 case kEvenOdd_PathFill:
bsalomon@google.comd302f142011-03-03 13:54:13 +0000269 passes[0] = &gEOStencilPass;
270 if (stencilOnly) {
271 passCount = 1;
272 lastPassIsBounds = false;
273 } else {
274 passCount = 2;
275 lastPassIsBounds = true;
276 if (reverse) {
277 passes[1] = &gInvEOColorPass;
278 } else {
279 passes[1] = &gEOColorPass;
280 }
281 }
282 drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000283 break;
284
285 case kInverseWinding_PathFill:
286 reverse = true;
287 // fallthrough
288 case kWinding_PathFill:
bsalomon@google.comd302f142011-03-03 13:54:13 +0000289 if (fSeparateStencil) {
290 if (fStencilWrapOps) {
291 passes[0] = &gWindStencilSeparateWithWrap;
292 } else {
293 passes[0] = &gWindStencilSeparateNoWrap;
294 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000295 passCount = 2;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000296 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000297 } else {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000298 if (fStencilWrapOps) {
299 passes[0] = &gWindSingleStencilWithWrapInc;
300 passes[1] = &gWindSingleStencilWithWrapDec;
301 } else {
302 passes[0] = &gWindSingleStencilNoWrapInc;
303 passes[1] = &gWindSingleStencilNoWrapDec;
304 }
305 // which is cw and which is ccw is arbitrary.
306 drawFace[0] = GrDrawTarget::kCW_DrawFace;
307 drawFace[1] = GrDrawTarget::kCCW_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000308 passCount = 3;
309 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000310 if (stencilOnly) {
311 lastPassIsBounds = false;
312 --passCount;
313 } else {
314 lastPassIsBounds = true;
315 drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace;
316 if (reverse) {
317 passes[passCount-1] = &gInvWindColorPass;
318 } else {
319 passes[passCount-1] = &gWindColorPass;
320 }
321 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000322 break;
323 default:
324 GrAssert(!"Unknown path fill!");
325 return;
326 }
327 }
328 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000329
330 GrPoint pts[4];
331
332 bool first = true;
333 int subpath = 0;
334
reed@google.com07f3ee12011-05-16 17:21:57 +0000335 SkPath::Iter iter(path, false);
336
bsalomon@google.comffca4002011-02-22 20:34:01 +0000337 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000338 GrPathCmd cmd = (GrPathCmd)iter.next(pts);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000339 switch (cmd) {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000340 case kMove_PathCmd:
bsalomon@google.comffca4002011-02-22 20:34:01 +0000341 if (!first) {
342 subpathVertCount[subpath] = vert-subpathBase;
343 subpathBase = vert;
344 ++subpath;
345 }
346 *vert = pts[0];
347 vert++;
348 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000349 case kLine_PathCmd:
bsalomon@google.comffca4002011-02-22 20:34:01 +0000350 *vert = pts[1];
351 vert++;
352 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000353 case kQuadratic_PathCmd: {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000354 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
355 tolSqd, &vert,
356 GrPathUtils::quadraticPointCount(pts, tol));
bsalomon@google.comffca4002011-02-22 20:34:01 +0000357 break;
358 }
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000359 case kCubic_PathCmd: {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000360 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
361 tolSqd, &vert,
362 GrPathUtils::cubicPointCount(pts, tol));
bsalomon@google.comffca4002011-02-22 20:34:01 +0000363 break;
364 }
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000365 case kClose_PathCmd:
bsalomon@google.comffca4002011-02-22 20:34:01 +0000366 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000367 case kEnd_PathCmd:
bsalomon@google.comffca4002011-02-22 20:34:01 +0000368 subpathVertCount[subpath] = vert-subpathBase;
369 ++subpath; // this could be only in debug
370 goto FINISHED;
371 }
372 first = false;
373 }
374FINISHED:
375 GrAssert(subpath == subpathCnt);
376 GrAssert((vert - base) <= maxPts);
377
378 if (translate) {
379 int count = vert - base;
380 for (int i = 0; i < count; i++) {
381 base[i].offset(translate->fX, translate->fY);
382 }
383 }
384
bsalomon@google.comd302f142011-03-03 13:54:13 +0000385 // if we're stenciling we will follow with a pass that draws
386 // a bounding rect to set the color. We're stenciling when
387 // passCount > 1.
388 const int& boundVertexStart = maxPts;
389 GrPoint* boundsVerts = base + boundVertexStart;
390 if (lastPassIsBounds) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000391 GrRect bounds;
392 if (reverse) {
393 GrAssert(NULL != target->getRenderTarget());
394 // draw over the whole world.
395 bounds.setLTRB(0, 0,
396 GrIntToScalar(target->getRenderTarget()->width()),
397 GrIntToScalar(target->getRenderTarget()->height()));
398 GrMatrix vmi;
399 if (target->getViewInverse(&vmi)) {
400 vmi.mapRect(&bounds);
401 }
402 } else {
403 bounds.setBounds((GrPoint*)base, vert - base);
404 }
405 boundsVerts[0].setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight,
406 bounds.fBottom);
407 }
408
409 for (int p = 0; p < passCount; ++p) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000410 target->setDrawFace(drawFace[p]);
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000411 if (NULL != passes[p]) {
412 target->setStencil(*passes[p]);
413 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000414
415 if (lastPassIsBounds && (p == passCount-1)) {
416 if (!colorWritesWereDisabled) {
417 target->disableState(GrDrawTarget::kNoColorWrites_StateBit);
418 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000419 target->drawNonIndexed(kTriangleFan_PrimitiveType,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000420 boundVertexStart, 4);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000421
422 } else {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000423 if (passCount > 1) {
424 target->enableState(GrDrawTarget::kNoColorWrites_StateBit);
425 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000426 int baseVertex = 0;
427 for (int sp = 0; sp < subpathCnt; ++sp) {
428 target->drawNonIndexed(type,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000429 baseVertex,
430 subpathVertCount[sp]);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000431 baseVertex += subpathVertCount[sp];
432 }
433 }
434 }
435}
bsalomon@google.comd302f142011-03-03 13:54:13 +0000436
437void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
438 GrDrawTarget::StageBitfield stages,
reed@google.com07f3ee12011-05-16 17:21:57 +0000439 const GrPath& path,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000440 GrPathFill fill,
441 const GrPoint* translate) {
bsalomon@google.combcdbbe62011-04-12 15:40:00 +0000442 this->onDrawPath(target, stages, path, fill, translate, false);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000443}
444
445void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000446 const GrPath& path,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000447 GrPathFill fill,
448 const GrPoint* translate) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000449 GrAssert(kInverseEvenOdd_PathFill != fill);
450 GrAssert(kInverseWinding_PathFill != fill);
bsalomon@google.combcdbbe62011-04-12 15:40:00 +0000451 this->onDrawPath(target, 0, path, fill, translate, true);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000452}