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