xref: /aosp_15_r20/external/skia/src/pathops/SkOpSpan.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2014 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 "include/core/SkPoint.h"
8 #include "include/core/SkTypes.h"
9 #include "include/private/base/SkTemplates.h"
10 #include "src/pathops/SkOpCoincidence.h"
11 #include "src/pathops/SkOpContour.h"
12 #include "src/pathops/SkOpSegment.h"
13 #include "src/pathops/SkOpSpan.h"
14 #include "src/pathops/SkPathOpsTypes.h"
15 
16 #include <algorithm>
17 
alias() const18 bool SkOpPtT::alias() const {
19     return this->span()->ptT() != this;
20 }
21 
active() const22 const SkOpPtT* SkOpPtT::active() const {
23     if (!fDeleted) {
24         return this;
25     }
26     const SkOpPtT* ptT = this;
27     const SkOpPtT* stopPtT = ptT;
28     while ((ptT = ptT->next()) != stopPtT) {
29         if (ptT->fSpan == fSpan && !ptT->fDeleted) {
30             return ptT;
31         }
32     }
33     return nullptr; // should never return deleted; caller must abort
34 }
35 
contains(const SkOpPtT * check) const36 bool SkOpPtT::contains(const SkOpPtT* check) const {
37     SkOPASSERT(this != check);
38     const SkOpPtT* ptT = this;
39     const SkOpPtT* stopPtT = ptT;
40     while ((ptT = ptT->next()) != stopPtT) {
41         if (ptT == check) {
42             return true;
43         }
44     }
45     return false;
46 }
47 
contains(const SkOpSegment * segment,const SkPoint & pt) const48 bool SkOpPtT::contains(const SkOpSegment* segment, const SkPoint& pt) const {
49     SkASSERT(this->segment() != segment);
50     const SkOpPtT* ptT = this;
51     const SkOpPtT* stopPtT = ptT;
52     while ((ptT = ptT->next()) != stopPtT) {
53         if (ptT->fPt == pt && ptT->segment() == segment) {
54             return true;
55         }
56     }
57     return false;
58 }
59 
contains(const SkOpSegment * segment,double t) const60 bool SkOpPtT::contains(const SkOpSegment* segment, double t) const {
61     const SkOpPtT* ptT = this;
62     const SkOpPtT* stopPtT = ptT;
63     while ((ptT = ptT->next()) != stopPtT) {
64         if (ptT->fT == t && ptT->segment() == segment) {
65             return true;
66         }
67     }
68     return false;
69 }
70 
contains(const SkOpSegment * check) const71 const SkOpPtT* SkOpPtT::contains(const SkOpSegment* check) const {
72     SkASSERT(this->segment() != check);
73     const SkOpPtT* ptT = this;
74     const SkOpPtT* stopPtT = ptT;
75     while ((ptT = ptT->next()) != stopPtT) {
76         if (ptT->segment() == check && !ptT->deleted()) {
77             return ptT;
78         }
79     }
80     return nullptr;
81 }
82 
contour() const83 SkOpContour* SkOpPtT::contour() const {
84     return segment()->contour();
85 }
86 
find(const SkOpSegment * segment) const87 const SkOpPtT* SkOpPtT::find(const SkOpSegment* segment) const {
88     const SkOpPtT* ptT = this;
89     const SkOpPtT* stopPtT = ptT;
90     do {
91         if (ptT->segment() == segment && !ptT->deleted()) {
92             return ptT;
93         }
94         ptT = ptT->fNext;
95     } while (stopPtT != ptT);
96 //    SkASSERT(0);
97     return nullptr;
98 }
99 
globalState() const100 SkOpGlobalState* SkOpPtT::globalState() const {
101     return contour()->globalState();
102 }
103 
init(SkOpSpanBase * span,double t,const SkPoint & pt,bool duplicate)104 void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
105     fT = t;
106     fPt = pt;
107     fSpan = span;
108     fNext = this;
109     fDuplicatePt = duplicate;
110     fDeleted = false;
111     fCoincident = false;
112     SkDEBUGCODE(fID = span->globalState()->nextPtTID());
113 }
114 
onEnd() const115 bool SkOpPtT::onEnd() const {
116     const SkOpSpanBase* span = this->span();
117     if (span->ptT() != this) {
118         return false;
119     }
120     const SkOpSegment* segment = this->segment();
121     return span == segment->head() || span == segment->tail();
122 }
123 
ptAlreadySeen(const SkOpPtT * check) const124 bool SkOpPtT::ptAlreadySeen(const SkOpPtT* check) const {
125     while (this != check) {
126         if (this->fPt == check->fPt) {
127             return true;
128         }
129         check = check->fNext;
130     }
131     return false;
132 }
133 
prev()134 SkOpPtT* SkOpPtT::prev() {
135     SkOpPtT* result = this;
136     SkOpPtT* next = this;
137     while ((next = next->fNext) != this) {
138         result = next;
139     }
140     SkASSERT(result->fNext == this);
141     return result;
142 }
143 
segment() const144 const SkOpSegment* SkOpPtT::segment() const {
145     return span()->segment();
146 }
147 
segment()148 SkOpSegment* SkOpPtT::segment() {
149     return span()->segment();
150 }
151 
setDeleted()152 void SkOpPtT::setDeleted() {
153     SkASSERT(this->span()->debugDeleted() || this->span()->ptT() != this);
154     SkOPASSERT(!fDeleted);
155     fDeleted = true;
156 }
157 
addOpp(SkOpSpanBase * opp)158 bool SkOpSpanBase::addOpp(SkOpSpanBase* opp) {
159     SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
160     if (!oppPrev) {
161         return true;
162     }
163     FAIL_IF(!this->mergeMatches(opp));
164     this->ptT()->addOpp(opp->ptT(), oppPrev);
165     this->checkForCollapsedCoincidence();
166     return true;
167 }
168 
collapsed(double s,double e) const169 SkOpSpanBase::Collapsed SkOpSpanBase::collapsed(double s, double e) const {
170     const SkOpPtT* start = &fPtT;
171     const SkOpPtT* startNext = nullptr;
172     const SkOpPtT* walk = start;
173     double min = walk->fT;
174     double max = min;
175     const SkOpSegment* segment = this->segment();
176     int safetyNet = 100000;
177     while ((walk = walk->next()) != start) {
178         if (!--safetyNet) {
179             return Collapsed::kError;
180         }
181         if (walk == startNext) {
182             return Collapsed::kError;
183         }
184         if (walk->segment() != segment) {
185             continue;
186         }
187         min = std::min(min, walk->fT);
188         max = std::max(max, walk->fT);
189         if (between(min, s, max) && between(min, e, max)) {
190             return Collapsed::kYes;
191         }
192         startNext = start->next();
193     }
194     return Collapsed::kNo;
195 }
196 
contains(const SkOpSpanBase * span) const197 bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
198     const SkOpPtT* start = &fPtT;
199     const SkOpPtT* check = &span->fPtT;
200     SkOPASSERT(start != check);
201     const SkOpPtT* walk = start;
202     while ((walk = walk->next()) != start) {
203         if (walk == check) {
204             return true;
205         }
206     }
207     return false;
208 }
209 
contains(const SkOpSegment * segment) const210 const SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) const {
211     const SkOpPtT* start = &fPtT;
212     const SkOpPtT* walk = start;
213     while ((walk = walk->next()) != start) {
214         if (walk->deleted()) {
215             continue;
216         }
217         if (walk->segment() == segment && walk->span()->ptT() == walk) {
218             return walk;
219         }
220     }
221     return nullptr;
222 }
223 
containsCoinEnd(const SkOpSegment * segment) const224 bool SkOpSpanBase::containsCoinEnd(const SkOpSegment* segment) const {
225     SkASSERT(this->segment() != segment);
226     const SkOpSpanBase* next = this;
227     while ((next = next->fCoinEnd) != this) {
228         if (next->segment() == segment) {
229             return true;
230         }
231     }
232     return false;
233 }
234 
contour() const235 SkOpContour* SkOpSpanBase::contour() const {
236     return segment()->contour();
237 }
238 
globalState() const239 SkOpGlobalState* SkOpSpanBase::globalState() const {
240     return contour()->globalState();
241 }
242 
initBase(SkOpSegment * segment,SkOpSpan * prev,double t,const SkPoint & pt)243 void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
244     fSegment = segment;
245     fPtT.init(this, t, pt, false);
246     fCoinEnd = this;
247     fFromAngle = nullptr;
248     fPrev = prev;
249     fSpanAdds = 0;
250     fAligned = true;
251     fChased = false;
252     SkDEBUGCODE(fCount = 1);
253     SkDEBUGCODE(fID = globalState()->nextSpanID());
254     SkDEBUGCODE(fDebugDeleted = false);
255 }
256 
257 // this pair of spans share a common t value or point; merge them and eliminate duplicates
258 // this does not compute the best t or pt value; this merely moves all data into a single list
merge(SkOpSpan * span)259 void SkOpSpanBase::merge(SkOpSpan* span) {
260     SkOpPtT* spanPtT = span->ptT();
261     SkASSERT(this->t() != spanPtT->fT);
262     SkASSERT(!zero_or_one(spanPtT->fT));
263     span->release(this->ptT());
264     if (this->contains(span)) {
265         SkOPASSERT(0);  // check to see if this ever happens -- should have been found earlier
266         return;  // merge is already in the ptT loop
267     }
268     SkOpPtT* remainder = spanPtT->next();
269     this->ptT()->insert(spanPtT);
270     while (remainder != spanPtT) {
271         SkOpPtT* next = remainder->next();
272         SkOpPtT* compare = spanPtT->next();
273         while (compare != spanPtT) {
274             SkOpPtT* nextC = compare->next();
275             if (nextC->span() == remainder->span() && nextC->fT == remainder->fT) {
276                 goto tryNextRemainder;
277             }
278             compare = nextC;
279         }
280         spanPtT->insert(remainder);
281 tryNextRemainder:
282         remainder = next;
283     }
284     fSpanAdds += span->fSpanAdds;
285 }
286 
287 // please keep in sync with debugCheckForCollapsedCoincidence()
checkForCollapsedCoincidence()288 void SkOpSpanBase::checkForCollapsedCoincidence() {
289     SkOpCoincidence* coins = this->globalState()->coincidence();
290     if (coins->isEmpty()) {
291         return;
292     }
293 // the insert above may have put both ends of a coincident run in the same span
294 // for each coincident ptT in loop; see if its opposite in is also in the loop
295 // this implementation is the motivation for marking that a ptT is referenced by a coincident span
296     SkOpPtT* head = this->ptT();
297     SkOpPtT* test = head;
298     do {
299         if (!test->coincident()) {
300             continue;
301         }
302         coins->markCollapsed(test);
303     } while ((test = test->next()) != head);
304     coins->releaseDeleted();
305 }
306 
307 // please keep in sync with debugMergeMatches()
308 // Look to see if pt-t linked list contains same segment more than once
309 // if so, and if each pt-t is directly pointed to by spans in that segment,
310 // merge them
311 // keep the points, but remove spans so that the segment doesn't have 2 or more
312 // spans pointing to the same pt-t loop at different loop elements
mergeMatches(SkOpSpanBase * opp)313 bool SkOpSpanBase::mergeMatches(SkOpSpanBase* opp) {
314     SkOpPtT* test = &fPtT;
315     SkOpPtT* testNext;
316     const SkOpPtT* stop = test;
317     int safetyHatch = 1000000;
318     do {
319         if (!--safetyHatch) {
320             return false;
321         }
322         testNext = test->next();
323         if (test->deleted()) {
324             continue;
325         }
326         SkOpSpanBase* testBase = test->span();
327         SkASSERT(testBase->ptT() == test);
328         SkOpSegment* segment = test->segment();
329         if (segment->done()) {
330             continue;
331         }
332         SkOpPtT* inner = opp->ptT();
333         const SkOpPtT* innerStop = inner;
334         do {
335             if (inner->segment() != segment) {
336                 continue;
337             }
338             if (inner->deleted()) {
339                 continue;
340             }
341             SkOpSpanBase* innerBase = inner->span();
342             SkASSERT(innerBase->ptT() == inner);
343             // when the intersection is first detected, the span base is marked if there are
344             // more than one point in the intersection.
345             if (!zero_or_one(inner->fT)) {
346                 innerBase->upCast()->release(test);
347             } else {
348                 SkOPASSERT(inner->fT != test->fT);
349                 if (!zero_or_one(test->fT)) {
350                     testBase->upCast()->release(inner);
351                 } else {
352                     segment->markAllDone();  // mark segment as collapsed
353                     SkDEBUGCODE(testBase->debugSetDeleted());
354                     test->setDeleted();
355                     SkDEBUGCODE(innerBase->debugSetDeleted());
356                     inner->setDeleted();
357                 }
358             }
359 #ifdef SK_DEBUG   // assert if another undeleted entry points to segment
360             const SkOpPtT* debugInner = inner;
361             while ((debugInner = debugInner->next()) != innerStop) {
362                 if (debugInner->segment() != segment) {
363                     continue;
364                 }
365                 if (debugInner->deleted()) {
366                     continue;
367                 }
368                 SkOPASSERT(0);
369             }
370 #endif
371             break;
372         } while ((inner = inner->next()) != innerStop);
373     } while ((test = testNext) != stop);
374     this->checkForCollapsedCoincidence();
375     return true;
376 }
377 
computeWindSum()378 int SkOpSpan::computeWindSum() {
379     SkOpGlobalState* globals = this->globalState();
380     SkOpContour* contourHead = globals->contourHead();
381     int windTry = 0;
382     while (!this->sortableTop(contourHead) && ++windTry < SkOpGlobalState::kMaxWindingTries) {
383     }
384     return this->windSum();
385 }
386 
containsCoincidence(const SkOpSegment * segment) const387 bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
388     SkASSERT(this->segment() != segment);
389     const SkOpSpan* next = fCoincident;
390     do {
391         if (next->segment() == segment) {
392             return true;
393         }
394     } while ((next = next->fCoincident) != this);
395     return false;
396 }
397 
init(SkOpSegment * segment,SkOpSpan * prev,double t,const SkPoint & pt)398 void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
399     SkASSERT(t != 1);
400     initBase(segment, prev, t, pt);
401     fCoincident = this;
402     fToAngle = nullptr;
403     fWindSum = fOppSum = SK_MinS32;
404     fWindValue = 1;
405     fOppValue = 0;
406     fTopTTry = 0;
407     fChased = fDone = false;
408     segment->bumpCount();
409     fAlreadyAdded = false;
410 }
411 
412 // Please keep this in sync with debugInsertCoincidence()
insertCoincidence(const SkOpSegment * segment,bool flipped,bool ordered)413 bool SkOpSpan::insertCoincidence(const SkOpSegment* segment, bool flipped, bool ordered) {
414     if (this->containsCoincidence(segment)) {
415         return true;
416     }
417     SkOpPtT* next = &fPtT;
418     while ((next = next->next()) != &fPtT) {
419         if (next->segment() == segment) {
420             SkOpSpan* span;
421             SkOpSpanBase* base = next->span();
422             if (!ordered) {
423                 const SkOpPtT* spanEndPtT = fNext->contains(segment);
424                 FAIL_IF(!spanEndPtT);
425                 const SkOpSpanBase* spanEnd = spanEndPtT->span();
426                 const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
427                 FAIL_IF(!start->span()->upCastable());
428                 span = const_cast<SkOpSpan*>(start->span()->upCast());
429             } else if (flipped) {
430                 span = base->prev();
431                 FAIL_IF(!span);
432             } else {
433                 FAIL_IF(!base->upCastable());
434                 span = base->upCast();
435             }
436             this->insertCoincidence(span);
437             return true;
438         }
439     }
440 #if DEBUG_COINCIDENCE
441     SkASSERT(0); // FIXME? if we get here, the span is missing its opposite segment...
442 #endif
443     return true;
444 }
445 
release(const SkOpPtT * kept)446 void SkOpSpan::release(const SkOpPtT* kept) {
447     SkDEBUGCODE(fDebugDeleted = true);
448     SkOPASSERT(kept->span() != this);
449     SkASSERT(!final());
450     SkOpSpan* prev = this->prev();
451     SkASSERT(prev);
452     SkOpSpanBase* next = this->next();
453     SkASSERT(next);
454     prev->setNext(next);
455     next->setPrev(prev);
456     this->segment()->release(this);
457     SkOpCoincidence* coincidence = this->globalState()->coincidence();
458     if (coincidence) {
459         coincidence->fixUp(this->ptT(), kept);
460     }
461     this->ptT()->setDeleted();
462     SkOpPtT* stopPtT = this->ptT();
463     SkOpPtT* testPtT = stopPtT;
464     const SkOpSpanBase* keptSpan = kept->span();
465     do {
466         if (this == testPtT->span()) {
467             testPtT->setSpan(keptSpan);
468         }
469     } while ((testPtT = testPtT->next()) != stopPtT);
470 }
471 
setOppSum(int oppSum)472 void SkOpSpan::setOppSum(int oppSum) {
473     SkASSERT(!final());
474     if (fOppSum != SK_MinS32 && fOppSum != oppSum) {
475         this->globalState()->setWindingFailed();
476         return;
477     }
478     SkASSERT(!DEBUG_LIMIT_WIND_SUM || SkTAbs(oppSum) <= DEBUG_LIMIT_WIND_SUM);
479     fOppSum = oppSum;
480 }
481 
setWindSum(int windSum)482 void SkOpSpan::setWindSum(int windSum) {
483     SkASSERT(!final());
484     if (fWindSum != SK_MinS32 && fWindSum != windSum) {
485         this->globalState()->setWindingFailed();
486         return;
487     }
488     SkASSERT(!DEBUG_LIMIT_WIND_SUM || SkTAbs(windSum) <= DEBUG_LIMIT_WIND_SUM);
489     fWindSum = windSum;
490 }
491