blob: 58bb12e01d298d685bb47cc2cafeea85447a0786 [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 {
bungeman@google.com8c5753e2011-05-20 19:11:50 +0000206 tol = GrScalarDiv(tol, stretch);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000207 }
208 GrScalar tolSqd = GrMul(tol, tol);
209
210 int subpathCnt;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000211 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000212
213 GrVertexLayout layout = 0;
214 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
215 if ((1 << s) & stages) {
216 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
217 }
218 }
219
220 // add 4 to hold the bounding rect
221 GrDrawTarget::AutoReleaseGeometry arg(target, layout, maxPts + 4, 0);
222
223 GrPoint* base = (GrPoint*) arg.vertices();
224 GrPoint* vert = base;
225 GrPoint* subpathBase = base;
226
227 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
228
bsalomon@google.comffca4002011-02-22 20:34:01 +0000229 // TODO: use primitve restart if available rather than multiple draws
230 GrPrimitiveType type;
231 int passCount = 0;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000232 const GrStencilSettings* passes[3];
233 GrDrawTarget::DrawFace drawFace[3];
bsalomon@google.comffca4002011-02-22 20:34:01 +0000234 bool reverse = false;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000235 bool lastPassIsBounds;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000236
237 if (kHairLine_PathFill == fill) {
238 type = kLineStrip_PrimitiveType;
239 passCount = 1;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000240 if (stencilOnly) {
241 passes[0] = &gDirectToStencil;
242 } else {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000243 passes[0] = NULL;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000244 }
245 lastPassIsBounds = false;
246 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000247 } else {
248 type = kTriangleFan_PrimitiveType;
reed@google.com07f3ee12011-05-16 17:21:57 +0000249 if (single_pass_path(*target, path, fill)) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000250 passCount = 1;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000251 if (stencilOnly) {
252 passes[0] = &gDirectToStencil;
253 } else {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000254 passes[0] = NULL;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000255 }
256 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
257 lastPassIsBounds = false;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000258 } else {
259 switch (fill) {
260 case kInverseEvenOdd_PathFill:
261 reverse = true;
262 // fallthrough
263 case kEvenOdd_PathFill:
bsalomon@google.comd302f142011-03-03 13:54:13 +0000264 passes[0] = &gEOStencilPass;
265 if (stencilOnly) {
266 passCount = 1;
267 lastPassIsBounds = false;
268 } else {
269 passCount = 2;
270 lastPassIsBounds = true;
271 if (reverse) {
272 passes[1] = &gInvEOColorPass;
273 } else {
274 passes[1] = &gEOColorPass;
275 }
276 }
277 drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000278 break;
279
280 case kInverseWinding_PathFill:
281 reverse = true;
282 // fallthrough
283 case kWinding_PathFill:
bsalomon@google.comd302f142011-03-03 13:54:13 +0000284 if (fSeparateStencil) {
285 if (fStencilWrapOps) {
286 passes[0] = &gWindStencilSeparateWithWrap;
287 } else {
288 passes[0] = &gWindStencilSeparateNoWrap;
289 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000290 passCount = 2;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000291 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000292 } else {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000293 if (fStencilWrapOps) {
294 passes[0] = &gWindSingleStencilWithWrapInc;
295 passes[1] = &gWindSingleStencilWithWrapDec;
296 } else {
297 passes[0] = &gWindSingleStencilNoWrapInc;
298 passes[1] = &gWindSingleStencilNoWrapDec;
299 }
300 // which is cw and which is ccw is arbitrary.
301 drawFace[0] = GrDrawTarget::kCW_DrawFace;
302 drawFace[1] = GrDrawTarget::kCCW_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000303 passCount = 3;
304 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000305 if (stencilOnly) {
306 lastPassIsBounds = false;
307 --passCount;
308 } else {
309 lastPassIsBounds = true;
310 drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace;
311 if (reverse) {
312 passes[passCount-1] = &gInvWindColorPass;
313 } else {
314 passes[passCount-1] = &gWindColorPass;
315 }
316 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000317 break;
318 default:
319 GrAssert(!"Unknown path fill!");
320 return;
321 }
322 }
323 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000324
325 GrPoint pts[4];
326
327 bool first = true;
328 int subpath = 0;
329
reed@google.com07f3ee12011-05-16 17:21:57 +0000330 SkPath::Iter iter(path, false);
331
bsalomon@google.comffca4002011-02-22 20:34:01 +0000332 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000333 GrPathCmd cmd = (GrPathCmd)iter.next(pts);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000334 switch (cmd) {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000335 case kMove_PathCmd:
bsalomon@google.comffca4002011-02-22 20:34:01 +0000336 if (!first) {
337 subpathVertCount[subpath] = vert-subpathBase;
338 subpathBase = vert;
339 ++subpath;
340 }
341 *vert = pts[0];
342 vert++;
343 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000344 case kLine_PathCmd:
bsalomon@google.comffca4002011-02-22 20:34:01 +0000345 *vert = pts[1];
346 vert++;
347 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000348 case kQuadratic_PathCmd: {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000349 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
350 tolSqd, &vert,
351 GrPathUtils::quadraticPointCount(pts, tol));
bsalomon@google.comffca4002011-02-22 20:34:01 +0000352 break;
353 }
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000354 case kCubic_PathCmd: {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000355 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
356 tolSqd, &vert,
357 GrPathUtils::cubicPointCount(pts, tol));
bsalomon@google.comffca4002011-02-22 20:34:01 +0000358 break;
359 }
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000360 case kClose_PathCmd:
bsalomon@google.comffca4002011-02-22 20:34:01 +0000361 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000362 case kEnd_PathCmd:
bsalomon@google.comffca4002011-02-22 20:34:01 +0000363 subpathVertCount[subpath] = vert-subpathBase;
364 ++subpath; // this could be only in debug
365 goto FINISHED;
366 }
367 first = false;
368 }
369FINISHED:
370 GrAssert(subpath == subpathCnt);
371 GrAssert((vert - base) <= maxPts);
372
373 if (translate) {
374 int count = vert - base;
375 for (int i = 0; i < count; i++) {
376 base[i].offset(translate->fX, translate->fY);
377 }
378 }
379
bsalomon@google.comd302f142011-03-03 13:54:13 +0000380 // if we're stenciling we will follow with a pass that draws
381 // a bounding rect to set the color. We're stenciling when
382 // passCount > 1.
383 const int& boundVertexStart = maxPts;
384 GrPoint* boundsVerts = base + boundVertexStart;
385 if (lastPassIsBounds) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000386 GrRect bounds;
387 if (reverse) {
388 GrAssert(NULL != target->getRenderTarget());
389 // draw over the whole world.
390 bounds.setLTRB(0, 0,
391 GrIntToScalar(target->getRenderTarget()->width()),
392 GrIntToScalar(target->getRenderTarget()->height()));
393 GrMatrix vmi;
394 if (target->getViewInverse(&vmi)) {
395 vmi.mapRect(&bounds);
396 }
397 } else {
398 bounds.setBounds((GrPoint*)base, vert - base);
399 }
400 boundsVerts[0].setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight,
401 bounds.fBottom);
402 }
403
404 for (int p = 0; p < passCount; ++p) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000405 target->setDrawFace(drawFace[p]);
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000406 if (NULL != passes[p]) {
407 target->setStencil(*passes[p]);
408 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000409
410 if (lastPassIsBounds && (p == passCount-1)) {
411 if (!colorWritesWereDisabled) {
412 target->disableState(GrDrawTarget::kNoColorWrites_StateBit);
413 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000414 target->drawNonIndexed(kTriangleFan_PrimitiveType,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000415 boundVertexStart, 4);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000416
417 } else {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000418 if (passCount > 1) {
419 target->enableState(GrDrawTarget::kNoColorWrites_StateBit);
420 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000421 int baseVertex = 0;
422 for (int sp = 0; sp < subpathCnt; ++sp) {
423 target->drawNonIndexed(type,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000424 baseVertex,
425 subpathVertCount[sp]);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000426 baseVertex += subpathVertCount[sp];
427 }
428 }
429 }
430}
bsalomon@google.comd302f142011-03-03 13:54:13 +0000431
432void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
433 GrDrawTarget::StageBitfield stages,
reed@google.com07f3ee12011-05-16 17:21:57 +0000434 const GrPath& path,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000435 GrPathFill fill,
436 const GrPoint* translate) {
bsalomon@google.combcdbbe62011-04-12 15:40:00 +0000437 this->onDrawPath(target, stages, path, fill, translate, false);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000438}
439
440void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000441 const GrPath& path,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000442 GrPathFill fill,
443 const GrPoint* translate) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000444 GrAssert(kInverseEvenOdd_PathFill != fill);
445 GrAssert(kInverseWinding_PathFill != fill);
bsalomon@google.combcdbbe62011-04-12 15:40:00 +0000446 this->onDrawPath(target, 0, path, fill, translate, true);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000447}