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