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