blob: e51a53bdc92f52cc6870c82ddc4c7ef3bb83e3b7 [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2 * Copyright 2012 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 */
Hal Canary50dbc092018-06-12 14:50:37 -04007
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/private/SkMacros.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#include "src/pathops/SkAddIntersections.h"
10#include "src/pathops/SkOpCoincidence.h"
11#include "src/pathops/SkOpEdgeBuilder.h"
12#include "src/pathops/SkPathOpsCommon.h"
13#include "src/pathops/SkPathWriter.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000014
caryclarkbca19f72015-05-13 08:23:48 -070015const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
16 bool* sortablePtr) {
17 // find first angle, initialize winding to computed fWindSum
18 SkOpSegment* segment = start->segment();
19 const SkOpAngle* angle = segment->spanToAngle(start, end);
20 if (!angle) {
21 *windingPtr = SK_MinS32;
halcanary96fcdcc2015-08-27 07:41:13 -070022 return nullptr;
caryclarkbca19f72015-05-13 08:23:48 -070023 }
24 bool computeWinding = false;
25 const SkOpAngle* firstAngle = angle;
26 bool loop = false;
27 bool unorderable = false;
28 int winding = SK_MinS32;
29 do {
30 angle = angle->next();
caryclarkaa7ceb62016-06-29 10:46:08 -070031 if (!angle) {
32 return nullptr;
33 }
caryclarkbca19f72015-05-13 08:23:48 -070034 unorderable |= angle->unorderable();
35 if ((computeWinding = unorderable || (angle == firstAngle && loop))) {
36 break; // if we get here, there's no winding, loop is unorderable
37 }
38 loop |= angle == firstAngle;
39 segment = angle->segment();
40 winding = segment->windSum(angle);
41 } while (winding == SK_MinS32);
42 // if the angle loop contains an unorderable span, the angle order may be useless
43 // directly compute the winding in this case for each span
44 if (computeWinding) {
45 firstAngle = angle;
46 winding = SK_MinS32;
47 do {
48 SkOpSpanBase* startSpan = angle->start();
49 SkOpSpanBase* endSpan = angle->end();
50 SkOpSpan* lesser = startSpan->starter(endSpan);
51 int testWinding = lesser->windSum();
52 if (testWinding == SK_MinS32) {
53 testWinding = lesser->computeWindSum();
54 }
55 if (testWinding != SK_MinS32) {
56 segment = angle->segment();
57 winding = testWinding;
58 }
59 angle = angle->next();
60 } while (angle != firstAngle);
61 }
62 *sortablePtr = !unorderable;
63 *windingPtr = winding;
64 return angle;
65}
66
Cary Clarkab2d73b2016-12-16 17:17:25 -050067SkOpSpan* FindUndone(SkOpContourHead* contourHead) {
68 SkOpContour* contour = contourHead;
caryclark624637c2015-05-11 07:21:27 -070069 do {
Cary Clarkab2d73b2016-12-16 17:17:25 -050070 if (contour->done()) {
71 continue;
72 }
73 SkOpSpan* result = contour->undoneSpan();
caryclark@google.com07393ca2013-04-08 11:47:37 +000074 if (result) {
75 return result;
76 }
caryclark624637c2015-05-11 07:21:27 -070077 } while ((contour = contour->next()));
halcanary96fcdcc2015-08-27 07:41:13 -070078 return nullptr;
caryclark@google.com07393ca2013-04-08 11:47:37 +000079}
80
caryclark54359292015-03-26 07:52:43 -070081SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
82 SkOpSpanBase** endPtr) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000083 while (chase->count()) {
caryclark54359292015-03-26 07:52:43 -070084 SkOpSpanBase* span;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000085 chase->pop(&span);
caryclark54359292015-03-26 07:52:43 -070086 SkOpSegment* segment = span->segment();
87 *startPtr = span->ptT()->next()->span();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000088 bool done = true;
halcanary96fcdcc2015-08-27 07:41:13 -070089 *endPtr = nullptr;
caryclarkbca19f72015-05-13 08:23:48 -070090 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
caryclark54359292015-03-26 07:52:43 -070091 *startPtr = last->start();
92 *endPtr = last->end();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000093 #if TRY_ROTATE
94 *chase->insert(0) = span;
95 #else
96 *chase->append() = span;
97 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +000098 return last->segment();
99 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000100 if (done) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000101 continue;
102 }
caryclark65f55312014-11-13 06:58:52 -0800103 // find first angle, initialize winding to computed wind sum
caryclarkbca19f72015-05-13 08:23:48 -0700104 int winding;
105 bool sortable;
106 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
caryclark8ccc0752016-08-17 06:14:06 -0700107 if (!angle) {
108 return nullptr;
109 }
caryclark54359292015-03-26 07:52:43 -0700110 if (winding == SK_MinS32) {
111 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000112 }
caryclarkbca19f72015-05-13 08:23:48 -0700113 int sumWinding SK_INIT_TO_AVOID_WARNING;
114 if (sortable) {
115 segment = angle->segment();
116 sumWinding = segment->updateWindingReverse(angle);
117 }
halcanary96fcdcc2015-08-27 07:41:13 -0700118 SkOpSegment* first = nullptr;
caryclarkbca19f72015-05-13 08:23:48 -0700119 const SkOpAngle* firstAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000120 while ((angle = angle->next()) != firstAngle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000121 segment = angle->segment();
caryclark54359292015-03-26 07:52:43 -0700122 SkOpSpanBase* start = angle->start();
123 SkOpSpanBase* end = angle->end();
djsollen2f124632016-04-29 13:53:05 -0700124 int maxWinding SK_INIT_TO_AVOID_WARNING;
caryclarkbca19f72015-05-13 08:23:48 -0700125 if (sortable) {
126 segment->setUpWinding(start, end, &maxWinding, &sumWinding);
127 }
caryclark54359292015-03-26 07:52:43 -0700128 if (!segment->done(angle)) {
caryclarkbca19f72015-05-13 08:23:48 -0700129 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
caryclark54359292015-03-26 07:52:43 -0700130 first = segment;
131 *startPtr = start;
132 *endPtr = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000133 }
caryclark54359292015-03-26 07:52:43 -0700134 // OPTIMIZATION: should this also add to the chase?
caryclarkbca19f72015-05-13 08:23:48 -0700135 if (sortable) {
Cary Clark2587f412018-07-24 12:40:10 -0400136 // TODO: add error handling
137 SkAssertResult(segment->markAngle(maxWinding, sumWinding, angle, nullptr));
caryclarkbca19f72015-05-13 08:23:48 -0700138 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000139 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000140 }
caryclark54359292015-03-26 07:52:43 -0700141 if (first) {
142 #if TRY_ROTATE
143 *chase->insert(0) = span;
144 #else
145 *chase->append() = span;
146 #endif
147 return first;
148 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000149 }
halcanary96fcdcc2015-08-27 07:41:13 -0700150 return nullptr;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000151}
152
caryclark624637c2015-05-11 07:21:27 -0700153bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) {
154 SkTDArray<SkOpContour* > list;
155 SkOpContour* contour = *contourList;
caryclark54359292015-03-26 07:52:43 -0700156 do {
157 if (contour->count()) {
158 contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
159 *list.append() = contour;
160 }
161 } while ((contour = contour->next()));
caryclark624637c2015-05-11 07:21:27 -0700162 int count = list.count();
163 if (!count) {
164 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000165 }
caryclark624637c2015-05-11 07:21:27 -0700166 if (count > 1) {
John Stiles70474c12020-07-13 18:09:07 -0400167 std::sort(list.begin(), list.end(),
168 [](const SkOpContour* a, const SkOpContour* b) { return *a < *b; });
caryclark624637c2015-05-11 07:21:27 -0700169 }
170 contour = list[0];
171 SkOpContourHead* contourHead = static_cast<SkOpContourHead*>(contour);
172 contour->globalState()->setContourHead(contourHead);
173 *contourList = contourHead;
174 for (int index = 1; index < count; ++index) {
175 SkOpContour* next = list[index];
176 contour->setNext(next);
177 contour = next;
178 }
halcanary96fcdcc2015-08-27 07:41:13 -0700179 contour->setNext(nullptr);
caryclark624637c2015-05-11 07:21:27 -0700180 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000181}
182
Cary Clarkab87d7a2016-10-04 10:01:04 -0400183static void calc_angles(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS()) {
184 DEBUG_STATIC_SET_PHASE(contourList);
caryclark624637c2015-05-11 07:21:27 -0700185 SkOpContour* contour = contourList;
186 do {
caryclark55888e42016-07-18 10:01:36 -0700187 contour->calcAngles();
caryclark624637c2015-05-11 07:21:27 -0700188 } while ((contour = contour->next()));
caryclark54359292015-03-26 07:52:43 -0700189}
190
Cary Clarkab87d7a2016-10-04 10:01:04 -0400191static bool missing_coincidence(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS()) {
192 DEBUG_STATIC_SET_PHASE(contourList);
caryclark624637c2015-05-11 07:21:27 -0700193 SkOpContour* contour = contourList;
caryclark27c8eb82015-07-06 11:38:33 -0700194 bool result = false;
caryclark624637c2015-05-11 07:21:27 -0700195 do {
caryclark55888e42016-07-18 10:01:36 -0700196 result |= contour->missingCoincidence();
caryclark624637c2015-05-11 07:21:27 -0700197 } while ((contour = contour->next()));
caryclark27c8eb82015-07-06 11:38:33 -0700198 return result;
caryclark54359292015-03-26 07:52:43 -0700199}
200
Cary Clarkab87d7a2016-10-04 10:01:04 -0400201static bool move_multiples(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS()) {
202 DEBUG_STATIC_SET_PHASE(contourList);
caryclark624637c2015-05-11 07:21:27 -0700203 SkOpContour* contour = contourList;
204 do {
caryclarkd78c0882016-02-24 09:03:07 -0800205 if (!contour->moveMultiples()) {
206 return false;
207 }
caryclark624637c2015-05-11 07:21:27 -0700208 } while ((contour = contour->next()));
caryclarkd78c0882016-02-24 09:03:07 -0800209 return true;
caryclark08bc8482015-04-24 09:08:57 -0700210}
211
Cary Clark28da2832017-03-21 10:30:50 -0400212static bool move_nearby(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400213 DEBUG_STATIC_SET_PHASE(contourList);
caryclark624637c2015-05-11 07:21:27 -0700214 SkOpContour* contour = contourList;
215 do {
Cary Clark28da2832017-03-21 10:30:50 -0400216 if (!contour->moveNearby()) {
217 return false;
218 }
caryclark624637c2015-05-11 07:21:27 -0700219 } while ((contour = contour->next()));
Cary Clark28da2832017-03-21 10:30:50 -0400220 return true;
caryclark54359292015-03-26 07:52:43 -0700221}
222
caryclarkb36a3cd2016-10-18 07:59:44 -0700223static bool sort_angles(SkOpContourHead* contourList) {
caryclark624637c2015-05-11 07:21:27 -0700224 SkOpContour* contour = contourList;
225 do {
caryclarkb36a3cd2016-10-18 07:59:44 -0700226 if (!contour->sortAngles()) {
227 return false;
228 }
caryclark624637c2015-05-11 07:21:27 -0700229 } while ((contour = contour->next()));
caryclarkb36a3cd2016-10-18 07:59:44 -0700230 return true;
caryclark54359292015-03-26 07:52:43 -0700231}
232
caryclark55888e42016-07-18 10:01:36 -0700233bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence) {
caryclark624637c2015-05-11 07:21:27 -0700234 SkOpGlobalState* globalState = contourList->globalState();
caryclark55888e42016-07-18 10:01:36 -0700235 // match up points within the coincident runs
Cary Clarkab87d7a2016-10-04 10:01:04 -0400236 if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kIntersecting))) {
caryclark55888e42016-07-18 10:01:36 -0700237 return false;
238 }
caryclark55888e42016-07-18 10:01:36 -0700239 // combine t values when multiple intersections occur on some segments but not others
Cary Clarkab87d7a2016-10-04 10:01:04 -0400240 if (!move_multiples(contourList DEBUG_PHASE_PARAMS(kWalking))) {
caryclarkd78c0882016-02-24 09:03:07 -0800241 return false;
242 }
caryclark54359292015-03-26 07:52:43 -0700243 // move t values and points together to eliminate small/tiny gaps
Cary Clark28da2832017-03-21 10:30:50 -0400244 if (!move_nearby(contourList DEBUG_COIN_PARAMS())) {
245 return false;
246 }
caryclark55888e42016-07-18 10:01:36 -0700247 // add coincidence formed by pairing on curve points and endpoints
Cary Clarkab87d7a2016-10-04 10:01:04 -0400248 coincidence->correctEnds(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
249 if (!coincidence->addEndMovedSpans(DEBUG_COIN_ONLY_PARAMS())) {
caryclark55888e42016-07-18 10:01:36 -0700250 return false;
251 }
Cary Clarke47ae292016-10-05 08:51:39 -0400252 const int SAFETY_COUNT = 3;
caryclark55888e42016-07-18 10:01:36 -0700253 int safetyHatch = SAFETY_COUNT;
254 // look for coincidence present in A-B and A-C but missing in B-C
caryclark81a478c2016-09-09 09:37:57 -0700255 do {
256 bool added;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400257 if (!coincidence->addMissing(&added DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
caryclark81a478c2016-09-09 09:37:57 -0700258 return false;
259 }
260 if (!added) {
261 break;
262 }
caryclark55888e42016-07-18 10:01:36 -0700263 if (!--safetyHatch) {
caryclark30b9fdd2016-08-31 14:36:29 -0700264 SkASSERT(globalState->debugSkipAssert());
caryclark3f0753d2016-06-28 09:23:57 -0700265 return false;
266 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400267 move_nearby(contourList DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch - 1));
caryclark81a478c2016-09-09 09:37:57 -0700268 } while (true);
caryclark55888e42016-07-18 10:01:36 -0700269 // check to see if, loosely, coincident ranges may be expanded
Cary Clarkab87d7a2016-10-04 10:01:04 -0400270 if (coincidence->expand(DEBUG_COIN_ONLY_PARAMS())) {
caryclark81a478c2016-09-09 09:37:57 -0700271 bool added;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400272 if (!coincidence->addMissing(&added DEBUG_COIN_PARAMS())) {
caryclark81a478c2016-09-09 09:37:57 -0700273 return false;
274 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400275 if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
caryclark55888e42016-07-18 10:01:36 -0700276 return false;
277 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400278 if (!move_multiples(contourList DEBUG_COIN_PARAMS())) {
caryclark55888e42016-07-18 10:01:36 -0700279 return false;
280 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400281 move_nearby(contourList DEBUG_COIN_PARAMS());
caryclark26ad22a2015-10-16 09:03:38 -0700282 }
caryclark27c8eb82015-07-06 11:38:33 -0700283 // the expanded ranges may not align -- add the missing spans
Cary Clarkab87d7a2016-10-04 10:01:04 -0400284 if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
caryclark8bc90e22016-07-25 06:05:08 -0700285 return false;
286 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400287 // mark spans of coincident segments as coincident
Cary Clarke47ae292016-10-05 08:51:39 -0400288 coincidence->mark(DEBUG_COIN_ONLY_PARAMS());
caryclark55888e42016-07-18 10:01:36 -0700289 // look for coincidence lines and curves undetected by intersection
Cary Clarkab87d7a2016-10-04 10:01:04 -0400290 if (missing_coincidence(contourList DEBUG_COIN_PARAMS())) {
291 (void) coincidence->expand(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
292 if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
caryclark26ad22a2015-10-16 09:03:38 -0700293 return false;
294 }
caryclarke6522ea2016-10-17 07:54:33 -0700295 if (!coincidence->mark(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
296 return false;
297 }
caryclark55888e42016-07-18 10:01:36 -0700298 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400299 (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
caryclark27c8eb82015-07-06 11:38:33 -0700300 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400301 (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
caryclark55888e42016-07-18 10:01:36 -0700302
caryclark55888e42016-07-18 10:01:36 -0700303 SkOpCoincidence overlaps(globalState);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400304 safetyHatch = SAFETY_COUNT;
caryclark27c8eb82015-07-06 11:38:33 -0700305 do {
306 SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400307 // adjust the winding value to account for coincident edges
caryclarka35ab3e2016-10-20 08:32:18 -0700308 if (!pairs->apply(DEBUG_ITER_ONLY_PARAMS(SAFETY_COUNT - safetyHatch))) {
309 return false;
Ben Wagner63fd7602017-10-09 15:45:33 -0400310 }
caryclark27c8eb82015-07-06 11:38:33 -0700311 // For each coincident pair that overlaps another, when the receivers (the 1st of the pair)
312 // are different, construct a new pair to resolve their mutual span
Cary Clark40f23782016-10-06 12:04:16 -0400313 if (!pairs->findOverlaps(&overlaps DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
314 return false;
315 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400316 if (!--safetyHatch) {
317 SkASSERT(globalState->debugSkipAssert());
318 return false;
319 }
caryclark27c8eb82015-07-06 11:38:33 -0700320 } while (!overlaps.isEmpty());
Cary Clarkab87d7a2016-10-04 10:01:04 -0400321 calc_angles(contourList DEBUG_COIN_PARAMS());
caryclarkb36a3cd2016-10-18 07:59:44 -0700322 if (!sort_angles(contourList)) {
323 return false;
324 }
caryclark55888e42016-07-18 10:01:36 -0700325#if DEBUG_COINCIDENCE_VERBOSE
caryclark624637c2015-05-11 07:21:27 -0700326 coincidence->debugShowCoincidence();
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000327#endif
caryclark55888e42016-07-18 10:01:36 -0700328#if DEBUG_COINCIDENCE
329 coincidence->debugValidate();
330#endif
331 SkPathOpsDebug::ShowActiveSpans(contourList);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000332 return true;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000333}