1 /*
2 * Copyright 2009 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/core/SkEdgeClipper.h"
9
10 #include "include/core/SkRect.h"
11 #include "include/core/SkTypes.h"
12 #include "include/private/base/SkMacros.h"
13 #include "src/core/SkGeometry.h"
14 #include "src/core/SkLineClipper.h"
15 #include "src/core/SkPathPriv.h"
16
17 #include <algorithm>
18 #include <cstring>
19
quick_reject(const SkRect & bounds,const SkRect & clip)20 static bool quick_reject(const SkRect& bounds, const SkRect& clip) {
21 return bounds.fTop >= clip.fBottom || bounds.fBottom <= clip.fTop;
22 }
23
clamp_le(SkScalar & value,SkScalar max)24 static inline void clamp_le(SkScalar& value, SkScalar max) {
25 if (value > max) {
26 value = max;
27 }
28 }
29
clamp_ge(SkScalar & value,SkScalar min)30 static inline void clamp_ge(SkScalar& value, SkScalar min) {
31 if (value < min) {
32 value = min;
33 }
34 }
35
36 /* src[] must be monotonic in Y. This routine copies src into dst, and sorts
37 it to be increasing in Y. If it had to reverse the order of the points,
38 it returns true, otherwise it returns false
39 */
sort_increasing_Y(SkPoint dst[],const SkPoint src[],int count)40 static bool sort_increasing_Y(SkPoint dst[], const SkPoint src[], int count) {
41 // we need the data to be monotonically increasing in Y
42 if (src[0].fY > src[count - 1].fY) {
43 for (int i = 0; i < count; i++) {
44 dst[i] = src[count - i - 1];
45 }
46 return true;
47 } else {
48 memcpy(dst, src, count * sizeof(SkPoint));
49 return false;
50 }
51 }
52
clipLine(SkPoint p0,SkPoint p1,const SkRect & clip)53 bool SkEdgeClipper::clipLine(SkPoint p0, SkPoint p1, const SkRect& clip) {
54 fCurrPoint = fPoints;
55 fCurrVerb = fVerbs;
56
57 SkPoint lines[SkLineClipper::kMaxPoints];
58 const SkPoint pts[] = { p0, p1 };
59 int lineCount = SkLineClipper::ClipLine(pts, clip, lines, fCanCullToTheRight);
60 for (int i = 0; i < lineCount; i++) {
61 this->appendLine(lines[i], lines[i + 1]);
62 }
63
64 *fCurrVerb = SkPath::kDone_Verb;
65 fCurrPoint = fPoints;
66 fCurrVerb = fVerbs;
67 return SkPath::kDone_Verb != fVerbs[0];
68 }
69
70 ///////////////////////////////////////////////////////////////////////////////
71
chopMonoQuadAt(SkScalar c0,SkScalar c1,SkScalar c2,SkScalar target,SkScalar * t)72 static bool chopMonoQuadAt(SkScalar c0, SkScalar c1, SkScalar c2,
73 SkScalar target, SkScalar* t) {
74 /* Solve F(t) = y where F(t) := [0](1-t)^2 + 2[1]t(1-t) + [2]t^2
75 * We solve for t, using quadratic equation, hence we have to rearrange
76 * our cooefficents to look like At^2 + Bt + C
77 */
78 SkScalar A = c0 - c1 - c1 + c2;
79 SkScalar B = 2*(c1 - c0);
80 SkScalar C = c0 - target;
81
82 SkScalar roots[2]; // we only expect one, but make room for 2 for safety
83 int count = SkFindUnitQuadRoots(A, B, C, roots);
84 if (count) {
85 *t = roots[0];
86 return true;
87 }
88 return false;
89 }
90
chopMonoQuadAtY(SkPoint pts[3],SkScalar y,SkScalar * t)91 static bool chopMonoQuadAtY(SkPoint pts[3], SkScalar y, SkScalar* t) {
92 return chopMonoQuadAt(pts[0].fY, pts[1].fY, pts[2].fY, y, t);
93 }
94
chopMonoQuadAtX(SkPoint pts[3],SkScalar x,SkScalar * t)95 static bool chopMonoQuadAtX(SkPoint pts[3], SkScalar x, SkScalar* t) {
96 return chopMonoQuadAt(pts[0].fX, pts[1].fX, pts[2].fX, x, t);
97 }
98
99 // Modify pts[] in place so that it is clipped in Y to the clip rect
chop_quad_in_Y(SkPoint pts[3],const SkRect & clip)100 static void chop_quad_in_Y(SkPoint pts[3], const SkRect& clip) {
101 SkScalar t;
102 SkPoint tmp[5]; // for SkChopQuadAt
103
104 // are we partially above
105 if (pts[0].fY < clip.fTop) {
106 if (chopMonoQuadAtY(pts, clip.fTop, &t)) {
107 // take the 2nd chopped quad
108 SkChopQuadAt(pts, tmp, t);
109 // clamp to clean up imprecise numerics in the chop
110 tmp[2].fY = clip.fTop;
111 clamp_ge(tmp[3].fY, clip.fTop);
112
113 pts[0] = tmp[2];
114 pts[1] = tmp[3];
115 } else {
116 // if chopMonoQuadAtY failed, then we may have hit inexact numerics
117 // so we just clamp against the top
118 for (int i = 0; i < 3; i++) {
119 if (pts[i].fY < clip.fTop) {
120 pts[i].fY = clip.fTop;
121 }
122 }
123 }
124 }
125
126 // are we partially below
127 if (pts[2].fY > clip.fBottom) {
128 if (chopMonoQuadAtY(pts, clip.fBottom, &t)) {
129 SkChopQuadAt(pts, tmp, t);
130 // clamp to clean up imprecise numerics in the chop
131 clamp_le(tmp[1].fY, clip.fBottom);
132 tmp[2].fY = clip.fBottom;
133
134 pts[1] = tmp[1];
135 pts[2] = tmp[2];
136 } else {
137 // if chopMonoQuadAtY failed, then we may have hit inexact numerics
138 // so we just clamp against the bottom
139 for (int i = 0; i < 3; i++) {
140 if (pts[i].fY > clip.fBottom) {
141 pts[i].fY = clip.fBottom;
142 }
143 }
144 }
145 }
146 }
147
148 // srcPts[] must be monotonic in X and Y
clipMonoQuad(const SkPoint srcPts[3],const SkRect & clip)149 void SkEdgeClipper::clipMonoQuad(const SkPoint srcPts[3], const SkRect& clip) {
150 SkPoint pts[3];
151 bool reverse = sort_increasing_Y(pts, srcPts, 3);
152
153 // are we completely above or below
154 if (pts[2].fY <= clip.fTop || pts[0].fY >= clip.fBottom) {
155 return;
156 }
157
158 // Now chop so that pts is contained within clip in Y
159 chop_quad_in_Y(pts, clip);
160
161 if (pts[0].fX > pts[2].fX) {
162 using std::swap;
163 swap(pts[0], pts[2]);
164 reverse = !reverse;
165 }
166 SkASSERT(pts[0].fX <= pts[1].fX);
167 SkASSERT(pts[1].fX <= pts[2].fX);
168
169 // Now chop in X has needed, and record the segments
170
171 if (pts[2].fX <= clip.fLeft) { // wholly to the left
172 this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse);
173 return;
174 }
175 if (pts[0].fX >= clip.fRight) { // wholly to the right
176 if (!this->canCullToTheRight()) {
177 this->appendVLine(clip.fRight, pts[0].fY, pts[2].fY, reverse);
178 }
179 return;
180 }
181
182 SkScalar t;
183 SkPoint tmp[5]; // for SkChopQuadAt
184
185 // are we partially to the left
186 if (pts[0].fX < clip.fLeft) {
187 if (chopMonoQuadAtX(pts, clip.fLeft, &t)) {
188 SkChopQuadAt(pts, tmp, t);
189 this->appendVLine(clip.fLeft, tmp[0].fY, tmp[2].fY, reverse);
190 // clamp to clean up imprecise numerics in the chop
191 tmp[2].fX = clip.fLeft;
192 clamp_ge(tmp[3].fX, clip.fLeft);
193
194 pts[0] = tmp[2];
195 pts[1] = tmp[3];
196 } else {
197 // if chopMonoQuadAtY failed, then we may have hit inexact numerics
198 // so we just clamp against the left
199 this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse);
200 return;
201 }
202 }
203
204 // are we partially to the right
205 if (pts[2].fX > clip.fRight) {
206 if (chopMonoQuadAtX(pts, clip.fRight, &t)) {
207 SkChopQuadAt(pts, tmp, t);
208 // clamp to clean up imprecise numerics in the chop
209 clamp_le(tmp[1].fX, clip.fRight);
210 tmp[2].fX = clip.fRight;
211
212 this->appendQuad(tmp, reverse);
213 this->appendVLine(clip.fRight, tmp[2].fY, tmp[4].fY, reverse);
214 } else {
215 // if chopMonoQuadAtY failed, then we may have hit inexact numerics
216 // so we just clamp against the right
217 pts[1].fX = std::min(pts[1].fX, clip.fRight);
218 pts[2].fX = std::min(pts[2].fX, clip.fRight);
219 this->appendQuad(pts, reverse);
220 }
221 } else { // wholly inside the clip
222 this->appendQuad(pts, reverse);
223 }
224 }
225
clipQuad(const SkPoint srcPts[3],const SkRect & clip)226 bool SkEdgeClipper::clipQuad(const SkPoint srcPts[3], const SkRect& clip) {
227 fCurrPoint = fPoints;
228 fCurrVerb = fVerbs;
229
230 SkRect bounds;
231 bounds.setBounds(srcPts, 3);
232
233 if (!quick_reject(bounds, clip)) {
234 SkPoint monoY[5];
235 int countY = SkChopQuadAtYExtrema(srcPts, monoY);
236 for (int y = 0; y <= countY; y++) {
237 SkPoint monoX[5];
238 int countX = SkChopQuadAtXExtrema(&monoY[y * 2], monoX);
239 for (int x = 0; x <= countX; x++) {
240 this->clipMonoQuad(&monoX[x * 2], clip);
241 SkASSERT(fCurrVerb - fVerbs < kMaxVerbs);
242 SkASSERT(fCurrPoint - fPoints <= kMaxPoints);
243 }
244 }
245 }
246
247 *fCurrVerb = SkPath::kDone_Verb;
248 fCurrPoint = fPoints;
249 fCurrVerb = fVerbs;
250 return SkPath::kDone_Verb != fVerbs[0];
251 }
252
253 ///////////////////////////////////////////////////////////////////////////////
254
mono_cubic_closestT(const SkScalar src[],SkScalar x)255 static SkScalar mono_cubic_closestT(const SkScalar src[], SkScalar x) {
256 SkScalar t = 0.5f;
257 SkScalar lastT;
258 SkScalar bestT SK_INIT_TO_AVOID_WARNING;
259 SkScalar step = 0.25f;
260 SkScalar D = src[0];
261 SkScalar A = src[6] + 3*(src[2] - src[4]) - D;
262 SkScalar B = 3*(src[4] - src[2] - src[2] + D);
263 SkScalar C = 3*(src[2] - D);
264 x -= D;
265 SkScalar closest = SK_ScalarMax;
266 do {
267 SkScalar loc = ((A * t + B) * t + C) * t;
268 SkScalar dist = SkScalarAbs(loc - x);
269 if (closest > dist) {
270 closest = dist;
271 bestT = t;
272 }
273 lastT = t;
274 t += loc < x ? step : -step;
275 step *= 0.5f;
276 } while (closest > 0.25f && lastT != t);
277 return bestT;
278 }
279
chop_mono_cubic_at_y(SkPoint src[4],SkScalar y,SkPoint dst[7])280 static void chop_mono_cubic_at_y(SkPoint src[4], SkScalar y, SkPoint dst[7]) {
281 if (SkChopMonoCubicAtY(src, y, dst)) {
282 return;
283 }
284 SkChopCubicAt(src, dst, mono_cubic_closestT(&src->fY, y));
285 }
286
287 // Modify pts[] in place so that it is clipped in Y to the clip rect
chop_cubic_in_Y(SkPoint pts[4],const SkRect & clip)288 static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
289
290 // are we partially above
291 if (pts[0].fY < clip.fTop) {
292 SkPoint tmp[7];
293 chop_mono_cubic_at_y(pts, clip.fTop, tmp);
294
295 /*
296 * For a large range in the points, we can do a poor job of chopping, such that the t
297 * we computed resulted in the lower cubic still being partly above the clip.
298 *
299 * If just the first or first 2 Y values are above the fTop, we can just smash them
300 * down. If the first 3 Ys are above fTop, we can't smash all 3, as that can really
301 * distort the cubic. In this case, we take the first output (tmp[3..6] and treat it as
302 * a guess, and re-chop against fTop. Then we fall through to checking if we need to
303 * smash the first 1 or 2 Y values.
304 */
305 if (tmp[3].fY < clip.fTop && tmp[4].fY < clip.fTop && tmp[5].fY < clip.fTop) {
306 SkPoint tmp2[4];
307 memcpy(tmp2, &tmp[3].fX, 4 * sizeof(SkPoint));
308 chop_mono_cubic_at_y(tmp2, clip.fTop, tmp);
309 }
310
311 // tmp[3, 4].fY should all be to the below clip.fTop.
312 // Since we can't trust the numerics of the chopper, we force those conditions now
313 tmp[3].fY = clip.fTop;
314 clamp_ge(tmp[4].fY, clip.fTop);
315
316 pts[0] = tmp[3];
317 pts[1] = tmp[4];
318 pts[2] = tmp[5];
319 }
320
321 // are we partially below
322 if (pts[3].fY > clip.fBottom) {
323 SkPoint tmp[7];
324 chop_mono_cubic_at_y(pts, clip.fBottom, tmp);
325 tmp[3].fY = clip.fBottom;
326 clamp_le(tmp[2].fY, clip.fBottom);
327
328 pts[1] = tmp[1];
329 pts[2] = tmp[2];
330 pts[3] = tmp[3];
331 }
332 }
333
chop_mono_cubic_at_x(SkPoint src[4],SkScalar x,SkPoint dst[7])334 static void chop_mono_cubic_at_x(SkPoint src[4], SkScalar x, SkPoint dst[7]) {
335 if (SkChopMonoCubicAtX(src, x, dst)) {
336 return;
337 }
338 SkChopCubicAt(src, dst, mono_cubic_closestT(&src->fX, x));
339 }
340
341 // srcPts[] must be monotonic in X and Y
clipMonoCubic(const SkPoint src[4],const SkRect & clip)342 void SkEdgeClipper::clipMonoCubic(const SkPoint src[4], const SkRect& clip) {
343 SkPoint pts[4];
344 bool reverse = sort_increasing_Y(pts, src, 4);
345
346 // are we completely above or below
347 if (pts[3].fY <= clip.fTop || pts[0].fY >= clip.fBottom) {
348 return;
349 }
350
351 // Now chop so that pts is contained within clip in Y
352 chop_cubic_in_Y(pts, clip);
353
354 if (pts[0].fX > pts[3].fX) {
355 using std::swap;
356 swap(pts[0], pts[3]);
357 swap(pts[1], pts[2]);
358 reverse = !reverse;
359 }
360
361 // Now chop in X has needed, and record the segments
362
363 if (pts[3].fX <= clip.fLeft) { // wholly to the left
364 this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse);
365 return;
366 }
367 if (pts[0].fX >= clip.fRight) { // wholly to the right
368 if (!this->canCullToTheRight()) {
369 this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse);
370 }
371 return;
372 }
373
374 // are we partially to the left
375 if (pts[0].fX < clip.fLeft) {
376 SkPoint tmp[7];
377 chop_mono_cubic_at_x(pts, clip.fLeft, tmp);
378 this->appendVLine(clip.fLeft, tmp[0].fY, tmp[3].fY, reverse);
379
380 // tmp[3, 4].fX should all be to the right of clip.fLeft.
381 // Since we can't trust the numerics of
382 // the chopper, we force those conditions now
383 tmp[3].fX = clip.fLeft;
384 clamp_ge(tmp[4].fX, clip.fLeft);
385
386 pts[0] = tmp[3];
387 pts[1] = tmp[4];
388 pts[2] = tmp[5];
389 }
390
391 // are we partially to the right
392 if (pts[3].fX > clip.fRight) {
393 SkPoint tmp[7];
394 chop_mono_cubic_at_x(pts, clip.fRight, tmp);
395 tmp[3].fX = clip.fRight;
396 clamp_le(tmp[2].fX, clip.fRight);
397
398 this->appendCubic(tmp, reverse);
399 this->appendVLine(clip.fRight, tmp[3].fY, tmp[6].fY, reverse);
400 } else { // wholly inside the clip
401 this->appendCubic(pts, reverse);
402 }
403 }
404
compute_cubic_bounds(const SkPoint pts[4])405 static SkRect compute_cubic_bounds(const SkPoint pts[4]) {
406 SkRect r;
407 r.setBounds(pts, 4);
408 return r;
409 }
410
too_big_for_reliable_float_math(const SkRect & r)411 static bool too_big_for_reliable_float_math(const SkRect& r) {
412 // limit set as the largest float value for which we can still reliably compute things like
413 // - chopping at XY extrema
414 // - chopping at Y or X values for clipping
415 //
416 // Current value chosen just by experiment. Larger (and still succeeds) is always better.
417 //
418 const SkScalar limit = 1 << 22;
419 return r.fLeft < -limit || r.fTop < -limit || r.fRight > limit || r.fBottom > limit;
420 }
421
clipCubic(const SkPoint srcPts[4],const SkRect & clip)422 bool SkEdgeClipper::clipCubic(const SkPoint srcPts[4], const SkRect& clip) {
423 fCurrPoint = fPoints;
424 fCurrVerb = fVerbs;
425
426 const SkRect bounds = compute_cubic_bounds(srcPts);
427 // check if we're clipped out vertically
428 if (bounds.fBottom > clip.fTop && bounds.fTop < clip.fBottom) {
429 if (too_big_for_reliable_float_math(bounds)) {
430 // can't safely clip the cubic, so we give up and draw a line (which we can safely clip)
431 //
432 // If we rewrote chopcubicat*extrema and chopmonocubic using doubles, we could very
433 // likely always handle the cubic safely, but (it seems) at a big loss in speed, so
434 // we'd only want to take that alternate impl if needed. Perhaps a TODO to try it.
435 //
436 return this->clipLine(srcPts[0], srcPts[3], clip);
437 } else {
438 SkPoint monoY[10];
439 int countY = SkChopCubicAtYExtrema(srcPts, monoY);
440 for (int y = 0; y <= countY; y++) {
441 SkPoint monoX[10];
442 int countX = SkChopCubicAtXExtrema(&monoY[y * 3], monoX);
443 for (int x = 0; x <= countX; x++) {
444 this->clipMonoCubic(&monoX[x * 3], clip);
445 SkASSERT(fCurrVerb - fVerbs < kMaxVerbs);
446 SkASSERT(fCurrPoint - fPoints <= kMaxPoints);
447 }
448 }
449 }
450 }
451
452 *fCurrVerb = SkPath::kDone_Verb;
453 fCurrPoint = fPoints;
454 fCurrVerb = fVerbs;
455 return SkPath::kDone_Verb != fVerbs[0];
456 }
457
458 ///////////////////////////////////////////////////////////////////////////////
459
appendLine(SkPoint p0,SkPoint p1)460 void SkEdgeClipper::appendLine(SkPoint p0, SkPoint p1) {
461 *fCurrVerb++ = SkPath::kLine_Verb;
462 fCurrPoint[0] = p0;
463 fCurrPoint[1] = p1;
464 fCurrPoint += 2;
465 }
466
appendVLine(SkScalar x,SkScalar y0,SkScalar y1,bool reverse)467 void SkEdgeClipper::appendVLine(SkScalar x, SkScalar y0, SkScalar y1, bool reverse) {
468 *fCurrVerb++ = SkPath::kLine_Verb;
469
470 if (reverse) {
471 using std::swap;
472 swap(y0, y1);
473 }
474 fCurrPoint[0].set(x, y0);
475 fCurrPoint[1].set(x, y1);
476 fCurrPoint += 2;
477 }
478
appendQuad(const SkPoint pts[3],bool reverse)479 void SkEdgeClipper::appendQuad(const SkPoint pts[3], bool reverse) {
480 *fCurrVerb++ = SkPath::kQuad_Verb;
481
482 if (reverse) {
483 fCurrPoint[0] = pts[2];
484 fCurrPoint[2] = pts[0];
485 } else {
486 fCurrPoint[0] = pts[0];
487 fCurrPoint[2] = pts[2];
488 }
489 fCurrPoint[1] = pts[1];
490 fCurrPoint += 3;
491 }
492
appendCubic(const SkPoint pts[4],bool reverse)493 void SkEdgeClipper::appendCubic(const SkPoint pts[4], bool reverse) {
494 *fCurrVerb++ = SkPath::kCubic_Verb;
495
496 if (reverse) {
497 for (int i = 0; i < 4; i++) {
498 fCurrPoint[i] = pts[3 - i];
499 }
500 } else {
501 memcpy(fCurrPoint, pts, 4 * sizeof(SkPoint));
502 }
503 fCurrPoint += 4;
504 }
505
next(SkPoint pts[])506 SkPath::Verb SkEdgeClipper::next(SkPoint pts[]) {
507 SkPath::Verb verb = *fCurrVerb;
508
509 switch (verb) {
510 case SkPath::kLine_Verb:
511 memcpy(pts, fCurrPoint, 2 * sizeof(SkPoint));
512 fCurrPoint += 2;
513 fCurrVerb += 1;
514 break;
515 case SkPath::kQuad_Verb:
516 memcpy(pts, fCurrPoint, 3 * sizeof(SkPoint));
517 fCurrPoint += 3;
518 fCurrVerb += 1;
519 break;
520 case SkPath::kCubic_Verb:
521 memcpy(pts, fCurrPoint, 4 * sizeof(SkPoint));
522 fCurrPoint += 4;
523 fCurrVerb += 1;
524 break;
525 case SkPath::kDone_Verb:
526 break;
527 default:
528 SkDEBUGFAIL("unexpected verb in quadclippper2 iter");
529 break;
530 }
531 return verb;
532 }
533
534 ///////////////////////////////////////////////////////////////////////////////
535
536 #ifdef SK_DEBUG
assert_monotonic(const SkScalar coord[],int count)537 static void assert_monotonic(const SkScalar coord[], int count) {
538 if (coord[0] > coord[(count - 1) * 2]) {
539 for (int i = 1; i < count; i++) {
540 SkASSERT(coord[2 * (i - 1)] >= coord[i * 2]);
541 }
542 } else if (coord[0] < coord[(count - 1) * 2]) {
543 for (int i = 1; i < count; i++) {
544 SkASSERT(coord[2 * (i - 1)] <= coord[i * 2]);
545 }
546 } else {
547 for (int i = 1; i < count; i++) {
548 SkASSERT(coord[2 * (i - 1)] == coord[i * 2]);
549 }
550 }
551 }
552
sk_assert_monotonic_y(const SkPoint pts[],int count)553 void sk_assert_monotonic_y(const SkPoint pts[], int count) {
554 if (count > 1) {
555 assert_monotonic(&pts[0].fY, count);
556 }
557 }
558
sk_assert_monotonic_x(const SkPoint pts[],int count)559 void sk_assert_monotonic_x(const SkPoint pts[], int count) {
560 if (count > 1) {
561 assert_monotonic(&pts[0].fX, count);
562 }
563 }
564 #endif
565
ClipPath(const SkPath & path,const SkRect & clip,bool canCullToTheRight,void (* consume)(SkEdgeClipper *,bool newCtr,void * ctx),void * ctx)566 void SkEdgeClipper::ClipPath(const SkPath& path, const SkRect& clip, bool canCullToTheRight,
567 void (*consume)(SkEdgeClipper*, bool newCtr, void* ctx), void* ctx) {
568 SkASSERT(path.isFinite());
569
570 SkAutoConicToQuads quadder;
571 const SkScalar conicTol = SK_Scalar1 / 4;
572
573 SkPathEdgeIter iter(path);
574 SkEdgeClipper clipper(canCullToTheRight);
575
576 while (auto e = iter.next()) {
577 switch (e.fEdge) {
578 case SkPathEdgeIter::Edge::kLine:
579 if (clipper.clipLine(e.fPts[0], e.fPts[1], clip)) {
580 consume(&clipper, e.fIsNewContour, ctx);
581 }
582 break;
583 case SkPathEdgeIter::Edge::kQuad:
584 if (clipper.clipQuad(e.fPts, clip)) {
585 consume(&clipper, e.fIsNewContour, ctx);
586 }
587 break;
588 case SkPathEdgeIter::Edge::kConic: {
589 const SkPoint* quadPts = quadder.computeQuads(e.fPts, iter.conicWeight(), conicTol);
590 for (int i = 0; i < quadder.countQuads(); ++i) {
591 if (clipper.clipQuad(quadPts, clip)) {
592 consume(&clipper, e.fIsNewContour, ctx);
593 }
594 quadPts += 2;
595 }
596 } break;
597 case SkPathEdgeIter::Edge::kCubic:
598 if (clipper.clipCubic(e.fPts, clip)) {
599 consume(&clipper, e.fIsNewContour, ctx);
600 }
601 break;
602 }
603 }
604 }
605