blob: 3e2b4b35c556626dffc22c33bfc1f4ffd9cff157 [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"
6#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
149#define STENCIL_OFF 0 // Always disable stencil (even when needed)
150static const GrScalar gTolerance = GR_Scalar1;
151
152static const uint32_t MAX_POINTS_PER_CURVE = 1 << 10;
153
154static uint32_t quadratic_point_count(const GrPoint points[], GrScalar tol) {
155 GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
156 if (d < tol) {
157 return 1;
158 } else {
159 // Each time we subdivide, d should be cut in 4. So we need to
160 // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
161 // points.
162 // 2^(log4(x)) = sqrt(x);
163 d = ceilf(sqrtf(d/tol));
164 return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE);
165 }
166}
167
168static uint32_t generate_quadratic_points(const GrPoint& p0,
169 const GrPoint& p1,
170 const GrPoint& p2,
171 GrScalar tolSqd,
172 GrPoint** points,
173 uint32_t pointsLeft) {
174 if (pointsLeft < 2 ||
175 (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) {
176 (*points)[0] = p2;
177 *points += 1;
178 return 1;
179 }
180
181 GrPoint q[] = {
182 GrPoint(GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY)),
183 GrPoint(GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY)),
184 };
185 GrPoint r(GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY));
186
187 pointsLeft >>= 1;
188 uint32_t a = generate_quadratic_points(p0, q[0], r, tolSqd, points, pointsLeft);
189 uint32_t b = generate_quadratic_points(r, q[1], p2, tolSqd, points, pointsLeft);
190 return a + b;
191}
192
193static uint32_t cubic_point_count(const GrPoint points[], GrScalar tol) {
194 GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
195 points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
196 d = sqrtf(d);
197 if (d < tol) {
198 return 1;
199 } else {
200 d = ceilf(sqrtf(d/tol));
201 return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE);
202 }
203}
204
205static uint32_t generate_cubic_points(const GrPoint& p0,
206 const GrPoint& p1,
207 const GrPoint& p2,
208 const GrPoint& p3,
209 GrScalar tolSqd,
210 GrPoint** points,
211 uint32_t pointsLeft) {
212 if (pointsLeft < 2 ||
213 (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd &&
214 p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) {
215 (*points)[0] = p3;
216 *points += 1;
217 return 1;
218 }
219 GrPoint q[] = {
220 GrPoint(GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY)),
221 GrPoint(GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY)),
222 GrPoint(GrScalarAve(p2.fX, p3.fX), GrScalarAve(p2.fY, p3.fY))
223 };
224 GrPoint r[] = {
225 GrPoint(GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY)),
226 GrPoint(GrScalarAve(q[1].fX, q[2].fX), GrScalarAve(q[1].fY, q[2].fY))
227 };
228 GrPoint s(GrScalarAve(r[0].fX, r[1].fX), GrScalarAve(r[0].fY, r[1].fY));
229 pointsLeft >>= 1;
230 uint32_t a = generate_cubic_points(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
231 uint32_t b = generate_cubic_points(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
232 return a + b;
233}
234
235static int worst_case_point_count(GrPathIter* path,
236 int* subpaths,
237 GrScalar tol) {
238 int pointCount = 0;
239 *subpaths = 1;
240
241 bool first = true;
242
243 GrPathIter::Command cmd;
244
245 GrPoint pts[4];
246 while ((cmd = path->next(pts)) != GrPathIter::kEnd_Command) {
247
248 switch (cmd) {
249 case GrPathIter::kLine_Command:
250 pointCount += 1;
251 break;
252 case GrPathIter::kQuadratic_Command:
253 pointCount += quadratic_point_count(pts, tol);
254 break;
255 case GrPathIter::kCubic_Command:
256 pointCount += cubic_point_count(pts, tol);
257 break;
258 case GrPathIter::kMove_Command:
259 pointCount += 1;
260 if (!first) {
261 ++(*subpaths);
262 }
263 break;
264 default:
265 break;
266 }
267 first = false;
268 }
269 return pointCount;
270}
271
272static inline bool single_pass_path(const GrPathIter& path,
273 GrPathFill fill,
274 const GrDrawTarget& target) {
275#if STENCIL_OFF
276 return true;
277#else
278 if (kEvenOdd_PathFill == fill) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000279 GrPathIter::ConvexHint hint = path.convexHint();
bsalomon@google.comffca4002011-02-22 20:34:01 +0000280 return hint == GrPathIter::kConvex_ConvexHint ||
281 hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint;
282 } else if (kWinding_PathFill == fill) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000283 GrPathIter::ConvexHint hint = path.convexHint();
bsalomon@google.comffca4002011-02-22 20:34:01 +0000284 return hint == GrPathIter::kConvex_ConvexHint ||
285 hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint ||
286 (hint == GrPathIter::kSameWindingConvexPieces_ConvexHint &&
287 target.canDisableBlend() && !target.isDitherState());
288
289 }
290 return false;
291#endif
292}
293
bsalomon@google.comd302f142011-03-03 13:54:13 +0000294void GrDefaultPathRenderer::drawPathHelper(GrDrawTarget* target,
295 GrDrawTarget::StageBitfield stages,
296 GrPathIter* path,
297 GrPathFill fill,
298 const GrPoint* translate,
299 bool stencilOnly) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000300
301 GrDrawTarget::AutoStateRestore asr(target);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000302 bool colorWritesWereDisabled = target->isColorWriteDisabled();
303 // face culling doesn't make sense here
304 GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
bsalomon@google.comffca4002011-02-22 20:34:01 +0000305
306 GrMatrix viewM = target->getViewMatrix();
307 // In order to tesselate the path we get a bound on how much the matrix can
308 // stretch when mapping to screen coordinates.
309 GrScalar stretch = viewM.getMaxStretch();
310 bool useStretch = stretch > 0;
311 GrScalar tol = gTolerance;
312
313 if (!useStretch) {
314 // TODO: deal with perspective in some better way.
315 tol /= 10;
316 } else {
317 GrScalar sinv = GR_Scalar1 / stretch;
318 tol = GrMul(tol, sinv);
319 }
320 GrScalar tolSqd = GrMul(tol, tol);
321
bsalomon@google.comd302f142011-03-03 13:54:13 +0000322 path->rewind();
323
bsalomon@google.comffca4002011-02-22 20:34:01 +0000324 int subpathCnt;
325 int maxPts = worst_case_point_count(path,
326 &subpathCnt,
327 tol);
328
329 GrVertexLayout layout = 0;
330 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
331 if ((1 << s) & stages) {
332 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
333 }
334 }
335
336 // add 4 to hold the bounding rect
337 GrDrawTarget::AutoReleaseGeometry arg(target, layout, maxPts + 4, 0);
338
339 GrPoint* base = (GrPoint*) arg.vertices();
340 GrPoint* vert = base;
341 GrPoint* subpathBase = base;
342
343 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
344
345 path->rewind();
346
347 // TODO: use primitve restart if available rather than multiple draws
348 GrPrimitiveType type;
349 int passCount = 0;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000350 const GrStencilSettings* passes[3];
351 GrDrawTarget::DrawFace drawFace[3];
bsalomon@google.comffca4002011-02-22 20:34:01 +0000352 bool reverse = false;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000353 bool lastPassIsBounds;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000354
355 if (kHairLine_PathFill == fill) {
356 type = kLineStrip_PrimitiveType;
357 passCount = 1;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000358 if (stencilOnly) {
359 passes[0] = &gDirectToStencil;
360 } else {
361 passes[0] = &GrStencilSettings::gDisabled;
362 }
363 lastPassIsBounds = false;
364 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000365 } else {
366 type = kTriangleFan_PrimitiveType;
367 if (single_pass_path(*path, fill, *target)) {
368 passCount = 1;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000369 if (stencilOnly) {
370 passes[0] = &gDirectToStencil;
371 } else {
372 passes[0] = &GrStencilSettings::gDisabled;
373 }
374 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
375 lastPassIsBounds = false;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000376 } else {
377 switch (fill) {
378 case kInverseEvenOdd_PathFill:
379 reverse = true;
380 // fallthrough
381 case kEvenOdd_PathFill:
bsalomon@google.comd302f142011-03-03 13:54:13 +0000382 passes[0] = &gEOStencilPass;
383 if (stencilOnly) {
384 passCount = 1;
385 lastPassIsBounds = false;
386 } else {
387 passCount = 2;
388 lastPassIsBounds = true;
389 if (reverse) {
390 passes[1] = &gInvEOColorPass;
391 } else {
392 passes[1] = &gEOColorPass;
393 }
394 }
395 drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000396 break;
397
398 case kInverseWinding_PathFill:
399 reverse = true;
400 // fallthrough
401 case kWinding_PathFill:
bsalomon@google.comd302f142011-03-03 13:54:13 +0000402 if (fSeparateStencil) {
403 if (fStencilWrapOps) {
404 passes[0] = &gWindStencilSeparateWithWrap;
405 } else {
406 passes[0] = &gWindStencilSeparateNoWrap;
407 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000408 passCount = 2;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000409 drawFace[0] = GrDrawTarget::kBoth_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000410 } else {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000411 if (fStencilWrapOps) {
412 passes[0] = &gWindSingleStencilWithWrapInc;
413 passes[1] = &gWindSingleStencilWithWrapDec;
414 } else {
415 passes[0] = &gWindSingleStencilNoWrapInc;
416 passes[1] = &gWindSingleStencilNoWrapDec;
417 }
418 // which is cw and which is ccw is arbitrary.
419 drawFace[0] = GrDrawTarget::kCW_DrawFace;
420 drawFace[1] = GrDrawTarget::kCCW_DrawFace;
bsalomon@google.comffca4002011-02-22 20:34:01 +0000421 passCount = 3;
422 }
bsalomon@google.comd302f142011-03-03 13:54:13 +0000423 if (stencilOnly) {
424 lastPassIsBounds = false;
425 --passCount;
426 } else {
427 lastPassIsBounds = true;
428 drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace;
429 if (reverse) {
430 passes[passCount-1] = &gInvWindColorPass;
431 } else {
432 passes[passCount-1] = &gWindColorPass;
433 }
434 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000435 break;
436 default:
437 GrAssert(!"Unknown path fill!");
438 return;
439 }
440 }
441 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000442
443 GrPoint pts[4];
444
445 bool first = true;
446 int subpath = 0;
447
448 for (;;) {
449 GrPathIter::Command cmd = path->next(pts);
450 switch (cmd) {
451 case GrPathIter::kMove_Command:
452 if (!first) {
453 subpathVertCount[subpath] = vert-subpathBase;
454 subpathBase = vert;
455 ++subpath;
456 }
457 *vert = pts[0];
458 vert++;
459 break;
460 case GrPathIter::kLine_Command:
461 *vert = pts[1];
462 vert++;
463 break;
464 case GrPathIter::kQuadratic_Command: {
465 generate_quadratic_points(pts[0], pts[1], pts[2],
466 tolSqd, &vert,
467 quadratic_point_count(pts, tol));
468 break;
469 }
470 case GrPathIter::kCubic_Command: {
471 generate_cubic_points(pts[0], pts[1], pts[2], pts[3],
472 tolSqd, &vert,
473 cubic_point_count(pts, tol));
474 break;
475 }
476 case GrPathIter::kClose_Command:
477 break;
478 case GrPathIter::kEnd_Command:
479 subpathVertCount[subpath] = vert-subpathBase;
480 ++subpath; // this could be only in debug
481 goto FINISHED;
482 }
483 first = false;
484 }
485FINISHED:
486 GrAssert(subpath == subpathCnt);
487 GrAssert((vert - base) <= maxPts);
488
489 if (translate) {
490 int count = vert - base;
491 for (int i = 0; i < count; i++) {
492 base[i].offset(translate->fX, translate->fY);
493 }
494 }
495
bsalomon@google.comd302f142011-03-03 13:54:13 +0000496 // if we're stenciling we will follow with a pass that draws
497 // a bounding rect to set the color. We're stenciling when
498 // passCount > 1.
499 const int& boundVertexStart = maxPts;
500 GrPoint* boundsVerts = base + boundVertexStart;
501 if (lastPassIsBounds) {
bsalomon@google.comffca4002011-02-22 20:34:01 +0000502 GrRect bounds;
503 if (reverse) {
504 GrAssert(NULL != target->getRenderTarget());
505 // draw over the whole world.
506 bounds.setLTRB(0, 0,
507 GrIntToScalar(target->getRenderTarget()->width()),
508 GrIntToScalar(target->getRenderTarget()->height()));
509 GrMatrix vmi;
510 if (target->getViewInverse(&vmi)) {
511 vmi.mapRect(&bounds);
512 }
513 } else {
514 bounds.setBounds((GrPoint*)base, vert - base);
515 }
516 boundsVerts[0].setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight,
517 bounds.fBottom);
518 }
519
520 for (int p = 0; p < passCount; ++p) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000521 target->setDrawFace(drawFace[p]);
522 target->setStencil(*passes[p]);
523
524 if (lastPassIsBounds && (p == passCount-1)) {
525 if (!colorWritesWereDisabled) {
526 target->disableState(GrDrawTarget::kNoColorWrites_StateBit);
527 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000528 target->drawNonIndexed(kTriangleFan_PrimitiveType,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000529 boundVertexStart, 4);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000530
531 } else {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000532 if (passCount > 1) {
533 target->enableState(GrDrawTarget::kNoColorWrites_StateBit);
534 }
bsalomon@google.comffca4002011-02-22 20:34:01 +0000535 int baseVertex = 0;
536 for (int sp = 0; sp < subpathCnt; ++sp) {
537 target->drawNonIndexed(type,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000538 baseVertex,
539 subpathVertCount[sp]);
bsalomon@google.comffca4002011-02-22 20:34:01 +0000540 baseVertex += subpathVertCount[sp];
541 }
542 }
543 }
544}
bsalomon@google.comd302f142011-03-03 13:54:13 +0000545
546void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
547 GrDrawTarget::StageBitfield stages,
548 GrPathIter* path,
549 GrPathFill fill,
550 const GrPoint* translate) {
551 this->drawPathHelper(target, stages, path, fill, translate, false);
552}
553
554void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target,
555 GrPathIter* path,
556 GrPathFill fill,
557 const GrPoint* translate) {
558 GrAssert(kInverseEvenOdd_PathFill != fill);
559 GrAssert(kInverseWinding_PathFill != fill);
560 this->drawPathHelper(target, 0, path, fill, translate, true);
561 }