LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/pathops - SkOpCoincidence.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 940 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 44 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2015 Google Inc.
       3             :  *
       4             :  * Use of this source code is governed by a BSD-style license that can be
       5             :  * found in the LICENSE file.
       6             :  */
       7             : #include "SkOpCoincidence.h"
       8             : #include "SkOpSegment.h"
       9             : #include "SkPathOpsTSect.h"
      10             : 
      11             : // returns true if coincident span's start and end are the same
      12           0 : bool SkCoincidentSpans::collapsed(const SkOpPtT* test) const {
      13           0 :     return (fCoinPtTStart == test && fCoinPtTEnd->contains(test))
      14           0 :         || (fCoinPtTEnd == test && fCoinPtTStart->contains(test))
      15           0 :         || (fOppPtTStart == test && fOppPtTEnd->contains(test))
      16           0 :         || (fOppPtTEnd == test && fOppPtTStart->contains(test));
      17             : }
      18             : 
      19             : // out of line since this function is referenced by address
      20           0 : const SkOpPtT* SkCoincidentSpans::coinPtTEnd() const {
      21           0 :     return fCoinPtTEnd;
      22             : }
      23             : 
      24             : // out of line since this function is referenced by address
      25           0 : const SkOpPtT* SkCoincidentSpans::coinPtTStart() const {
      26           0 :     return fCoinPtTStart;
      27             : }
      28             : 
      29             : // sets the span's end to the ptT referenced by the previous-next
      30           0 : void SkCoincidentSpans::correctOneEnd(
      31             :         const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
      32             :         void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) ) {
      33           0 :     const SkOpPtT* origPtT = (this->*getEnd)();
      34           0 :     const SkOpSpanBase* origSpan = origPtT->span();
      35           0 :     const SkOpSpan* prev = origSpan->prev();
      36           0 :     const SkOpPtT* testPtT = prev ? prev->next()->ptT()
      37           0 :             : origSpan->upCast()->next()->prev()->ptT();
      38           0 :     if (origPtT != testPtT) {
      39           0 :         (this->*setEnd)(testPtT);
      40             :     }
      41           0 : }
      42             : 
      43             : /* Please keep this in sync with debugCorrectEnds */
      44             : // FIXME: member pointers have fallen out of favor and can be replaced with
      45             : // an alternative approach.
      46             : // makes all span ends agree with the segment's spans that define them
      47           0 : void SkCoincidentSpans::correctEnds() {
      48           0 :     this->correctOneEnd(&SkCoincidentSpans::coinPtTStart, &SkCoincidentSpans::setCoinPtTStart);
      49           0 :     this->correctOneEnd(&SkCoincidentSpans::coinPtTEnd, &SkCoincidentSpans::setCoinPtTEnd);
      50           0 :     this->correctOneEnd(&SkCoincidentSpans::oppPtTStart, &SkCoincidentSpans::setOppPtTStart);
      51           0 :     this->correctOneEnd(&SkCoincidentSpans::oppPtTEnd, &SkCoincidentSpans::setOppPtTEnd);
      52           0 : }
      53             : 
      54             : /* Please keep this in sync with debugExpand */
      55             : // expand the range by checking adjacent spans for coincidence
      56           0 : bool SkCoincidentSpans::expand() {
      57           0 :     bool expanded = false;
      58           0 :     const SkOpSegment* segment = coinPtTStart()->segment();
      59           0 :     const SkOpSegment* oppSegment = oppPtTStart()->segment();
      60             :     do {
      61           0 :         const SkOpSpan* start = coinPtTStart()->span()->upCast();
      62           0 :         const SkOpSpan* prev = start->prev();
      63             :         const SkOpPtT* oppPtT;
      64           0 :         if (!prev || !(oppPtT = prev->contains(oppSegment))) {
      65           0 :             break;
      66             :         }
      67           0 :         double midT = (prev->t() + start->t()) / 2;
      68           0 :         if (!segment->isClose(midT, oppSegment)) {
      69           0 :             break;
      70             :         }
      71           0 :         setStarts(prev->ptT(), oppPtT);
      72           0 :         expanded = true;
      73             :     } while (true);
      74             :     do {
      75           0 :         const SkOpSpanBase* end = coinPtTEnd()->span();
      76           0 :         SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
      77           0 :         if (next && next->deleted()) {
      78           0 :             break;
      79             :         }
      80             :         const SkOpPtT* oppPtT;
      81           0 :         if (!next || !(oppPtT = next->contains(oppSegment))) {
      82           0 :             break;
      83             :         }
      84           0 :         double midT = (end->t() + next->t()) / 2;
      85           0 :         if (!segment->isClose(midT, oppSegment)) {
      86           0 :             break;
      87             :         }
      88           0 :         setEnds(next->ptT(), oppPtT);
      89           0 :         expanded = true;
      90             :     } while (true);
      91           0 :     return expanded;
      92             : }
      93             : 
      94             : // increase the range of this span
      95           0 : bool SkCoincidentSpans::extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
      96             :         const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
      97           0 :     bool result = false;
      98           0 :     if (fCoinPtTStart->fT > coinPtTStart->fT || (this->flipped()
      99           0 :             ? fOppPtTStart->fT < oppPtTStart->fT : fOppPtTStart->fT > oppPtTStart->fT)) {
     100           0 :         this->setStarts(coinPtTStart, oppPtTStart);
     101           0 :         result = true;
     102             :     }
     103           0 :     if (fCoinPtTEnd->fT < coinPtTEnd->fT || (this->flipped()
     104           0 :             ? fOppPtTEnd->fT > oppPtTEnd->fT : fOppPtTEnd->fT < oppPtTEnd->fT)) {
     105           0 :         this->setEnds(coinPtTEnd, oppPtTEnd);
     106           0 :         result = true;
     107             :     }
     108           0 :     return result;
     109             : }
     110             : 
     111             : // set the range of this span
     112           0 : void SkCoincidentSpans::set(SkCoincidentSpans* next, const SkOpPtT* coinPtTStart,
     113             :         const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
     114           0 :     SkASSERT(SkOpCoincidence::Ordered(coinPtTStart, oppPtTStart));
     115           0 :     fNext = next;
     116           0 :     this->setStarts(coinPtTStart, oppPtTStart);
     117           0 :     this->setEnds(coinPtTEnd, oppPtTEnd);
     118           0 : }
     119             : 
     120             : // returns true if both points are inside this
     121           0 : bool SkCoincidentSpans::contains(const SkOpPtT* s, const SkOpPtT* e) const {
     122           0 :     if (s->fT > e->fT) {
     123           0 :         SkTSwap(s, e);
     124             :     }
     125           0 :     if (s->segment() == fCoinPtTStart->segment()) {
     126           0 :         return fCoinPtTStart->fT <= s->fT && e->fT <= fCoinPtTEnd->fT;
     127             :     } else {
     128           0 :         SkASSERT(s->segment() == fOppPtTStart->segment());
     129           0 :         double oppTs = fOppPtTStart->fT;
     130           0 :         double oppTe = fOppPtTEnd->fT;
     131           0 :         if (oppTs > oppTe) {
     132           0 :             SkTSwap(oppTs, oppTe);
     133             :         }
     134           0 :         return oppTs <= s->fT && e->fT <= oppTe;
     135             :     }
     136             : }
     137             : 
     138             : // out of line since this function is referenced by address
     139           0 : const SkOpPtT* SkCoincidentSpans::oppPtTStart() const {
     140           0 :     return fOppPtTStart;
     141             : }
     142             : 
     143             : // out of line since this function is referenced by address
     144           0 : const SkOpPtT* SkCoincidentSpans::oppPtTEnd() const {
     145           0 :     return fOppPtTEnd;
     146             : }
     147             : 
     148             : // A coincident span is unordered if the pairs of points in the main and opposite curves'
     149             : // t values do not ascend or descend. For instance, if a tightly arced quadratic is
     150             : // coincident with another curve, it may intersect it out of order.
     151           0 : bool SkCoincidentSpans::ordered(bool* result) const {
     152           0 :     const SkOpSpanBase* start = this->coinPtTStart()->span();
     153           0 :     const SkOpSpanBase* end = this->coinPtTEnd()->span();
     154           0 :     const SkOpSpanBase* next = start->upCast()->next();
     155           0 :     if (next == end) {
     156           0 :         *result = true;
     157           0 :         return true;
     158             :     }
     159           0 :     bool flipped = this->flipped();
     160           0 :     const SkOpSegment* oppSeg = this->oppPtTStart()->segment();
     161           0 :     double oppLastT = fOppPtTStart->fT;
     162             :     do {
     163           0 :         const SkOpPtT* opp = next->contains(oppSeg);
     164           0 :         if (!opp) {
     165             : //            SkOPOBJASSERT(start, 0);  // may assert if coincident span isn't fully processed
     166           0 :             return false;
     167             :         }
     168           0 :         if ((oppLastT > opp->fT) != flipped) {
     169           0 :             *result = false;
     170           0 :             return true;
     171             :         }
     172           0 :         oppLastT = opp->fT;
     173           0 :         if (next == end) {
     174           0 :             break;
     175             :         }
     176           0 :         if (!next->upCastable()) {
     177           0 :             *result = false;
     178           0 :             return true;
     179             :         }
     180           0 :         next = next->upCast()->next();
     181             :     } while (true);
     182           0 :     *result = true;
     183           0 :     return true;
     184             : }
     185             : 
     186             : // if there is an existing pair that overlaps the addition, extend it
     187           0 : bool SkOpCoincidence::extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
     188             :         const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
     189           0 :     SkCoincidentSpans* test = fHead;
     190           0 :     if (!test) {
     191           0 :         return false;
     192             :     }
     193           0 :     const SkOpSegment* coinSeg = coinPtTStart->segment();
     194           0 :     const SkOpSegment* oppSeg = oppPtTStart->segment();
     195           0 :     if (!Ordered(coinPtTStart, oppPtTStart)) {
     196           0 :         SkTSwap(coinSeg, oppSeg);
     197           0 :         SkTSwap(coinPtTStart, oppPtTStart);
     198           0 :         SkTSwap(coinPtTEnd, oppPtTEnd);
     199           0 :         if (coinPtTStart->fT > coinPtTEnd->fT) {
     200           0 :             SkTSwap(coinPtTStart, coinPtTEnd);
     201           0 :             SkTSwap(oppPtTStart, oppPtTEnd);
     202             :         }
     203             :     }
     204           0 :     double oppMinT = SkTMin(oppPtTStart->fT, oppPtTEnd->fT);
     205           0 :     SkDEBUGCODE(double oppMaxT = SkTMax(oppPtTStart->fT, oppPtTEnd->fT));
     206           0 :     do {
     207           0 :         if (coinSeg != test->coinPtTStart()->segment()) {
     208           0 :             continue;
     209             :         }
     210           0 :         if (oppSeg != test->oppPtTStart()->segment()) {
     211           0 :             continue;
     212             :         }
     213           0 :         double oTestMinT = SkTMin(test->oppPtTStart()->fT, test->oppPtTEnd()->fT);
     214           0 :         double oTestMaxT = SkTMax(test->oppPtTStart()->fT, test->oppPtTEnd()->fT);
     215             :         // if debug check triggers, caller failed to check if extended already exists
     216           0 :         SkASSERT(test->coinPtTStart()->fT > coinPtTStart->fT
     217             :                 || coinPtTEnd->fT > test->coinPtTEnd()->fT
     218             :                 || oTestMinT > oppMinT || oppMaxT > oTestMaxT);
     219           0 :         if ((test->coinPtTStart()->fT <= coinPtTEnd->fT
     220           0 :                 && coinPtTStart->fT <= test->coinPtTEnd()->fT)
     221           0 :                 || (oTestMinT <= oTestMaxT && oppMinT <= oTestMaxT)) {
     222           0 :             test->extend(coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
     223           0 :             return true;
     224             :         }
     225             :     } while ((test = test->next()));
     226           0 :     return false;
     227             : }
     228             : 
     229             : // verifies that the coincidence hasn't already been added
     230           0 : static void DebugCheckAdd(const SkCoincidentSpans* check, const SkOpPtT* coinPtTStart,
     231             :         const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
     232             : #if DEBUG_COINCIDENCE
     233             :     while (check) {
     234             :         SkASSERT(check->coinPtTStart() != coinPtTStart || check->coinPtTEnd() != coinPtTEnd
     235             :                 || check->oppPtTStart() != oppPtTStart || check->oppPtTEnd() != oppPtTEnd);
     236             :         SkASSERT(check->coinPtTStart() != oppPtTStart || check->coinPtTEnd() != oppPtTEnd
     237             :                 || check->oppPtTStart() != coinPtTStart || check->oppPtTEnd() != coinPtTEnd);
     238             :         check = check->next();
     239             :     }
     240             : #endif
     241           0 : }
     242             : 
     243             : // adds a new coincident pair
     244           0 : void SkOpCoincidence::add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
     245             :         SkOpPtT* oppPtTEnd) {
     246             :     // OPTIMIZE: caller should have already sorted
     247           0 :     if (!Ordered(coinPtTStart, oppPtTStart)) {
     248           0 :         if (oppPtTStart->fT < oppPtTEnd->fT) {
     249           0 :             this->add(oppPtTStart, oppPtTEnd, coinPtTStart, coinPtTEnd);
     250             :         } else {
     251           0 :             this->add(oppPtTEnd, oppPtTStart, coinPtTEnd, coinPtTStart);
     252             :         }
     253           0 :         return;
     254             :     }
     255           0 :     SkASSERT(Ordered(coinPtTStart, oppPtTStart));
     256             :     // choose the ptT at the front of the list to track
     257           0 :     coinPtTStart = coinPtTStart->span()->ptT();
     258           0 :     coinPtTEnd = coinPtTEnd->span()->ptT();
     259           0 :     oppPtTStart = oppPtTStart->span()->ptT();
     260           0 :     oppPtTEnd = oppPtTEnd->span()->ptT();
     261           0 :     SkOPASSERT(coinPtTStart->fT < coinPtTEnd->fT);
     262           0 :     SkOPASSERT(oppPtTStart->fT != oppPtTEnd->fT);
     263           0 :     SkOPASSERT(!coinPtTStart->deleted());
     264           0 :     SkOPASSERT(!coinPtTEnd->deleted());
     265           0 :     SkOPASSERT(!oppPtTStart->deleted());
     266           0 :     SkOPASSERT(!oppPtTEnd->deleted());
     267           0 :     DebugCheckAdd(fHead, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
     268           0 :     DebugCheckAdd(fTop, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
     269           0 :     SkCoincidentSpans* coinRec = SkOpTAllocator<SkCoincidentSpans>::Allocate(
     270           0 :             this->globalState()->allocator());
     271           0 :     coinRec->init(SkDEBUGCODE(fGlobalState));
     272           0 :     coinRec->set(this->fHead, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
     273           0 :     fHead = coinRec;
     274             : }
     275             : 
     276             : // description below
     277           0 : bool SkOpCoincidence::addEndMovedSpans(const SkOpSpan* base, const SkOpSpanBase* testSpan) {
     278           0 :     const SkOpPtT* testPtT = testSpan->ptT();
     279           0 :     const SkOpPtT* stopPtT = testPtT;
     280           0 :     const SkOpSegment* baseSeg = base->segment();
     281           0 :     int escapeHatch = 100000;  // this is 100 times larger than the debugLoopLimit test
     282           0 :     while ((testPtT = testPtT->next()) != stopPtT) {
     283           0 :         if (--escapeHatch <= 0) {
     284           0 :             return false;  // if triggered (likely by a fuzz-generated test) too complex to succeed
     285             :         }
     286           0 :         const SkOpSegment* testSeg = testPtT->segment();
     287           0 :         if (testPtT->deleted()) {
     288           0 :             continue;
     289             :         }
     290           0 :         if (testSeg == baseSeg) {
     291           0 :             continue;
     292             :         }
     293           0 :         if (testPtT->span()->ptT() != testPtT) {
     294           0 :             continue;
     295             :         }
     296           0 :         if (this->contains(baseSeg, testSeg, testPtT->fT)) {
     297           0 :             continue;
     298             :         }
     299             :         // intersect perp with base->ptT() with testPtT->segment()
     300           0 :         SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
     301           0 :         const SkPoint& pt = base->pt();
     302           0 :         SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
     303           0 :         SkIntersections i  SkDEBUGCODE((this->globalState()));
     304           0 :         (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
     305           0 :         for (int index = 0; index < i.used(); ++index) {
     306           0 :             double t = i[0][index];
     307           0 :             if (!between(0, t, 1)) {
     308           0 :                 continue;
     309             :             }
     310           0 :             SkDPoint oppPt = i.pt(index);
     311           0 :             if (!oppPt.approximatelyEqual(pt)) {
     312           0 :                 continue;
     313             :             }
     314           0 :             SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
     315           0 :             SkOpPtT* oppStart = writableSeg->addT(t);
     316           0 :             if (oppStart == testPtT) {
     317           0 :                 continue;
     318             :             }
     319           0 :             SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
     320           0 :             oppStart->span()->addOpp(writableBase);
     321           0 :             if (oppStart->deleted()) {
     322           0 :                 continue;
     323             :             }
     324           0 :             SkOpSegment* coinSeg = base->segment();
     325           0 :             SkOpSegment* oppSeg = oppStart->segment();
     326             :             double coinTs, coinTe, oppTs, oppTe;
     327           0 :             if (Ordered(coinSeg, oppSeg)) {
     328           0 :                 coinTs = base->t();
     329           0 :                 coinTe = testSpan->t();
     330           0 :                 oppTs = oppStart->fT;
     331           0 :                 oppTe = testPtT->fT;
     332             :             } else {
     333           0 :                 SkTSwap(coinSeg, oppSeg);
     334           0 :                 coinTs = oppStart->fT;
     335           0 :                 coinTe = testPtT->fT;
     336           0 :                 oppTs = base->t();
     337           0 :                 oppTe = testSpan->t();
     338             :             }
     339           0 :             if (coinTs > coinTe) {
     340           0 :                 SkTSwap(coinTs, coinTe);
     341           0 :                 SkTSwap(oppTs, oppTe);
     342             :             }
     343             :             bool added;
     344           0 :             if (!this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added)) {
     345           0 :                 return false;
     346             :             }
     347             :         }
     348             :     }
     349           0 :     return true;
     350             : }
     351             : 
     352             : // description below
     353           0 : bool SkOpCoincidence::addEndMovedSpans(const SkOpPtT* ptT) {
     354           0 :     FAIL_IF(!ptT->span()->upCastable());
     355           0 :     const SkOpSpan* base = ptT->span()->upCast();
     356           0 :     const SkOpSpan* prev = base->prev();
     357           0 :     FAIL_IF(!prev);
     358           0 :     if (!prev->isCanceled()) {
     359           0 :         if (!this->addEndMovedSpans(base, base->prev())) {
     360           0 :             return false;
     361             :         }
     362             :     }
     363           0 :     if (!base->isCanceled()) {
     364           0 :         if (!this->addEndMovedSpans(base, base->next())) {
     365           0 :             return false;
     366             :         }
     367             :     }
     368           0 :     return true;
     369             : }
     370             : 
     371             : /*  If A is coincident with B and B includes an endpoint, and A's matching point
     372             :     is not the endpoint (i.e., there's an implied line connecting B-end and A)
     373             :     then assume that the same implied line may intersect another curve close to B.
     374             :     Since we only care about coincidence that was undetected, look at the
     375             :     ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
     376             :     next door) and see if the A matching point is close enough to form another
     377             :     coincident pair. If so, check for a new coincident span between B-end/A ptT loop
     378             :     and the adjacent ptT loop.
     379             : */
     380           0 : bool SkOpCoincidence::addEndMovedSpans(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
     381             :     DEBUG_SET_PHASE();
     382           0 :     SkCoincidentSpans* span = fHead;
     383           0 :     if (!span) {
     384           0 :         return true;
     385             :     }
     386           0 :     fTop = span;
     387           0 :     fHead = nullptr;
     388           0 :     do {
     389           0 :         if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
     390           0 :             FAIL_IF(1 == span->coinPtTStart()->fT);
     391           0 :             bool onEnd = span->coinPtTStart()->fT == 0;
     392           0 :             bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
     393           0 :             if (onEnd) {
     394           0 :                 if (!oOnEnd) {  // if both are on end, any nearby intersect was already found
     395           0 :                     if (!this->addEndMovedSpans(span->oppPtTStart())) {
     396           0 :                         return false;
     397             :                     }
     398             :                 }
     399           0 :             } else if (oOnEnd) {
     400           0 :                 if (!this->addEndMovedSpans(span->coinPtTStart())) {
     401           0 :                     return false;
     402             :                 }
     403             :             }
     404             :         }
     405           0 :         if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
     406           0 :             bool onEnd = span->coinPtTEnd()->fT == 1;
     407           0 :             bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
     408           0 :             if (onEnd) {
     409           0 :                 if (!oOnEnd) {
     410           0 :                     if (!this->addEndMovedSpans(span->oppPtTEnd())) {
     411           0 :                         return false;
     412             :                     }
     413             :                 }
     414           0 :             } else if (oOnEnd) {
     415           0 :                 if (!this->addEndMovedSpans(span->coinPtTEnd())) {
     416           0 :                     return false;
     417             :                 }
     418             :             }
     419             :         }
     420             :     } while ((span = span->next()));
     421           0 :     this->restoreHead();
     422           0 :     return true;
     423             : }
     424             : 
     425             : /* Please keep this in sync with debugAddExpanded */
     426             : // for each coincident pair, match the spans
     427             : // if the spans don't match, add the missing pt to the segment and loop it in the opposite span
     428           0 : bool SkOpCoincidence::addExpanded(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
     429             :     DEBUG_SET_PHASE();
     430           0 :     SkCoincidentSpans* coin = this->fHead;
     431           0 :     if (!coin) {
     432           0 :         return true;
     433             :     }
     434           0 :     do {
     435           0 :         const SkOpPtT* startPtT = coin->coinPtTStart();
     436           0 :         const SkOpPtT* oStartPtT = coin->oppPtTStart();
     437           0 :         double priorT = startPtT->fT;
     438           0 :         double oPriorT = oStartPtT->fT;
     439           0 :         FAIL_IF(!startPtT->contains(oStartPtT));
     440           0 :         SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
     441           0 :         const SkOpSpanBase* start = startPtT->span();
     442           0 :         const SkOpSpanBase* oStart = oStartPtT->span();
     443           0 :         const SkOpSpanBase* end = coin->coinPtTEnd()->span();
     444           0 :         const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
     445           0 :         FAIL_IF(oEnd->deleted());
     446           0 :         FAIL_IF(!start->upCastable());
     447           0 :         const SkOpSpanBase* test = start->upCast()->next();
     448           0 :         FAIL_IF(!coin->flipped() && !oStart->upCastable());
     449           0 :         const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
     450           0 :         FAIL_IF(!oTest);
     451           0 :         SkOpSegment* seg = start->segment();
     452           0 :         SkOpSegment* oSeg = oStart->segment();
     453           0 :         while (test != end || oTest != oEnd) {
     454           0 :             const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
     455           0 :             const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
     456           0 :             if (!containedOpp || !containedThis) {
     457             :                 // choose the ends, or the first common pt-t list shared by both
     458             :                 double nextT, oNextT;
     459           0 :                 if (containedOpp) {
     460           0 :                     nextT = test->t();
     461           0 :                     oNextT = containedOpp->fT;
     462           0 :                 } else if (containedThis) {
     463           0 :                     nextT = containedThis->fT;
     464           0 :                     oNextT = oTest->t();
     465             :                 } else {
     466             :                     // iterate through until a pt-t list found that contains the other
     467           0 :                     const SkOpSpanBase* walk = test;
     468             :                     const SkOpPtT* walkOpp;
     469           0 :                     do {
     470           0 :                         FAIL_IF(!walk->upCastable());
     471           0 :                         walk = walk->upCast()->next();
     472           0 :                     } while (!(walkOpp = walk->ptT()->contains(oSeg))
     473           0 :                             && walk != coin->coinPtTEnd()->span());
     474           0 :                     FAIL_IF(!walkOpp);
     475           0 :                     nextT = walk->t();
     476           0 :                     oNextT = walkOpp->fT;
     477             :                 }
     478             :                 // use t ranges to guess which one is missing
     479           0 :                 double startRange = nextT - priorT;
     480           0 :                 FAIL_IF(!startRange);
     481           0 :                 double startPart = (test->t() - priorT) / startRange;
     482           0 :                 double oStartRange = oNextT - oPriorT;
     483           0 :                 FAIL_IF(!oStartRange);
     484           0 :                 double oStartPart = (oTest->t() - oPriorT) / oStartRange;
     485           0 :                 FAIL_IF(startPart == oStartPart);
     486           0 :                 bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
     487           0 :                         : !!containedThis;
     488           0 :                 bool startOver = false;
     489           0 :                 bool success = addToOpp ? oSeg->addExpanded(
     490           0 :                         oPriorT + oStartRange * startPart, test, &startOver)
     491           0 :                         : seg->addExpanded(
     492           0 :                         priorT + startRange * oStartPart, oTest, &startOver);
     493           0 :                 FAIL_IF(!success);
     494           0 :                 if (startOver) {
     495           0 :                     test = start;
     496           0 :                     oTest = oStart;
     497             :                 }
     498           0 :                 end = coin->coinPtTEnd()->span();
     499           0 :                 oEnd = coin->oppPtTEnd()->span();
     500             :             }
     501           0 :             if (test != end) {
     502           0 :                 FAIL_IF(!test->upCastable());
     503           0 :                 priorT = test->t();
     504           0 :                 test = test->upCast()->next();
     505             :             }
     506           0 :             if (oTest != oEnd) {
     507           0 :                 oPriorT = oTest->t();
     508           0 :                 if (coin->flipped()) {
     509           0 :                     oTest = oTest->prev();
     510             :                 } else {
     511           0 :                     FAIL_IF(!oTest->upCastable());
     512           0 :                     oTest = oTest->upCast()->next();
     513             :                 }
     514           0 :                 FAIL_IF(!oTest);
     515             :             }
     516             : 
     517             :         }
     518             :     } while ((coin = coin->next()));
     519           0 :     return true;
     520             : }
     521             : 
     522             : // given a t span, map the same range on the coincident span
     523             : /*
     524             : the curves may not scale linearly, so interpolation may only happen within known points
     525             : remap over1s, over1e, cointPtTStart, coinPtTEnd to smallest range that captures over1s
     526             : then repeat to capture over1e
     527             : */
     528           0 : double SkOpCoincidence::TRange(const SkOpPtT* overS, double t,
     529             :        const SkOpSegment* coinSeg  SkDEBUGPARAMS(const SkOpPtT* overE)) {
     530           0 :     const SkOpSpanBase* work = overS->span();
     531           0 :     const SkOpPtT* foundStart = nullptr;
     532           0 :     const SkOpPtT* foundEnd = nullptr;
     533           0 :     const SkOpPtT* coinStart = nullptr;
     534           0 :     const SkOpPtT* coinEnd = nullptr;
     535           0 :     do {
     536           0 :         const SkOpPtT* contained = work->contains(coinSeg);
     537           0 :         if (!contained) {
     538           0 :             if (work->final()) {
     539           0 :                 break;
     540             :             }
     541           0 :             continue;
     542             :         }
     543           0 :         if (work->t() <= t) {
     544           0 :             coinStart = contained;
     545           0 :             foundStart = work->ptT();
     546             :         }
     547           0 :         if (work->t() >= t) {
     548           0 :             coinEnd = contained;
     549           0 :             foundEnd = work->ptT();
     550           0 :             break;
     551             :         }
     552           0 :         SkASSERT(work->ptT() != overE);
     553           0 :     } while ((work = work->upCast()->next()));
     554           0 :     if (!coinStart || !coinEnd) {
     555           0 :         return 1;
     556             :     }
     557             :     // while overS->fT <=t and overS contains coinSeg
     558           0 :     double denom = foundEnd->fT - foundStart->fT;
     559           0 :     double sRatio = denom ? (t - foundStart->fT) / denom : 1;
     560           0 :     return coinStart->fT + (coinEnd->fT - coinStart->fT) * sRatio;
     561             : }
     562             : 
     563             : // return true if span overlaps existing and needs to adjust the coincident list
     564           0 : bool SkOpCoincidence::checkOverlap(SkCoincidentSpans* check,
     565             :         const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
     566             :         double coinTs, double coinTe, double oppTs, double oppTe,
     567             :         SkTDArray<SkCoincidentSpans*>* overlaps) const {
     568           0 :     if (!Ordered(coinSeg, oppSeg)) {
     569           0 :         if (oppTs < oppTe) {
     570           0 :             return this->checkOverlap(check, oppSeg, coinSeg, oppTs, oppTe, coinTs, coinTe,
     571           0 :                     overlaps);
     572             :         }
     573           0 :         return this->checkOverlap(check, oppSeg, coinSeg, oppTe, oppTs, coinTe, coinTs, overlaps);
     574             :     }
     575           0 :     bool swapOpp = oppTs > oppTe;
     576           0 :     if (swapOpp) {
     577           0 :         SkTSwap(oppTs, oppTe);
     578             :     }
     579           0 :     do {
     580           0 :         if (check->coinPtTStart()->segment() != coinSeg) {
     581           0 :             continue;
     582             :         }
     583           0 :         if (check->oppPtTStart()->segment() != oppSeg) {
     584           0 :             continue;
     585             :         }
     586           0 :         double checkTs = check->coinPtTStart()->fT;
     587           0 :         double checkTe = check->coinPtTEnd()->fT;
     588           0 :         bool coinOutside = coinTe < checkTs || coinTs > checkTe;
     589           0 :         double oCheckTs = check->oppPtTStart()->fT;
     590           0 :         double oCheckTe = check->oppPtTEnd()->fT;
     591           0 :         if (swapOpp) {
     592           0 :             if (oCheckTs <= oCheckTe) {
     593           0 :                 return false;
     594             :             }
     595           0 :             SkTSwap(oCheckTs, oCheckTe);
     596             :         }
     597           0 :         bool oppOutside = oppTe < oCheckTs || oppTs > oCheckTe;
     598           0 :         if (coinOutside && oppOutside) {
     599           0 :             continue;
     600             :         }
     601           0 :         bool coinInside = coinTe <= checkTe && coinTs >= checkTs;
     602           0 :         bool oppInside = oppTe <= oCheckTe && oppTs >= oCheckTs;
     603           0 :         if (coinInside && oppInside) {  // already included, do nothing
     604           0 :             return false;
     605             :         }
     606           0 :         *overlaps->append() = check; // partial overlap, extend existing entry
     607             :     } while ((check = check->next()));
     608           0 :     return true;
     609             : }
     610             : 
     611             : /* Please keep this in sync with debugAddIfMissing() */
     612             : // note that over1s, over1e, over2s, over2e are ordered
     613           0 : bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over2s,
     614             :         double tStart, double tEnd, SkOpSegment* coinSeg, SkOpSegment* oppSeg, bool* added  
     615             :         SkDEBUGPARAMS(const SkOpPtT* over1e) SkDEBUGPARAMS(const SkOpPtT* over2e)) {
     616           0 :     SkASSERT(tStart < tEnd);
     617           0 :     SkASSERT(over1s->fT < over1e->fT);
     618           0 :     SkASSERT(between(over1s->fT, tStart, over1e->fT));
     619           0 :     SkASSERT(between(over1s->fT, tEnd, over1e->fT));
     620           0 :     SkASSERT(over2s->fT < over2e->fT);
     621           0 :     SkASSERT(between(over2s->fT, tStart, over2e->fT));
     622           0 :     SkASSERT(between(over2s->fT, tEnd, over2e->fT));
     623           0 :     SkASSERT(over1s->segment() == over1e->segment());
     624           0 :     SkASSERT(over2s->segment() == over2e->segment());
     625           0 :     SkASSERT(over1s->segment() == over2s->segment());
     626           0 :     SkASSERT(over1s->segment() != coinSeg);
     627           0 :     SkASSERT(over1s->segment() != oppSeg);
     628           0 :     SkASSERT(coinSeg != oppSeg);
     629             :     double coinTs, coinTe, oppTs, oppTe;
     630           0 :     coinTs = TRange(over1s, tStart, coinSeg  SkDEBUGPARAMS(over1e));
     631           0 :     coinTe = TRange(over1s, tEnd, coinSeg  SkDEBUGPARAMS(over1e));
     632           0 :     if (coinSeg->collapsed(coinTs, coinTe)) {
     633           0 :         return true;
     634             :     }
     635           0 :     oppTs = TRange(over2s, tStart, oppSeg  SkDEBUGPARAMS(over2e));
     636           0 :     oppTe = TRange(over2s, tEnd, oppSeg  SkDEBUGPARAMS(over2e));
     637           0 :     if (oppSeg->collapsed(oppTs, oppTe)) {
     638           0 :         return true;
     639             :     }
     640           0 :     if (coinTs > coinTe) {
     641           0 :         SkTSwap(coinTs, coinTe);
     642           0 :         SkTSwap(oppTs, oppTe);
     643             :     }
     644           0 :     return this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added);
     645             : }
     646             : 
     647             : /* Please keep this in sync with debugAddOrOverlap() */
     648             : // If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
     649             : // If this is called by AddIfMissing(), a returned false indicates there was nothing to add
     650           0 : bool SkOpCoincidence::addOrOverlap(SkOpSegment* coinSeg, SkOpSegment* oppSeg,
     651             :         double coinTs, double coinTe, double oppTs, double oppTe, bool* added) {
     652           0 :     SkTDArray<SkCoincidentSpans*> overlaps;
     653           0 :     FAIL_IF(!fTop);
     654           0 :     if (!this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &overlaps)) {
     655           0 :         return true;
     656             :     }
     657           0 :     if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
     658             :             coinTe, oppTs, oppTe, &overlaps)) {
     659           0 :         return true;
     660             :     }
     661           0 :     SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
     662           0 :     for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
     663           0 :         SkCoincidentSpans* test = overlaps[index];
     664           0 :         if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
     665           0 :             overlap->setCoinPtTStart(test->coinPtTStart());
     666             :         }
     667           0 :         if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
     668           0 :             overlap->setCoinPtTEnd(test->coinPtTEnd());
     669             :         }
     670           0 :         if (overlap->flipped()
     671           0 :                 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
     672           0 :                 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
     673           0 :             overlap->setOppPtTStart(test->oppPtTStart());
     674             :         }
     675           0 :         if (overlap->flipped()
     676           0 :                 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
     677           0 :                 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
     678           0 :             overlap->setOppPtTEnd(test->oppPtTEnd());
     679             :         }
     680           0 :         if (!fHead || !this->release(fHead, test)) {
     681           0 :             SkAssertResult(this->release(fTop, test));
     682             :         }
     683             :     }
     684           0 :     const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
     685           0 :     const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
     686           0 :     if (overlap && cs && ce && overlap->contains(cs, ce)) {
     687           0 :         return true;
     688             :     }
     689           0 :     FAIL_IF(cs == ce && cs);
     690           0 :     const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
     691           0 :     const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
     692           0 :     if (overlap && os && oe && overlap->contains(os, oe)) {
     693           0 :         return true;
     694             :     }
     695           0 :     SkASSERT(!cs || !cs->deleted());
     696           0 :     SkASSERT(!os || !os->deleted());
     697           0 :     SkASSERT(!ce || !ce->deleted());
     698           0 :     SkASSERT(!oe || !oe->deleted());
     699           0 :     const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
     700           0 :     const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
     701           0 :     FAIL_IF(csExisting && csExisting == ceExisting);
     702             : //    FAIL_IF(csExisting && (csExisting == ce ||
     703             : //            csExisting->contains(ceExisting ? ceExisting : ce)));
     704           0 :     FAIL_IF(ceExisting && (ceExisting == cs ||
     705             :             ceExisting->contains(csExisting ? csExisting : cs)));
     706           0 :     const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
     707           0 :     const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
     708           0 :     FAIL_IF(osExisting && osExisting == oeExisting);
     709           0 :     FAIL_IF(osExisting && (osExisting == oe ||
     710             :             osExisting->contains(oeExisting ? oeExisting : oe)));
     711           0 :     FAIL_IF(oeExisting && (oeExisting == os ||
     712             :             oeExisting->contains(osExisting ? osExisting : os)));
     713             :     // extra line in debug code
     714           0 :     this->debugValidate();
     715           0 :     if (!cs || !os) {
     716           0 :         SkOpPtT* csWritable = cs ? const_cast<SkOpPtT*>(cs)
     717           0 :             : coinSeg->addT(coinTs);
     718           0 :         if (csWritable == ce) {
     719           0 :             return true;
     720             :         }
     721           0 :         SkOpPtT* osWritable = os ? const_cast<SkOpPtT*>(os)
     722           0 :             : oppSeg->addT(oppTs);
     723           0 :         FAIL_IF(!csWritable || !osWritable);
     724           0 :         csWritable->span()->addOpp(osWritable->span());
     725           0 :         cs = csWritable;
     726           0 :         os = osWritable->active();
     727           0 :         FAIL_IF((ce && ce->deleted()) || (oe && oe->deleted()));
     728             :     }
     729           0 :     if (!ce || !oe) {
     730           0 :         SkOpPtT* ceWritable = ce ? const_cast<SkOpPtT*>(ce)
     731           0 :             : coinSeg->addT(coinTe);
     732           0 :         SkOpPtT* oeWritable = oe ? const_cast<SkOpPtT*>(oe)
     733           0 :             : oppSeg->addT(oppTe);
     734           0 :         ceWritable->span()->addOpp(oeWritable->span());
     735           0 :         ce = ceWritable;
     736           0 :         oe = oeWritable;
     737             :     }
     738           0 :     this->debugValidate();
     739           0 :     FAIL_IF(cs->deleted());
     740           0 :     FAIL_IF(os->deleted());
     741           0 :     FAIL_IF(ce->deleted());
     742           0 :     FAIL_IF(oe->deleted());
     743           0 :     FAIL_IF(cs->contains(ce) || os->contains(oe));
     744           0 :     bool result = true;
     745           0 :     if (overlap) {
     746           0 :         if (overlap->coinPtTStart()->segment() == coinSeg) {
     747           0 :             result = overlap->extend(cs, ce, os, oe);
     748             :         } else {
     749           0 :             if (os->fT > oe->fT) {
     750           0 :                 SkTSwap(cs, ce);
     751           0 :                 SkTSwap(os, oe);
     752             :             }
     753           0 :             result = overlap->extend(os, oe, cs, ce);
     754             :         }
     755             : #if DEBUG_COINCIDENCE_VERBOSE
     756             :         if (result) {
     757             :             overlaps[0]->debugShow();
     758             :         }
     759             : #endif
     760             :     } else {
     761           0 :         this->add(cs, ce, os, oe);
     762             : #if DEBUG_COINCIDENCE_VERBOSE
     763             :         fHead->debugShow();
     764             : #endif
     765             :     }
     766           0 :     this->debugValidate();
     767           0 :     if (result) {
     768           0 :         *added = true;
     769             :     }
     770           0 :     return true;
     771             : }
     772             : 
     773             : // Please keep this in sync with debugAddMissing()
     774             : /* detects overlaps of different coincident runs on same segment */
     775             : /* does not detect overlaps for pairs without any segments in common */
     776             : // returns true if caller should loop again
     777           0 : bool SkOpCoincidence::addMissing(bool* added  DEBUG_COIN_DECLARE_PARAMS()) {
     778           0 :     SkCoincidentSpans* outer = fHead;
     779           0 :     *added = false;
     780           0 :     if (!outer) {
     781           0 :         return true;
     782             :     }
     783           0 :     fTop = outer;
     784           0 :     fHead = nullptr;
     785           0 :     do {
     786             :     // addifmissing can modify the list that this is walking
     787             :     // save head so that walker can iterate over old data unperturbed
     788             :     // addifmissing adds to head freely then add saved head in the end
     789           0 :         const SkOpPtT* ocs = outer->coinPtTStart();
     790           0 :         FAIL_IF(ocs->deleted());
     791           0 :         const SkOpSegment* outerCoin = ocs->segment();
     792           0 :         SkASSERT(!outerCoin->done());  // if it's done, should have already been removed from list
     793           0 :         const SkOpPtT* oos = outer->oppPtTStart();
     794           0 :         if (oos->deleted()) {
     795           0 :             return true;
     796             :         }
     797           0 :         const SkOpSegment* outerOpp = oos->segment();
     798           0 :         SkOPASSERT(!outerOpp->done());
     799           0 :         SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
     800           0 :         SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
     801           0 :         SkCoincidentSpans* inner = outer;
     802           0 :         while ((inner = inner->next())) {
     803           0 :             this->debugValidate();
     804             :             double overS, overE;
     805           0 :             const SkOpPtT* ics = inner->coinPtTStart();
     806           0 :             FAIL_IF(ics->deleted());
     807           0 :             const SkOpSegment* innerCoin = ics->segment();
     808           0 :             FAIL_IF(innerCoin->done());
     809           0 :             const SkOpPtT* ios = inner->oppPtTStart();
     810           0 :             FAIL_IF(ios->deleted());
     811           0 :             const SkOpSegment* innerOpp = ios->segment();
     812           0 :             SkOPASSERT(!innerOpp->done());
     813           0 :             SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
     814           0 :             SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
     815           0 :             if (outerCoin == innerCoin) {
     816           0 :                 const SkOpPtT* oce = outer->coinPtTEnd();
     817           0 :                 if (oce->deleted()) {
     818           0 :                     return true;
     819             :                 }
     820           0 :                 const SkOpPtT* ice = inner->coinPtTEnd();
     821           0 :                 FAIL_IF(ice->deleted());
     822           0 :                 if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
     823           0 :                     (void) this->addIfMissing(ocs->starter(oce), ics->starter(ice),
     824             :                             overS, overE, outerOppWritable, innerOppWritable, added
     825             :                             SkDEBUGPARAMS(ocs->debugEnder(oce))
     826           0 :                             SkDEBUGPARAMS(ics->debugEnder(ice)));
     827             :                 }
     828           0 :             } else if (outerCoin == innerOpp) {
     829           0 :                 const SkOpPtT* oce = outer->coinPtTEnd();
     830           0 :                 SkASSERT(!oce->deleted());
     831           0 :                 const SkOpPtT* ioe = inner->oppPtTEnd();
     832           0 :                 SkASSERT(!ioe->deleted());
     833           0 :                 if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
     834           0 :                     (void) this->addIfMissing(ocs->starter(oce), ios->starter(ioe),
     835             :                             overS, overE, outerOppWritable, innerCoinWritable, added
     836             :                             SkDEBUGPARAMS(ocs->debugEnder(oce))
     837           0 :                             SkDEBUGPARAMS(ios->debugEnder(ioe)));
     838             :                 }
     839           0 :             } else if (outerOpp == innerCoin) {
     840           0 :                 const SkOpPtT* ooe = outer->oppPtTEnd();
     841           0 :                 SkASSERT(!ooe->deleted());
     842           0 :                 const SkOpPtT* ice = inner->coinPtTEnd();
     843           0 :                 SkASSERT(!ice->deleted());
     844           0 :                 SkASSERT(outerCoin != innerOpp);
     845           0 :                 if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
     846           0 :                     (void) this->addIfMissing(oos->starter(ooe), ics->starter(ice),
     847             :                             overS, overE, outerCoinWritable, innerOppWritable, added
     848             :                             SkDEBUGPARAMS(oos->debugEnder(ooe))
     849           0 :                             SkDEBUGPARAMS(ics->debugEnder(ice)));
     850             :                 }
     851           0 :             } else if (outerOpp == innerOpp) {
     852           0 :                 const SkOpPtT* ooe = outer->oppPtTEnd();
     853           0 :                 SkASSERT(!ooe->deleted());
     854           0 :                 const SkOpPtT* ioe = inner->oppPtTEnd();
     855           0 :                 if (ioe->deleted()) {
     856           0 :                     return true;
     857             :                 }
     858           0 :                 SkASSERT(outerCoin != innerCoin);
     859           0 :                 if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
     860           0 :                     (void) this->addIfMissing(oos->starter(ooe), ios->starter(ioe),
     861             :                             overS, overE, outerCoinWritable, innerCoinWritable, added
     862             :                             SkDEBUGPARAMS(oos->debugEnder(ooe))
     863           0 :                             SkDEBUGPARAMS(ios->debugEnder(ioe)));
     864             :                 }
     865             :             }
     866           0 :             this->debugValidate();
     867             :         }
     868             :     } while ((outer = outer->next()));
     869           0 :     this->restoreHead();
     870           0 :     return true;
     871             : }
     872             : 
     873           0 : bool SkOpCoincidence::addOverlap(const SkOpSegment* seg1, const SkOpSegment* seg1o,
     874             :         const SkOpSegment* seg2, const SkOpSegment* seg2o,
     875             :         const SkOpPtT* overS, const SkOpPtT* overE) {
     876           0 :     const SkOpPtT* s1 = overS->find(seg1);
     877           0 :     const SkOpPtT* e1 = overE->find(seg1);
     878           0 :     FAIL_IF(!s1);
     879           0 :     FAIL_IF(!e1);
     880           0 :     if (!s1->starter(e1)->span()->upCast()->windValue()) {
     881           0 :         s1 = overS->find(seg1o);
     882           0 :         e1 = overE->find(seg1o);
     883           0 :         FAIL_IF(!s1);
     884           0 :         FAIL_IF(!e1);
     885           0 :         if (!s1->starter(e1)->span()->upCast()->windValue()) {
     886           0 :             return true;
     887             :         }
     888             :     }
     889           0 :     const SkOpPtT* s2 = overS->find(seg2);
     890           0 :     const SkOpPtT* e2 = overE->find(seg2);
     891           0 :     FAIL_IF(!s2);
     892           0 :     FAIL_IF(!e2);
     893           0 :     if (!s2->starter(e2)->span()->upCast()->windValue()) {
     894           0 :         s2 = overS->find(seg2o);
     895           0 :         e2 = overE->find(seg2o);
     896           0 :         FAIL_IF(!s2);
     897           0 :         FAIL_IF(!e2);
     898           0 :         if (!s2->starter(e2)->span()->upCast()->windValue()) {
     899           0 :             return true;
     900             :         }
     901             :     }
     902           0 :     if (s1->segment() == s2->segment()) {
     903           0 :         return true;
     904             :     }
     905           0 :     if (s1->fT > e1->fT) {
     906           0 :         SkTSwap(s1, e1);
     907           0 :         SkTSwap(s2, e2);
     908             :     }
     909           0 :     this->add(s1, e1, s2, e2);
     910           0 :     return true;
     911             : }
     912             : 
     913           0 : bool SkOpCoincidence::contains(const SkOpSegment* seg, const SkOpSegment* opp, double oppT) const {
     914           0 :     if (this->contains(fHead, seg, opp, oppT)) {
     915           0 :         return true;
     916             :     }
     917           0 :     if (this->contains(fTop, seg, opp, oppT)) {
     918           0 :         return true;
     919             :     }
     920           0 :     return false;
     921             : }
     922             : 
     923           0 : bool SkOpCoincidence::contains(const SkCoincidentSpans* coin, const SkOpSegment* seg,
     924             :         const SkOpSegment* opp, double oppT) const {
     925           0 :     if (!coin) {
     926           0 :         return false;
     927             :    }
     928           0 :     do {
     929           0 :         if (coin->coinPtTStart()->segment() == seg && coin->oppPtTStart()->segment() == opp
     930           0 :                 && between(coin->oppPtTStart()->fT, oppT, coin->oppPtTEnd()->fT)) {
     931           0 :             return true;
     932             :         }
     933           0 :         if (coin->oppPtTStart()->segment() == seg && coin->coinPtTStart()->segment() == opp
     934           0 :                 && between(coin->coinPtTStart()->fT, oppT, coin->coinPtTEnd()->fT)) {
     935           0 :             return true;
     936             :         }
     937             :     } while ((coin = coin->next()));
     938           0 :     return false;
     939             : }
     940             : 
     941           0 : bool SkOpCoincidence::contains(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
     942             :         const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const {
     943           0 :     const SkCoincidentSpans* test = fHead;
     944           0 :     if (!test) {
     945           0 :         return false;
     946             :     }
     947           0 :     const SkOpSegment* coinSeg = coinPtTStart->segment();
     948           0 :     const SkOpSegment* oppSeg = oppPtTStart->segment();
     949           0 :     if (!Ordered(coinPtTStart, oppPtTStart)) {
     950           0 :         SkTSwap(coinSeg, oppSeg);
     951           0 :         SkTSwap(coinPtTStart, oppPtTStart);
     952           0 :         SkTSwap(coinPtTEnd, oppPtTEnd);
     953           0 :         if (coinPtTStart->fT > coinPtTEnd->fT) {
     954           0 :             SkTSwap(coinPtTStart, coinPtTEnd);
     955           0 :             SkTSwap(oppPtTStart, oppPtTEnd);
     956             :         }
     957             :     }
     958           0 :     double oppMinT = SkTMin(oppPtTStart->fT, oppPtTEnd->fT);
     959           0 :     double oppMaxT = SkTMax(oppPtTStart->fT, oppPtTEnd->fT);
     960           0 :     do {
     961           0 :         if (coinSeg != test->coinPtTStart()->segment()) {
     962           0 :             continue;
     963             :         }
     964           0 :         if (coinPtTStart->fT < test->coinPtTStart()->fT) {
     965           0 :             continue;
     966             :         }
     967           0 :         if (coinPtTEnd->fT > test->coinPtTEnd()->fT) {
     968           0 :             continue;
     969             :         }
     970           0 :         if (oppSeg != test->oppPtTStart()->segment()) {
     971           0 :             continue;
     972             :         }
     973           0 :         if (oppMinT < SkTMin(test->oppPtTStart()->fT, test->oppPtTEnd()->fT)) {
     974           0 :             continue;
     975             :         }
     976           0 :         if (oppMaxT > SkTMax(test->oppPtTStart()->fT, test->oppPtTEnd()->fT)) {
     977           0 :             continue;
     978             :         }
     979           0 :         return true;
     980             :     } while ((test = test->next()));
     981           0 :     return false;
     982             : }
     983             : 
     984           0 : void SkOpCoincidence::correctEnds(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
     985             :     DEBUG_SET_PHASE();
     986           0 :     SkCoincidentSpans* coin = fHead;
     987           0 :     if (!coin) {
     988           0 :         return;
     989             :     }
     990           0 :     do {
     991           0 :         coin->correctEnds();
     992             :     } while ((coin = coin->next()));
     993             : }
     994             : 
     995             : // walk span sets in parallel, moving winding from one to the other
     996           0 : bool SkOpCoincidence::apply(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
     997             :     DEBUG_SET_PHASE();
     998           0 :     SkCoincidentSpans* coin = fHead;
     999           0 :     if (!coin) {
    1000           0 :         return true;
    1001             :     }
    1002           0 :     do {
    1003           0 :         SkOpSpanBase* startSpan = coin->coinPtTStartWritable()->span();
    1004           0 :         FAIL_IF(!startSpan->upCastable());
    1005           0 :         SkOpSpan* start = startSpan->upCast();
    1006           0 :         if (start->deleted()) {
    1007           0 :             continue;
    1008             :         }
    1009           0 :         const SkOpSpanBase* end = coin->coinPtTEnd()->span();
    1010           0 :         SkASSERT(start == start->starter(end));
    1011           0 :         bool flipped = coin->flipped();
    1012             :         SkOpSpanBase* oStartBase = (flipped ? coin->oppPtTEndWritable()
    1013           0 :                 : coin->oppPtTStartWritable())->span();
    1014           0 :         FAIL_IF(!oStartBase->upCastable());
    1015           0 :         SkOpSpan* oStart = oStartBase->upCast();
    1016           0 :         if (oStart->deleted()) {
    1017           0 :             continue;
    1018             :         }
    1019           0 :         const SkOpSpanBase* oEnd = (flipped ? coin->oppPtTStart() : coin->oppPtTEnd())->span();
    1020           0 :         SkASSERT(oStart == oStart->starter(oEnd));
    1021           0 :         SkOpSegment* segment = start->segment();
    1022           0 :         SkOpSegment* oSegment = oStart->segment();
    1023           0 :         bool operandSwap = segment->operand() != oSegment->operand();
    1024           0 :         if (flipped) {
    1025           0 :             if (oEnd->deleted()) {
    1026           0 :                 continue;
    1027             :             }
    1028             :             do {
    1029           0 :                 SkOpSpanBase* oNext = oStart->next();
    1030           0 :                 if (oNext == oEnd) {
    1031           0 :                     break;
    1032             :                 }
    1033           0 :                 FAIL_IF(!oNext->upCastable());
    1034           0 :                 oStart = oNext->upCast();
    1035             :             } while (true);
    1036             :         }
    1037             :         do {
    1038           0 :             int windValue = start->windValue();
    1039           0 :             int oppValue = start->oppValue();
    1040           0 :             int oWindValue = oStart->windValue();
    1041           0 :             int oOppValue = oStart->oppValue();
    1042             :             // winding values are added or subtracted depending on direction and wind type
    1043             :             // same or opposite values are summed depending on the operand value
    1044           0 :             int windDiff = operandSwap ? oOppValue : oWindValue;
    1045           0 :             int oWindDiff = operandSwap ? oppValue : windValue;
    1046           0 :             if (!flipped) {
    1047           0 :                 windDiff = -windDiff;
    1048           0 :                 oWindDiff = -oWindDiff;
    1049             :             }
    1050           0 :             bool addToStart = windValue && (windValue > windDiff || (windValue == windDiff
    1051           0 :                     && oWindValue <= oWindDiff));
    1052           0 :             if (addToStart ? start->done() : oStart->done()) {
    1053           0 :                 addToStart ^= true;
    1054             :             }
    1055           0 :             if (addToStart) {
    1056           0 :                 if (operandSwap) {
    1057           0 :                     SkTSwap(oWindValue, oOppValue);
    1058             :                 }
    1059           0 :                 if (flipped) {
    1060           0 :                     windValue -= oWindValue;
    1061           0 :                     oppValue -= oOppValue;
    1062             :                 } else {
    1063           0 :                     windValue += oWindValue;
    1064           0 :                     oppValue += oOppValue;
    1065             :                 }
    1066           0 :                 if (segment->isXor()) {
    1067           0 :                     windValue &= 1;
    1068             :                 }
    1069           0 :                 if (segment->oppXor()) {
    1070           0 :                     oppValue &= 1;
    1071             :                 }
    1072           0 :                 oWindValue = oOppValue = 0;
    1073             :             } else {
    1074           0 :                 if (operandSwap) {
    1075           0 :                     SkTSwap(windValue, oppValue);
    1076             :                 }
    1077           0 :                 if (flipped) {
    1078           0 :                     oWindValue -= windValue;
    1079           0 :                     oOppValue -= oppValue;
    1080             :                 } else {
    1081           0 :                     oWindValue += windValue;
    1082           0 :                     oOppValue += oppValue;
    1083             :                 }
    1084           0 :                 if (oSegment->isXor()) {
    1085           0 :                     oWindValue &= 1;
    1086             :                 }
    1087           0 :                 if (oSegment->oppXor()) {
    1088           0 :                     oOppValue &= 1;
    1089             :                 }
    1090           0 :                 windValue = oppValue = 0;
    1091             :             }
    1092             : #if 0 && DEBUG_COINCIDENCE
    1093             :             SkDebugf("seg=%d span=%d windValue=%d oppValue=%d\n", segment->debugID(),
    1094             :                     start->debugID(), windValue, oppValue);
    1095             :             SkDebugf("seg=%d span=%d windValue=%d oppValue=%d\n", oSegment->debugID(),
    1096             :                     oStart->debugID(), oWindValue, oOppValue);
    1097             : #endif
    1098           0 :             start->setWindValue(windValue);
    1099           0 :             start->setOppValue(oppValue);
    1100           0 :             FAIL_IF(oWindValue == -1);
    1101           0 :             oStart->setWindValue(oWindValue);
    1102           0 :             oStart->setOppValue(oOppValue);
    1103           0 :             if (!windValue && !oppValue) {
    1104           0 :                 segment->markDone(start);
    1105             :             }
    1106           0 :             if (!oWindValue && !oOppValue) {
    1107           0 :                 oSegment->markDone(oStart);
    1108             :             }
    1109           0 :             SkOpSpanBase* next = start->next();
    1110           0 :             SkOpSpanBase* oNext = flipped ? oStart->prev() : oStart->next();
    1111           0 :             if (next == end) {
    1112           0 :                 break;
    1113             :             }
    1114           0 :             FAIL_IF(!next->upCastable());
    1115           0 :             start = next->upCast();
    1116             :             // if the opposite ran out too soon, just reuse the last span
    1117           0 :             if (!oNext || !oNext->upCastable()) {
    1118           0 :                oNext = oStart;
    1119             :             }
    1120           0 :             oStart = oNext->upCast();
    1121             :         } while (true);
    1122             :     } while ((coin = coin->next()));
    1123           0 :     return true;
    1124             : }
    1125             : 
    1126             : // Please keep this in sync with debugRelease()
    1127           0 : bool SkOpCoincidence::release(SkCoincidentSpans* coin, SkCoincidentSpans* remove)  {
    1128           0 :     SkCoincidentSpans* head = coin;
    1129           0 :     SkCoincidentSpans* prev = nullptr;
    1130             :     SkCoincidentSpans* next;
    1131           0 :     do {
    1132           0 :         next = coin->next();
    1133           0 :         if (coin == remove) {
    1134           0 :             if (prev) {
    1135           0 :                 prev->setNext(next);
    1136           0 :             } else if (head == fHead) {
    1137           0 :                 fHead = next;
    1138             :             } else {
    1139           0 :                 fTop = next;
    1140             :             }
    1141           0 :             break;
    1142             :         }
    1143           0 :         prev = coin;
    1144           0 :     } while ((coin = next));
    1145           0 :     return coin != nullptr;
    1146             : }
    1147             : 
    1148           0 : void SkOpCoincidence::releaseDeleted(SkCoincidentSpans* coin) {
    1149           0 :     if (!coin) {
    1150           0 :         return;
    1151             :     }
    1152           0 :     SkCoincidentSpans* head = coin;
    1153           0 :     SkCoincidentSpans* prev = nullptr;
    1154             :     SkCoincidentSpans* next;
    1155           0 :     do {
    1156           0 :         next = coin->next();
    1157           0 :         if (coin->coinPtTStart()->deleted()) {
    1158           0 :             SkOPASSERT(coin->flipped() ? coin->oppPtTEnd()->deleted() :
    1159             :                     coin->oppPtTStart()->deleted());
    1160           0 :             if (prev) {
    1161           0 :                 prev->setNext(next);
    1162           0 :             } else if (head == fHead) {
    1163           0 :                 fHead = next;
    1164             :             } else {
    1165           0 :                 fTop = next;
    1166             :             }
    1167             :         } else {
    1168           0 :              SkOPASSERT(coin->flipped() ? !coin->oppPtTEnd()->deleted() :
    1169             :                     !coin->oppPtTStart()->deleted());
    1170           0 :             prev = coin;
    1171             :         }
    1172           0 :     } while ((coin = next));
    1173             : }
    1174             : 
    1175           0 : void SkOpCoincidence::releaseDeleted() {
    1176           0 :     this->releaseDeleted(fHead);
    1177           0 :     this->releaseDeleted(fTop);
    1178           0 : }
    1179             : 
    1180           0 : void SkOpCoincidence::restoreHead() {
    1181           0 :     SkCoincidentSpans** headPtr = &fHead;
    1182           0 :     while (*headPtr) {
    1183           0 :         headPtr = (*headPtr)->nextPtr();
    1184             :     }
    1185           0 :     *headPtr = fTop;
    1186           0 :     fTop = nullptr;
    1187             :     // segments may have collapsed in the meantime; remove empty referenced segments
    1188           0 :     headPtr = &fHead;
    1189           0 :     while (*headPtr) {
    1190           0 :         SkCoincidentSpans* test = *headPtr;
    1191           0 :         if (test->coinPtTStart()->segment()->done() || test->oppPtTStart()->segment()->done()) {
    1192           0 :             *headPtr = test->next();
    1193           0 :             continue;
    1194             :         }
    1195           0 :         headPtr = (*headPtr)->nextPtr();
    1196             :     }
    1197           0 : }
    1198             : 
    1199             : // Please keep this in sync with debugExpand()
    1200             : // expand the range by checking adjacent spans for coincidence
    1201           0 : bool SkOpCoincidence::expand(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
    1202             :     DEBUG_SET_PHASE();
    1203           0 :     SkCoincidentSpans* coin = fHead;
    1204           0 :     if (!coin) {
    1205           0 :         return false;
    1206             :     }
    1207           0 :     bool expanded = false;
    1208           0 :     do {
    1209           0 :         if (coin->expand()) {
    1210             :             // check to see if multiple spans expanded so they are now identical
    1211           0 :             SkCoincidentSpans* test = fHead;
    1212           0 :             do {
    1213           0 :                 if (coin == test) {
    1214           0 :                     continue;
    1215             :                 }
    1216           0 :                 if (coin->coinPtTStart() == test->coinPtTStart()
    1217           0 :                         && coin->oppPtTStart() == test->oppPtTStart()) {
    1218           0 :                     this->release(fHead, test);
    1219           0 :                     break;
    1220             :                 }
    1221             :             } while ((test = test->next()));
    1222           0 :             expanded = true;
    1223             :         }
    1224             :     } while ((coin = coin->next()));
    1225           0 :     return expanded;
    1226             : }
    1227             : 
    1228           0 : bool SkOpCoincidence::findOverlaps(SkOpCoincidence* overlaps  DEBUG_COIN_DECLARE_PARAMS()) const {
    1229             :     DEBUG_SET_PHASE();
    1230           0 :     overlaps->fHead = overlaps->fTop = nullptr;
    1231           0 :     SkCoincidentSpans* outer = fHead;
    1232           0 :     while (outer) {
    1233           0 :         const SkOpSegment* outerCoin = outer->coinPtTStart()->segment();
    1234           0 :         const SkOpSegment* outerOpp = outer->oppPtTStart()->segment();
    1235           0 :         SkCoincidentSpans* inner = outer;
    1236           0 :         while ((inner = inner->next())) {
    1237           0 :             const SkOpSegment* innerCoin = inner->coinPtTStart()->segment();
    1238           0 :             if (outerCoin == innerCoin) {
    1239           0 :                 continue;  // both winners are the same segment, so there's no additional overlap
    1240             :             }
    1241           0 :             const SkOpSegment* innerOpp = inner->oppPtTStart()->segment();
    1242             :             const SkOpPtT* overlapS;
    1243             :             const SkOpPtT* overlapE;
    1244           0 :             if ((outerOpp == innerCoin && SkOpPtT::Overlaps(outer->oppPtTStart(),
    1245             :                     outer->oppPtTEnd(),inner->coinPtTStart(), inner->coinPtTEnd(), &overlapS,
    1246             :                     &overlapE))
    1247           0 :                     || (outerCoin == innerOpp && SkOpPtT::Overlaps(outer->coinPtTStart(),
    1248             :                     outer->coinPtTEnd(), inner->oppPtTStart(), inner->oppPtTEnd(),
    1249             :                     &overlapS, &overlapE))
    1250           0 :                     || (outerOpp == innerOpp && SkOpPtT::Overlaps(outer->oppPtTStart(),
    1251             :                     outer->oppPtTEnd(), inner->oppPtTStart(), inner->oppPtTEnd(),
    1252             :                     &overlapS, &overlapE))) {
    1253           0 :                 if (!overlaps->addOverlap(outerCoin, outerOpp, innerCoin, innerOpp,
    1254             :                         overlapS, overlapE)) {
    1255           0 :                     return false;
    1256             :                 }
    1257             :              }
    1258             :         }
    1259           0 :         outer = outer->next();
    1260             :     }
    1261           0 :     return true;
    1262             : }
    1263             : 
    1264           0 : void SkOpCoincidence::fixUp(SkOpPtT* deleted, const SkOpPtT* kept) {
    1265           0 :     SkOPASSERT(deleted != kept);
    1266           0 :     if (fHead) {
    1267           0 :         this->fixUp(fHead, deleted, kept);
    1268             :     }
    1269           0 :     if (fTop) {
    1270           0 :         this->fixUp(fTop, deleted, kept);
    1271             :     }
    1272           0 : }
    1273             : 
    1274           0 : void SkOpCoincidence::fixUp(SkCoincidentSpans* coin, SkOpPtT* deleted, const SkOpPtT* kept) {
    1275           0 :     SkCoincidentSpans* head = coin;
    1276           0 :     do {
    1277           0 :         if (coin->coinPtTStart() == deleted) {
    1278           0 :             if (coin->coinPtTEnd()->span() == kept->span()) {
    1279           0 :                 this->release(head, coin);
    1280           0 :                 continue;
    1281             :             }
    1282           0 :             coin->setCoinPtTStart(kept);
    1283             :         }
    1284           0 :         if (coin->coinPtTEnd() == deleted) {
    1285           0 :             if (coin->coinPtTStart()->span() == kept->span()) {
    1286           0 :                 this->release(head, coin);
    1287           0 :                 continue;
    1288             :             }
    1289           0 :             coin->setCoinPtTEnd(kept);
    1290             :        }
    1291           0 :         if (coin->oppPtTStart() == deleted) {
    1292           0 :             if (coin->oppPtTEnd()->span() == kept->span()) {
    1293           0 :                 this->release(head, coin);
    1294           0 :                 continue;
    1295             :             }
    1296           0 :             coin->setOppPtTStart(kept);
    1297             :         }
    1298           0 :         if (coin->oppPtTEnd() == deleted) {
    1299           0 :             if (coin->oppPtTStart()->span() == kept->span()) {
    1300           0 :                 this->release(head, coin);
    1301           0 :                 continue;
    1302             :             }
    1303           0 :             coin->setOppPtTEnd(kept);
    1304             :         }
    1305             :     } while ((coin = coin->next()));
    1306           0 : }
    1307             : 
    1308             : // Please keep this in sync with debugMark()
    1309             : /* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
    1310           0 : bool SkOpCoincidence::mark(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
    1311             :     DEBUG_SET_PHASE();
    1312           0 :     SkCoincidentSpans* coin = fHead;
    1313           0 :     if (!coin) {
    1314           0 :         return true;
    1315             :     }
    1316           0 :     do {
    1317           0 :         SkOpSpanBase* startBase = coin->coinPtTStartWritable()->span();
    1318           0 :         FAIL_IF(!startBase->upCastable());
    1319           0 :         SkOpSpan* start = startBase->upCast();
    1320           0 :         FAIL_IF(start->deleted());
    1321           0 :         SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
    1322           0 :         SkOPASSERT(!end->deleted());
    1323           0 :         SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
    1324           0 :         SkOPASSERT(!oStart->deleted());
    1325           0 :         SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
    1326           0 :         SkASSERT(!oEnd->deleted());
    1327           0 :         bool flipped = coin->flipped();
    1328           0 :         if (flipped) {
    1329           0 :             SkTSwap(oStart, oEnd);
    1330             :         }
    1331             :         /* coin and opp spans may not match up. Mark the ends, and then let the interior
    1332             :            get marked as many times as the spans allow */
    1333           0 :         FAIL_IF(!oStart->upCastable());
    1334           0 :         start->insertCoincidence(oStart->upCast());
    1335           0 :         end->insertCoinEnd(oEnd);
    1336           0 :         const SkOpSegment* segment = start->segment();
    1337           0 :         const SkOpSegment* oSegment = oStart->segment();
    1338           0 :         SkOpSpanBase* next = start;
    1339           0 :         SkOpSpanBase* oNext = oStart;
    1340             :         bool ordered;
    1341           0 :         FAIL_IF(!coin->ordered(&ordered));
    1342           0 :         while ((next = next->upCast()->next()) != end) {
    1343           0 :             FAIL_IF(!next->upCastable());
    1344           0 :             FAIL_IF(!next->upCast()->insertCoincidence(oSegment, flipped, ordered));
    1345             :         }
    1346           0 :         while ((oNext = oNext->upCast()->next()) != oEnd) {
    1347           0 :             FAIL_IF(!oNext->upCastable());
    1348           0 :             FAIL_IF(!oNext->upCast()->insertCoincidence(segment, flipped, ordered));
    1349             :         }
    1350             :     } while ((coin = coin->next()));
    1351           0 :     return true;
    1352             : }
    1353             : 
    1354             : // Please keep in sync with debugMarkCollapsed()
    1355           0 : void SkOpCoincidence::markCollapsed(SkCoincidentSpans* coin, SkOpPtT* test) {
    1356           0 :     SkCoincidentSpans* head = coin;
    1357           0 :     while (coin) {
    1358           0 :         if (coin->collapsed(test)) {
    1359           0 :             if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
    1360           0 :                 coin->coinPtTStartWritable()->segment()->markAllDone();
    1361             :             }
    1362           0 :             if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
    1363           0 :                 coin->oppPtTStartWritable()->segment()->markAllDone();
    1364             :             }
    1365           0 :             this->release(head, coin);
    1366             :         }
    1367           0 :         coin = coin->next();
    1368             :     }
    1369           0 : }
    1370             : 
    1371             : // Please keep in sync with debugMarkCollapsed()
    1372           0 : void SkOpCoincidence::markCollapsed(SkOpPtT* test) {
    1373           0 :     markCollapsed(fHead, test);
    1374           0 :     markCollapsed(fTop, test);
    1375           0 : }
    1376             : 
    1377           0 : bool SkOpCoincidence::Ordered(const SkOpSegment* coinSeg, const SkOpSegment* oppSeg) {
    1378           0 :     if (coinSeg->verb() < oppSeg->verb()) {
    1379           0 :         return true;
    1380             :     }
    1381           0 :     if (coinSeg->verb() > oppSeg->verb()) {
    1382           0 :         return false;
    1383             :     }
    1384           0 :     int count = (SkPathOpsVerbToPoints(coinSeg->verb()) + 1) * 2;
    1385           0 :     const SkScalar* cPt = &coinSeg->pts()[0].fX;
    1386           0 :     const SkScalar* oPt = &oppSeg->pts()[0].fX;
    1387           0 :     for (int index = 0; index < count; ++index) {
    1388           0 :         if (*cPt < *oPt) {
    1389           0 :             return true;
    1390             :         }
    1391           0 :         if (*cPt > *oPt) {
    1392           0 :             return false;
    1393             :         }
    1394           0 :         ++cPt;
    1395           0 :         ++oPt;
    1396             :     }
    1397           0 :     return true;
    1398             : }
    1399             : 
    1400           0 : bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e,
    1401             :         const SkOpPtT* coin2s, const SkOpPtT* coin2e, double* overS, double* overE) const {
    1402           0 :     SkASSERT(coin1s->segment() == coin2s->segment());
    1403           0 :     *overS = SkTMax(SkTMin(coin1s->fT, coin1e->fT), SkTMin(coin2s->fT, coin2e->fT));
    1404           0 :     *overE = SkTMin(SkTMax(coin1s->fT, coin1e->fT), SkTMax(coin2s->fT, coin2e->fT));
    1405           0 :     return *overS < *overE;
    1406             : }
    1407             : 
    1408             : // Commented-out lines keep this in sync with debugRelease()
    1409           0 : void SkOpCoincidence::release(const SkOpSegment* deleted) {
    1410           0 :     SkCoincidentSpans* coin = fHead;
    1411           0 :     if (!coin) {
    1412           0 :         return;
    1413             :     }
    1414           0 :     do {
    1415           0 :         if (coin->coinPtTStart()->segment() == deleted
    1416           0 :                 || coin->coinPtTEnd()->segment() == deleted
    1417           0 :                 || coin->oppPtTStart()->segment() == deleted
    1418           0 :                 || coin->oppPtTEnd()->segment() == deleted) {
    1419           0 :             this->release(fHead, coin);
    1420             :         }
    1421             :     } while ((coin = coin->next()));
    1422             : }

Generated by: LCOV version 1.13