blob: f226a99873eacf7c9063684a89cf2f6316e0e381 [file] [log] [blame]
bsalomon@google.comffca4002011-02-22 20:34:01 +00001#include "GrPathRenderer.h"
2
3#include "GrPoint.h"
4#include "GrDrawTarget.h"
5#include "GrPathIter.h"
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +00006#include "GrPathUtils.h"
bsalomon@google.comffca4002011-02-22 20:34:01 +00007#include "GrMemory.h"
8#include "GrTexture.h"
9
bsalomon@google.comd302f142011-03-03 13:54:13 +000010GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
11 bool stencilWrapOpsSupport)
12 : fSeparateStencil(separateStencilSupport),
13 fStencilWrapOps(stencilWrapOpsSupport) {
bsalomon@google.comffca4002011-02-22 20:34:01 +000014
15}
16
17////////////////////////////////////////////////////////////////////////////////
bsalomon@google.comd302f142011-03-03 13:54:13 +000018// Stencil rules for paths
19
20////// Even/Odd
21
22static const GrStencilSettings gEOStencilPass = {
23 kInvert_StencilOp, kInvert_StencilOp,
24 kKeep_StencilOp, kKeep_StencilOp,
25 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
26 0xffffffff, 0xffffffff,
27 0xffffffff, 0xffffffff,
28 0xffffffff, 0xffffffff
29};
30
31// ok not to check clip b/c stencil pass only wrote inside clip
32static const GrStencilSettings gEOColorPass = {
33 kZero_StencilOp, kZero_StencilOp,
34 kZero_StencilOp, kZero_StencilOp,
35 kNotEqual_StencilFunc, kNotEqual_StencilFunc,
36 0xffffffff, 0xffffffff,
37 0x0, 0x0,
38 0xffffffff, 0xffffffff
39};
40
41// have to check clip b/c outside clip will always be zero.
42static const GrStencilSettings gInvEOColorPass = {
43 kZero_StencilOp, kZero_StencilOp,
44 kZero_StencilOp, kZero_StencilOp,
45 kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
46 0xffffffff, 0xffffffff,
47 0x0, 0x0,
48 0xffffffff, 0xffffffff
49};
50
51////// Winding
52
53// when we have separate stencil we increment front faces / decrement back faces
54// when we don't have wrap incr and decr we use the stencil test to simulate
55// them.
56
57static const GrStencilSettings gWindStencilSeparateWithWrap = {
58 kIncWrap_StencilOp, kDecWrap_StencilOp,
59 kKeep_StencilOp, kKeep_StencilOp,
60 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
61 0xffffffff, 0xffffffff,
62 0xffffffff, 0xffffffff,
63 0xffffffff, 0xffffffff
64};
65
66// if inc'ing the max value, invert to make 0
67// if dec'ing zero invert to make all ones.
68// we can't avoid touching the stencil on both passing and
69// failing, so we can't resctrict ourselves to the clip.
70static const GrStencilSettings gWindStencilSeparateNoWrap = {
71 kInvert_StencilOp, kInvert_StencilOp,
72 kIncClamp_StencilOp, kDecClamp_StencilOp,
73 kEqual_StencilFunc, kEqual_StencilFunc,
74 0xffffffff, 0xffffffff,
75 0xffffffff, 0x0,
76 0xffffffff, 0xffffffff
77};
78
79// When there are no separate faces we do two passes to setup the winding rule
80// stencil. First we draw the front faces and inc, then we draw the back faces
81// and dec. These are same as the above two split into the incrementing and
82// decrementing passes.
83static const GrStencilSettings gWindSingleStencilWithWrapInc = {
84 kIncWrap_StencilOp, kIncWrap_StencilOp,
85 kKeep_StencilOp, kKeep_StencilOp,
86 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
87 0xffffffff, 0xffffffff,
88 0xffffffff, 0xffffffff,
89 0xffffffff, 0xffffffff
90};
91static const GrStencilSettings gWindSingleStencilWithWrapDec = {
92 kDecWrap_StencilOp, kDecWrap_StencilOp,
93 kKeep_StencilOp, kKeep_StencilOp,
94 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
95 0xffffffff, 0xffffffff,
96 0xffffffff, 0xffffffff,
97 0xffffffff, 0xffffffff
98};
99static const GrStencilSettings gWindSingleStencilNoWrapInc = {
100 kInvert_StencilOp, kInvert_StencilOp,
101 kIncClamp_StencilOp, kIncClamp_StencilOp,
102 kEqual_StencilFunc, kEqual_StencilFunc,
103 0xffffffff, 0xffffffff,
104 0xffffffff, 0xffffffff,
105 0xffffffff, 0xffffffff
106};
107static const GrStencilSettings gWindSingleStencilNoWrapDec = {
108 kInvert_StencilOp, kInvert_StencilOp,
109 kDecClamp_StencilOp, kDecClamp_StencilOp,
110 kEqual_StencilFunc, kEqual_StencilFunc,
111 0xffffffff, 0xffffffff,
112 0x0, 0x0,
113 0xffffffff, 0xffffffff
114};
115
116static const GrStencilSettings gWindColorPass = {
117 kZero_StencilOp, kZero_StencilOp,
118 kZero_StencilOp, kZero_StencilOp,
119 kNonZeroIfInClip_StencilFunc, kNonZeroIfInClip_StencilFunc,
120 0xffffffff, 0xffffffff,
121 0x0, 0x0,
122 0xffffffff, 0xffffffff
123};
124
125static const GrStencilSettings gInvWindColorPass = {
126 kZero_StencilOp, kZero_StencilOp,
127 kZero_StencilOp, kZero_StencilOp,
128 kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
129 0xffffffff, 0xffffffff,
130 0x0, 0x0,
131 0xffffffff, 0xffffffff
132};
133
134////// Normal render to stencil
135
136// Sometimes the default path renderer can draw a path directly to the stencil
137// buffer without having to first resolve the interior / exterior.
138static const GrStencilSettings gDirectToStencil = {
139 kZero_StencilOp, kZero_StencilOp,
140 kIncClamp_StencilOp, kIncClamp_StencilOp,
141 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
142 0xffffffff, 0xffffffff,
143 0x0, 0x0,
144 0xffffffff, 0xffffffff
145};
146
147////////////////////////////////////////////////////////////////////////////////
148// Helpers for drawPath
bsalomon@google.comffca4002011-02-22 20:34:01 +0000149
150#define STENCIL_OFF 0 // Always disable stencil (even when needed)
bsalomon@google.comffca4002011-02-22 20:34:01 +0000151
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000152static inline bool single_pass_path(const GrDrawTarget& target,
153 const GrPathIter& path,
154 GrPathFill fill) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000155#if STENCIL_OFF
156 return true;
157#else
158 if (kEvenOdd_PathFill == fill) {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000159 GrConvexHint hint = path.convexHint();
160 return hint == kConvex_ConvexHint ||
161 hint == kNonOverlappingConvexPieces_ConvexHint;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000162 } else if (kWinding_PathFill == fill) {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000163 GrConvexHint hint = path.convexHint();
164 return hint == kConvex_ConvexHint ||
165 hint == kNonOverlappingConvexPieces_ConvexHint ||
166 (hint == kSameWindingConvexPieces_ConvexHint &&
bsalomon@google.comffca4002011-02-22 20:34:01 +0000167 target.canDisableBlend() && !target.isDitherState());
168
169 }
170 return false;
171#endif
172}
173
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000174bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target,
175 GrPathIter* path,
176 GrPathFill fill) const {
bsalomon@google.com7f5875d2011-03-24 16:55:45 +0000177 return !single_pass_path(*target, *path, fill);
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000178}
179
bsalomon@google.comd302f142011-03-03 13:54:13 +0000180void GrDefaultPathRenderer::drawPathHelper(GrDrawTarget* target,
181 GrDrawTarget::StageBitfield stages,
182 GrPathIter* path,
183 GrPathFill fill,
184 const GrPoint* translate,
185 bool stencilOnly) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000186
187 GrDrawTarget::AutoStateRestore asr(target);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000188 bool colorWritesWereDisabled = target->isColorWriteDisabled();
189 // face culling doesn't make sense here
190 GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
bsalomon@google.comffca4002011-02-22 20:34:01 +0000191
192 GrMatrix viewM = target->getViewMatrix();
193 // In order to tesselate the path we get a bound on how much the matrix can
194 // stretch when mapping to screen coordinates.
195 GrScalar stretch = viewM.getMaxStretch();
196 bool useStretch = stretch > 0;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000197 GrScalar tol = GrPathUtils::gTolerance;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000198
199 if (!useStretch) {
200 // TODO: deal with perspective in some better way.
201 tol /= 10;
202 } else {
203 GrScalar sinv = GR_Scalar1 / stretch;
204 tol = GrMul(tol, sinv);
205 }
206 GrScalar tolSqd = GrMul(tol, tol);
207
bsalomon@google.comd302f142011-03-03 13:54:13 +0000208 path->rewind();
209
bsalomon@google.comffca4002011-02-22 20:34:01 +0000210 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
229 path->rewind();
230
231 // TODO: use primitve restart if available rather than multiple draws
232 GrPrimitiveType type;
233 int passCount = 0;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000234 const GrStencilSettings* passes[3];
235 GrDrawTarget::DrawFace drawFace[3];
bsalomon@google.comffca4002011-02-22 20:34:01 +0000236 bool reverse = false;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000237 bool lastPassIsBounds;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000238
239 if (kHairLine_PathFill == fill) {
240 type = kLineStrip_PrimitiveType;
241 passCount = 1;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000242 if (stencilOnly) {
243 passes[0] = &gDirectToStencil;
244 } else {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000245 passes[0] = NULL;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000246 }
247 lastPassIsBounds = false;
248 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000249 } else {
250 type = kTriangleFan_PrimitiveType;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000251 if (single_pass_path(*target, *path, fill)) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000252 passCount = 1;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000253 if (stencilOnly) {
254 passes[0] = &gDirectToStencil;
255 } else {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000256 passes[0] = NULL;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000257 }
258 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
259 lastPassIsBounds = false;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000260 } else {
261 switch (fill) {
262 case kInverseEvenOdd_PathFill:
263 reverse = true;
264 // fallthrough
265 case kEvenOdd_PathFill:
bsalomon@google.comd302f142011-03-03 13:54:13 +0000266 passes[0] = &gEOStencilPass;
267 if (stencilOnly) {
268 passCount = 1;
269 lastPassIsBounds = false;
270 } else {
271 passCount = 2;
272 lastPassIsBounds = true;
273 if (reverse) {
274 passes[1] = &gInvEOColorPass;
275 } else {
276 passes[1] = &gEOColorPass;
277 }
278 }
279 drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000280 break;
281
282 case kInverseWinding_PathFill:
283 reverse = true;
284 // fallthrough
285 case kWinding_PathFill:
bsalomon@google.comd302f142011-03-03 13:54:13 +0000286 if (fSeparateStencil) {
287 if (fStencilWrapOps) {
288 passes[0] = &gWindStencilSeparateWithWrap;
289 } else {
290 passes[0] = &gWindStencilSeparateNoWrap;
291 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000292 passCount = 2;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000293 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000294 } else {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000295 if (fStencilWrapOps) {
296 passes[0] = &gWindSingleStencilWithWrapInc;
297 passes[1] = &gWindSingleStencilWithWrapDec;
298 } else {
299 passes[0] = &gWindSingleStencilNoWrapInc;
300 passes[1] = &gWindSingleStencilNoWrapDec;
301 }
302 // which is cw and which is ccw is arbitrary.
303 drawFace[0] = GrDrawTarget::kCW_DrawFace;
304 drawFace[1] = GrDrawTarget::kCCW_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000305 passCount = 3;
306 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000307 if (stencilOnly) {
308 lastPassIsBounds = false;
309 --passCount;
310 } else {
311 lastPassIsBounds = true;
312 drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace;
313 if (reverse) {
314 passes[passCount-1] = &gInvWindColorPass;
315 } else {
316 passes[passCount-1] = &gWindColorPass;
317 }
318 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000319 break;
320 default:
321 GrAssert(!"Unknown path fill!");
322 return;
323 }
324 }
325 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000326
327 GrPoint pts[4];
328
329 bool first = true;
330 int subpath = 0;
331
332 for (;;) {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000333 GrPathCmd cmd = path->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,
434 GrPathIter* path,
435 GrPathFill fill,
436 const GrPoint* translate) {
437 this->drawPathHelper(target, stages, path, fill, translate, false);
438}
439
440void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target,
441 GrPathIter* path,
442 GrPathFill fill,
443 const GrPoint* translate) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000444 GrAssert(kInverseEvenOdd_PathFill != fill);
445 GrAssert(kInverseWinding_PathFill != fill);
446 this->drawPathHelper(target, 0, path, fill, translate, true);
447}