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