| /* |
| * Copyright 2013 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkPathOpsDebug.h" |
| #include "SkPath.h" |
| |
| #if defined SK_DEBUG || !FORCE_RELEASE |
| |
| const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"}; |
| |
| #if defined(SK_DEBUG) || !FORCE_RELEASE |
| int SkPathOpsDebug::gContourID = 0; |
| int SkPathOpsDebug::gSegmentID = 0; |
| #endif |
| |
| #if DEBUG_SORT || DEBUG_SWAP_TOP |
| int SkPathOpsDebug::gSortCountDefault = SK_MaxS32; |
| int SkPathOpsDebug::gSortCount; |
| #endif |
| |
| #if DEBUG_ACTIVE_OP |
| const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"}; |
| #endif |
| |
| bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpan *>& chaseArray, |
| const SkOpSpan* span) { |
| for (int index = 0; index < chaseArray.count(); ++index) { |
| const SkOpSpan* entry = chaseArray[index]; |
| if (entry == span) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) { |
| size_t len = strlen(str); |
| bool num = false; |
| for (size_t idx = 0; idx < len; ++idx) { |
| if (num && str[idx] == 'e') { |
| if (len + 2 >= bufferLen) { |
| return; |
| } |
| memmove(&str[idx + 2], &str[idx + 1], len - idx); |
| str[idx] = '*'; |
| str[idx + 1] = '^'; |
| ++len; |
| } |
| num = str[idx] >= '0' && str[idx] <= '9'; |
| } |
| } |
| |
| bool SkPathOpsDebug::ValidWind(int wind) { |
| return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF; |
| } |
| |
| void SkPathOpsDebug::WindingPrintf(int wind) { |
| if (wind == SK_MinS32) { |
| SkDebugf("?"); |
| } else { |
| SkDebugf("%d", wind); |
| } |
| } |
| |
| #if DEBUG_SHOW_TEST_NAME |
| void* SkPathOpsDebug::CreateNameStr() { |
| return SkNEW_ARRAY(char, DEBUG_FILENAME_STRING_LENGTH); |
| } |
| |
| void SkPathOpsDebug::DeleteNameStr(void* v) { |
| SkDELETE_ARRAY(reinterpret_cast<char* >(v)); |
| } |
| |
| void SkPathOpsDebug::BumpTestName(char* test) { |
| char* num = test + strlen(test); |
| while (num[-1] >= '0' && num[-1] <= '9') { |
| --num; |
| } |
| if (num[0] == '\0') { |
| return; |
| } |
| int dec = atoi(num); |
| if (dec == 0) { |
| return; |
| } |
| ++dec; |
| SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec); |
| } |
| #endif |
| |
| #if !DEBUG_SHOW_TEST_NAME // enable when building without extended test |
| void SkPathOpsDebug::ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name) { |
| } |
| #endif |
| |
| #endif // defined SK_DEBUG || !FORCE_RELEASE |
| |
| #include "SkOpAngle.h" |
| #include "SkOpSegment.h" |
| |
| #if DEBUG_SORT |
| void SkOpAngle::debugLoop() const { |
| const SkOpAngle* first = this; |
| const SkOpAngle* next = this; |
| do { |
| next->dumpOne(true); |
| SkDebugf("\n"); |
| next = next->fNext; |
| } while (next && next != first); |
| } |
| #endif |
| |
| #if DEBUG_ANGLE |
| void SkOpAngle::debugSameAs(const SkOpAngle* compare) const { |
| SK_ALWAYSBREAK(fSegment == compare->fSegment); |
| const SkOpSpan& startSpan = fSegment->span(fStart); |
| const SkOpSpan& oStartSpan = fSegment->span(compare->fStart); |
| SK_ALWAYSBREAK(startSpan.fToAngle == oStartSpan.fToAngle); |
| SK_ALWAYSBREAK(startSpan.fFromAngle == oStartSpan.fFromAngle); |
| const SkOpSpan& endSpan = fSegment->span(fEnd); |
| const SkOpSpan& oEndSpan = fSegment->span(compare->fEnd); |
| SK_ALWAYSBREAK(endSpan.fToAngle == oEndSpan.fToAngle); |
| SK_ALWAYSBREAK(endSpan.fFromAngle == oEndSpan.fFromAngle); |
| } |
| #endif |
| |
| #if DEBUG_VALIDATE |
| void SkOpAngle::debugValidateNext() const { |
| const SkOpAngle* first = this; |
| const SkOpAngle* next = first; |
| SkTDArray<const SkOpAngle*>(angles); |
| do { |
| // SK_ALWAYSBREAK(next->fSegment->debugContains(next)); |
| angles.push(next); |
| next = next->next(); |
| if (next == first) { |
| break; |
| } |
| SK_ALWAYSBREAK(!angles.contains(next)); |
| if (!next) { |
| return; |
| } |
| } while (true); |
| } |
| |
| void SkOpAngle::debugValidateLoop() const { |
| const SkOpAngle* first = this; |
| const SkOpAngle* next = first; |
| SK_ALWAYSBREAK(first->next() != first); |
| int signSum = 0; |
| int oppSum = 0; |
| bool firstOperand = fSegment->operand(); |
| bool unorderable = false; |
| do { |
| unorderable |= next->fUnorderable; |
| const SkOpSegment* segment = next->fSegment; |
| bool operandsMatch = firstOperand == segment->operand(); |
| signSum += operandsMatch ? segment->spanSign(next) : segment->oppSign(next); |
| oppSum += operandsMatch ? segment->oppSign(next) : segment->spanSign(next); |
| const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd)); |
| if (segment->_xor()) { |
| // SK_ALWAYSBREAK(span.fWindValue == 1); |
| // SK_ALWAYSBREAK(span.fWindSum == SK_MinS32 || span.fWindSum == 1); |
| } |
| if (segment->oppXor()) { |
| SK_ALWAYSBREAK(span.fOppValue == 0 || abs(span.fOppValue) == 1); |
| // SK_ALWAYSBREAK(span.fOppSum == SK_MinS32 || span.fOppSum == 0 || abs(span.fOppSum) == 1); |
| } |
| next = next->next(); |
| if (!next) { |
| return; |
| } |
| } while (next != first); |
| if (unorderable) { |
| return; |
| } |
| SK_ALWAYSBREAK(!signSum || fSegment->_xor()); |
| SK_ALWAYSBREAK(!oppSum || fSegment->oppXor()); |
| int lastWinding; |
| int lastOppWinding; |
| int winding; |
| int oppWinding; |
| do { |
| const SkOpSegment* segment = next->fSegment; |
| const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd)); |
| winding = span.fWindSum; |
| if (winding != SK_MinS32) { |
| // SK_ALWAYSBREAK(winding != 0); |
| SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding)); |
| lastWinding = winding; |
| int diffWinding = segment->spanSign(next); |
| if (!segment->_xor()) { |
| SK_ALWAYSBREAK(diffWinding != 0); |
| bool sameSign = (winding > 0) == (diffWinding > 0); |
| winding -= sameSign ? diffWinding : -diffWinding; |
| SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding)); |
| SK_ALWAYSBREAK(abs(winding) <= abs(lastWinding)); |
| if (!sameSign) { |
| SkTSwap(winding, lastWinding); |
| } |
| } |
| lastOppWinding = oppWinding = span.fOppSum; |
| if (oppWinding != SK_MinS32 && !segment->oppXor()) { |
| int oppDiffWinding = segment->oppSign(next); |
| // SK_ALWAYSBREAK(abs(oppDiffWinding) <= abs(diffWinding) || segment->_xor()); |
| if (oppDiffWinding) { |
| bool oppSameSign = (oppWinding > 0) == (oppDiffWinding > 0); |
| oppWinding -= oppSameSign ? oppDiffWinding : -oppDiffWinding; |
| SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding)); |
| SK_ALWAYSBREAK(abs(oppWinding) <= abs(lastOppWinding)); |
| if (!oppSameSign) { |
| SkTSwap(oppWinding, lastOppWinding); |
| } |
| } |
| } |
| firstOperand = segment->operand(); |
| break; |
| } |
| SK_ALWAYSBREAK(span.fOppSum == SK_MinS32); |
| next = next->next(); |
| } while (next != first); |
| if (winding == SK_MinS32) { |
| return; |
| } |
| SK_ALWAYSBREAK(oppWinding == SK_MinS32 || SkPathOpsDebug::ValidWind(oppWinding)); |
| first = next; |
| next = next->next(); |
| do { |
| const SkOpSegment* segment = next->fSegment; |
| lastWinding = winding; |
| lastOppWinding = oppWinding; |
| bool operandsMatch = firstOperand == segment->operand(); |
| if (operandsMatch) { |
| if (!segment->_xor()) { |
| winding -= segment->spanSign(next); |
| SK_ALWAYSBREAK(winding != lastWinding); |
| SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding)); |
| } |
| if (!segment->oppXor()) { |
| int oppDiffWinding = segment->oppSign(next); |
| if (oppWinding != SK_MinS32) { |
| oppWinding -= oppDiffWinding; |
| SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding)); |
| } else { |
| SK_ALWAYSBREAK(oppDiffWinding == 0); |
| } |
| } |
| } else { |
| if (!segment->oppXor()) { |
| winding -= segment->oppSign(next); |
| SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding)); |
| } |
| if (!segment->_xor()) { |
| oppWinding -= segment->spanSign(next); |
| SK_ALWAYSBREAK(oppWinding != lastOppWinding); |
| SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding)); |
| } |
| } |
| bool useInner = SkOpSegment::UseInnerWinding(lastWinding, winding); |
| int sumWinding = useInner ? winding : lastWinding; |
| bool oppUseInner = SkOpSegment::UseInnerWinding(lastOppWinding, oppWinding); |
| int oppSumWinding = oppUseInner ? oppWinding : lastOppWinding; |
| if (!operandsMatch) { |
| SkTSwap(useInner, oppUseInner); |
| SkTSwap(sumWinding, oppSumWinding); |
| } |
| const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd)); |
| if (winding == -lastWinding) { |
| if (span.fWindSum != SK_MinS32) { |
| SkDebugf("%s useInner=%d spanSign=%d lastWinding=%d winding=%d windSum=%d\n", |
| __FUNCTION__, |
| useInner, segment->spanSign(next), lastWinding, winding, span.fWindSum); |
| } |
| } |
| if (oppWinding != SK_MinS32) { |
| if (span.fOppSum != SK_MinS32) { |
| SK_ALWAYSBREAK(span.fOppSum == oppSumWinding || segment->oppXor() || segment->_xor()); |
| } |
| } else { |
| SK_ALWAYSBREAK(!firstOperand); |
| SK_ALWAYSBREAK(!segment->operand()); |
| SK_ALWAYSBREAK(!span.fOppValue); |
| } |
| next = next->next(); |
| } while (next != first); |
| } |
| #endif |
| |
| #if DEBUG_SWAP_TOP |
| bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const { |
| if (fVerb != SkPath::kCubic_Verb) { |
| return false; |
| } |
| SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT); |
| return dst.controlsContainedByEnds(); |
| } |
| #endif |
| |
| #if DEBUG_CONCIDENT |
| // SK_ALWAYSBREAK if pair has not already been added |
| void SkOpSegment::debugAddTPair(double t, const SkOpSegment& other, double otherT) const { |
| for (int i = 0; i < fTs.count(); ++i) { |
| if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) { |
| return; |
| } |
| } |
| SK_ALWAYSBREAK(0); |
| } |
| #endif |
| |
| #if DEBUG_ANGLE |
| void SkOpSegment::debugCheckPointsEqualish(int tStart, int tEnd) const { |
| const SkPoint& basePt = fTs[tStart].fPt; |
| while (++tStart < tEnd) { |
| const SkPoint& cmpPt = fTs[tStart].fPt; |
| SK_ALWAYSBREAK(SkDPoint::ApproximatelyEqual(basePt, cmpPt)); |
| } |
| } |
| #endif |
| |
| #if DEBUG_SWAP_TOP |
| int SkOpSegment::debugInflections(int tStart, int tEnd) const { |
| if (fVerb != SkPath::kCubic_Verb) { |
| return false; |
| } |
| SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT); |
| double inflections[2]; |
| return dst.findInflections(inflections); |
| } |
| #endif |
| |
| const SkOpAngle* SkOpSegment::debugLastAngle() const { |
| const SkOpAngle* result = NULL; |
| for (int index = 0; index < count(); ++index) { |
| const SkOpSpan& span = this->span(index); |
| if (span.fToAngle) { |
| SkASSERT(!result); |
| result = span.fToAngle; |
| } |
| } |
| SkASSERT(result); |
| return result; |
| } |
| |
| void SkOpSegment::debugReset() { |
| fTs.reset(); |
| fAngles.reset(); |
| } |
| |
| #if DEBUG_CONCIDENT |
| void SkOpSegment::debugShowTs(const char* prefix) const { |
| SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID); |
| int lastWind = -1; |
| int lastOpp = -1; |
| double lastT = -1; |
| int i; |
| for (i = 0; i < fTs.count(); ++i) { |
| bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue |
| || lastOpp != fTs[i].fOppValue; |
| if (change && lastWind >= 0) { |
| SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]", |
| lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp); |
| } |
| if (change) { |
| SkDebugf(" [o=%d", fTs[i].fOther->fID); |
| lastWind = fTs[i].fWindValue; |
| lastOpp = fTs[i].fOppValue; |
| lastT = fTs[i].fT; |
| } else { |
| SkDebugf(",%d", fTs[i].fOther->fID); |
| } |
| } |
| if (i <= 0) { |
| return; |
| } |
| SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]", |
| lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp); |
| if (fOperand) { |
| SkDebugf(" operand"); |
| } |
| if (done()) { |
| SkDebugf(" done"); |
| } |
| SkDebugf("\n"); |
| } |
| #endif |
| |
| #if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY |
| void SkOpSegment::debugShowActiveSpans() const { |
| debugValidate(); |
| if (done()) { |
| return; |
| } |
| #if DEBUG_ACTIVE_SPANS_SHORT_FORM |
| int lastId = -1; |
| double lastT = -1; |
| #endif |
| for (int i = 0; i < fTs.count(); ++i) { |
| if (fTs[i].fDone) { |
| continue; |
| } |
| SK_ALWAYSBREAK(i < fTs.count() - 1); |
| #if DEBUG_ACTIVE_SPANS_SHORT_FORM |
| if (lastId == fID && lastT == fTs[i].fT) { |
| continue; |
| } |
| lastId = fID; |
| lastT = fTs[i].fT; |
| #endif |
| SkDebugf("%s id=%d", __FUNCTION__, fID); |
| SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); |
| for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { |
| SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); |
| } |
| const SkOpSpan* span = &fTs[i]; |
| SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span)); |
| int iEnd = i + 1; |
| while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) { |
| ++iEnd; |
| } |
| SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT); |
| const SkOpSegment* other = fTs[i].fOther; |
| SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=", |
| other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex); |
| if (fTs[i].fWindSum == SK_MinS32) { |
| SkDebugf("?"); |
| } else { |
| SkDebugf("%d", fTs[i].fWindSum); |
| } |
| SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue); |
| } |
| } |
| #endif |
| |
| #if DEBUG_MARK_DONE || DEBUG_UNSORTABLE |
| void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) { |
| const SkPoint& pt = xyAtT(&span); |
| SkDebugf("%s id=%d", fun, fID); |
| SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); |
| for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { |
| SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); |
| } |
| SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther-> |
| fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]); |
| SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d windSum=", |
| span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, |
| (&span)[1].fT, winding); |
| if (span.fWindSum == SK_MinS32) { |
| SkDebugf("?"); |
| } else { |
| SkDebugf("%d", span.fWindSum); |
| } |
| SkDebugf(" windValue=%d\n", span.fWindValue); |
| } |
| |
| void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding, |
| int oppWinding) { |
| const SkPoint& pt = xyAtT(&span); |
| SkDebugf("%s id=%d", fun, fID); |
| SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); |
| for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { |
| SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); |
| } |
| SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther-> |
| fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]); |
| SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=", |
| span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, |
| (&span)[1].fT, winding, oppWinding); |
| if (span.fOppSum == SK_MinS32) { |
| SkDebugf("?"); |
| } else { |
| SkDebugf("%d", span.fOppSum); |
| } |
| SkDebugf(" windSum="); |
| if (span.fWindSum == SK_MinS32) { |
| SkDebugf("?"); |
| } else { |
| SkDebugf("%d", span.fWindSum); |
| } |
| SkDebugf(" windValue=%d oppValue=%d\n", span.fWindValue, span.fOppValue); |
| } |
| #endif |
| |
| #if DEBUG_SHOW_WINDING |
| int SkOpSegment::debugShowWindingValues(int slotCount, int ofInterest) const { |
| if (!(1 << fID & ofInterest)) { |
| return 0; |
| } |
| int sum = 0; |
| SkTArray<char, true> slots(slotCount * 2); |
| memset(slots.begin(), ' ', slotCount * 2); |
| for (int i = 0; i < fTs.count(); ++i) { |
| // if (!(1 << fTs[i].fOther->fID & ofInterest)) { |
| // continue; |
| // } |
| sum += fTs[i].fWindValue; |
| slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue); |
| sum += fTs[i].fOppValue; |
| slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue); |
| } |
| SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount, |
| slots.begin() + slotCount); |
| return sum; |
| } |
| #endif |
| |
| void SkOpSegment::debugValidate() const { |
| #if DEBUG_VALIDATE |
| int count = fTs.count(); |
| SK_ALWAYSBREAK(count >= 2); |
| SK_ALWAYSBREAK(fTs[0].fT == 0); |
| SK_ALWAYSBREAK(fTs[count - 1].fT == 1); |
| int done = 0; |
| double t = -1; |
| const SkOpSpan* last = NULL; |
| bool tinyTFound = false; |
| bool hasLoop = false; |
| for (int i = 0; i < count; ++i) { |
| const SkOpSpan& span = fTs[i]; |
| SK_ALWAYSBREAK(t <= span.fT); |
| t = span.fT; |
| int otherIndex = span.fOtherIndex; |
| const SkOpSegment* other = span.fOther; |
| SK_ALWAYSBREAK(other != this || fVerb == SkPath::kCubic_Verb); |
| const SkOpSpan& otherSpan = other->fTs[otherIndex]; |
| SK_ALWAYSBREAK(otherSpan.fPt == span.fPt); |
| SK_ALWAYSBREAK(otherSpan.fOtherT == t); |
| SK_ALWAYSBREAK(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]); |
| done += span.fDone; |
| if (last) { |
| SK_ALWAYSBREAK(last->fT != span.fT || last->fOther != span.fOther); |
| bool tsEqual = last->fT == span.fT; |
| bool tsPreciselyEqual = precisely_equal(last->fT, span.fT); |
| SK_ALWAYSBREAK(!tsEqual || tsPreciselyEqual); |
| bool pointsEqual = last->fPt == span.fPt; |
| bool pointsNearlyEqual = AlmostEqualUlps(last->fPt, span.fPt); |
| #if 0 // bufferOverflow test triggers this |
| SK_ALWAYSBREAK(!tsPreciselyEqual || pointsNearlyEqual); |
| #endif |
| // SK_ALWAYSBREAK(!last->fTiny || !tsPreciselyEqual || span.fTiny || tinyTFound); |
| SK_ALWAYSBREAK(last->fTiny || tsPreciselyEqual || !pointsEqual || hasLoop); |
| SK_ALWAYSBREAK(!last->fTiny || pointsEqual); |
| SK_ALWAYSBREAK(!last->fTiny || last->fDone); |
| SK_ALWAYSBREAK(!last->fSmall || pointsNearlyEqual); |
| SK_ALWAYSBREAK(!last->fSmall || last->fDone); |
| // SK_ALWAYSBREAK(!last->fSmall || last->fTiny); |
| // SK_ALWAYSBREAK(last->fTiny || !pointsEqual || last->fDone == span.fDone); |
| if (last->fTiny) { |
| tinyTFound |= !tsPreciselyEqual; |
| } else { |
| tinyTFound = false; |
| } |
| } |
| last = &span; |
| hasLoop |= last->fLoop; |
| } |
| SK_ALWAYSBREAK(done == fDoneSpans); |
| // if (fAngles.count() ) { |
| // fAngles.begin()->debugValidateLoop(); |
| // } |
| #endif |
| } |