/* * Copyright 2014 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkChunkAlloc.h" #include "SkPathOpsRect.h" #include "SkPathOpsQuad.h" #include "SkIntersections.h" #include "SkTArray.h" /* TCurve is either SkDQuadratic or SkDCubic */ template class SkTCoincident { public: bool isCoincident() const { return fCoincident; } void init() { fCoincident = false; SkDEBUGCODE(fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN); SkDEBUGCODE(fPerpT = SK_ScalarNaN); } void markCoincident() { if (!fCoincident) { fPerpT = -1; } fCoincident = true; } const SkDPoint& perpPt() const { return fPerpPt; } double perpT() const { return fPerpT; } void setPerp(const TCurve& c1, double t, const SkDPoint& cPt, const TCurve& ); private: SkDPoint fPerpPt; double fPerpT; // perpendicular intersection on opposite curve bool fCoincident; }; template class SkTSect; /* Curve is either TCurve or SkDCubic */ template class SkTSpan { public: void init(const TCurve& ); void initBounds(const TCurve& ); double closestBoundedT(const SkDPoint& pt) const; bool contains(double t) const { return !! const_cast(this)->innerFind(t); } bool contains(const SkTSpan* span) const; double endT() const { return fEndT; } SkTSpan* find(double t) { SkTSpan* result = innerFind(t); SkASSERT(result); return result; } bool intersects(const SkTSpan* span, bool* check); const SkTSpan* next() const { return fNext; } const TCurve& part() const { return fPart; } void reset() { fBounded.reset(); } bool split(SkTSpan* work) { return splitAt(work, (work->fStartT + work->fEndT) * 0.5); } bool splitAt(SkTSpan* work, double t); double startT() const { return fStartT; } bool tightBoundsIntersects(const SkTSpan* span) const; // implementation is for testing only void dump() const { dump(NULL); } private: SkTSpan* innerFind(double t); bool linearIntersects(const TCurve& ) const; // implementation is for testing only #if DEBUG_T_SECT int debugID(const SkTSect* ) const { return fDebugID; } #else int debugID(const SkTSect* ) const; #endif void dump(const SkTSect* ) const; void dumpID(const SkTSect* ) const; #if DEBUG_T_SECT void validate() const; #endif TCurve fPart; SkTCoincident fCoinStart; SkTCoincident fCoinEnd; SkSTArray<4, SkTSpan*, true> fBounded; SkTSpan* fPrev; SkTSpan* fNext; SkDRect fBounds; double fStartT; double fEndT; double fBoundsMax; bool fCollapsed; bool fHasPerp; bool fIsLinear; #if DEBUG_T_SECT int fDebugID; bool fDebugDeleted; #endif friend class SkTSect; }; template class SkTSect { public: SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id)); static void BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersections* intersections); // for testing only void dump() const; void dumpBoth(const SkTSect& opp) const; void dumpBoth(const SkTSect* opp) const; void dumpCurves() const; private: enum { kZeroS1Set = 1, kOneS1Set = 2, kZeroS2Set = 4, kOneS2Set = 8 }; SkTSpan* addOne(); bool binarySearchCoin(const SkTSect& , double tStart, double tStep, double* t, double* oppT); SkTSpan* boundsMax() const; void coincidentCheck(SkTSect* sect2); static int EndsEqual(const SkTSect* sect1, const SkTSect* sect2, SkIntersections* ); bool intersects(SkTSpan* span, const SkTSect* opp, const SkTSpan* oppSpan) const; void onCurveCheck(SkTSect* sect2, SkTSpan* first, SkTSpan* last); void recoverCollapsed(); void removeSpan(SkTSpan* span); void removeOne(const SkTSpan* test, SkTSpan* span); void removeSpans(SkTSpan* span, SkTSect* opp); void setPerp(const TCurve& opp, SkTSpan* first, SkTSpan* last); const SkTSpan* tail() const; void trim(SkTSpan* span, SkTSect* opp); #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* fHead; SkTSpan* fDeleted; int fActiveCount; #if DEBUG_T_SECT int fDebugID; int fDebugCount; int fDebugAllocatedCount; #endif friend class SkTSpan; // only used by debug id }; #define COINCIDENT_SPAN_COUNT 9 template void SkTCoincident::setPerp(const TCurve& c1, double t, const SkDPoint& cPt, const TCurve& c2) { SkDVector dxdy = c1.dxdyAtT(t); SkDLine perp = {{ cPt, {cPt.fX + dxdy.fY, cPt.fY - dxdy.fX} }}; SkIntersections i; int used = i.intersectRay(c2, perp); // only keep closest if (used == 0) { fPerpT = -1; return; } fPerpT = i[0][0]; fPerpPt = i.pt(0); SkASSERT(used <= 2); if (used == 2) { double distSq = (fPerpPt - cPt).lengthSquared(); double dist2Sq = (i.pt(1) - cPt).lengthSquared(); if (dist2Sq < distSq) { fPerpT = i[0][1]; fPerpPt = i.pt(1); } } fCoincident = cPt.approximatelyEqual(fPerpPt); #if DEBUG_T_SECT if (fCoincident) { SkDebugf(""); // allow setting breakpoint } #endif } template void SkTSpan::init(const TCurve& c) { fPrev = fNext = NULL; fIsLinear = false; fStartT = 0; fEndT = 1; initBounds(c); } template void SkTSpan::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 } #endif } template double SkTSpan::closestBoundedT(const SkDPoint& pt) const { int count = fBounded.count(); double result = -1; double closest = FLT_MAX; for (int index = 0; index < count; ++index) { const SkTSpan* test = fBounded[index]; double startDist = test->fPart[0].distanceSquared(pt); if (closest > startDist) { closest = startDist; result = test->fStartT; } double endDist = test->fPart[TCurve::kPointLast].distanceSquared(pt); if (closest > endDist) { closest = endDist; result = test->fEndT; } } SkASSERT(between(0, result, 1)); return result; } template bool SkTSpan::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; } } return false; } template SkTSpan* SkTSpan::innerFind(double t) { SkTSpan* work = this; do { if (between(work->fStartT, t, work->fEndT)) { return work; } } while ((work = work->fNext)); return NULL; } // 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 bool SkTSpan::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; } if (!fIsLinear && fPart.hullIntersects(span->fPart, check)) { if (!*check) { return true; } fIsLinear = true; } if (fIsLinear) { *check = false; return linearIntersects(span->fPart); } return *check; } template bool SkTSpan::linearIntersects(const TCurve& q2) const { // looks like q1 is near-linear 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) { for (int inner = outer + 1; inner < TCurve::kPointCount; ++inner) { double test = (fPart[outer] - fPart[inner]).lengthSquared(); if (dist > test) { continue; } dist = test; start = outer; end = inner; } } } // see if q2 is on one side of the line formed by the extreme points double origX = fPart[start].fX; double origY = fPart[start].fY; double adj = fPart[end].fX - origX; double opp = fPart[end].fY - origY; double sign; for (int n = 0; n < TCurve::kPointCount; ++n) { double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp; if (precisely_zero(test)) { return true; } if (n == 0) { sign = test; continue; } if (test * sign < 0) { return true; } } return false; } template bool SkTSpan::splitAt(SkTSpan* work, double t) { fStartT = t; fEndT = work->fEndT; if (fStartT == fEndT) { fCollapsed = true; return false; } work->fEndT = t; if (work->fStartT == work->fEndT) { work->fCollapsed = true; return false; } fPrev = work; fNext = work->fNext; fIsLinear = work->fIsLinear; work->fNext = this; if (fNext) { fNext->fPrev = this; } fBounded = work->fBounded; int count = fBounded.count(); for (int index = 0; index < count; ++index) { fBounded[index]->fBounded.push_back() = this; } return true; } template bool SkTSpan::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; } } 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; } } } // 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; } } #if DEBUG_T_SECT template void SkTSpan::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]; SkASSERT(((fDebugID ^ overlap->fDebugID) & 1) == 1); SkASSERT(overlap->contains(this)); } } #endif template SkTSect::SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id)) : fCurve(c) , fHeap(sizeof(SkTSpan) * 4) , fDeleted(NULL) , fActiveCount(0) PATH_OPS_DEBUG_PARAMS(fDebugID(id)) PATH_OPS_DEBUG_PARAMS(fDebugCount(0)) PATH_OPS_DEBUG_PARAMS(fDebugAllocatedCount(0)) { fHead = addOne(); fHead->init(c); } template SkTSpan* SkTSect::addOne() { SkTSpan* result; if (fDeleted) { result = fDeleted; result->reset(); fDeleted = result->fNext; } else { result = SkNEW_PLACEMENT(fHeap.allocThrow(sizeof(SkTSpan)), SkTSpan); #if DEBUG_T_SECT ++fDebugAllocatedCount; #endif } ++fActiveCount; #if DEBUG_T_SECT result->fDebugID = fDebugCount++ * 2 + fDebugID; #endif return result; } template bool SkTSect::binarySearchCoin(const SkTSect& sect2, double tStart, double tStep, double* resultT, double* oppT) { SkTSpan work; double result = work.fStartT = work.fEndT = tStart; SkDPoint last = fCurve.ptAtT(tStart); SkDPoint oppPt; bool flip = false; SkDEBUGCODE(bool down = tStep < 0); const TCurve& opp = sect2.fCurve; do { tStep *= 0.5; work.fStartT += tStep; if (flip) { tStep = -tStep; flip = false; } work.initBounds(fCurve); if (work.fCollapsed) { return false; } if (last.approximatelyEqual(work.fPart[0])) { break; } last = work.fPart[0]; work.fCoinStart.setPerp(fCurve, work.fStartT, last, opp); if (work.fCoinStart.isCoincident()) { double oppTTest = work.fCoinStart.perpT(); if (sect2.fHead->contains(oppTTest)) { *oppT = oppTTest; oppPt = work.fCoinStart.perpPt(); SkASSERT(down ? result > work.fStartT : result < work.fStartT); result = work.fStartT; continue; } } tStep = -tStep; flip = true; } while (true); if (last.approximatelyEqual(fCurve[0])) { result = 0; } else if (last.approximatelyEqual(fCurve[TCurve::kPointLast])) { result = 1; } if (oppPt.approximatelyEqual(opp[0])) { *oppT = 0; } else if (oppPt.approximatelyEqual(opp[TCurve::kPointLast])) { *oppT = 1; } *resultT = result; return true; } // OPTIMIZE ? keep a sorted list of sizes in the form of a doubly-linked list in quad span // so that each quad sect has a pointer to the largest, and can update it as spans // are split template SkTSpan* SkTSect::boundsMax() const { SkTSpan* test = fHead; SkTSpan* largest = fHead; bool largestCoin = largest->fCoinStart.isCoincident() && largest->fCoinEnd.isCoincident(); while ((test = test->fNext)) { 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 largestCoin ? NULL : largest; } template void SkTSect::coincidentCheck(SkTSect* sect2) { SkTSpan* first = fHead; SkTSpan* next; do { int consecutive = 1; SkTSpan* 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; } setPerp(sect2->fCurve, first, last); // check to see if a range of points are on the curve onCurveCheck(sect2, first, last); SkTSpan* removalCandidate = NULL; if (!first->fCoinStart.isCoincident()) { SkTSpan* firstCoin = first->fNext; removalCandidate = first; first = firstCoin; } if (!first->fCoinStart.isCoincident()) { continue; } if (removalCandidate) { removeSpans(removalCandidate, sect2); } if (!last->fCoinStart.isCoincident()) { continue; } if (!last->fCoinEnd.isCoincident()) { if (--consecutive < COINCIDENT_SPAN_COUNT) { continue; } last = last->fPrev; SkASSERT(last->fCoinStart.isCoincident()); SkASSERT(last->fCoinEnd.isCoincident()); } 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); } 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); } SkTSpan* removeMe = first->fNext; while (removeMe != last) { SkTSpan* removeNext = removeMe->fNext; removeSpans(removeMe, sect2); removeMe = removeNext; } } while ((first = next)); } template bool SkTSect::intersects(SkTSpan* span, const SkTSect* opp, const SkTSpan* oppSpan) const { bool check; // we ignore whether the end points are in common or not if (!span->intersects(oppSpan, &check)) { return false; } if (fActiveCount < COINCIDENT_SPAN_COUNT || opp->fActiveCount < COINCIDENT_SPAN_COUNT) { return true; } return span->tightBoundsIntersects(oppSpan); } template void SkTSect::onCurveCheck(SkTSect* sect2, SkTSpan* first, SkTSpan* last) { SkTSpan* work = first; first = NULL; do { if (work->fCoinStart.isCoincident()) { if (!first) { first = work; } } else if (first) { break; } if (work == last) { break; } work = work->fNext; SkASSERT(work); } while (true); if (!first) { return; } // march outwards to find limit of coincidence from here to previous and next spans double startT = first->fStartT; double oppT; SkTSpan* 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* oppStart = sect2->fHead->find(oppT); if (oppStart->fStartT < oppT && oppT < oppStart->fEndT) { // split prev at coinStart if needed SkTSpan* 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* oppHalf = sect2->addOne(); oppHalf->splitAt(oppStart, oppT); oppHalf->initBounds(sect2->fCurve); oppStart->initBounds(sect2->fCurve); } else { 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(); } } } } 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* oppStart = sect2->fHead->find(oppT); if (oppStart->fStartT < oppT && oppT < oppStart->fEndT) { SkASSERT(coinEnd < work->fEndT); // split prev at coinEnd if needed SkTSpan* half2 = addOne(); half2->splitAt(work, coinEnd); half2->initBounds(fCurve); work->initBounds(fCurve); work->fCoinStart.markCoincident(); work->fCoinEnd.markCoincident(); half2->fCoinStart.markCoincident(); SkTSpan* oppHalf = sect2->addOne(); oppHalf->splitAt(oppStart, oppT); oppHalf->initBounds(sect2->fCurve); oppStart->initBounds(sect2->fCurve); } else { SkASSERT(oppStart->fStartT == oppT || oppT == oppStart->fEndT); SkTSpan* 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); } } } } } } template void SkTSect::recoverCollapsed() { SkTSpan* deleted = fDeleted; while (deleted) { SkTSpan* delNext = deleted->fNext; if (deleted->fCollapsed) { SkTSpan** spanPtr = &fHead; while (*spanPtr && (*spanPtr)->fEndT <= deleted->fStartT) { spanPtr = &(*spanPtr)->fNext; } deleted->fNext = *spanPtr; *spanPtr = deleted; } deleted = delNext; } } template void SkTSect::removeSpan(SkTSpan* span) { SkTSpan* prev = span->fPrev; SkTSpan* next = span->fNext; if (prev) { prev->fNext = next; if (next) { next->fPrev = prev; } } else { fHead = next; if (next) { next->fPrev = NULL; } } --fActiveCount; span->fNext = fDeleted; fDeleted = span; #if DEBUG_T_SECT SkASSERT(!span->fDebugDeleted); span->fDebugDeleted = true; #endif } template void SkTSect::removeOne(const SkTSpan* test, SkTSpan* 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; } } } template void SkTSect::removeSpans(SkTSpan* span, SkTSect* opp) { int count = span->fBounded.count(); for (int index = 0; index < count; ++index) { SkTSpan* bounded = span->fBounded[0]; removeOne(bounded, span); // shuffles last into position 0 opp->removeOne(span, bounded); } } template void SkTSect::setPerp(const TCurve& opp, SkTSpan* first, SkTSpan* last) { SkTSpan* work = first; if (!work->fHasPerp) { work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp); } do { if (!work->fHasPerp) { work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp); work->fHasPerp = true; } if (work == last) { break; } SkTSpan* last = work; work = work->fNext; SkASSERT(work); if (!work->fHasPerp) { work->fCoinStart = last->fCoinEnd; } } while (true); } template const SkTSpan* SkTSect::tail() const { const SkTSpan* result = fHead; const SkTSpan* next = fHead; while ((next = next->fNext)) { if (next->fEndT > result->fEndT) { result = next; } } return result; } /* Each span has a range of opposite spans it intersects. After the span is split in two, adjust the range to its new size */ template void SkTSect::trim(SkTSpan* span, SkTSect* opp) { span->initBounds(fCurve); int count = span->fBounded.count(); for (int index = 0; index < count; ) { SkTSpan* test = span->fBounded[index]; bool sects = intersects(span, opp, test); if (sects) { ++index; } else { removeOne(test, span); opp->removeOne(span, test); --count; } } } #if DEBUG_T_SECT template void SkTSect::validate() const { int count = 0; if (fHead) { const SkTSpan* span = fHead; SkASSERT(!span->fPrev); double last = 0; do { span->validate(); SkASSERT(span->fStartT >= last); last = span->fEndT; ++count; } while ((span = span->fNext) != NULL); } SkASSERT(count == fActiveCount); SkASSERT(fActiveCount <= fDebugAllocatedCount); int deletedCount = 0; const SkTSpan* deleted = fDeleted; while (deleted) { ++deletedCount; deleted = deleted->fNext; } SkASSERT(fActiveCount + deletedCount == fDebugAllocatedCount); } #endif template int SkTSect::EndsEqual(const SkTSect* sect1, const SkTSect* sect2, SkIntersections* intersections) { int zeroOneSet = 0; // check for zero if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) { zeroOneSet |= kZeroS1Set | kZeroS2Set; 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; 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 (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) { zeroOneSet |= kOneS1Set | kZeroS2Set; 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; 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 struct SkClosestRecord { void addIntersection(SkIntersections* intersections) const { double r1t = fC1Index ? fC1Span->endT() : fC1Span->startT(); double r2t = fC2Index ? fC2Span->endT() : fC2Span->startT(); intersections->insert(r1t, r2t, fC1Span->part()[fC1Index]); } void findEnd(const SkTSpan* span1, const SkTSpan* span2, int c1Index, int c2Index) { const TCurve& c1 = span1->part(); const TCurve& c2 = span2->part(); if (!c1[c1Index].approximatelyEqual(c2[c2Index])) { return; } double dist = c1[c1Index].distanceSquared(c2[c2Index]); if (fClosest < dist) { return; } fC1Span = span1; fC2Span = span2; fC1StartT = span1->startT(); fC1EndT = span1->endT(); fC2StartT = span2->startT(); fC2EndT = span2->endT(); fC1Index = c1Index; fC2Index = c2Index; fClosest = dist; } bool matesWith(const SkClosestRecord& mate) const { SkASSERT(fC1Span == mate.fC1Span || fC1Span->endT() <= mate.fC1Span->startT() || mate.fC1Span->endT() <= fC1Span->startT()); SkASSERT(fC2Span == mate.fC2Span || fC2Span->endT() <= mate.fC2Span->startT() || mate.fC2Span->endT() <= fC2Span->startT()); return fC1Span == mate.fC1Span || fC1Span->endT() == mate.fC1Span->startT() || fC1Span->startT() == mate.fC1Span->endT() || fC2Span == mate.fC2Span || fC2Span->endT() == mate.fC2Span->startT() || fC2Span->startT() == mate.fC2Span->endT(); } void merge(const SkClosestRecord& mate) { fC1Span = mate.fC1Span; fC2Span = mate.fC2Span; fClosest = mate.fClosest; fC1Index = mate.fC1Index; fC2Index = mate.fC2Index; } void reset() { fClosest = FLT_MAX; SkDEBUGCODE(fC1Span = fC2Span = NULL); SkDEBUGCODE(fC1Index = fC2Index = -1); } void update(const SkClosestRecord& mate) { fC1StartT = SkTMin(fC1StartT, mate.fC1StartT); fC1EndT = SkTMax(fC1EndT, mate.fC1EndT); fC2StartT = SkTMin(fC2StartT, mate.fC2StartT); fC2EndT = SkTMax(fC2EndT, mate.fC2EndT); } const SkTSpan* fC1Span; const SkTSpan* fC2Span; double fC1StartT; double fC1EndT; double fC2StartT; double fC2EndT; double fClosest; int fC1Index; int fC2Index; }; template struct SkClosestSect { SkClosestSect() : fUsed(0) { fClosest.push_back().reset(); } void find(const SkTSpan* span1, const SkTSpan* span2) { SkClosestRecord* 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; } for (int index = 0; index < fUsed; ++index) { SkClosestRecord* test = &fClosest[index]; if (test->matesWith(*record)) { if (test->fClosest > record->fClosest) { test->merge(*record); } test->update(*record); record->reset(); return; } } ++fUsed; fClosest.push_back().reset(); } void finish(SkIntersections* intersections) const { for (int index = 0; index < fUsed; ++index) { const SkClosestRecord& test = fClosest[index]; test.addIntersection(intersections); } } // this is oversized by one so that an extra record can merge into final one SkSTArray, true> fClosest; int fUsed; }; // returns true if the rect is too small to consider template void SkTSect::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersections* intersections) { intersections->reset(); intersections->setMax(TCurve::kMaxIntersections); SkTSpan* span1 = sect1->fHead; SkTSpan* span2 = sect2->fHead; bool check; if (!span1->intersects(span2, &check)) { return; } if (check) { (void) EndsEqual(sect1, sect2, intersections); return; } span1->fBounded.push_back() = span2; span2->fBounded.push_back() = span1; do { // find the largest bounds SkTSpan* largest1 = sect1->boundsMax(); if (!largest1) { break; } SkTSpan* largest2 = sect2->boundsMax(); bool split1 = !largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsMax || (!largest1->fCollapsed && largest2->fCollapsed))); // split it SkTSect* splitSect = split1 ? sect1 : sect2; SkTSpan* half1 = split1 ? largest1 : largest2; SkASSERT(half1); if (half1->fCollapsed) { break; } // trim parts that don't intersect the opposite SkTSpan* half2 = splitSect->addOne(); SkTSect* unsplitSect = split1 ? sect2 : sect1; if (!half2->split(half1)) { break; } splitSect->trim(half1, unsplitSect); splitSect->trim(half2, unsplitSect); // 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); } #if DEBUG_T_SECT sect1->validate(); sect2->validate(); #endif #if DEBUG_T_SECT_DUMP > 1 sect1->dumpBoth(*sect2); #endif if (!sect1->fHead || !sect2->fHead) { return; } } while (true); if (sect1->fActiveCount >= 2 && sect2->fActiveCount >= 2) { // check for coincidence SkTSpan* first = sect1->fHead; do { if (!first->fCoinStart.isCoincident()) { continue; } int spanCount = 1; SkTSpan* last = first; while (last->fCoinEnd.isCoincident()) { SkTSpan* 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 ((first = first->fNext)); } int zeroOneSet = EndsEqual(sect1, sect2, intersections); sect1->recoverCollapsed(); sect2->recoverCollapsed(); SkTSpan* result1 = sect1->fHead; // check heads and tails for zero and ones and insert them if we haven't already done so const SkTSpan* head1 = result1; if (!(zeroOneSet & kZeroS1Set) && approximately_less_than_zero(head1->fStartT)) { const SkDPoint& start1 = sect1->fCurve[0]; double t = head1->closestBoundedT(start1); if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) { intersections->insert(0, t, start1); } } const SkTSpan* head2 = sect2->fHead; if (!(zeroOneSet & kZeroS2Set) && approximately_less_than_zero(head2->fStartT)) { const SkDPoint& start2 = sect2->fCurve[0]; double t = head2->closestBoundedT(start2); if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) { intersections->insert(t, 0, start2); } } const SkTSpan* tail1 = sect1->tail(); if (!(zeroOneSet & kOneS1Set) && approximately_greater_than_one(tail1->fEndT)) { const SkDPoint& end1 = sect1->fCurve[TCurve::kPointLast]; double t = tail1->closestBoundedT(end1); if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) { intersections->insert(1, t, end1); } } const SkTSpan* tail2 = sect2->tail(); if (!(zeroOneSet & kOneS2Set) && approximately_greater_than_one(tail2->fEndT)) { const SkDPoint& end2 = sect2->fCurve[TCurve::kPointLast]; double t = tail2->closestBoundedT(end2); if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) { intersections->insert(t, 1, end2); } } SkClosestSect closest; do { while (result1 && result1->fCoinStart.isCoincident() && result1->fCoinEnd.isCoincident()) { result1 = result1->fNext; } if (!result1) { break; } SkTSpan* result2 = sect2->fHead; while (result2) { closest.find(result1, result2); result2 = result2->fNext; } } while ((result1 = result1->fNext)); closest.finish(intersections); }