blob: 5bea5e6b981e05f1d50a4cd6f4b2921dc7550df1 [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
bsalomon@google.comd302f142011-03-03 13:54:13 +00009GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
10 bool stencilWrapOpsSupport)
11 : fSeparateStencil(separateStencilSupport),
12 fStencilWrapOps(stencilWrapOpsSupport) {
bsalomon@google.comffca4002011-02-22 20:34:01 +000013
14}
15
16////////////////////////////////////////////////////////////////////////////////
bsalomon@google.comd302f142011-03-03 13:54:13 +000017// Stencil rules for paths
18
19////// Even/Odd
20
21static const GrStencilSettings gEOStencilPass = {
22 kInvert_StencilOp, kInvert_StencilOp,
23 kKeep_StencilOp, kKeep_StencilOp,
24 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
25 0xffffffff, 0xffffffff,
26 0xffffffff, 0xffffffff,
27 0xffffffff, 0xffffffff
28};
29
30// ok not to check clip b/c stencil pass only wrote inside clip
31static const GrStencilSettings gEOColorPass = {
32 kZero_StencilOp, kZero_StencilOp,
33 kZero_StencilOp, kZero_StencilOp,
34 kNotEqual_StencilFunc, kNotEqual_StencilFunc,
35 0xffffffff, 0xffffffff,
36 0x0, 0x0,
37 0xffffffff, 0xffffffff
38};
39
40// have to check clip b/c outside clip will always be zero.
41static const GrStencilSettings gInvEOColorPass = {
42 kZero_StencilOp, kZero_StencilOp,
43 kZero_StencilOp, kZero_StencilOp,
44 kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
45 0xffffffff, 0xffffffff,
46 0x0, 0x0,
47 0xffffffff, 0xffffffff
48};
49
50////// Winding
51
52// when we have separate stencil we increment front faces / decrement back faces
53// when we don't have wrap incr and decr we use the stencil test to simulate
54// them.
55
56static const GrStencilSettings gWindStencilSeparateWithWrap = {
57 kIncWrap_StencilOp, kDecWrap_StencilOp,
58 kKeep_StencilOp, kKeep_StencilOp,
59 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
60 0xffffffff, 0xffffffff,
61 0xffffffff, 0xffffffff,
62 0xffffffff, 0xffffffff
63};
64
65// if inc'ing the max value, invert to make 0
66// if dec'ing zero invert to make all ones.
67// we can't avoid touching the stencil on both passing and
68// failing, so we can't resctrict ourselves to the clip.
69static const GrStencilSettings gWindStencilSeparateNoWrap = {
70 kInvert_StencilOp, kInvert_StencilOp,
71 kIncClamp_StencilOp, kDecClamp_StencilOp,
72 kEqual_StencilFunc, kEqual_StencilFunc,
73 0xffffffff, 0xffffffff,
74 0xffffffff, 0x0,
75 0xffffffff, 0xffffffff
76};
77
78// When there are no separate faces we do two passes to setup the winding rule
79// stencil. First we draw the front faces and inc, then we draw the back faces
80// and dec. These are same as the above two split into the incrementing and
81// decrementing passes.
82static const GrStencilSettings gWindSingleStencilWithWrapInc = {
83 kIncWrap_StencilOp, kIncWrap_StencilOp,
84 kKeep_StencilOp, kKeep_StencilOp,
85 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
86 0xffffffff, 0xffffffff,
87 0xffffffff, 0xffffffff,
88 0xffffffff, 0xffffffff
89};
90static const GrStencilSettings gWindSingleStencilWithWrapDec = {
91 kDecWrap_StencilOp, kDecWrap_StencilOp,
92 kKeep_StencilOp, kKeep_StencilOp,
93 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
94 0xffffffff, 0xffffffff,
95 0xffffffff, 0xffffffff,
96 0xffffffff, 0xffffffff
97};
98static const GrStencilSettings gWindSingleStencilNoWrapInc = {
99 kInvert_StencilOp, kInvert_StencilOp,
100 kIncClamp_StencilOp, kIncClamp_StencilOp,
101 kEqual_StencilFunc, kEqual_StencilFunc,
102 0xffffffff, 0xffffffff,
103 0xffffffff, 0xffffffff,
104 0xffffffff, 0xffffffff
105};
106static const GrStencilSettings gWindSingleStencilNoWrapDec = {
107 kInvert_StencilOp, kInvert_StencilOp,
108 kDecClamp_StencilOp, kDecClamp_StencilOp,
109 kEqual_StencilFunc, kEqual_StencilFunc,
110 0xffffffff, 0xffffffff,
111 0x0, 0x0,
112 0xffffffff, 0xffffffff
113};
114
115static const GrStencilSettings gWindColorPass = {
116 kZero_StencilOp, kZero_StencilOp,
117 kZero_StencilOp, kZero_StencilOp,
118 kNonZeroIfInClip_StencilFunc, kNonZeroIfInClip_StencilFunc,
119 0xffffffff, 0xffffffff,
120 0x0, 0x0,
121 0xffffffff, 0xffffffff
122};
123
124static const GrStencilSettings gInvWindColorPass = {
125 kZero_StencilOp, kZero_StencilOp,
126 kZero_StencilOp, kZero_StencilOp,
127 kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
128 0xffffffff, 0xffffffff,
129 0x0, 0x0,
130 0xffffffff, 0xffffffff
131};
132
133////// Normal render to stencil
134
135// Sometimes the default path renderer can draw a path directly to the stencil
136// buffer without having to first resolve the interior / exterior.
137static const GrStencilSettings gDirectToStencil = {
138 kZero_StencilOp, kZero_StencilOp,
139 kIncClamp_StencilOp, kIncClamp_StencilOp,
140 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
141 0xffffffff, 0xffffffff,
142 0x0, 0x0,
143 0xffffffff, 0xffffffff
144};
145
146////////////////////////////////////////////////////////////////////////////////
147// Helpers for drawPath
bsalomon@google.comffca4002011-02-22 20:34:01 +0000148
reed@google.com07f3ee12011-05-16 17:21:57 +0000149static GrConvexHint getConvexHint(const SkPath& path) {
150 return path.isConvex() ? kConvex_ConvexHint : kConcave_ConvexHint;
151}
152
bsalomon@google.comffca4002011-02-22 20:34:01 +0000153#define STENCIL_OFF 0 // Always disable stencil (even when needed)
bsalomon@google.comffca4002011-02-22 20:34:01 +0000154
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000155static inline bool single_pass_path(const GrDrawTarget& target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000156 const GrPath& path,
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000157 GrPathFill fill) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000158#if STENCIL_OFF
159 return true;
160#else
161 if (kEvenOdd_PathFill == fill) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000162 GrConvexHint hint = getConvexHint(path);
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000163 return hint == kConvex_ConvexHint ||
164 hint == kNonOverlappingConvexPieces_ConvexHint;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000165 } else if (kWinding_PathFill == fill) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000166 GrConvexHint hint = getConvexHint(path);
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000167 return hint == kConvex_ConvexHint ||
168 hint == kNonOverlappingConvexPieces_ConvexHint ||
169 (hint == kSameWindingConvexPieces_ConvexHint &&
bsalomon@google.comffca4002011-02-22 20:34:01 +0000170 target.canDisableBlend() && !target.isDitherState());
171
172 }
173 return false;
174#endif
175}
176
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000177bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000178 const GrPath& path,
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000179 GrPathFill fill) const {
reed@google.com07f3ee12011-05-16 17:21:57 +0000180 return !single_pass_path(*target, path, fill);
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000181}
182
bsalomon@google.combcdbbe62011-04-12 15:40:00 +0000183void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
184 GrDrawTarget::StageBitfield stages,
reed@google.com07f3ee12011-05-16 17:21:57 +0000185 const GrPath& path,
bsalomon@google.combcdbbe62011-04-12 15:40:00 +0000186 GrPathFill fill,
187 const GrPoint* translate,
188 bool stencilOnly) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000189
190 GrDrawTarget::AutoStateRestore asr(target);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000191 bool colorWritesWereDisabled = target->isColorWriteDisabled();
192 // face culling doesn't make sense here
193 GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
bsalomon@google.comffca4002011-02-22 20:34:01 +0000194
195 GrMatrix viewM = target->getViewMatrix();
196 // In order to tesselate the path we get a bound on how much the matrix can
197 // stretch when mapping to screen coordinates.
198 GrScalar stretch = viewM.getMaxStretch();
199 bool useStretch = stretch > 0;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000200 GrScalar tol = GrPathUtils::gTolerance;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000201
202 if (!useStretch) {
203 // TODO: deal with perspective in some better way.
204 tol /= 10;
205 } else {
206 GrScalar sinv = GR_Scalar1 / stretch;
207 tol = GrMul(tol, sinv);
208 }
209 GrScalar tolSqd = GrMul(tol, tol);
210
211 int subpathCnt;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000212 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000213
214 GrVertexLayout layout = 0;
215 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
216 if ((1 << s) & stages) {
217 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
218 }
219 }
220
221 // add 4 to hold the bounding rect
222 GrDrawTarget::AutoReleaseGeometry arg(target, layout, maxPts + 4, 0);
223
224 GrPoint* base = (GrPoint*) arg.vertices();
225 GrPoint* vert = base;
226 GrPoint* subpathBase = base;
227
228 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
229
bsalomon@google.comffca4002011-02-22 20:34:01 +0000230 // TODO: use primitve restart if available rather than multiple draws
231 GrPrimitiveType type;
232 int passCount = 0;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000233 const GrStencilSettings* passes[3];
234 GrDrawTarget::DrawFace drawFace[3];
bsalomon@google.comffca4002011-02-22 20:34:01 +0000235 bool reverse = false;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000236 bool lastPassIsBounds;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000237
238 if (kHairLine_PathFill == fill) {
239 type = kLineStrip_PrimitiveType;
240 passCount = 1;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000241 if (stencilOnly) {
242 passes[0] = &gDirectToStencil;
243 } else {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000244 passes[0] = NULL;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000245 }
246 lastPassIsBounds = false;
247 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000248 } else {
249 type = kTriangleFan_PrimitiveType;
reed@google.com07f3ee12011-05-16 17:21:57 +0000250 if (single_pass_path(*target, path, fill)) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000251 passCount = 1;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000252 if (stencilOnly) {
253 passes[0] = &gDirectToStencil;
254 } else {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000255 passes[0] = NULL;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000256 }
257 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
258 lastPassIsBounds = false;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000259 } else {
260 switch (fill) {
261 case kInverseEvenOdd_PathFill:
262 reverse = true;
263 // fallthrough
264 case kEvenOdd_PathFill:
bsalomon@google.comd302f142011-03-03 13:54:13 +0000265 passes[0] = &gEOStencilPass;
266 if (stencilOnly) {
267 passCount = 1;
268 lastPassIsBounds = false;
269 } else {
270 passCount = 2;
271 lastPassIsBounds = true;
272 if (reverse) {
273 passes[1] = &gInvEOColorPass;
274 } else {
275 passes[1] = &gEOColorPass;
276 }
277 }
278 drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000279 break;
280
281 case kInverseWinding_PathFill:
282 reverse = true;
283 // fallthrough
284 case kWinding_PathFill:
bsalomon@google.comd302f142011-03-03 13:54:13 +0000285 if (fSeparateStencil) {
286 if (fStencilWrapOps) {
287 passes[0] = &gWindStencilSeparateWithWrap;
288 } else {
289 passes[0] = &gWindStencilSeparateNoWrap;
290 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000291 passCount = 2;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000292 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000293 } else {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000294 if (fStencilWrapOps) {
295 passes[0] = &gWindSingleStencilWithWrapInc;
296 passes[1] = &gWindSingleStencilWithWrapDec;
297 } else {
298 passes[0] = &gWindSingleStencilNoWrapInc;
299 passes[1] = &gWindSingleStencilNoWrapDec;
300 }
301 // which is cw and which is ccw is arbitrary.
302 drawFace[0] = GrDrawTarget::kCW_DrawFace;
303 drawFace[1] = GrDrawTarget::kCCW_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000304 passCount = 3;
305 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000306 if (stencilOnly) {
307 lastPassIsBounds = false;
308 --passCount;
309 } else {
310 lastPassIsBounds = true;
311 drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace;
312 if (reverse) {
313 passes[passCount-1] = &gInvWindColorPass;
314 } else {
315 passes[passCount-1] = &gWindColorPass;
316 }
317 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000318 break;
319 default:
320 GrAssert(!"Unknown path fill!");
321 return;
322 }
323 }
324 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000325
326 GrPoint pts[4];
327
328 bool first = true;
329 int subpath = 0;
330
reed@google.com07f3ee12011-05-16 17:21:57 +0000331 SkPath::Iter iter(path, false);
332
bsalomon@google.comffca4002011-02-22 20:34:01 +0000333 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000334 GrPathCmd cmd = (GrPathCmd)iter.next(pts);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000335 switch (cmd) {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000336 case kMove_PathCmd:
bsalomon@google.comffca4002011-02-22 20:34:01 +0000337 if (!first) {
338 subpathVertCount[subpath] = vert-subpathBase;
339 subpathBase = vert;
340 ++subpath;
341 }
342 *vert = pts[0];
343 vert++;
344 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000345 case kLine_PathCmd:
bsalomon@google.comffca4002011-02-22 20:34:01 +0000346 *vert = pts[1];
347 vert++;
348 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000349 case kQuadratic_PathCmd: {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000350 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
351 tolSqd, &vert,
352 GrPathUtils::quadraticPointCount(pts, tol));
bsalomon@google.comffca4002011-02-22 20:34:01 +0000353 break;
354 }
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000355 case kCubic_PathCmd: {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000356 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
357 tolSqd, &vert,
358 GrPathUtils::cubicPointCount(pts, tol));
bsalomon@google.comffca4002011-02-22 20:34:01 +0000359 break;
360 }
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000361 case kClose_PathCmd:
bsalomon@google.comffca4002011-02-22 20:34:01 +0000362 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000363 case kEnd_PathCmd:
bsalomon@google.comffca4002011-02-22 20:34:01 +0000364 subpathVertCount[subpath] = vert-subpathBase;
365 ++subpath; // this could be only in debug
366 goto FINISHED;
367 }
368 first = false;
369 }
370FINISHED:
371 GrAssert(subpath == subpathCnt);
372 GrAssert((vert - base) <= maxPts);
373
374 if (translate) {
375 int count = vert - base;
376 for (int i = 0; i < count; i++) {
377 base[i].offset(translate->fX, translate->fY);
378 }
379 }
380
bsalomon@google.comd302f142011-03-03 13:54:13 +0000381 // if we're stenciling we will follow with a pass that draws
382 // a bounding rect to set the color. We're stenciling when
383 // passCount > 1.
384 const int& boundVertexStart = maxPts;
385 GrPoint* boundsVerts = base + boundVertexStart;
386 if (lastPassIsBounds) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000387 GrRect bounds;
388 if (reverse) {
389 GrAssert(NULL != target->getRenderTarget());
390 // draw over the whole world.
391 bounds.setLTRB(0, 0,
392 GrIntToScalar(target->getRenderTarget()->width()),
393 GrIntToScalar(target->getRenderTarget()->height()));
394 GrMatrix vmi;
395 if (target->getViewInverse(&vmi)) {
396 vmi.mapRect(&bounds);
397 }
398 } else {
399 bounds.setBounds((GrPoint*)base, vert - base);
400 }
401 boundsVerts[0].setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight,
402 bounds.fBottom);
403 }
404
405 for (int p = 0; p < passCount; ++p) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000406 target->setDrawFace(drawFace[p]);
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000407 if (NULL != passes[p]) {
408 target->setStencil(*passes[p]);
409 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000410
411 if (lastPassIsBounds && (p == passCount-1)) {
412 if (!colorWritesWereDisabled) {
413 target->disableState(GrDrawTarget::kNoColorWrites_StateBit);
414 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000415 target->drawNonIndexed(kTriangleFan_PrimitiveType,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000416 boundVertexStart, 4);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000417
418 } else {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000419 if (passCount > 1) {
420 target->enableState(GrDrawTarget::kNoColorWrites_StateBit);
421 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000422 int baseVertex = 0;
423 for (int sp = 0; sp < subpathCnt; ++sp) {
424 target->drawNonIndexed(type,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000425 baseVertex,
426 subpathVertCount[sp]);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000427 baseVertex += subpathVertCount[sp];
428 }
429 }
430 }
431}
bsalomon@google.comd302f142011-03-03 13:54:13 +0000432
433void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
434 GrDrawTarget::StageBitfield stages,
reed@google.com07f3ee12011-05-16 17:21:57 +0000435 const GrPath& path,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000436 GrPathFill fill,
437 const GrPoint* translate) {
bsalomon@google.combcdbbe62011-04-12 15:40:00 +0000438 this->onDrawPath(target, stages, path, fill, translate, false);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000439}
440
441void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000442 const GrPath& path,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000443 GrPathFill fill,
444 const GrPoint* translate) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000445 GrAssert(kInverseEvenOdd_PathFill != fill);
446 GrAssert(kInverseWinding_PathFill != fill);
bsalomon@google.combcdbbe62011-04-12 15:40:00 +0000447 this->onDrawPath(target, 0, path, fill, translate, true);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000448}