SkPathOpsDebug.cpp revision e4097e3a0b10bb0047a45b6949ca01826f0807a7
1/*
2 * Copyright 2013 Google Inc.
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 "SkPathOpsDebug.h"
9#include "SkPath.h"
10
11#if defined SK_DEBUG || !FORCE_RELEASE
12
13const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"};
14
15#if defined(SK_DEBUG) || !FORCE_RELEASE
16int SkPathOpsDebug::gContourID = 0;
17int SkPathOpsDebug::gSegmentID = 0;
18#endif
19
20#if DEBUG_SORT || DEBUG_SWAP_TOP
21int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
22int SkPathOpsDebug::gSortCount;
23#endif
24
25#if DEBUG_ACTIVE_OP
26const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
27#endif
28
29bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpan *>& chaseArray,
30        const SkOpSpan* span) {
31    for (int index = 0; index < chaseArray.count(); ++index) {
32        const SkOpSpan* entry = chaseArray[index];
33        if (entry == span) {
34            return true;
35        }
36    }
37    return false;
38}
39
40void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
41    size_t len = strlen(str);
42    bool num = false;
43    for (size_t idx = 0; idx < len; ++idx) {
44        if (num && str[idx] == 'e') {
45            if (len + 2 >= bufferLen) {
46                return;
47            }
48            memmove(&str[idx + 2], &str[idx + 1], len - idx);
49            str[idx] = '*';
50            str[idx + 1] = '^';
51            ++len;
52        }
53        num = str[idx] >= '0' && str[idx] <= '9';
54    }
55}
56
57bool SkPathOpsDebug::ValidWind(int wind) {
58    return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
59}
60
61void SkPathOpsDebug::WindingPrintf(int wind) {
62    if (wind == SK_MinS32) {
63        SkDebugf("?");
64    } else {
65        SkDebugf("%d", wind);
66    }
67}
68
69#if DEBUG_SHOW_TEST_NAME
70void* SkPathOpsDebug::CreateNameStr() {
71    return SkNEW_ARRAY(char, DEBUG_FILENAME_STRING_LENGTH);
72}
73
74void SkPathOpsDebug::DeleteNameStr(void* v) {
75    SkDELETE_ARRAY(reinterpret_cast<char* >(v));
76}
77
78void SkPathOpsDebug::BumpTestName(char* test) {
79    char* num = test + strlen(test);
80    while (num[-1] >= '0' && num[-1] <= '9') {
81        --num;
82    }
83    if (num[0] == '\0') {
84        return;
85    }
86    int dec = atoi(num);
87    if (dec == 0) {
88        return;
89    }
90    ++dec;
91    SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec);
92}
93#endif
94
95#if !DEBUG_SHOW_TEST_NAME  // enable when building without extended test
96void SkPathOpsDebug::ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name) {
97}
98#endif
99
100#endif //  defined SK_DEBUG || !FORCE_RELEASE
101
102#include "SkOpAngle.h"
103#include "SkOpSegment.h"
104
105#if DEBUG_SORT
106void SkOpAngle::debugLoop() const {
107    const SkOpAngle* first = this;
108    const SkOpAngle* next = this;
109    do {
110        next->dumpOne(true);
111        next = next->fNext;
112    } while (next && next != first);
113}
114#endif
115
116#if DEBUG_ANGLE
117void SkOpAngle::debugSameAs(const SkOpAngle* compare) const {
118    SK_ALWAYSBREAK(fSegment == compare->fSegment);
119    const SkOpSpan& startSpan = fSegment->span(fStart);
120    const SkOpSpan& oStartSpan = fSegment->span(compare->fStart);
121    SK_ALWAYSBREAK(startSpan.fToAngle == oStartSpan.fToAngle);
122    SK_ALWAYSBREAK(startSpan.fFromAngle == oStartSpan.fFromAngle);
123    const SkOpSpan& endSpan = fSegment->span(fEnd);
124    const SkOpSpan& oEndSpan = fSegment->span(compare->fEnd);
125    SK_ALWAYSBREAK(endSpan.fToAngle == oEndSpan.fToAngle);
126    SK_ALWAYSBREAK(endSpan.fFromAngle == oEndSpan.fFromAngle);
127}
128#endif
129
130#if DEBUG_VALIDATE
131void SkOpAngle::debugValidateNext() const {
132    const SkOpAngle* first = this;
133    const SkOpAngle* next = first;
134    SkTDArray<const SkOpAngle*>(angles);
135    do {
136//        SK_ALWAYSBREAK(next->fSegment->debugContains(next));
137        angles.push(next);
138        next = next->next();
139        if (next == first) {
140            break;
141        }
142        SK_ALWAYSBREAK(!angles.contains(next));
143        if (!next) {
144            return;
145        }
146    } while (true);
147}
148
149void SkOpAngle::debugValidateLoop() const {
150    const SkOpAngle* first = this;
151    const SkOpAngle* next = first;
152    SK_ALWAYSBREAK(first->next() != first);
153    int signSum = 0;
154    int oppSum = 0;
155    bool firstOperand = fSegment->operand();
156    bool unorderable = false;
157    do {
158        unorderable |= next->fUnorderable;
159        const SkOpSegment* segment = next->fSegment;
160        bool operandsMatch = firstOperand == segment->operand();
161        signSum += operandsMatch ? segment->spanSign(next) : segment->oppSign(next);
162        oppSum += operandsMatch ? segment->oppSign(next) : segment->spanSign(next);
163        const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
164        if (segment->_xor()) {
165//            SK_ALWAYSBREAK(span.fWindValue == 1);
166//            SK_ALWAYSBREAK(span.fWindSum == SK_MinS32 || span.fWindSum == 1);
167        }
168        if (segment->oppXor()) {
169            SK_ALWAYSBREAK(span.fOppValue == 0 || abs(span.fOppValue) == 1);
170//            SK_ALWAYSBREAK(span.fOppSum == SK_MinS32 || span.fOppSum == 0 || abs(span.fOppSum) == 1);
171        }
172        next = next->next();
173        if (!next) {
174            return;
175        }
176    } while (next != first);
177    if (unorderable) {
178        return;
179    }
180    SK_ALWAYSBREAK(!signSum || fSegment->_xor());
181    SK_ALWAYSBREAK(!oppSum || fSegment->oppXor());
182    int lastWinding;
183    int lastOppWinding;
184    int winding;
185    int oppWinding;
186    do {
187        const SkOpSegment* segment = next->fSegment;
188        const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
189        winding = span.fWindSum;
190        if (winding != SK_MinS32) {
191//            SK_ALWAYSBREAK(winding != 0);
192            SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
193            lastWinding = winding;
194            int diffWinding = segment->spanSign(next);
195            if (!segment->_xor()) {
196                SK_ALWAYSBREAK(diffWinding != 0);
197                bool sameSign = (winding > 0) == (diffWinding > 0);
198                winding -= sameSign ? diffWinding : -diffWinding;
199                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
200                SK_ALWAYSBREAK(abs(winding) <= abs(lastWinding));
201                if (!sameSign) {
202                    SkTSwap(winding, lastWinding);
203                }
204            }
205            lastOppWinding = oppWinding = span.fOppSum;
206            if (oppWinding != SK_MinS32 && !segment->oppXor()) {
207                int oppDiffWinding = segment->oppSign(next);
208//                SK_ALWAYSBREAK(abs(oppDiffWinding) <= abs(diffWinding) || segment->_xor());
209                if (oppDiffWinding) {
210                    bool oppSameSign = (oppWinding > 0) == (oppDiffWinding > 0);
211                    oppWinding -= oppSameSign ? oppDiffWinding : -oppDiffWinding;
212                    SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
213                    SK_ALWAYSBREAK(abs(oppWinding) <= abs(lastOppWinding));
214                    if (!oppSameSign) {
215                        SkTSwap(oppWinding, lastOppWinding);
216                    }
217                }
218            }
219            firstOperand = segment->operand();
220            break;
221        }
222        SK_ALWAYSBREAK(span.fOppSum == SK_MinS32);
223        next = next->next();
224    } while (next != first);
225    if (winding == SK_MinS32) {
226        return;
227    }
228    SK_ALWAYSBREAK(oppWinding == SK_MinS32 || SkPathOpsDebug::ValidWind(oppWinding));
229    first = next;
230    next = next->next();
231    do {
232        const SkOpSegment* segment = next->fSegment;
233        lastWinding = winding;
234        lastOppWinding = oppWinding;
235        bool operandsMatch = firstOperand == segment->operand();
236        if (operandsMatch) {
237            if (!segment->_xor()) {
238                winding -= segment->spanSign(next);
239                SK_ALWAYSBREAK(winding != lastWinding);
240                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
241            }
242            if (!segment->oppXor()) {
243                int oppDiffWinding = segment->oppSign(next);
244                if (oppWinding != SK_MinS32) {
245                    oppWinding -= oppDiffWinding;
246                    SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
247                } else {
248                    SK_ALWAYSBREAK(oppDiffWinding == 0);
249                }
250            }
251        } else {
252            if (!segment->oppXor()) {
253                winding -= segment->oppSign(next);
254                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
255            }
256            if (!segment->_xor()) {
257                oppWinding -= segment->spanSign(next);
258                SK_ALWAYSBREAK(oppWinding != lastOppWinding);
259                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
260            }
261        }
262        bool useInner = SkOpSegment::UseInnerWinding(lastWinding, winding);
263        int sumWinding = useInner ? winding : lastWinding;
264        bool oppUseInner = SkOpSegment::UseInnerWinding(lastOppWinding, oppWinding);
265        int oppSumWinding = oppUseInner ? oppWinding : lastOppWinding;
266        if (!operandsMatch) {
267            SkTSwap(useInner, oppUseInner);
268            SkTSwap(sumWinding, oppSumWinding);
269        }
270        const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
271        if (winding == -lastWinding) {
272            if (span.fWindSum != SK_MinS32) {
273                SkDebugf("%s useInner=%d spanSign=%d lastWinding=%d winding=%d windSum=%d\n",
274                        __FUNCTION__,
275                        useInner, segment->spanSign(next), lastWinding, winding, span.fWindSum);
276            }
277        }
278        if (oppWinding != SK_MinS32) {
279            if (span.fOppSum != SK_MinS32) {
280                SK_ALWAYSBREAK(span.fOppSum == oppSumWinding || segment->oppXor() || segment->_xor());
281            }
282        } else {
283            SK_ALWAYSBREAK(!firstOperand);
284            SK_ALWAYSBREAK(!segment->operand());
285            SK_ALWAYSBREAK(!span.fOppValue);
286        }
287        next = next->next();
288    } while (next != first);
289}
290#endif
291
292#if DEBUG_SWAP_TOP
293bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const {
294    if (fVerb != SkPath::kCubic_Verb) {
295        return false;
296    }
297    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
298    return dst.controlsContainedByEnds();
299}
300#endif
301
302#if DEBUG_CONCIDENT
303// SK_ALWAYSBREAK if pair has not already been added
304void SkOpSegment::debugAddTPair(double t, const SkOpSegment& other, double otherT) const {
305    for (int i = 0; i < fTs.count(); ++i) {
306        if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
307            return;
308        }
309    }
310    SK_ALWAYSBREAK(0);
311}
312#endif
313
314#if DEBUG_ANGLE
315void SkOpSegment::debugCheckPointsEqualish(int tStart, int tEnd) const {
316    const SkPoint& basePt = fTs[tStart].fPt;
317    while (++tStart < tEnd) {
318       const SkPoint& cmpPt = fTs[tStart].fPt;
319       SK_ALWAYSBREAK(SkDPoint::ApproximatelyEqual(basePt, cmpPt));
320    }
321}
322#endif
323
324#if DEBUG_SWAP_TOP
325int SkOpSegment::debugInflections(int tStart, int tEnd) const {
326    if (fVerb != SkPath::kCubic_Verb) {
327        return false;
328    }
329    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
330    double inflections[2];
331    return dst.findInflections(inflections);
332}
333#endif
334
335const SkOpAngle* SkOpSegment::debugLastAngle() const {
336    const SkOpAngle* result = NULL;
337    for (int index = 0; index < count(); ++index) {
338        const SkOpSpan& span = this->span(index);
339        if (span.fToAngle) {
340            SkASSERT(!result);
341            result = span.fToAngle;
342        }
343    }
344    SkASSERT(result);
345    return result;
346}
347
348void SkOpSegment::debugReset() {
349    fTs.reset();
350    fAngles.reset();
351}
352
353#if DEBUG_CONCIDENT
354void SkOpSegment::debugShowTs(const char* prefix) const {
355    SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID);
356    int lastWind = -1;
357    int lastOpp = -1;
358    double lastT = -1;
359    int i;
360    for (i = 0; i < fTs.count(); ++i) {
361        bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
362                || lastOpp != fTs[i].fOppValue;
363        if (change && lastWind >= 0) {
364            SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
365                    lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
366        }
367        if (change) {
368            SkDebugf(" [o=%d", fTs[i].fOther->fID);
369            lastWind = fTs[i].fWindValue;
370            lastOpp = fTs[i].fOppValue;
371            lastT = fTs[i].fT;
372        } else {
373            SkDebugf(",%d", fTs[i].fOther->fID);
374        }
375    }
376    if (i <= 0) {
377        return;
378    }
379    SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
380            lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
381    if (fOperand) {
382        SkDebugf(" operand");
383    }
384    if (done()) {
385        SkDebugf(" done");
386    }
387    SkDebugf("\n");
388}
389#endif
390
391#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
392void SkOpSegment::debugShowActiveSpans() const {
393    debugValidate();
394    if (done()) {
395        return;
396    }
397#if DEBUG_ACTIVE_SPANS_SHORT_FORM
398    int lastId = -1;
399    double lastT = -1;
400#endif
401    for (int i = 0; i < fTs.count(); ++i) {
402        if (fTs[i].fDone) {
403            continue;
404        }
405        SK_ALWAYSBREAK(i < fTs.count() - 1);
406#if DEBUG_ACTIVE_SPANS_SHORT_FORM
407        if (lastId == fID && lastT == fTs[i].fT) {
408            continue;
409        }
410        lastId = fID;
411        lastT = fTs[i].fT;
412#endif
413        SkDebugf("%s id=%d", __FUNCTION__, fID);
414        SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
415        for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
416            SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
417        }
418        const SkOpSpan* span = &fTs[i];
419        SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span));
420        int iEnd = i + 1;
421        while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) {
422            ++iEnd;
423        }
424        SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT);
425        const SkOpSegment* other = fTs[i].fOther;
426        SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
427                other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
428        if (fTs[i].fWindSum == SK_MinS32) {
429            SkDebugf("?");
430        } else {
431            SkDebugf("%d", fTs[i].fWindSum);
432        }
433        SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
434    }
435}
436#endif
437
438#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
439void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) {
440    const SkPoint& pt = xyAtT(&span);
441    SkDebugf("%s id=%d", fun, fID);
442    SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
443    for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
444        SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
445    }
446    SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
447            fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
448    SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d windSum=",
449            span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
450            (&span)[1].fT, winding);
451    if (span.fWindSum == SK_MinS32) {
452        SkDebugf("?");
453    } else {
454        SkDebugf("%d", span.fWindSum);
455    }
456    SkDebugf(" windValue=%d\n", span.fWindValue);
457}
458
459void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding,
460                                      int oppWinding) {
461    const SkPoint& pt = xyAtT(&span);
462    SkDebugf("%s id=%d", fun, fID);
463    SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
464    for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
465        SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
466    }
467    SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
468            fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
469    SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=",
470            span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
471            (&span)[1].fT, winding, oppWinding);
472    if (span.fOppSum == SK_MinS32) {
473        SkDebugf("?");
474    } else {
475        SkDebugf("%d", span.fOppSum);
476    }
477    SkDebugf(" windSum=");
478    if (span.fWindSum == SK_MinS32) {
479        SkDebugf("?");
480    } else {
481        SkDebugf("%d", span.fWindSum);
482    }
483    SkDebugf(" windValue=%d oppValue=%d\n", span.fWindValue, span.fOppValue);
484}
485#endif
486
487#if DEBUG_SHOW_WINDING
488int SkOpSegment::debugShowWindingValues(int slotCount, int ofInterest) const {
489    if (!(1 << fID & ofInterest)) {
490        return 0;
491    }
492    int sum = 0;
493    SkTArray<char, true> slots(slotCount * 2);
494    memset(slots.begin(), ' ', slotCount * 2);
495    for (int i = 0; i < fTs.count(); ++i) {
496   //     if (!(1 << fTs[i].fOther->fID & ofInterest)) {
497   //         continue;
498   //     }
499        sum += fTs[i].fWindValue;
500        slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
501        sum += fTs[i].fOppValue;
502        slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
503    }
504    SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
505            slots.begin() + slotCount);
506    return sum;
507}
508#endif
509
510void SkOpSegment::debugValidate() const {
511#if DEBUG_VALIDATE
512    int count = fTs.count();
513    SK_ALWAYSBREAK(count >= 2);
514    SK_ALWAYSBREAK(fTs[0].fT == 0);
515    SK_ALWAYSBREAK(fTs[count - 1].fT == 1);
516    int done = 0;
517    double t = -1;
518    const SkOpSpan* last = NULL;
519    bool tinyTFound = false;
520    bool hasLoop = false;
521    for (int i = 0; i < count; ++i) {
522        const SkOpSpan& span = fTs[i];
523        SK_ALWAYSBREAK(t <= span.fT);
524        t = span.fT;
525        int otherIndex = span.fOtherIndex;
526        const SkOpSegment* other = span.fOther;
527        SK_ALWAYSBREAK(other != this || fVerb == SkPath::kCubic_Verb);
528        const SkOpSpan& otherSpan = other->fTs[otherIndex];
529        SK_ALWAYSBREAK(otherSpan.fPt == span.fPt);
530        SK_ALWAYSBREAK(otherSpan.fOtherT == t);
531        SK_ALWAYSBREAK(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]);
532        done += span.fDone;
533        if (last) {
534            SK_ALWAYSBREAK(last->fT != span.fT || last->fOther != span.fOther);
535            bool tsEqual = last->fT == span.fT;
536            bool tsPreciselyEqual = precisely_equal(last->fT, span.fT);
537            SK_ALWAYSBREAK(!tsEqual || tsPreciselyEqual);
538            bool pointsEqual = last->fPt == span.fPt;
539            bool pointsNearlyEqual = AlmostEqualUlps(last->fPt, span.fPt);
540#if 0  // bufferOverflow test triggers this
541            SK_ALWAYSBREAK(!tsPreciselyEqual || pointsNearlyEqual);
542#endif
543//            SK_ALWAYSBREAK(!last->fTiny || !tsPreciselyEqual || span.fTiny || tinyTFound);
544            SK_ALWAYSBREAK(last->fTiny || tsPreciselyEqual || !pointsEqual || hasLoop);
545            SK_ALWAYSBREAK(!last->fTiny || pointsEqual);
546            SK_ALWAYSBREAK(!last->fTiny || last->fDone);
547            SK_ALWAYSBREAK(!last->fSmall || pointsNearlyEqual);
548            SK_ALWAYSBREAK(!last->fSmall || last->fDone);
549//            SK_ALWAYSBREAK(!last->fSmall || last->fTiny);
550//            SK_ALWAYSBREAK(last->fTiny || !pointsEqual || last->fDone == span.fDone);
551            if (last->fTiny) {
552                tinyTFound |= !tsPreciselyEqual;
553            } else {
554                tinyTFound = false;
555            }
556        }
557        last = &span;
558        hasLoop |= last->fLoop;
559    }
560    SK_ALWAYSBREAK(done == fDoneSpans);
561//    if (fAngles.count() ) {
562//        fAngles.begin()->debugValidateLoop();
563//    }
564#endif
565}
566