diff options
author | reed <reed@google.com> | 2015-03-24 13:55:33 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-03-24 13:55:33 -0700 |
commit | 0dc4dd6dda9a7912f696b46d9c02155ec1d1ba5f (patch) | |
tree | 994c85a8e418986415175ddccc71adf924df3846 /src/pathops/SkPathOpsTSect.h | |
parent | 82dec0e16ae10026194ce45b67af931700510450 (diff) |
Revert of pathops version two (patchset #16 id:150001 of https://codereview.chromium.org/1002693002/)
Reason for revert:
ASAN investigation
Original issue's description:
> pathops version two
>
> R=reed@google.com
>
> marked 'no commit' to attempt to get trybots to run
>
> TBR=reed@google.com
>
> Committed: https://skia.googlesource.com/skia/+/ccec0f958ffc71a9986d236bc2eb335cb2111119
TBR=caryclark@google.com
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
Review URL: https://codereview.chromium.org/1029993002
Diffstat (limited to 'src/pathops/SkPathOpsTSect.h')
-rw-r--r-- | src/pathops/SkPathOpsTSect.h | 1636 |
1 files changed, 465 insertions, 1171 deletions
diff --git a/src/pathops/SkPathOpsTSect.h b/src/pathops/SkPathOpsTSect.h index 5c76da7e83..4e7d3b1795 100644 --- a/src/pathops/SkPathOpsTSect.h +++ b/src/pathops/SkPathOpsTSect.h @@ -6,25 +6,15 @@ */ #include "SkChunkAlloc.h" -#include "SkPathOpsBounds.h" #include "SkPathOpsRect.h" #include "SkPathOpsQuad.h" #include "SkIntersections.h" -#include "SkTSort.h" +#include "SkTArray.h" /* TCurve is either SkDQuadratic or SkDCubic */ template<typename TCurve> class SkTCoincident { public: - SkTCoincident() - : fCoincident(false) { - } - - void clear() { - fPerpT = -1; - fCoincident = false; - } - bool isCoincident() const { return fCoincident; } @@ -64,73 +54,41 @@ template<typename TCurve> class SkTSect; template<typename TCurve> class SkTSpan { public: - void addBounded(SkTSpan* ); + void init(const TCurve& ); + void initBounds(const TCurve& ); + double closestBoundedT(const SkDPoint& pt) const; - bool contains(double t) const; - const SkTSect<TCurve>* debugOpp() const; - const SkTSpan* debugSpan(int ) const; - const SkTSpan* debugT(double t) const; -#ifdef SK_DEBUG - bool debugIsBefore(const SkTSpan* span) const; -#endif - void dump() const; - void dumpBounds(int id) const; + bool contains(double t) const { + return !! const_cast<SkTSpan*>(this)->innerFind(t); + } + + bool contains(const SkTSpan* span) const; double endT() const { return fEndT; } - SkTSpan* findOppSpan(const SkTSpan* opp) const; - - SkTSpan* findOppT(double t) const { - SkTSpan* result = oppT(t); + SkTSpan* find(double t) { + SkTSpan* result = innerFind(t); SkASSERT(result); return result; } - bool hasOppT(double t) const { - return SkToBool(oppT(t)); - } - - int hullsIntersect(SkTSpan* span, bool* start, bool* oppStart); - void init(const TCurve& ); - void initBounds(const TCurve& ); - - bool isBounded() const { - return fBounded.count() > 0; - } - - bool linearsIntersect(SkTSpan* span); - double linearT(const SkDPoint& ) const; - - void markCoincident() { - fCoinStart.markCoincident(); - fCoinEnd.markCoincident(); - } + bool intersects(const SkTSpan* span, bool* check); const SkTSpan* next() const { return fNext; } - bool onlyEndPointsInCommon(const SkTSpan* opp, bool* start, bool* oppStart, bool* ptsInCommon); - const TCurve& part() const { return fPart; } - bool removeAllBounded(); - bool removeBounded(const SkTSpan* opp); - void reset() { fBounded.reset(); } - void resetBounds(const TCurve& curve) { - fIsLinear = fIsLine = false; - initBounds(curve); - } - bool split(SkTSpan* work) { return splitAt(work, (work->fStartT + work->fEndT) * 0.5); } @@ -141,23 +99,29 @@ public: return fStartT; } -private: + bool tightBoundsIntersects(const SkTSpan* span) const; // implementation is for testing only - int debugID() const { - return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1); + void dump() const { + dump(NULL); } - void dumpID() const; +private: + SkTSpan* innerFind(double t); + bool linearIntersects(const TCurve& ) const; - int hullCheck(const SkTSpan* opp, bool* start, bool* oppStart); - int linearIntersects(const TCurve& ) const; - SkTSpan* oppT(double t) const; + // implementation is for testing only +#if DEBUG_T_SECT + int debugID(const SkTSect<TCurve>* ) const { return fDebugID; } +#else + int debugID(const SkTSect<TCurve>* ) const; +#endif + void dump(const SkTSect<TCurve>* ) const; + void dumpID(const SkTSect<TCurve>* ) const; +#if DEBUG_T_SECT void validate() const; - void validateBounded() const; - void validatePerpT(double oppT) const; - void validatePerpPt(double t, const SkDPoint& ) const; +#endif TCurve fPart; SkTCoincident<TCurve> fCoinStart; @@ -172,33 +136,23 @@ private: bool fCollapsed; bool fHasPerp; bool fIsLinear; - bool fIsLine; - bool fDeleted; - PATH_OPS_DEBUG_CODE(SkTSect<TCurve>* fDebugSect); - PATH_OPS_DEBUG_T_SECT_CODE(int fID); +#if DEBUG_T_SECT + int fDebugID; + bool fDebugDeleted; +#endif friend class SkTSect<TCurve>; }; template<typename TCurve> class SkTSect { public: - SkTSect(const TCurve& c PATH_OPS_DEBUG_T_SECT_PARAMS(int id)); + SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id)); static void BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersections* intersections); // for testing only - bool debugHasBounded(const SkTSpan<TCurve>* ) const; - - const SkTSect* debugOpp() const { - return PATH_OPS_DEBUG_RELEASE(fOppSect, NULL); - } - - const SkTSpan<TCurve>* debugSpan(int id) const; - const SkTSpan<TCurve>* debugT(double t) const; void dump() const; - void dumpBoth(SkTSect* ) const; - void dumpBounds(int id) const; - void dumpCoin() const; - void dumpCoinCurves() const; + void dumpBoth(const SkTSect& opp) const; + void dumpBoth(const SkTSect* opp) const; void dumpCurves() const; private: @@ -209,72 +163,36 @@ private: kOneS2Set = 8 }; - SkTSpan<TCurve>* addFollowing(SkTSpan<TCurve>* prior); - void addForPerp(SkTSpan<TCurve>* span, double t); SkTSpan<TCurve>* addOne(); - - SkTSpan<TCurve>* addSplitAt(SkTSpan<TCurve>* span, double t) { - SkTSpan<TCurve>* result = this->addOne(); - result->splitAt(span, t); - result->initBounds(fCurve); - span->initBounds(fCurve); - return result; - } - - bool binarySearchCoin(SkTSect* , double tStart, double tStep, double* t, double* oppT); + bool binarySearchCoin(const SkTSect& , double tStart, double tStep, double* t, double* oppT); SkTSpan<TCurve>* boundsMax() const; void coincidentCheck(SkTSect* sect2); - bool coincidentHasT(double t); - void computePerpendiculars(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last); - int countConsecutiveSpans(SkTSpan<TCurve>* first, SkTSpan<TCurve>** last) const; - - int debugID() const { - return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1); - } - - void deleteEmptySpans(); - void dumpCommon(const SkTSpan<TCurve>* ) const; - void dumpCommonCurves(const SkTSpan<TCurve>* ) const; static int EndsEqual(const SkTSect* sect1, const SkTSect* sect2, SkIntersections* ); - SkTSpan<TCurve>* extractCoincident(SkTSect* sect2, SkTSpan<TCurve>* first, - SkTSpan<TCurve>* last); - SkTSpan<TCurve>* findCoincidentRun(SkTSpan<TCurve>* first, SkTSpan<TCurve>** lastPtr, - const SkTSect* sect2); - int intersects(SkTSpan<TCurve>* span, const SkTSect* opp, - SkTSpan<TCurve>* oppSpan, int* oppResult) const; - int linesIntersect(const SkTSpan<TCurve>* span, const SkTSect* opp, - const SkTSpan<TCurve>* oppSpan, SkIntersections* ) const; - void markSpanGone(SkTSpan<TCurve>* span); - bool matchedDirection(double t, const SkTSect* sect2, double t2) const; - void matchedDirCheck(double t, const SkTSect* sect2, double t2, - bool* calcMatched, bool* oppMatched) const; - void mergeCoincidence(SkTSect* sect2); - SkTSpan<TCurve>* prev(SkTSpan<TCurve>* ) const; - void removeByPerpendicular(SkTSect* opp); + bool intersects(SkTSpan<TCurve>* span, const SkTSect* opp, + const SkTSpan<TCurve>* oppSpan) const; + void onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last); void recoverCollapsed(); - void removeCoincident(SkTSpan<TCurve>* span, bool isBetween); - void removeAllBut(const SkTSpan<TCurve>* keep, SkTSpan<TCurve>* span, SkTSect* opp); void removeSpan(SkTSpan<TCurve>* span); - void removeSpanRange(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last); + void removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span); void removeSpans(SkTSpan<TCurve>* span, SkTSect* opp); - SkTSpan<TCurve>* spanAtT(double t, SkTSpan<TCurve>** priorSpan); - SkTSpan<TCurve>* tail(); + void setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last); + const SkTSpan<TCurve>* tail() const; void trim(SkTSpan<TCurve>* span, SkTSect* opp); - void unlinkSpan(SkTSpan<TCurve>* span); - bool updateBounded(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last, SkTSpan<TCurve>* oppFirst); - void validate() const; - void validateBounded() const; +#if DEBUG_T_SECT + int debugID() const { return fDebugID; } + void validate() const; +#else + int debugID() const { return 0; } +#endif const TCurve& fCurve; SkChunkAlloc fHeap; SkTSpan<TCurve>* fHead; - SkTSpan<TCurve>* fCoincident; SkTSpan<TCurve>* fDeleted; int fActiveCount; - PATH_OPS_DEBUG_CODE(SkTSect* fOppSect); - PATH_OPS_DEBUG_T_SECT_CODE(int fID); - PATH_OPS_DEBUG_T_SECT_CODE(int fDebugCount); #if DEBUG_T_SECT + int fDebugID; + int fDebugCount; int fDebugAllocatedCount; #endif friend class SkTSpan<TCurve>; // only used by debug id @@ -290,8 +208,8 @@ void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t, SkIntersections i; int used = i.intersectRay(c2, perp); // only keep closest - if (used == 0 || used == 3) { - this->clear(); + if (used == 0) { + fPerpT = -1; return; } fPerpT = i[0][0]; @@ -305,10 +223,6 @@ void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t, fPerpPt = i.pt(1); } } -#if DEBUG_T_SECT - SkDebugf("%s cPt=(%1.9g,%1.9g) %s fPerpPt=(%1.9g,%1.9g)\n", __FUNCTION__, cPt.fX, cPt.fY, - cPt.approximatelyEqual(fPerpPt) ? "==" : "!=", fPerpPt.fX, fPerpPt.fY); -#endif fCoincident = cPt.approximatelyEqual(fPerpPt); #if DEBUG_T_SECT if (fCoincident) { @@ -318,55 +232,29 @@ void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t, } template<typename TCurve> -void SkTSpan<TCurve>::addBounded(SkTSpan* span) { - if (this->findOppSpan(span)) { - return; - } - fBounded.push_back() = span; +void SkTSpan<TCurve>::init(const TCurve& c) { + fPrev = fNext = NULL; + fIsLinear = false; + fStartT = 0; + fEndT = 1; + initBounds(c); } template<typename TCurve> -SkTSpan<TCurve>* SkTSect<TCurve>::addFollowing(SkTSpan<TCurve>* prior) { - SkTSpan<TCurve>* result = this->addOne(); - result->fStartT = prior ? prior->fEndT : 0; - SkTSpan<TCurve>* next = prior ? prior->fNext : fHead; - result->fEndT = next ? next->fStartT : 1; - result->fPrev = prior; - result->fNext = next; - if (prior) { - prior->fNext = result; - } else { - fHead = result; - } - if (next) { - next->fPrev = result; +void SkTSpan<TCurve>::initBounds(const TCurve& c) { + fPart = c.subDivide(fStartT, fEndT); + fBounds.setBounds(fPart); + fCoinStart.init(); + fCoinEnd.init(); + fBoundsMax = SkTMax(fBounds.width(), fBounds.height()); + fCollapsed = fPart.collapsed(); + fHasPerp = false; +#if DEBUG_T_SECT + fDebugDeleted = false; + if (fCollapsed) { + SkDebugf(""); // for convenient breakpoints } - result->resetBounds(fCurve); - return result; -} - -template<typename TCurve> -void SkTSect<TCurve>::addForPerp(SkTSpan<TCurve>* span, double t) { - if (!span->hasOppT(t)) { - SkTSpan<TCurve>* priorSpan; - SkTSpan<TCurve>* opp = this->spanAtT(t, &priorSpan); - if (!opp) { - opp = this->addFollowing(priorSpan); -#if DEBUG_PERP - SkDebugf("%s priorSpan=%d t=%1.9g opp=%d\n", __FUNCTION__, priorSpan->debugID(), t, - opp->debugID()); #endif - } -#if DEBUG_PERP - opp->dump(); SkDebugf("\n"); - SkDebugf("%s fBounded.push_back span=%d opp=%d\n", __FUNCTION__, priorSpan->debugID(), - opp->debugID()); -#endif - opp->fBounded.push_back(span); - span->fBounded.push_back(opp); - } - this->validate(); - span->validatePerpT(t); } template<typename TCurve> @@ -391,143 +279,55 @@ double SkTSpan<TCurve>::closestBoundedT(const SkDPoint& pt) const { return result; } -#ifdef SK_DEBUG template<typename TCurve> -bool SkTSpan<TCurve>::debugIsBefore(const SkTSpan* span) const { - const SkTSpan* work = this; - do { - if (span == work) { +bool SkTSpan<TCurve>::contains(const SkTSpan* span) const { + int count = fBounded.count(); + for (int index = 0; index < count; ++index) { + const SkTSpan* test = fBounded[index]; + if (span == test) { return true; } - } while ((work = work->fNext)); + } return false; } -#endif template<typename TCurve> -bool SkTSpan<TCurve>::contains(double t) const { - const SkTSpan* work = this; +SkTSpan<TCurve>* SkTSpan<TCurve>::innerFind(double t) { + SkTSpan* work = this; do { if (between(work->fStartT, t, work->fEndT)) { - return true; + return work; } } while ((work = work->fNext)); - return false; -} - -template<typename TCurve> -const SkTSect<TCurve>* SkTSpan<TCurve>::debugOpp() const { - return PATH_OPS_DEBUG_RELEASE(fDebugSect->debugOpp(), NULL); -} - -template<typename TCurve> -SkTSpan<TCurve>* SkTSpan<TCurve>::findOppSpan(const SkTSpan* opp) const { - int count = fBounded.count(); - for (int index = 0; index < count; ++index) { - SkTSpan* test = fBounded[index]; - if (opp == test) { - return test; - } - } return NULL; } -// returns 0 if no hull intersection -// 1 if hulls intersect -// 2 if hulls only share a common endpoint -// -1 if linear and further checking is required -template<typename TCurve> -int SkTSpan<TCurve>::hullCheck(const SkTSpan* opp, bool* start, bool* oppStart) { - if (fIsLinear) { - return -1; - } - bool ptsInCommon; - if (onlyEndPointsInCommon(opp, start, oppStart, &ptsInCommon)) { - SkASSERT(ptsInCommon); - return 2; - } - bool linear; - if (fPart.hullIntersects(opp->fPart, &linear)) { - if (!linear) { // check set true if linear - return 1; - } - fIsLinear = true; - fIsLine = fPart.controlsInside(); - return ptsInCommon ? 2 : -1; - } else { // hull is not linear; check set true if intersected at the end points - return ((int) ptsInCommon) << 1; // 0 or 2 - } - return 0; -} - // OPTIMIZE ? If at_most_end_pts_in_common detects that one quad is near linear, // use line intersection to guess a better split than 0.5 // OPTIMIZE Once at_most_end_pts_in_common detects linear, mark span so all future splits are linear template<typename TCurve> -int SkTSpan<TCurve>::hullsIntersect(SkTSpan* opp, bool* start, bool* oppStart) { - if (!fBounds.intersects(opp->fBounds)) { - return 0; +bool SkTSpan<TCurve>::intersects(const SkTSpan* span, bool* check) { + if (!fBounds.intersects(span->fBounds)) { + *check = false; // no need to check to see if the bounds have end points in common + return false; } - int hullSect = this->hullCheck(opp, start, oppStart); - if (hullSect >= 0) { - return hullSect; + if (!fIsLinear && fPart.hullIntersects(span->fPart, check)) { + if (!*check) { + return true; + } + fIsLinear = true; } - hullSect = opp->hullCheck(this, oppStart, start); - if (hullSect >= 0) { - return hullSect; + if (fIsLinear) { + *check = false; + return linearIntersects(span->fPart); } - return -1; + return *check; } template<typename TCurve> -void SkTSpan<TCurve>::init(const TCurve& c) { - fPrev = fNext = NULL; - fStartT = 0; - fEndT = 1; - resetBounds(c); -} - -template<typename TCurve> -void SkTSpan<TCurve>::initBounds(const TCurve& c) { - fPart = c.subDivide(fStartT, fEndT); - fBounds.setBounds(fPart); - fCoinStart.init(); - fCoinEnd.init(); - fBoundsMax = SkTMax(fBounds.width(), fBounds.height()); - fCollapsed = fPart.collapsed(); - fHasPerp = false; - fDeleted = false; -#if DEBUG_T_SECT - if (fCollapsed) { - SkDebugf(""); // for convenient breakpoints - } -#endif -} - -template<typename TCurve> -bool SkTSpan<TCurve>::linearsIntersect(SkTSpan* span) { - int result = this->linearIntersects(span->fPart); - if (result <= 1) { - return SkToBool(result); - } - SkASSERT(span->fIsLinear); - result = span->linearIntersects(this->fPart); -// SkASSERT(result <= 1); - return SkToBool(result); -} - -template<typename TCurve> -double SkTSpan<TCurve>::linearT(const SkDPoint& pt) const { - SkDVector len = fPart[TCurve::kPointLast] - fPart[0]; - return fabs(len.fX) > fabs(len.fY) - ? (pt.fX - fPart[0].fX) / len.fX - : (pt.fY - fPart[0].fY) / len.fY; -} - -template<typename TCurve> -int SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const { +bool SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const { // looks like q1 is near-linear - int start = 0, end = TCurve::kPointLast; // the outside points are usually the extremes + int start = 0, end = TCurve::kPointCount - 1; // the outside points are usually the extremes if (!fPart.controlsInside()) { double dist = 0; // if there's any question, compute distance to find best outsiders for (int outer = 0; outer < TCurve::kPointCount - 1; ++outer) { @@ -547,116 +347,20 @@ int SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const { double origY = fPart[start].fY; double adj = fPart[end].fX - origX; double opp = fPart[end].fY - origY; - double maxPart = SkTMax(fabs(adj), fabs(opp)); - double sign = 0; // initialization to shut up warning in release build + double sign; for (int n = 0; n < TCurve::kPointCount; ++n) { - double dx = q2[n].fY - origY; - double dy = q2[n].fX - origX; - double maxVal = SkTMax(maxPart, SkTMax(fabs(dx), fabs(dy))); double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp; - if (precisely_zero_when_compared_to(test, maxVal)) { - return 1; - } - if (approximately_zero_when_compared_to(test, maxVal)) { - return 3; + if (precisely_zero(test)) { + return true; } if (n == 0) { sign = test; continue; } if (test * sign < 0) { - return 1; - } - } - return 0; -} - -template<typename TCurve> -bool SkTSpan<TCurve>::onlyEndPointsInCommon(const SkTSpan* opp, bool* start, bool* oppStart, - bool* ptsInCommon) { - if (opp->fPart[0] == fPart[0]) { - *start = *oppStart = true; - } else if (opp->fPart[0] == fPart[TCurve::kPointLast]) { - *start = false; - *oppStart = true; - } else if (opp->fPart[TCurve::kPointLast] == fPart[0]) { - *start = true; - *oppStart = false; - } else if (opp->fPart[TCurve::kPointLast] == fPart[TCurve::kPointLast]) { - *start = *oppStart = false; - } else { - *ptsInCommon = false; - return false; - } - *ptsInCommon = true; - const SkDPoint* o1Pts[TCurve::kPointCount - 1], * o2Pts[TCurve::kPointCount - 1]; - int baseIndex = *start ? 0 : TCurve::kPointLast; - fPart.otherPts(baseIndex, o1Pts); - opp->fPart.otherPts(*oppStart ? 0 : TCurve::kPointLast, o2Pts); - const SkDPoint& base = fPart[baseIndex]; - for (int o1 = 0; o1 < (int) SK_ARRAY_COUNT(o1Pts); ++o1) { - SkDVector v1 = *o1Pts[o1] - base; - for (int o2 = 0; o2 < (int) SK_ARRAY_COUNT(o2Pts); ++o2) { - SkDVector v2 = *o2Pts[o2] - base; - if (v2.dot(v1) >= 0) { - return false; - } - } - } - return true; -} - -template<typename TCurve> -SkTSpan<TCurve>* SkTSpan<TCurve>::oppT(double t) const { - int count = fBounded.count(); - for (int index = 0; index < count; ++index) { - SkTSpan* test = fBounded[index]; - if (between(test->fStartT, t, test->fEndT)) { - return test; - } - } - return NULL; -} - -template<typename TCurve> -bool SkTSpan<TCurve>::removeAllBounded() { - bool deleteSpan = false; - int count = fBounded.count(); - for (int index = 0; index < count; ++index) { - SkTSpan* opp = fBounded[index]; - deleteSpan |= opp->removeBounded(this); - } - return deleteSpan; -} - -template<typename TCurve> -bool SkTSpan<TCurve>::removeBounded(const SkTSpan* opp) { - int count = fBounded.count(); - if (fHasPerp) { - bool foundStart = false; - bool foundEnd = false; - for (int index = 0; index < count; ++index) { - const SkTSpan* test = fBounded[index]; - if (opp == test) { - continue; - } - foundStart |= between(test->fStartT, fCoinStart.perpT(), test->fEndT); - foundEnd |= between(test->fStartT, fCoinEnd.perpT(), test->fEndT); - } - if (!foundStart || !foundEnd) { - fHasPerp = false; - fCoinStart.init(); - fCoinEnd.init(); - } - } - for (int index = 0; index < count; ++index) { - if (opp == fBounded[index]) { - fBounded.removeShuffle(index); - SkASSERT((count == 1) == (fBounded.count() == 0)); - return count == 1; + return true; } } - SkASSERT(0); return false; } @@ -676,8 +380,6 @@ bool SkTSpan<TCurve>::splitAt(SkTSpan* work, double t) { fPrev = work; fNext = work->fNext; fIsLinear = work->fIsLinear; - fIsLine = work->fIsLine; - work->fNext = this; if (fNext) { fNext->fPrev = this; @@ -691,74 +393,102 @@ bool SkTSpan<TCurve>::splitAt(SkTSpan* work, double t) { } template<typename TCurve> -void SkTSpan<TCurve>::validate() const { -#if DEBUG_T_SECT - SkASSERT(fNext == NULL || fNext != fPrev); - SkASSERT(fNext == NULL || this == fNext->fPrev); - SkASSERT(fPrev == NULL || this == fPrev->fNext); - SkASSERT(fBounds.width() || fBounds.height() || fCollapsed); - SkASSERT(fBoundsMax == SkTMax(fBounds.width(), fBounds.height())); - SkASSERT(0 <= fStartT); - SkASSERT(fEndT <= 1); - SkASSERT(fStartT <= fEndT); - SkASSERT(fBounded.count() > 0); - this->validateBounded(); - if (fHasPerp) { - if (fCoinStart.isCoincident()) { - validatePerpT(fCoinStart.perpT()); - validatePerpPt(fCoinStart.perpT(), fCoinStart.perpPt()); +bool SkTSpan<TCurve>::tightBoundsIntersects(const SkTSpan* span) const { + // skew all to an axis + SkDVector v2_0 = fPart[TCurve::kPointLast] - fPart[0]; + bool skewToXAxis = fabs(v2_0.fX) > fabs(v2_0.fY); + double ratio = skewToXAxis ? v2_0.fY / v2_0.fX : v2_0.fX / v2_0.fY; + TCurve r1 = fPart; + if (skewToXAxis) { + r1[1].fY -= (fPart[1].fX - r1[0].fX) * ratio; + if (TCurve::IsCubic()) { + r1[2].fY -= (fPart[2].fX - r1[0].fX) * ratio; + r1[3].fY = r1[0].fY; + } else { + r1[2].fY = r1[0].fY; } - if (fCoinEnd.isCoincident()) { - validatePerpT(fCoinEnd.perpT()); - validatePerpPt(fCoinEnd.perpT(), fCoinEnd.perpPt()); + } else { + r1[1].fX -= (fPart[1].fY - r1[0].fY) * ratio; + if (TCurve::IsCubic()) { + r1[2].fX -= (fPart[2].fY - r1[0].fY) * ratio; + r1[3].fX = r1[0].fX; + } else { + r1[2].fX = r1[0].fX; + } + } + // compute the tight skewed bounds + SkDRect bounds; + bounds.setBounds(r1); + // see if opposite ends are within range of tight skewed bounds + TCurve r2 = span->fPart; + for (int i = 0; i < TCurve::kPointCount; i += 2) { + if (skewToXAxis) { + r2[i].fY -= (r2[i].fX - r1[0].fX) * ratio; + if (between(bounds.fTop, r2[i].fY, bounds.fBottom)) { + return true; + } + } else { + r2[i].fX -= (r2[i].fY - r1[0].fY) * ratio; + if (between(bounds.fLeft, r2[i].fX, bounds.fRight)) { + return true; + } } } -#endif -} - -template<typename TCurve> -void SkTSpan<TCurve>::validateBounded() const { -#if DEBUG_VALIDATE - for (int index = 0; index < fBounded.count(); ++index) { - const SkTSpan* overlap = fBounded[index]; - SkASSERT(!overlap->fDeleted); - SkASSERT(((this->debugID() ^ overlap->debugID()) & 1) == 1); - SkASSERT(overlap->findOppSpan(this)); + // see if opposite ends are on either side of tight skewed bounds + if ((skewToXAxis ? (r2[0].fY - r1[0].fY) * (r2[TCurve::kPointLast].fY - r1[0].fY) + : (r2[0].fX - r1[0].fX) * (r2[TCurve::kPointLast].fX - r1[0].fX)) < 0) { + return true; + } + // compute opposite tight skewed bounds + if (skewToXAxis) { + r2[1].fY -= (r2[1].fX - r1[0].fX) * ratio; + if (TCurve::IsCubic()) { + r2[2].fY -= (r2[2].fX - r1[0].fX) * ratio; + } + } else { + r2[1].fX -= (r2[1].fY - r1[0].fY) * ratio; + if (TCurve::IsCubic()) { + r2[2].fX -= (r2[2].fY - r1[0].fY) * ratio; + } + } + SkDRect sBounds; + sBounds.setBounds(r2); + // see if tight bounds overlap + if (skewToXAxis) { + return bounds.fTop <= sBounds.fBottom && sBounds.fTop <= bounds.fBottom; + } else { + return bounds.fLeft <= sBounds.fRight && sBounds.fLeft <= bounds.fRight; } -#endif } +#if DEBUG_T_SECT template<typename TCurve> -void SkTSpan<TCurve>::validatePerpT(double oppT) const { -#if DEBUG_VALIDATE +void SkTSpan<TCurve>::validate() const { + SkASSERT(fNext == NULL || fNext != fPrev); + SkASSERT(fNext == NULL || this == fNext->fPrev); + SkASSERT(fBounds.width() || fBounds.height()); + SkASSERT(fBoundsMax == SkTMax(fBounds.width(), fBounds.height())); + SkASSERT(0 <= fStartT); + SkASSERT(fEndT <= 1); + SkASSERT(fStartT < fEndT); + SkASSERT(fBounded.count() > 0); for (int index = 0; index < fBounded.count(); ++index) { const SkTSpan* overlap = fBounded[index]; - if (between(overlap->fStartT, oppT, overlap->fEndT)) { - return; - } + SkASSERT(((fDebugID ^ overlap->fDebugID) & 1) == 1); + SkASSERT(overlap->contains(this)); } - SkASSERT(0); -#endif } - -template<typename TCurve> -void SkTSpan<TCurve>::validatePerpPt(double t, const SkDPoint& pt) const { -#if DEBUG_T_SECT - PATH_OPS_DEBUG_CODE(SkASSERT(fDebugSect->fOppSect->fCurve.ptAtT(t) == pt)); #endif -} - template<typename TCurve> -SkTSect<TCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_T_SECT_PARAMS(int id)) +SkTSect<TCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id)) : fCurve(c) , fHeap(sizeof(SkTSpan<TCurve>) * 4) - , fCoincident(NULL) , fDeleted(NULL) , fActiveCount(0) - PATH_OPS_DEBUG_T_SECT_PARAMS(fID(id)) - PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugCount(0)) - PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugAllocatedCount(0)) + PATH_OPS_DEBUG_PARAMS(fDebugID(id)) + PATH_OPS_DEBUG_PARAMS(fDebugCount(0)) + PATH_OPS_DEBUG_PARAMS(fDebugAllocatedCount(0)) { fHead = addOne(); fHead->init(c); @@ -778,22 +508,22 @@ SkTSpan<TCurve>* SkTSect<TCurve>::addOne() { #endif } ++fActiveCount; - PATH_OPS_DEBUG_T_SECT_CODE(result->fID = fDebugCount++ * 2 + fID); - PATH_OPS_DEBUG_CODE(result->fDebugSect = this); +#if DEBUG_T_SECT + result->fDebugID = fDebugCount++ * 2 + fDebugID; +#endif return result; } template<typename TCurve> -bool SkTSect<TCurve>::binarySearchCoin(SkTSect* sect2, double tStart, double tStep, +bool SkTSect<TCurve>::binarySearchCoin(const SkTSect& sect2, double tStart, double tStep, double* resultT, double* oppT) { SkTSpan<TCurve> work; double result = work.fStartT = work.fEndT = tStart; - PATH_OPS_DEBUG_CODE(work.fDebugSect = this); SkDPoint last = fCurve.ptAtT(tStart); SkDPoint oppPt; bool flip = false; SkDEBUGCODE(bool down = tStep < 0); - const TCurve& opp = sect2->fCurve; + const TCurve& opp = sect2.fCurve; do { tStep *= 0.5; work.fStartT += tStep; @@ -811,11 +541,8 @@ bool SkTSect<TCurve>::binarySearchCoin(SkTSect* sect2, double tStart, double tSt last = work.fPart[0]; work.fCoinStart.setPerp(fCurve, work.fStartT, last, opp); if (work.fCoinStart.isCoincident()) { -#if DEBUG_T_SECT - work.validatePerpPt(work.fCoinStart.perpT(), work.fCoinStart.perpPt()); -#endif double oppTTest = work.fCoinStart.perpT(); - if (sect2->fHead->contains(oppTTest)) { + if (sect2.fHead->contains(oppTTest)) { *oppT = oppTTest; oppPt = work.fCoinStart.perpPt(); SkASSERT(down ? result > work.fStartT : result < work.fStartT); @@ -847,472 +574,194 @@ template<typename TCurve> SkTSpan<TCurve>* SkTSect<TCurve>::boundsMax() const { SkTSpan<TCurve>* test = fHead; SkTSpan<TCurve>* largest = fHead; - bool lCollapsed = largest->fCollapsed; + bool largestCoin = largest->fCoinStart.isCoincident() && largest->fCoinEnd.isCoincident(); while ((test = test->fNext)) { - bool tCollapsed = test->fCollapsed; - if ((lCollapsed && !tCollapsed) || (lCollapsed == tCollapsed && - largest->fBoundsMax < test->fBoundsMax)) { + bool testCoin = test->fCoinStart.isCoincident() || test->fCoinEnd.isCoincident(); + if ((largestCoin && !testCoin) || (largestCoin == testCoin + && (largest->fBoundsMax < test->fBoundsMax + || (largest->fCollapsed && !test->fCollapsed)))) { largest = test; + largestCoin = testCoin; } } - return largest; + return largestCoin ? NULL : largest; } template<typename TCurve> void SkTSect<TCurve>::coincidentCheck(SkTSect* sect2) { SkTSpan<TCurve>* first = fHead; - SkTSpan<TCurve>* last, * next; + SkTSpan<TCurve>* next; do { - int consecutive = this->countConsecutiveSpans(first, &last); - next = last->fNext; + int consecutive = 1; + SkTSpan<TCurve>* last = first; + do { + next = last->fNext; + if (!next) { + break; + } + if (next->fStartT > last->fEndT) { + break; + } + ++consecutive; + last = next; + } while (true); if (consecutive < COINCIDENT_SPAN_COUNT) { continue; } - this->validate(); - sect2->validate(); - this->computePerpendiculars(sect2, first, last); - this->validate(); - sect2->validate(); + setPerp(sect2->fCurve, first, last); // check to see if a range of points are on the curve - SkTSpan<TCurve>* coinStart = first; - do { - coinStart = this->extractCoincident(sect2, coinStart, last); - } while (coinStart && !last->fDeleted); - } while ((first = next)); -} - -template<typename TCurve> -bool SkTSect<TCurve>::coincidentHasT(double t) { - SkTSpan<TCurve>* test = fCoincident; - while (test) { - if (between(test->fStartT, t, test->fEndT)) { - return true; + onCurveCheck(sect2, first, last); + SkTSpan<TCurve>* removalCandidate = NULL; + if (!first->fCoinStart.isCoincident()) { + SkTSpan<TCurve>* firstCoin = first->fNext; + removalCandidate = first; + first = firstCoin; + } + if (!first->fCoinStart.isCoincident()) { + continue; } - test = test->fNext; - } - return false; -} - -template<typename TCurve> -void SkTSect<TCurve>::computePerpendiculars(SkTSect* sect2, SkTSpan<TCurve>* first, - SkTSpan<TCurve>* last) { - const TCurve& opp = sect2->fCurve; - SkTSpan<TCurve>* work = first; - SkTSpan<TCurve>* prior = NULL; - do { - if (!work->fHasPerp && !work->fCollapsed) { - if (prior) { - work->fCoinStart = prior->fCoinEnd; - } else { - work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp); - } - if (work->fCoinStart.isCoincident()) { - double perpT = work->fCoinStart.perpT(); - if (sect2->coincidentHasT(perpT)) { - work->fCoinStart.clear(); - } else { - sect2->addForPerp(work, perpT); - } - } - work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp); - if (work->fCoinEnd.isCoincident()) { - double perpT = work->fCoinEnd.perpT(); - if (sect2->coincidentHasT(perpT)) { - work->fCoinEnd.clear(); - } else { - sect2->addForPerp(work, perpT); - } + if (removalCandidate) { + removeSpans(removalCandidate, sect2); + } + if (!last->fCoinStart.isCoincident()) { + continue; + } + if (!last->fCoinEnd.isCoincident()) { + if (--consecutive < COINCIDENT_SPAN_COUNT) { + continue; } - work->fHasPerp = true; + last = last->fPrev; + SkASSERT(last->fCoinStart.isCoincident()); + SkASSERT(last->fCoinEnd.isCoincident()); } - if (work == last) { - break; + SkASSERT(between(0, first->fCoinStart.perpT(), 1) || first->fCoinStart.perpT() == -1); + if (first->fCoinStart.perpT() < 0) { + first->fCoinStart.setPerp(fCurve, first->fStartT, first->fPart[0], sect2->fCurve); } - prior = work; - work = work->fNext; - SkASSERT(work); - } while (true); -} - -template<typename TCurve> -int SkTSect<TCurve>::countConsecutiveSpans(SkTSpan<TCurve>* first, - SkTSpan<TCurve>** lastPtr) const { - int consecutive = 1; - SkTSpan<TCurve>* last = first; - do { - SkTSpan<TCurve>* next = last->fNext; - if (!next) { - break; + SkASSERT(between(0, last->fCoinEnd.perpT(), 1) || last->fCoinEnd.perpT() == -1); + if (last->fCoinEnd.perpT() < 0) { + last->fCoinEnd.setPerp(fCurve, last->fEndT, last->fPart[TCurve::kPointLast], + sect2->fCurve); } - if (next->fStartT > last->fEndT) { - break; + SkTSpan<TCurve>* removeMe = first->fNext; + while (removeMe != last) { + SkTSpan<TCurve>* removeNext = removeMe->fNext; + removeSpans(removeMe, sect2); + removeMe = removeNext; } - ++consecutive; - last = next; - } while (true); - *lastPtr = last; - return consecutive; + } while ((first = next)); } template<typename TCurve> -bool SkTSect<TCurve>::debugHasBounded(const SkTSpan<TCurve>* span) const { - const SkTSpan<TCurve>* test = fHead; - if (!test) { +bool SkTSect<TCurve>::intersects(SkTSpan<TCurve>* span, const SkTSect* opp, + const SkTSpan<TCurve>* oppSpan) const { + bool check; // we ignore whether the end points are in common or not + if (!span->intersects(oppSpan, &check)) { return false; } - do { - if (test->findOppSpan(span)) { - return true; - } - } while ((test = test->next())); - return false; -} - -template<typename TCurve> -void SkTSect<TCurve>::deleteEmptySpans() { - SkTSpan<TCurve>* test; - SkTSpan<TCurve>* next = fHead; - while ((test = next)) { - next = test->fNext; - if (test->fBounded.count() == 0) { - this->removeSpan(test); - } - } -} - -template<typename TCurve> -SkTSpan<TCurve>* SkTSect<TCurve>::extractCoincident(SkTSect* sect2, SkTSpan<TCurve>* first, - SkTSpan<TCurve>* last) { - first = findCoincidentRun(first, &last, sect2); - if (!first) { - return NULL; + if (fActiveCount < COINCIDENT_SPAN_COUNT || opp->fActiveCount < COINCIDENT_SPAN_COUNT) { + return true; } - // march outwards to find limit of coincidence from here to previous and next spans - double startT = first->fStartT; - double oppStartT; - double oppEndT SK_INIT_TO_AVOID_WARNING; - SkTSpan<TCurve>* prev = first->fPrev; - SkASSERT(first->fCoinStart.isCoincident()); - SkTSpan<TCurve>* oppFirst = first->findOppT(first->fCoinStart.perpT()); - SkASSERT(last->fCoinEnd.isCoincident()); - bool oppMatched = first->fCoinStart.perpT() < first->fCoinEnd.perpT(); - double coinStart; - SkDEBUGCODE(double coinEnd); - if (prev && prev->fEndT == startT - && this->binarySearchCoin(sect2, startT, prev->fStartT - startT, &coinStart, - &oppStartT) - && prev->fStartT < coinStart && coinStart < startT) { - oppFirst = prev->findOppT(oppStartT); // find opp start before splitting prev - SkASSERT(oppFirst); - first = this->addSplitAt(prev, coinStart); - first->markCoincident(); - prev->fCoinEnd.markCoincident(); - if (oppFirst->fStartT < oppStartT && oppStartT < oppFirst->fEndT) { - SkTSpan<TCurve>* oppHalf = sect2->addSplitAt(oppFirst, oppStartT); - if (oppMatched) { - oppFirst->fCoinEnd.markCoincident(); - oppHalf->markCoincident(); - oppFirst = oppHalf; - } else { - oppFirst->markCoincident(); - oppHalf->fCoinStart.markCoincident(); - } - } - } else { - SkDEBUGCODE(coinStart = first->fStartT); - SkDEBUGCODE(oppStartT = oppMatched ? oppFirst->fStartT : oppFirst->fEndT); - } - SkTSpan<TCurve>* oppLast; - SkASSERT(last->fCoinEnd.isCoincident()); - oppLast = last->findOppT(last->fCoinEnd.perpT()); - SkDEBUGCODE(coinEnd = last->fEndT); - SkDEBUGCODE(oppEndT = oppMatched ? oppLast->fEndT : oppLast->fStartT); - if (!oppMatched) { - SkTSwap(oppFirst, oppLast); - SkTSwap(oppStartT, oppEndT); - } - SkASSERT(oppStartT < oppEndT); - SkASSERT(coinStart == first->fStartT); - SkASSERT(coinEnd == last->fEndT); - SkASSERT(oppStartT == oppFirst->fStartT); - SkASSERT(oppEndT == oppLast->fEndT); - // reduce coincident runs to single entries - this->validate(); - sect2->validate(); - bool deleteThisSpan = this->updateBounded(first, last, oppFirst); - bool deleteSect2Span = sect2->updateBounded(oppFirst, oppLast, first); - this->removeSpanRange(first, last); - sect2->removeSpanRange(oppFirst, oppLast); - first->fEndT = last->fEndT; - first->resetBounds(this->fCurve); - first->fCoinStart.setPerp(fCurve, first->fStartT, first->fPart[0], sect2->fCurve); - first->fCoinEnd.setPerp(fCurve, first->fEndT, first->fPart[TCurve::kPointLast], sect2->fCurve); - oppStartT = first->fCoinStart.perpT(); - oppEndT = first->fCoinEnd.perpT(); - if (between(0, oppStartT, 1) && between(0, oppEndT, 1)) { - if (!oppMatched) { - SkTSwap(oppStartT, oppEndT); - } - oppFirst->fStartT = oppStartT; - oppFirst->fEndT = oppEndT; - oppFirst->resetBounds(sect2->fCurve); - } - this->validateBounded(); - sect2->validateBounded(); - last = first->fNext; - this->removeCoincident(first, false); - sect2->removeCoincident(oppFirst, true); - if (deleteThisSpan) { - this->deleteEmptySpans(); - } - if (deleteSect2Span) { - sect2->deleteEmptySpans(); - } - this->validate(); - sect2->validate(); - return last && !last->fDeleted ? last : NULL; + return span->tightBoundsIntersects(oppSpan); } template<typename TCurve> -SkTSpan<TCurve>* SkTSect<TCurve>::findCoincidentRun(SkTSpan<TCurve>* first, - SkTSpan<TCurve>** lastPtr, const SkTSect* sect2) { +void SkTSect<TCurve>::onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) { SkTSpan<TCurve>* work = first; - SkTSpan<TCurve>* lastCandidate = NULL; first = NULL; - // find the first fully coincident span do { if (work->fCoinStart.isCoincident()) { - work->validatePerpT(work->fCoinStart.perpT()); - work->validatePerpPt(work->fCoinStart.perpT(), work->fCoinStart.perpPt()); - SkASSERT(work->hasOppT(work->fCoinStart.perpT())); - if (!work->fCoinEnd.isCoincident()) { - break; - } - lastCandidate = work; if (!first) { first = work; } - } else { - lastCandidate = NULL; - SkASSERT(!first); + } else if (first) { + break; } - if (work == *lastPtr) { - return first; + if (work == last) { + break; } work = work->fNext; SkASSERT(work); } while (true); - if (lastCandidate) { - *lastPtr = lastCandidate; + if (!first) { + return; } - return first; -} - -template<typename TCurve> -int SkTSect<TCurve>::intersects(SkTSpan<TCurve>* span, const SkTSect* opp, - SkTSpan<TCurve>* oppSpan, int* oppResult) const { - bool spanStart, oppStart; - int hullResult = span->hullsIntersect(oppSpan, &spanStart, &oppStart); - if (hullResult >= 0) { - if (hullResult == 2) { // hulls have one point in common - if (span->fBounded.count() <= 1) { - SkASSERT(span->fBounded.count() == 0 || span->fBounded[0] == oppSpan); - if (spanStart) { - span->fEndT = span->fStartT; - } else { - span->fStartT = span->fEndT; - } - } else { - hullResult = 1; - } - if (oppSpan->fBounded.count() <= 1) { - SkASSERT(span->fBounded.count() == 0 || oppSpan->fBounded[0] == span); - if (oppStart) { - oppSpan->fEndT = oppSpan->fStartT; + // march outwards to find limit of coincidence from here to previous and next spans + double startT = first->fStartT; + double oppT; + SkTSpan<TCurve>* prev = first->fPrev; + if (prev) { + double coinStart; + if (binarySearchCoin(*sect2, startT, prev->fStartT - startT, &coinStart, &oppT)) { + if (coinStart < startT) { + SkASSERT(prev->fStartT < coinStart && coinStart < prev->fEndT); + SkTSpan<TCurve>* oppStart = sect2->fHead->find(oppT); + if (oppStart->fStartT < oppT && oppT < oppStart->fEndT) { + // split prev at coinStart if needed + SkTSpan<TCurve>* half2 = addOne(); + half2->splitAt(prev, coinStart); + half2->initBounds(fCurve); + prev->initBounds(fCurve); + prev->fCoinEnd.markCoincident(); + half2->fCoinStart.markCoincident(); + half2->fCoinEnd.markCoincident(); + // find span containing opposite t, and split that too + SkTSpan<TCurve>* oppHalf = sect2->addOne(); + oppHalf->splitAt(oppStart, oppT); + oppHalf->initBounds(sect2->fCurve); + oppStart->initBounds(sect2->fCurve); } else { - oppSpan->fStartT = oppSpan->fEndT; + SkASSERT(oppStart->fStartT == oppT || oppT == oppStart->fEndT); + first->fStartT = coinStart; + prev->fEndT = coinStart; + first->initBounds(fCurve); + prev->initBounds(fCurve); + first->fCoinStart.markCoincident(); + first->fCoinEnd.markCoincident(); } - *oppResult = 2; - } else { - *oppResult = 1; } - } else { - *oppResult = 1; - } - return hullResult; - } - if (span->fIsLine && oppSpan->fIsLine) { - SkIntersections i; - int sects = this->linesIntersect(span, opp, oppSpan, &i); - if (!sects) { - return -1; } - span->fStartT = span->fEndT = i[0][0]; - oppSpan->fStartT = oppSpan->fEndT = i[1][0]; - return *oppResult = 2; - } - if (span->fIsLinear || oppSpan->fIsLinear) { - return *oppResult = (int) span->linearsIntersect(oppSpan); } - return *oppResult = 1; -} - -// while the intersection points are sufficiently far apart: -// construct the tangent lines from the intersections -// find the point where the tangent line intersects the opposite curve -template<typename TCurve> -int SkTSect<TCurve>::linesIntersect(const SkTSpan<TCurve>* span, const SkTSect* opp, - const SkTSpan<TCurve>* oppSpan, SkIntersections* i) const { - SkIntersections thisRayI, oppRayI; - SkDLine thisLine = {{ span->fPart[0], span->fPart[TCurve::kPointLast] }}; - SkDLine oppLine = {{ oppSpan->fPart[0], oppSpan->fPart[TCurve::kPointLast] }}; - int loopCount = 0; - double bestDistSq = DBL_MAX; - do { - if (!thisRayI.intersectRay(opp->fCurve, thisLine)) { - return 0; - } - if (!oppRayI.intersectRay(this->fCurve, oppLine)) { - return 0; - } - // pick the closest pair of points - double closest = DBL_MAX; - int closeIndex SK_INIT_TO_AVOID_WARNING; - int oppCloseIndex SK_INIT_TO_AVOID_WARNING; - for (int index = 0; index < oppRayI.used(); ++index) { - if (!roughly_between(span->fStartT, oppRayI[0][index], span->fEndT)) { - continue; - } - for (int oIndex = 0; oIndex < thisRayI.used(); ++oIndex) { - if (!roughly_between(oppSpan->fStartT, thisRayI[0][oIndex], oppSpan->fEndT)) { - continue; - } - double distSq = thisRayI.pt(index).distanceSquared(oppRayI.pt(oIndex)); - if (closest > distSq) { - closest = distSq; - closeIndex = index; - oppCloseIndex = oIndex; + if (!work->fCoinEnd.isCoincident()) { + if (work->fEndT == 1) { + SkDebugf("!"); + } +// SkASSERT(work->fEndT < 1); + startT = work->fStartT; + double coinEnd; + if (binarySearchCoin(*sect2, startT, work->fEndT - startT, &coinEnd, &oppT)) { + if (coinEnd > startT) { + SkTSpan<TCurve>* oppStart = sect2->fHead->find(oppT); + if (oppStart->fStartT < oppT && oppT < oppStart->fEndT) { + SkASSERT(coinEnd < work->fEndT); + // split prev at coinEnd if needed + SkTSpan<TCurve>* half2 = addOne(); + half2->splitAt(work, coinEnd); + half2->initBounds(fCurve); + work->initBounds(fCurve); + work->fCoinStart.markCoincident(); + work->fCoinEnd.markCoincident(); + half2->fCoinStart.markCoincident(); + SkTSpan<TCurve>* oppHalf = sect2->addOne(); + oppHalf->splitAt(oppStart, oppT); + oppHalf->initBounds(sect2->fCurve); + oppStart->initBounds(sect2->fCurve); + } else { + SkASSERT(oppStart->fStartT == oppT || oppT == oppStart->fEndT); + SkTSpan<TCurve>* next = work->fNext; + bool hasNext = next && work->fEndT == next->fStartT; + work->fEndT = coinEnd; + work->initBounds(fCurve); + work->fCoinStart.markCoincident(); + work->fCoinEnd.markCoincident(); + if (hasNext) { + next->fStartT = coinEnd; + next->initBounds(fCurve); + } } } } - if (closest == DBL_MAX) { - return 0; - } - const SkDPoint& oppIPt = thisRayI.pt(oppCloseIndex); - const SkDPoint& iPt = oppRayI.pt(closeIndex); - if (between(span->fStartT, oppRayI[0][closeIndex], span->fEndT) - && between(oppSpan->fStartT, thisRayI[0][oppCloseIndex], oppSpan->fEndT) - && oppIPt.approximatelyEqual(iPt)) { - i->merge(oppRayI, closeIndex, thisRayI, oppCloseIndex); - return i->used(); - } - double distSq = oppIPt.distanceSquared(iPt); - if (bestDistSq < distSq || ++loopCount > 5) { - break; - } - bestDistSq = distSq; - thisLine[0] = fCurve.ptAtT(oppRayI[0][closeIndex]); - thisLine[1] = thisLine[0] + fCurve.dxdyAtT(oppRayI[0][closeIndex]); - oppLine[0] = opp->fCurve.ptAtT(thisRayI[0][oppCloseIndex]); - oppLine[1] = oppLine[0] + opp->fCurve.dxdyAtT(thisRayI[0][oppCloseIndex]); - } while (true); - return false; -} - - -template<typename TCurve> -void SkTSect<TCurve>::markSpanGone(SkTSpan<TCurve>* span) { - --fActiveCount; - span->fNext = fDeleted; - fDeleted = span; - SkASSERT(!span->fDeleted); - span->fDeleted = true; -} - -template<typename TCurve> -bool SkTSect<TCurve>::matchedDirection(double t, const SkTSect* sect2, double t2) const { - SkDVector dxdy = this->fCurve.dxdyAtT(t); - SkDVector dxdy2 = sect2->fCurve.dxdyAtT(t2); - return dxdy.dot(dxdy2) >= 0; -} - -template<typename TCurve> -void SkTSect<TCurve>::matchedDirCheck(double t, const SkTSect* sect2, double t2, - bool* calcMatched, bool* oppMatched) const { - if (*calcMatched) { - SkASSERT(*oppMatched == this->matchedDirection(t, sect2, t2)); - } else { - *oppMatched = this->matchedDirection(t, sect2, t2); - *calcMatched = true; - } -} - -template<typename TCurve> -void SkTSect<TCurve>::mergeCoincidence(SkTSect* sect2) { - double smallLimit = 0; - do { - // find the smallest unprocessed span - SkTSpan<TCurve>* smaller = NULL; - SkTSpan<TCurve>* test = fCoincident; - do { - if (test->fStartT < smallLimit) { - continue; - } - if (smaller && smaller->fEndT < test->fStartT) { - continue; - } - smaller = test; - } while ((test = test->fNext)); - if (!smaller) { - return; - } - smallLimit = smaller->fEndT; - // find next larger span - SkTSpan<TCurve>* prior = NULL; - SkTSpan<TCurve>* larger = NULL; - SkTSpan<TCurve>* largerPrior = NULL; - test = fCoincident; - do { - if (test->fStartT < smaller->fEndT) { - continue; - } - SkASSERT(test->fStartT != smaller->fEndT); - if (larger && larger->fStartT < test->fStartT) { - continue; - } - largerPrior = prior; - larger = test; - } while ((prior = test), (test = test->fNext)); - if (!larger) { - continue; - } - // check middle t value to see if it is coincident as well - double midT = (smaller->fEndT + larger->fStartT) / 2; - SkDPoint midPt = fCurve.ptAtT(midT); - SkTCoincident<TCurve> coin; - coin.setPerp(fCurve, midT, midPt, sect2->fCurve); - if (coin.isCoincident()) { - smaller->fEndT = larger->fEndT; - smaller->fCoinEnd = larger->fCoinEnd; - if (largerPrior) { - largerPrior->fNext = larger->fNext; - } else { - fCoincident = larger->fNext; - } - } - } while (true); -} - -template<typename TCurve> -SkTSpan<TCurve>* SkTSect<TCurve>::prev(SkTSpan<TCurve>* span) const { - SkTSpan<TCurve>* result = NULL; - SkTSpan<TCurve>* test = fHead; - while (span != test) { - result = test; - test = test->fNext; - SkASSERT(test); } - return result; } template<typename TCurve> @@ -1333,84 +782,41 @@ void SkTSect<TCurve>::recoverCollapsed() { } template<typename TCurve> -void SkTSect<TCurve>::removeAllBut(const SkTSpan<TCurve>* keep, SkTSpan<TCurve>* span, - SkTSect* opp) { - int count = span->fBounded.count(); - for (int index = 0; index < count; ++index) { - SkTSpan<TCurve>* bounded = span->fBounded[0]; - if (bounded == keep) { - continue; - } - if (bounded->fDeleted) { // may have been deleted when opp did 'remove all but' - continue; +void SkTSect<TCurve>::removeSpan(SkTSpan<TCurve>* span) { + SkTSpan<TCurve>* prev = span->fPrev; + SkTSpan<TCurve>* next = span->fNext; + if (prev) { + prev->fNext = next; + if (next) { + next->fPrev = prev; } - SkAssertResult(SkDEBUGCODE(!) span->removeBounded(bounded)); - if (bounded->removeBounded(span)) { - opp->removeSpan(bounded); + } else { + fHead = next; + if (next) { + next->fPrev = NULL; } } - SkASSERT(!span->fDeleted); - SkASSERT(span->findOppSpan(keep)); - SkASSERT(keep->findOppSpan(span)); -} - -template<typename TCurve> -void SkTSect<TCurve>::removeByPerpendicular(SkTSect<TCurve>* opp) { - SkTSpan<TCurve>* test = fHead; - SkTSpan<TCurve>* next; - do { - next = test->fNext; - if (test->fCoinStart.perpT() < 0 || test->fCoinEnd.perpT() < 0) { - continue; - } - SkDVector startV = test->fCoinStart.perpPt() - test->fPart[0]; - SkDVector endV = test->fCoinEnd.perpPt() - test->fPart[TCurve::kPointLast]; + --fActiveCount; + span->fNext = fDeleted; + fDeleted = span; #if DEBUG_T_SECT - SkDebugf("%s startV=(%1.9g,%1.9g) endV=(%1.9g,%1.9g) dot=%1.9g\n", __FUNCTION__, - startV.fX, startV.fY, endV.fX, endV.fY, startV.dot(endV)); + SkASSERT(!span->fDebugDeleted); + span->fDebugDeleted = true; #endif - if (startV.dot(endV) <= 0) { - continue; - } - this->removeSpans(test, opp); - } while ((test = next)); } template<typename TCurve> -void SkTSect<TCurve>::removeCoincident(SkTSpan<TCurve>* span, bool isBetween) { - this->unlinkSpan(span); - if (isBetween || between(0, span->fCoinStart.perpT(), 1)) { - --fActiveCount; - span->fNext = fCoincident; - fCoincident = span; - } else { - this->markSpanGone(span); - } -} - -template<typename TCurve> -void SkTSect<TCurve>::removeSpan(SkTSpan<TCurve>* span) { - this->unlinkSpan(span); - this->markSpanGone(span); -} - -template<typename TCurve> -void SkTSect<TCurve>::removeSpanRange(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) { - if (first == last) { - return; - } - SkTSpan<TCurve>* span = first; - SkASSERT(span); - SkTSpan<TCurve>* final = last->fNext; - SkTSpan<TCurve>* next = span->fNext; - while ((span = next) && span != final) { - next = span->fNext; - this->markSpanGone(span); - } - if (final) { - final->fPrev = first; +void SkTSect<TCurve>::removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span) { + int last = span->fBounded.count() - 1; + for (int index = 0; index <= last; ++index) { + if (span->fBounded[index] == test) { + span->fBounded.removeShuffle(index); + if (!last) { + removeSpan(span); + } + return; + } } - first->fNext = final; } template<typename TCurve> @@ -1418,33 +824,38 @@ void SkTSect<TCurve>::removeSpans(SkTSpan<TCurve>* span, SkTSect<TCurve>* opp) { int count = span->fBounded.count(); for (int index = 0; index < count; ++index) { SkTSpan<TCurve>* bounded = span->fBounded[0]; - if (span->removeBounded(bounded)) { // shuffles last into position 0 - this->removeSpan(span); - } - if (bounded->removeBounded(span)) { - opp->removeSpan(bounded); - } - SkASSERT(!span->fDeleted || !opp->debugHasBounded(span)); - + removeOne(bounded, span); // shuffles last into position 0 + opp->removeOne(span, bounded); } } template<typename TCurve> -SkTSpan<TCurve>* SkTSect<TCurve>::spanAtT(double t, SkTSpan<TCurve>** priorSpan) { - SkTSpan<TCurve>* test = fHead; - SkTSpan<TCurve>* prev = NULL; - while (test && test->fEndT < t) { - prev = test; - test = test->fNext; +void SkTSect<TCurve>::setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) { + SkTSpan<TCurve>* work = first; + if (!work->fHasPerp) { + work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp); } - *priorSpan = prev; - return test && test->fStartT <= t ? test : NULL; + do { + if (!work->fHasPerp) { + work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp); + work->fHasPerp = true; + } + if (work == last) { + break; + } + SkTSpan<TCurve>* last = work; + work = work->fNext; + SkASSERT(work); + if (!work->fHasPerp) { + work->fCoinStart = last->fCoinEnd; + } + } while (true); } template<typename TCurve> -SkTSpan<TCurve>* SkTSect<TCurve>::tail() { - SkTSpan<TCurve>* result = fHead; - SkTSpan<TCurve>* next = fHead; +const SkTSpan<TCurve>* SkTSect<TCurve>::tail() const { + const SkTSpan<TCurve>* result = fHead; + const SkTSpan<TCurve>* next = fHead; while ((next = next->fNext)) { if (next->fEndT > result->fEndT) { result = next; @@ -1458,75 +869,32 @@ SkTSpan<TCurve>* SkTSect<TCurve>::tail() { template<typename TCurve> void SkTSect<TCurve>::trim(SkTSpan<TCurve>* span, SkTSect* opp) { span->initBounds(fCurve); - for (int index = 0; index < span->fBounded.count(); ) { + int count = span->fBounded.count(); + for (int index = 0; index < count; ) { SkTSpan<TCurve>* test = span->fBounded[index]; - int oppSects, sects = this->intersects(span, opp, test, &oppSects); - if (sects >= 1) { - if (sects == 2) { - span->initBounds(fCurve); - this->removeAllBut(test, span, opp); - } - if (oppSects == 2) { - test->initBounds(opp->fCurve); - opp->removeAllBut(span, test, this); - } + bool sects = intersects(span, opp, test); + if (sects) { ++index; } else { - if (span->removeBounded(test)) { - this->removeSpan(span); - } - if (test->removeBounded(span)) { - opp->removeSpan(test); - } + removeOne(test, span); + opp->removeOne(span, test); + --count; } } } -template<typename TCurve> -void SkTSect<TCurve>::unlinkSpan(SkTSpan<TCurve>* span) { - SkTSpan<TCurve>* prev = span->fPrev; - SkTSpan<TCurve>* next = span->fNext; - if (prev) { - prev->fNext = next; - if (next) { - next->fPrev = prev; - } - } else { - fHead = next; - if (next) { - next->fPrev = NULL; - } - } -} - -template<typename TCurve> -bool SkTSect<TCurve>::updateBounded(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last, - SkTSpan<TCurve>* oppFirst) { - SkTSpan<TCurve>* test = first; - const SkTSpan<TCurve>* final = last->next(); - bool deleteSpan = false; - do { - deleteSpan |= test->removeAllBounded(); - } while ((test = test->fNext) != final); - first->fBounded.resize_back(1); - first->fBounded[0] = oppFirst; - // cannot call validate until remove span range is called - return deleteSpan; -} - - +#if DEBUG_T_SECT template<typename TCurve> void SkTSect<TCurve>::validate() const { -#if DEBUG_T_SECT int count = 0; if (fHead) { const SkTSpan<TCurve>* span = fHead; SkASSERT(!span->fPrev); - SkDEBUGCODE(double last = 0); + double last = 0; do { span->validate(); SkASSERT(span->fStartT >= last); - SkDEBUGCODE(last = span->fEndT); + last = span->fEndT; ++count; } while ((span = span->fNext) != NULL); } @@ -1538,81 +906,54 @@ void SkTSect<TCurve>::validate() const { ++deletedCount; deleted = deleted->fNext; } - const SkTSpan<TCurve>* coincident = fCoincident; - while (coincident) { - ++deletedCount; - coincident = coincident->fNext; - } SkASSERT(fActiveCount + deletedCount == fDebugAllocatedCount); -#endif } - -template<typename TCurve> -void SkTSect<TCurve>::validateBounded() const { -#if DEBUG_T_SECT - if (!fHead) { - return; - } - const SkTSpan<TCurve>* span = fHead; - do { - span->validateBounded(); - } while ((span = span->fNext) != NULL); #endif -} template<typename TCurve> int SkTSect<TCurve>::EndsEqual(const SkTSect* sect1, const SkTSect* sect2, SkIntersections* intersections) { int zeroOneSet = 0; - if (sect1->fCurve[0] == sect2->fCurve[0]) { - zeroOneSet |= kZeroS1Set | kZeroS2Set; - intersections->insert(0, 0, sect1->fCurve[0]); - } - if (sect1->fCurve[0] == sect2->fCurve[TCurve::kPointLast]) { - zeroOneSet |= kZeroS1Set | kOneS2Set; - intersections->insert(0, 1, sect1->fCurve[0]); - } - if (sect1->fCurve[TCurve::kPointLast] == sect2->fCurve[0]) { - zeroOneSet |= kOneS1Set | kZeroS2Set; - intersections->insert(1, 0, sect1->fCurve[TCurve::kPointLast]); - } - if (sect1->fCurve[TCurve::kPointLast] == sect2->fCurve[TCurve::kPointLast]) { - zeroOneSet |= kOneS1Set | kOneS2Set; - intersections->insert(1, 1, sect1->fCurve[TCurve::kPointLast]); - } // check for zero - if (!(zeroOneSet & (kZeroS1Set | kZeroS2Set)) - && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) { + if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) { zeroOneSet |= kZeroS1Set | kZeroS2Set; - intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]); - } - if (!(zeroOneSet & (kZeroS1Set | kOneS2Set)) - && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) { + if (sect1->fCurve[0] != sect2->fCurve[0]) { + intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]); + } else { + intersections->insert(0, 0, sect1->fCurve[0]); + } + } + if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) { zeroOneSet |= kZeroS1Set | kOneS2Set; - intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[TCurve::kPointLast]); - } + if (sect1->fCurve[0] != sect2->fCurve[TCurve::kPointLast]) { + intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[TCurve::kPointLast]); + } else { + intersections->insert(0, 1, sect1->fCurve[0]); + } + } // check for one - if (!(zeroOneSet & (kOneS1Set | kZeroS2Set)) - && sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) { + if (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) { zeroOneSet |= kOneS1Set | kZeroS2Set; - intersections->insertNear(1, 0, sect1->fCurve[TCurve::kPointLast], sect2->fCurve[0]); - } - if (!(zeroOneSet & (kOneS1Set | kOneS2Set)) - && sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[ - TCurve::kPointLast])) { + if (sect1->fCurve[TCurve::kPointLast] != sect2->fCurve[0]) { + intersections->insertNear(1, 0, sect1->fCurve[TCurve::kPointLast], sect2->fCurve[0]); + } else { + intersections->insert(1, 0, sect1->fCurve[TCurve::kPointLast]); + } + } + if (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) { zeroOneSet |= kOneS1Set | kOneS2Set; - intersections->insertNear(1, 1, sect1->fCurve[TCurve::kPointLast], - sect2->fCurve[TCurve::kPointLast]); + if (sect1->fCurve[TCurve::kPointLast] != sect2->fCurve[TCurve::kPointLast]) { + intersections->insertNear(1, 1, sect1->fCurve[TCurve::kPointLast], + sect2->fCurve[TCurve::kPointLast]); + } else { + intersections->insert(1, 1, sect1->fCurve[TCurve::kPointLast]); + } } return zeroOneSet; } template<typename TCurve> struct SkClosestRecord { - bool operator<(const SkClosestRecord& rh) const { - return fClosest < rh.fClosest; - } - void addIntersection(SkIntersections* intersections) const { double r1t = fC1Index ? fC1Span->endT() : fC1Span->startT(); double r2t = fC2Index ? fC2Span->endT() : fC2Span->startT(); @@ -1692,14 +1033,14 @@ struct SkClosestSect { fClosest.push_back().reset(); } - bool find(const SkTSpan<TCurve>* span1, const SkTSpan<TCurve>* span2) { + void find(const SkTSpan<TCurve>* span1, const SkTSpan<TCurve>* span2) { SkClosestRecord<TCurve>* record = &fClosest[fUsed]; record->findEnd(span1, span2, 0, 0); record->findEnd(span1, span2, 0, TCurve::kPointLast); record->findEnd(span1, span2, TCurve::kPointLast, 0); record->findEnd(span1, span2, TCurve::kPointLast, TCurve::kPointLast); if (record->fClosest == FLT_MAX) { - return false; + return; } for (int index = 0; index < fUsed; ++index) { SkClosestRecord<TCurve>* test = &fClosest[index]; @@ -1709,46 +1050,37 @@ struct SkClosestSect { } test->update(*record); record->reset(); - return false; + return; } } ++fUsed; fClosest.push_back().reset(); - return true; } void finish(SkIntersections* intersections) const { - SkSTArray<TCurve::kMaxIntersections * 2, const SkClosestRecord<TCurve>*, true> closestPtrs; - for (int index = 0; index < fUsed; ++index) { - closestPtrs.push_back(&fClosest[index]); - } - SkTQSort<const SkClosestRecord<TCurve> >(closestPtrs.begin(), closestPtrs.end() - 1); for (int index = 0; index < fUsed; ++index) { - const SkClosestRecord<TCurve>* test = closestPtrs[index]; - test->addIntersection(intersections); + const SkClosestRecord<TCurve>& test = fClosest[index]; + test.addIntersection(intersections); } } - // this is oversized so that an extra records can merge into final one - SkSTArray<TCurve::kMaxIntersections * 2, SkClosestRecord<TCurve>, true> fClosest; + // this is oversized by one so that an extra record can merge into final one + SkSTArray<TCurve::kMaxIntersections + 1, SkClosestRecord<TCurve>, true> fClosest; int fUsed; }; // returns true if the rect is too small to consider template<typename TCurve> void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersections* intersections) { - PATH_OPS_DEBUG_CODE(sect1->fOppSect = sect2); - PATH_OPS_DEBUG_CODE(sect2->fOppSect = sect1); intersections->reset(); - intersections->setMax(TCurve::kMaxIntersections * 2); // give extra for slop + intersections->setMax(TCurve::kMaxIntersections); SkTSpan<TCurve>* span1 = sect1->fHead; SkTSpan<TCurve>* span2 = sect2->fHead; - int oppSect, sect = sect1->intersects(span1, sect2, span2, &oppSect); -// SkASSERT(between(0, sect, 2)); - if (!sect) { + bool check; + if (!span1->intersects(span2, &check)) { return; } - if (sect == 2 && oppSect == 2) { + if (check) { (void) EndsEqual(sect1, sect2, intersections); return; } @@ -1764,12 +1096,12 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio bool split1 = !largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsMax || (!largest1->fCollapsed && largest2->fCollapsed))); // split it + SkTSect* splitSect = split1 ? sect1 : sect2; SkTSpan<TCurve>* half1 = split1 ? largest1 : largest2; SkASSERT(half1); if (half1->fCollapsed) { break; } - SkTSect* splitSect = split1 ? sect1 : sect2; // trim parts that don't intersect the opposite SkTSpan<TCurve>* half2 = splitSect->addOne(); SkTSect* unsplitSect = split1 ? sect2 : sect1; @@ -1778,52 +1110,50 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio } splitSect->trim(half1, unsplitSect); splitSect->trim(half2, unsplitSect); - sect1->validate(); - sect2->validate(); // if there are 9 or more continuous spans on both sects, suspect coincidence if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) { sect1->coincidentCheck(sect2); - sect1->validate(); - sect2->validate(); } - if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT - && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) { - sect1->computePerpendiculars(sect2, sect1->fHead, sect1->tail()); - sect2->computePerpendiculars(sect1, sect2->fHead, sect2->tail()); - sect1->removeByPerpendicular(sect2); - sect1->validate(); - sect2->validate(); - } -#if DEBUG_T_SECT_DUMP - sect1->dumpBoth(sect2); +#if DEBUG_T_SECT + sect1->validate(); + sect2->validate(); +#endif +#if DEBUG_T_SECT_DUMP > 1 + sect1->dumpBoth(*sect2); #endif if (!sect1->fHead || !sect2->fHead) { - break; + return; } } while (true); - SkTSpan<TCurve>* coincident = sect1->fCoincident; - if (coincident) { - // if there is more than one coincident span, check loosely to see if they should be joined - if (coincident->fNext) { - sect1->mergeCoincidence(sect2); - coincident = sect1->fCoincident; - } - SkASSERT(sect2->fCoincident); // courtesy check : coincidence only looks at sect 1 + if (sect1->fActiveCount >= 2 && sect2->fActiveCount >= 2) { + // check for coincidence + SkTSpan<TCurve>* first = sect1->fHead; do { - SkASSERT(coincident->fCoinStart.isCoincident()); - SkASSERT(coincident->fCoinEnd.isCoincident()); - int index = intersections->insertCoincident(coincident->fStartT, - coincident->fCoinStart.perpT(), coincident->fPart[0]); - if ((intersections->insertCoincident(coincident->fEndT, - coincident->fCoinEnd.perpT(), - coincident->fPart[TCurve::kPointLast]) < 0) && index >= 0) { + if (!first->fCoinStart.isCoincident()) { + continue; + } + int spanCount = 1; + SkTSpan<TCurve>* last = first; + while (last->fCoinEnd.isCoincident()) { + SkTSpan<TCurve>* next = last->fNext; + if (!next || !next->fCoinEnd.isCoincident()) { + break; + } + last = next; + ++spanCount; + } + if (spanCount < 2) { + first = last; + continue; + } + int index = intersections->insertCoincident(first->fStartT, first->fCoinStart.perpT(), + first->fPart[0]); + if (intersections->insertCoincident(last->fEndT, last->fCoinEnd.perpT(), + last->fPart[TCurve::kPointLast]) < 0) { intersections->clearCoincidence(index); } - } while ((coincident = coincident->fNext)); - } - if (!sect1->fHead || !sect2->fHead) { - return; + } while ((first = first->fNext)); } int zeroOneSet = EndsEqual(sect1, sect2, intersections); sect1->recoverCollapsed(); @@ -1833,41 +1163,33 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio const SkTSpan<TCurve>* head1 = result1; if (!(zeroOneSet & kZeroS1Set) && approximately_less_than_zero(head1->fStartT)) { const SkDPoint& start1 = sect1->fCurve[0]; - if (head1->isBounded()) { - double t = head1->closestBoundedT(start1); - if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) { - intersections->insert(0, t, start1); - } + double t = head1->closestBoundedT(start1); + if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) { + intersections->insert(0, t, start1); } } const SkTSpan<TCurve>* head2 = sect2->fHead; if (!(zeroOneSet & kZeroS2Set) && approximately_less_than_zero(head2->fStartT)) { const SkDPoint& start2 = sect2->fCurve[0]; - if (head2->isBounded()) { - double t = head2->closestBoundedT(start2); - if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) { - intersections->insert(t, 0, start2); - } + double t = head2->closestBoundedT(start2); + if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) { + intersections->insert(t, 0, start2); } } const SkTSpan<TCurve>* tail1 = sect1->tail(); if (!(zeroOneSet & kOneS1Set) && approximately_greater_than_one(tail1->fEndT)) { const SkDPoint& end1 = sect1->fCurve[TCurve::kPointLast]; - if (tail1->isBounded()) { - double t = tail1->closestBoundedT(end1); - if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) { - intersections->insert(1, t, end1); - } + double t = tail1->closestBoundedT(end1); + if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) { + intersections->insert(1, t, end1); } } const SkTSpan<TCurve>* tail2 = sect2->tail(); if (!(zeroOneSet & kOneS2Set) && approximately_greater_than_one(tail2->fEndT)) { const SkDPoint& end2 = sect2->fCurve[TCurve::kPointLast]; - if (tail2->isBounded()) { - double t = tail2->closestBoundedT(end2); - if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) { - intersections->insert(t, 1, end2); - } + double t = tail2->closestBoundedT(end2); + if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) { + intersections->insert(t, 1, end2); } } SkClosestSect<TCurve> closest; @@ -1879,39 +1201,11 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio break; } SkTSpan<TCurve>* result2 = sect2->fHead; - bool found = false; while (result2) { - found |= closest.find(result1, result2); + closest.find(result1, result2); result2 = result2->fNext; } + } while ((result1 = result1->fNext)); closest.finish(intersections); - // if there is more than one intersection and it isn't already coincident, check - int last = intersections->used() - 1; - for (int index = 0; index < last; ) { - if (intersections->isCoincident(index) && intersections->isCoincident(index + 1)) { - ++index; - continue; - } - double midT = ((*intersections)[0][index] + (*intersections)[0][index + 1]) / 2; - SkDPoint midPt = sect1->fCurve.ptAtT(midT); - // intersect perpendicular with opposite curve - SkTCoincident<TCurve> perp; - perp.setPerp(sect1->fCurve, midT, midPt, sect2->fCurve); - if (!perp.isCoincident()) { - ++index; - continue; - } - if (intersections->isCoincident(index)) { - intersections->removeOne(index); - --last; - } else if (intersections->isCoincident(index + 1)) { - intersections->removeOne(index + 1); - --last; - } else { - intersections->setCoincident(index++); - } - intersections->setCoincident(index); - } - SkASSERT(intersections->used() <= TCurve::kMaxIntersections); } |