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