• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ChaseContains(const SkTDArray<SkOpSpan * > & chaseArray,const SkOpSpan * span)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 
MathematicaIze(char * str,size_t bufferLen)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 
ValidWind(int wind)57 bool SkPathOpsDebug::ValidWind(int wind) {
58     return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
59 }
60 
WindingPrintf(int wind)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
CreateNameStr()70 void* SkPathOpsDebug::CreateNameStr() {
71     return SkNEW_ARRAY(char, DEBUG_FILENAME_STRING_LENGTH);
72 }
73 
DeleteNameStr(void * v)74 void SkPathOpsDebug::DeleteNameStr(void* v) {
75     SkDELETE_ARRAY(reinterpret_cast<char* >(v));
76 }
77 
BumpTestName(char * test)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
ShowPath(const SkPath & one,const SkPath & two,SkPathOp op,const char * name)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
debugLoop() const106 void 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
debugSameAs(const SkOpAngle * compare) const117 void 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
debugValidateNext() const131 void 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 
debugValidateLoop() const149 void 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
controlsContainedByEnds(int tStart,int tEnd) const293 bool 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
debugAddTPair(double t,const SkOpSegment & other,double otherT) const304 void 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
debugCheckPointsEqualish(int tStart,int tEnd) const315 void 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
debugInflections(int tStart,int tEnd) const325 int 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 
debugLastAngle() const335 const 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 
debugReset()348 void SkOpSegment::debugReset() {
349     fTs.reset();
350     fAngles.reset();
351 }
352 
353 #if DEBUG_CONCIDENT
debugShowTs(const char * prefix) const354 void 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
debugShowActiveSpans() const392 void 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
debugShowNewWinding(const char * fun,const SkOpSpan & span,int winding)439 void 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 
debugShowNewWinding(const char * fun,const SkOpSpan & span,int winding,int oppWinding)459 void 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
debugShowWindingValues(int slotCount,int ofInterest) const488 int 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 
debugValidate() const510 void 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