Upstream version 7.36.149.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->debugOne(true);
111         SkDebugf("\n");
112         next = next->fNext;
113     } while (next && next != first);
114 }
115
116 void 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
173 void 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
187 void 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
205 void 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
349 bool 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
360 void 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
371 void 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
381 bool 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
397 int 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
407 void SkOpSegment::debugReset() {
408     fTs.reset();
409     fAngles.reset();
410 }
411
412 #if DEBUG_CONCIDENT
413 void 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
451 void 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
498 void 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
518 void 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
547 int 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
569 void 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 }