blob: 32d2376a0f3c60e0b65e8f965f64993a684d1ef3 [file] [log] [blame]
caryclark54359292015-03-26 07:52:43 -07001/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "SkOpCoincidence.h"
8#include "SkOpContour.h"
9#include "SkOpSegment.h"
10#include "SkPathWriter.h"
11
12bool SkOpPtT::alias() const {
13 return this->span()->ptT() != this;
14}
15
16SkOpContour* SkOpPtT::contour() const {
17 return segment()->contour();
18}
19
20SkOpGlobalState* SkOpPtT::globalState() const {
21 return contour()->globalState();
22}
23
24void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
25 fT = t;
26 fPt = pt;
27 fSpan = span;
28 fNext = this;
29 fDuplicatePt = duplicate;
30 fDeleted = false;
caryclark1049f122015-04-20 08:31:59 -070031 SkDEBUGCODE(fID = span->globalState()->nextPtTID());
caryclark54359292015-03-26 07:52:43 -070032}
33
34bool SkOpPtT::onEnd() const {
35 const SkOpSpanBase* span = this->span();
36 if (span->ptT() != this) {
37 return false;
38 }
39 const SkOpSegment* segment = this->segment();
40 return span == segment->head() || span == segment->tail();
41}
42
43SkOpPtT* SkOpPtT::prev() {
44 SkOpPtT* result = this;
45 SkOpPtT* next = this;
46 while ((next = next->fNext) != this) {
47 result = next;
48 }
49 SkASSERT(result->fNext == this);
50 return result;
51}
52
53SkOpPtT* SkOpPtT::remove() {
54 SkOpPtT* prev = this;
55 do {
56 SkOpPtT* next = prev->fNext;
57 if (next == this) {
58 prev->removeNext(this);
59 SkASSERT(prev->fNext != prev);
60 fDeleted = true;
61 return prev;
62 }
63 prev = next;
64 } while (prev != this);
65 SkASSERT(0);
66 return NULL;
67}
68
69void SkOpPtT::removeNext(SkOpPtT* kept) {
70 SkASSERT(this->fNext);
71 SkOpPtT* next = this->fNext;
72 SkASSERT(this != next->fNext);
73 this->fNext = next->fNext;
74 SkOpSpanBase* span = next->span();
75 next->setDeleted();
76 if (span->ptT() == next) {
77 span->upCast()->detach(kept);
78 }
79}
80
81const SkOpSegment* SkOpPtT::segment() const {
82 return span()->segment();
83}
84
85SkOpSegment* SkOpPtT::segment() {
86 return span()->segment();
87}
88
89// find the starting or ending span with an existing loop of angles
90// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
91// FIXME? assert that only one other span has a valid windValue or oppValue
caryclark1049f122015-04-20 08:31:59 -070092bool SkOpSpanBase::addSimpleAngle(bool checkFrom, SkChunkAlloc* allocator) {
caryclark54359292015-03-26 07:52:43 -070093 SkOpAngle* angle;
94 if (checkFrom) {
caryclark1049f122015-04-20 08:31:59 -070095 if (!this->final()) {
96 return false;
97 }
caryclark54359292015-03-26 07:52:43 -070098 if (this->fromAngle()) {
99 SkASSERT(this->fromAngle()->loopCount() == 2);
caryclark1049f122015-04-20 08:31:59 -0700100 return true;
caryclark54359292015-03-26 07:52:43 -0700101 }
102 angle = this->segment()->addEndSpan(allocator);
103 } else {
104 SkASSERT(this->t() == 0);
105 SkOpSpan* span = this->upCast();
106 if (span->toAngle()) {
107 SkASSERT(span->toAngle()->loopCount() == 2);
108 SkASSERT(!span->fromAngle());
109 span->setFromAngle(span->toAngle()->next());
caryclark1049f122015-04-20 08:31:59 -0700110 return true;
caryclark54359292015-03-26 07:52:43 -0700111 }
112 angle = this->segment()->addStartSpan(allocator);
113 }
114 SkOpPtT* ptT = this->ptT();
115 SkOpSpanBase* oSpanBase;
116 SkOpSpan* oSpan;
117 SkOpSegment* other;
118 do {
119 ptT = ptT->next();
120 oSpanBase = ptT->span();
121 oSpan = oSpanBase->upCastable();
122 other = oSpanBase->segment();
123 if (oSpan && oSpan->windValue()) {
124 break;
125 }
126 if (oSpanBase->t() == 0) {
127 continue;
128 }
129 SkOpSpan* oFromSpan = oSpanBase->prev();
130 SkASSERT(oFromSpan->t() < 1);
131 if (oFromSpan->windValue()) {
132 break;
133 }
134 } while (ptT != this->ptT());
135 SkOpAngle* oAngle;
136 if (checkFrom) {
137 oAngle = other->addStartSpan(allocator);
138 SkASSERT(oSpan && !oSpan->final());
139 SkASSERT(oAngle == oSpan->toAngle());
140 } else {
141 oAngle = other->addEndSpan(allocator);
142 SkASSERT(oAngle == oSpanBase->fromAngle());
143 }
144 angle->insert(oAngle);
caryclark1049f122015-04-20 08:31:59 -0700145 return true;
caryclark54359292015-03-26 07:52:43 -0700146}
147
148void SkOpSpanBase::align() {
149 if (this->fAligned) {
150 return;
151 }
152 SkASSERT(!zero_or_one(this->fPtT.fT));
153 SkASSERT(this->fPtT.next());
154 // if a linked pt/t pair has a t of zero or one, use it as the base for alignment
155 SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
156 while ((ptT = ptT->next()) != stopPtT) {
157 if (zero_or_one(ptT->fT)) {
158 SkOpSegment* segment = ptT->segment();
159 SkASSERT(this->segment() != segment);
160 SkASSERT(segment->head()->ptT() == ptT || segment->tail()->ptT() == ptT);
161 if (ptT->fT) {
162 segment->tail()->alignEnd(1, segment->lastPt());
163 } else {
164 segment->head()->alignEnd(0, segment->pts()[0]);
165 }
166 return;
167 }
168 }
169 alignInner();
170 this->fAligned = true;
171}
172
173
174// FIXME: delete spans that collapse
175// delete segments that collapse
176// delete contours that collapse
177void SkOpSpanBase::alignEnd(double t, const SkPoint& pt) {
178 SkASSERT(zero_or_one(t));
179 SkOpSegment* segment = this->segment();
180 SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
181 alignInner();
182 *segment->writablePt(!!t) = pt;
183 SkOpPtT* ptT = &this->fPtT;
184 SkASSERT(t == ptT->fT);
185 SkASSERT(pt == ptT->fPt);
186 SkOpPtT* test = ptT, * stopPtT = ptT;
187 while ((test = test->next()) != stopPtT) {
188 SkOpSegment* other = test->segment();
189 if (other == this->segment()) {
190 continue;
191 }
192 if (!zero_or_one(test->fT)) {
193 continue;
194 }
195 *other->writablePt(!!test->fT) = pt;
196 }
197 this->fAligned = true;
198}
199
200void SkOpSpanBase::alignInner() {
201 // force the spans to share points and t values
202 SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
203 const SkPoint& pt = ptT->fPt;
204 do {
205 ptT->fPt = pt;
206 const SkOpSpanBase* span = ptT->span();
207 SkOpPtT* test = ptT;
208 do {
209 SkOpPtT* prev = test;
210 if ((test = test->next()) == stopPtT) {
211 break;
212 }
213 if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) {
214 // omit aliases that alignment makes redundant
215 if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
216 SkASSERT(test->alias());
217 prev->removeNext(ptT);
218 test = prev;
219 } else {
220 SkASSERT(ptT->alias());
221 stopPtT = ptT = ptT->remove();
222 break;
223 }
224 }
225 } while (true);
226 } while ((ptT = ptT->next()) != stopPtT);
227}
228
229bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
230 const SkOpPtT* start = &fPtT;
231 const SkOpPtT* check = &span->fPtT;
232 SkASSERT(start != check);
233 const SkOpPtT* walk = start;
234 while ((walk = walk->next()) != start) {
235 if (walk == check) {
236 return true;
237 }
238 }
239 return false;
240}
241
242SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) {
243 SkOpPtT* start = &fPtT;
244 SkOpPtT* walk = start;
245 while ((walk = walk->next()) != start) {
246 if (walk->segment() == segment) {
247 return walk;
248 }
249 }
250 return NULL;
251}
252
253bool SkOpSpanBase::containsCoinEnd(const SkOpSegment* segment) const {
254 SkASSERT(this->segment() != segment);
255 const SkOpSpanBase* next = this;
256 while ((next = next->fCoinEnd) != this) {
257 if (next->segment() == segment) {
258 return true;
259 }
260 }
261 return false;
262}
263
264SkOpContour* SkOpSpanBase::contour() const {
265 return segment()->contour();
266}
267
268SkOpGlobalState* SkOpSpanBase::globalState() const {
269 return contour()->globalState();
270}
271
272void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
273 fSegment = segment;
274 fPtT.init(this, t, pt, false);
275 fCoinEnd = this;
276 fFromAngle = NULL;
277 fPrev = prev;
278 fAligned = true;
279 fChased = false;
caryclark1049f122015-04-20 08:31:59 -0700280 SkDEBUGCODE(fCount = 1);
281 SkDEBUGCODE(fID = globalState()->nextSpanID());
caryclark54359292015-03-26 07:52:43 -0700282}
283
284// this pair of spans share a common t value or point; merge them and eliminate duplicates
285// this does not compute the best t or pt value; this merely moves all data into a single list
286void SkOpSpanBase::merge(SkOpSpan* span) {
287 SkOpPtT* spanPtT = span->ptT();
288 SkASSERT(this->t() != spanPtT->fT);
289 SkASSERT(!zero_or_one(spanPtT->fT));
290 span->detach(this->ptT());
291 SkOpPtT* remainder = spanPtT->next();
292 ptT()->insert(spanPtT);
293 while (remainder != spanPtT) {
294 SkOpPtT* next = remainder->next();
295 SkOpPtT* compare = spanPtT->next();
296 while (compare != spanPtT) {
297 SkOpPtT* nextC = compare->next();
298 if (nextC->span() == remainder->span() && nextC->fT == remainder->fT) {
299 goto tryNextRemainder;
300 }
301 compare = nextC;
302 }
303 spanPtT->insert(remainder);
304tryNextRemainder:
305 remainder = next;
306 }
307}
308
309void SkOpSpan::applyCoincidence(SkOpSpan* opp) {
310 SkASSERT(!final());
311 SkASSERT(0); // incomplete
312}
313
314bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
315 SkASSERT(this->segment() != segment);
316 const SkOpSpan* next = fCoincident;
317 do {
318 if (next->segment() == segment) {
319 return true;
320 }
321 } while ((next = next->fCoincident) != this);
322 return false;
323}
324
325void SkOpSpan::detach(SkOpPtT* kept) {
326 SkASSERT(!final());
327 SkOpSpan* prev = this->prev();
328 SkASSERT(prev);
329 SkOpSpanBase* next = this->next();
330 SkASSERT(next);
331 prev->setNext(next);
332 next->setPrev(prev);
333 this->segment()->detach(this);
334 this->globalState()->coincidence()->fixUp(this->ptT(), kept);
335 this->ptT()->setDeleted();
336}
337
338void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
339 SkASSERT(t != 1);
340 initBase(segment, prev, t, pt);
341 fCoincident = this;
342 fToAngle = NULL;
343 fWindSum = fOppSum = SK_MinS32;
344 fWindValue = 1;
345 fOppValue = 0;
346 fChased = fDone = false;
347 segment->bumpCount();
348}
349
350void SkOpSpan::setOppSum(int oppSum) {
351 SkASSERT(!final());
352 if (fOppSum != SK_MinS32 && fOppSum != oppSum) {
353 this->globalState()->setWindingFailed();
354 return;
355 }
356 SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(oppSum) <= DEBUG_LIMIT_WIND_SUM);
357 fOppSum = oppSum;
358}