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