blob: c1b134fd751160956de915a74a781ecdb2d5de47 [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
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00008#include "SkAddIntersections.h"
caryclark54359292015-03-26 07:52:43 -07009#include "SkOpCoincidence.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000010#include "SkOpEdgeBuilder.h"
Hal Canary50dbc092018-06-12 14:50:37 -040011#include "SkMacros.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000012#include "SkPathOpsCommon.h"
13#include "SkPathWriter.h"
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +000014#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000015
caryclark55888e42016-07-18 10:01:36 -070016SkScalar ScaleFactor(const SkPath& path) {
17 static const SkScalar twoTo10 = 1024.f;
18 SkScalar largest = 0;
19 const SkScalar* oneBounds = &path.getBounds().fLeft;
20 for (int index = 0; index < 4; ++index) {
21 largest = SkTMax(largest, SkScalarAbs(oneBounds[index]));
22 }
23 SkScalar scale = twoTo10;
24 SkScalar next;
25 while ((next = scale * twoTo10) < largest) {
26 scale = next;
27 }
28 return scale == twoTo10 ? SK_Scalar1 : scale;
29}
30
31void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled) {
32 SkMatrix matrix;
33 matrix.setScale(scale, scale);
34 *scaled = path;
35 scaled->transform(matrix);
36}
37
caryclarkbca19f72015-05-13 08:23:48 -070038const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
39 bool* sortablePtr) {
40 // find first angle, initialize winding to computed fWindSum
41 SkOpSegment* segment = start->segment();
42 const SkOpAngle* angle = segment->spanToAngle(start, end);
43 if (!angle) {
44 *windingPtr = SK_MinS32;
halcanary96fcdcc2015-08-27 07:41:13 -070045 return nullptr;
caryclarkbca19f72015-05-13 08:23:48 -070046 }
47 bool computeWinding = false;
48 const SkOpAngle* firstAngle = angle;
49 bool loop = false;
50 bool unorderable = false;
51 int winding = SK_MinS32;
52 do {
53 angle = angle->next();
caryclarkaa7ceb62016-06-29 10:46:08 -070054 if (!angle) {
55 return nullptr;
56 }
caryclarkbca19f72015-05-13 08:23:48 -070057 unorderable |= angle->unorderable();
58 if ((computeWinding = unorderable || (angle == firstAngle && loop))) {
59 break; // if we get here, there's no winding, loop is unorderable
60 }
61 loop |= angle == firstAngle;
62 segment = angle->segment();
63 winding = segment->windSum(angle);
64 } while (winding == SK_MinS32);
65 // if the angle loop contains an unorderable span, the angle order may be useless
66 // directly compute the winding in this case for each span
67 if (computeWinding) {
68 firstAngle = angle;
69 winding = SK_MinS32;
70 do {
71 SkOpSpanBase* startSpan = angle->start();
72 SkOpSpanBase* endSpan = angle->end();
73 SkOpSpan* lesser = startSpan->starter(endSpan);
74 int testWinding = lesser->windSum();
75 if (testWinding == SK_MinS32) {
76 testWinding = lesser->computeWindSum();
77 }
78 if (testWinding != SK_MinS32) {
79 segment = angle->segment();
80 winding = testWinding;
81 }
82 angle = angle->next();
83 } while (angle != firstAngle);
84 }
85 *sortablePtr = !unorderable;
86 *windingPtr = winding;
87 return angle;
88}
89
Cary Clarkab2d73b2016-12-16 17:17:25 -050090SkOpSpan* FindUndone(SkOpContourHead* contourHead) {
91 SkOpContour* contour = contourHead;
caryclark624637c2015-05-11 07:21:27 -070092 do {
Cary Clarkab2d73b2016-12-16 17:17:25 -050093 if (contour->done()) {
94 continue;
95 }
96 SkOpSpan* result = contour->undoneSpan();
caryclark@google.com07393ca2013-04-08 11:47:37 +000097 if (result) {
98 return result;
99 }
caryclark624637c2015-05-11 07:21:27 -0700100 } while ((contour = contour->next()));
halcanary96fcdcc2015-08-27 07:41:13 -0700101 return nullptr;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000102}
103
caryclark54359292015-03-26 07:52:43 -0700104SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
105 SkOpSpanBase** endPtr) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000106 while (chase->count()) {
caryclark54359292015-03-26 07:52:43 -0700107 SkOpSpanBase* span;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000108 chase->pop(&span);
caryclark54359292015-03-26 07:52:43 -0700109 SkOpSegment* segment = span->segment();
110 *startPtr = span->ptT()->next()->span();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000111 bool done = true;
halcanary96fcdcc2015-08-27 07:41:13 -0700112 *endPtr = nullptr;
caryclarkbca19f72015-05-13 08:23:48 -0700113 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
caryclark54359292015-03-26 07:52:43 -0700114 *startPtr = last->start();
115 *endPtr = last->end();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000116 #if TRY_ROTATE
117 *chase->insert(0) = span;
118 #else
119 *chase->append() = span;
120 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000121 return last->segment();
122 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000123 if (done) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000124 continue;
125 }
caryclark65f55312014-11-13 06:58:52 -0800126 // find first angle, initialize winding to computed wind sum
caryclarkbca19f72015-05-13 08:23:48 -0700127 int winding;
128 bool sortable;
129 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
caryclark8ccc0752016-08-17 06:14:06 -0700130 if (!angle) {
131 return nullptr;
132 }
caryclark54359292015-03-26 07:52:43 -0700133 if (winding == SK_MinS32) {
134 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000135 }
caryclarkbca19f72015-05-13 08:23:48 -0700136 int sumWinding SK_INIT_TO_AVOID_WARNING;
137 if (sortable) {
138 segment = angle->segment();
139 sumWinding = segment->updateWindingReverse(angle);
140 }
halcanary96fcdcc2015-08-27 07:41:13 -0700141 SkOpSegment* first = nullptr;
caryclarkbca19f72015-05-13 08:23:48 -0700142 const SkOpAngle* firstAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000143 while ((angle = angle->next()) != firstAngle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000144 segment = angle->segment();
caryclark54359292015-03-26 07:52:43 -0700145 SkOpSpanBase* start = angle->start();
146 SkOpSpanBase* end = angle->end();
djsollen2f124632016-04-29 13:53:05 -0700147 int maxWinding SK_INIT_TO_AVOID_WARNING;
caryclarkbca19f72015-05-13 08:23:48 -0700148 if (sortable) {
149 segment->setUpWinding(start, end, &maxWinding, &sumWinding);
150 }
caryclark54359292015-03-26 07:52:43 -0700151 if (!segment->done(angle)) {
caryclarkbca19f72015-05-13 08:23:48 -0700152 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
caryclark54359292015-03-26 07:52:43 -0700153 first = segment;
154 *startPtr = start;
155 *endPtr = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000156 }
caryclark54359292015-03-26 07:52:43 -0700157 // OPTIMIZATION: should this also add to the chase?
caryclarkbca19f72015-05-13 08:23:48 -0700158 if (sortable) {
Cary Clark2587f412018-07-24 12:40:10 -0400159 // TODO: add error handling
160 SkAssertResult(segment->markAngle(maxWinding, sumWinding, angle, nullptr));
caryclarkbca19f72015-05-13 08:23:48 -0700161 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000162 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000163 }
caryclark54359292015-03-26 07:52:43 -0700164 if (first) {
165 #if TRY_ROTATE
166 *chase->insert(0) = span;
167 #else
168 *chase->append() = span;
169 #endif
170 return first;
171 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000172 }
halcanary96fcdcc2015-08-27 07:41:13 -0700173 return nullptr;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000174}
175
caryclark624637c2015-05-11 07:21:27 -0700176bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) {
177 SkTDArray<SkOpContour* > list;
178 SkOpContour* contour = *contourList;
caryclark54359292015-03-26 07:52:43 -0700179 do {
180 if (contour->count()) {
181 contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
182 *list.append() = contour;
183 }
184 } while ((contour = contour->next()));
caryclark624637c2015-05-11 07:21:27 -0700185 int count = list.count();
186 if (!count) {
187 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000188 }
caryclark624637c2015-05-11 07:21:27 -0700189 if (count > 1) {
190 SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
191 }
192 contour = list[0];
193 SkOpContourHead* contourHead = static_cast<SkOpContourHead*>(contour);
194 contour->globalState()->setContourHead(contourHead);
195 *contourList = contourHead;
196 for (int index = 1; index < count; ++index) {
197 SkOpContour* next = list[index];
198 contour->setNext(next);
199 contour = next;
200 }
halcanary96fcdcc2015-08-27 07:41:13 -0700201 contour->setNext(nullptr);
caryclark624637c2015-05-11 07:21:27 -0700202 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000203}
204
Cary Clarkab87d7a2016-10-04 10:01:04 -0400205static void calc_angles(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS()) {
206 DEBUG_STATIC_SET_PHASE(contourList);
caryclark624637c2015-05-11 07:21:27 -0700207 SkOpContour* contour = contourList;
208 do {
caryclark55888e42016-07-18 10:01:36 -0700209 contour->calcAngles();
caryclark624637c2015-05-11 07:21:27 -0700210 } while ((contour = contour->next()));
caryclark54359292015-03-26 07:52:43 -0700211}
212
Cary Clarkab87d7a2016-10-04 10:01:04 -0400213static bool missing_coincidence(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS()) {
214 DEBUG_STATIC_SET_PHASE(contourList);
caryclark624637c2015-05-11 07:21:27 -0700215 SkOpContour* contour = contourList;
caryclark27c8eb82015-07-06 11:38:33 -0700216 bool result = false;
caryclark624637c2015-05-11 07:21:27 -0700217 do {
caryclark55888e42016-07-18 10:01:36 -0700218 result |= contour->missingCoincidence();
caryclark624637c2015-05-11 07:21:27 -0700219 } while ((contour = contour->next()));
caryclark27c8eb82015-07-06 11:38:33 -0700220 return result;
caryclark54359292015-03-26 07:52:43 -0700221}
222
Cary Clarkab87d7a2016-10-04 10:01:04 -0400223static bool move_multiples(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS()) {
224 DEBUG_STATIC_SET_PHASE(contourList);
caryclark624637c2015-05-11 07:21:27 -0700225 SkOpContour* contour = contourList;
226 do {
caryclarkd78c0882016-02-24 09:03:07 -0800227 if (!contour->moveMultiples()) {
228 return false;
229 }
caryclark624637c2015-05-11 07:21:27 -0700230 } while ((contour = contour->next()));
caryclarkd78c0882016-02-24 09:03:07 -0800231 return true;
caryclark08bc8482015-04-24 09:08:57 -0700232}
233
Cary Clark28da2832017-03-21 10:30:50 -0400234static bool move_nearby(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400235 DEBUG_STATIC_SET_PHASE(contourList);
caryclark624637c2015-05-11 07:21:27 -0700236 SkOpContour* contour = contourList;
237 do {
Cary Clark28da2832017-03-21 10:30:50 -0400238 if (!contour->moveNearby()) {
239 return false;
240 }
caryclark624637c2015-05-11 07:21:27 -0700241 } while ((contour = contour->next()));
Cary Clark28da2832017-03-21 10:30:50 -0400242 return true;
caryclark54359292015-03-26 07:52:43 -0700243}
244
caryclarkb36a3cd2016-10-18 07:59:44 -0700245static bool sort_angles(SkOpContourHead* contourList) {
caryclark624637c2015-05-11 07:21:27 -0700246 SkOpContour* contour = contourList;
247 do {
caryclarkb36a3cd2016-10-18 07:59:44 -0700248 if (!contour->sortAngles()) {
249 return false;
250 }
caryclark624637c2015-05-11 07:21:27 -0700251 } while ((contour = contour->next()));
caryclarkb36a3cd2016-10-18 07:59:44 -0700252 return true;
caryclark54359292015-03-26 07:52:43 -0700253}
254
caryclark55888e42016-07-18 10:01:36 -0700255bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence) {
caryclark624637c2015-05-11 07:21:27 -0700256 SkOpGlobalState* globalState = contourList->globalState();
caryclark55888e42016-07-18 10:01:36 -0700257 // match up points within the coincident runs
Cary Clarkab87d7a2016-10-04 10:01:04 -0400258 if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kIntersecting))) {
caryclark55888e42016-07-18 10:01:36 -0700259 return false;
260 }
caryclark55888e42016-07-18 10:01:36 -0700261 // combine t values when multiple intersections occur on some segments but not others
Cary Clarkab87d7a2016-10-04 10:01:04 -0400262 if (!move_multiples(contourList DEBUG_PHASE_PARAMS(kWalking))) {
caryclarkd78c0882016-02-24 09:03:07 -0800263 return false;
264 }
caryclark54359292015-03-26 07:52:43 -0700265 // move t values and points together to eliminate small/tiny gaps
Cary Clark28da2832017-03-21 10:30:50 -0400266 if (!move_nearby(contourList DEBUG_COIN_PARAMS())) {
267 return false;
268 }
caryclark55888e42016-07-18 10:01:36 -0700269 // add coincidence formed by pairing on curve points and endpoints
Cary Clarkab87d7a2016-10-04 10:01:04 -0400270 coincidence->correctEnds(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
271 if (!coincidence->addEndMovedSpans(DEBUG_COIN_ONLY_PARAMS())) {
caryclark55888e42016-07-18 10:01:36 -0700272 return false;
273 }
Cary Clarke47ae292016-10-05 08:51:39 -0400274 const int SAFETY_COUNT = 3;
caryclark55888e42016-07-18 10:01:36 -0700275 int safetyHatch = SAFETY_COUNT;
276 // look for coincidence present in A-B and A-C but missing in B-C
caryclark81a478c2016-09-09 09:37:57 -0700277 do {
278 bool added;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400279 if (!coincidence->addMissing(&added DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
caryclark81a478c2016-09-09 09:37:57 -0700280 return false;
281 }
282 if (!added) {
283 break;
284 }
caryclark55888e42016-07-18 10:01:36 -0700285 if (!--safetyHatch) {
caryclark30b9fdd2016-08-31 14:36:29 -0700286 SkASSERT(globalState->debugSkipAssert());
caryclark3f0753d2016-06-28 09:23:57 -0700287 return false;
288 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400289 move_nearby(contourList DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch - 1));
caryclark81a478c2016-09-09 09:37:57 -0700290 } while (true);
caryclark55888e42016-07-18 10:01:36 -0700291 // check to see if, loosely, coincident ranges may be expanded
Cary Clarkab87d7a2016-10-04 10:01:04 -0400292 if (coincidence->expand(DEBUG_COIN_ONLY_PARAMS())) {
caryclark81a478c2016-09-09 09:37:57 -0700293 bool added;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400294 if (!coincidence->addMissing(&added DEBUG_COIN_PARAMS())) {
caryclark81a478c2016-09-09 09:37:57 -0700295 return false;
296 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400297 if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
caryclark55888e42016-07-18 10:01:36 -0700298 return false;
299 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400300 if (!move_multiples(contourList DEBUG_COIN_PARAMS())) {
caryclark55888e42016-07-18 10:01:36 -0700301 return false;
302 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400303 move_nearby(contourList DEBUG_COIN_PARAMS());
caryclark26ad22a2015-10-16 09:03:38 -0700304 }
caryclark27c8eb82015-07-06 11:38:33 -0700305 // the expanded ranges may not align -- add the missing spans
Cary Clarkab87d7a2016-10-04 10:01:04 -0400306 if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
caryclark8bc90e22016-07-25 06:05:08 -0700307 return false;
308 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400309 // mark spans of coincident segments as coincident
Cary Clarke47ae292016-10-05 08:51:39 -0400310 coincidence->mark(DEBUG_COIN_ONLY_PARAMS());
caryclark55888e42016-07-18 10:01:36 -0700311 // look for coincidence lines and curves undetected by intersection
Cary Clarkab87d7a2016-10-04 10:01:04 -0400312 if (missing_coincidence(contourList DEBUG_COIN_PARAMS())) {
313 (void) coincidence->expand(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
314 if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
caryclark26ad22a2015-10-16 09:03:38 -0700315 return false;
316 }
caryclarke6522ea2016-10-17 07:54:33 -0700317 if (!coincidence->mark(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
318 return false;
319 }
caryclark55888e42016-07-18 10:01:36 -0700320 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400321 (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
caryclark27c8eb82015-07-06 11:38:33 -0700322 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400323 (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
caryclark55888e42016-07-18 10:01:36 -0700324
caryclark55888e42016-07-18 10:01:36 -0700325 SkOpCoincidence overlaps(globalState);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400326 safetyHatch = SAFETY_COUNT;
caryclark27c8eb82015-07-06 11:38:33 -0700327 do {
328 SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400329 // adjust the winding value to account for coincident edges
caryclarka35ab3e2016-10-20 08:32:18 -0700330 if (!pairs->apply(DEBUG_ITER_ONLY_PARAMS(SAFETY_COUNT - safetyHatch))) {
331 return false;
Ben Wagner63fd7602017-10-09 15:45:33 -0400332 }
caryclark27c8eb82015-07-06 11:38:33 -0700333 // For each coincident pair that overlaps another, when the receivers (the 1st of the pair)
334 // are different, construct a new pair to resolve their mutual span
Cary Clark40f23782016-10-06 12:04:16 -0400335 if (!pairs->findOverlaps(&overlaps DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
336 return false;
337 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400338 if (!--safetyHatch) {
339 SkASSERT(globalState->debugSkipAssert());
340 return false;
341 }
caryclark27c8eb82015-07-06 11:38:33 -0700342 } while (!overlaps.isEmpty());
Cary Clarkab87d7a2016-10-04 10:01:04 -0400343 calc_angles(contourList DEBUG_COIN_PARAMS());
caryclarkb36a3cd2016-10-18 07:59:44 -0700344 if (!sort_angles(contourList)) {
345 return false;
346 }
caryclark55888e42016-07-18 10:01:36 -0700347#if DEBUG_COINCIDENCE_VERBOSE
caryclark624637c2015-05-11 07:21:27 -0700348 coincidence->debugShowCoincidence();
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000349#endif
caryclark55888e42016-07-18 10:01:36 -0700350#if DEBUG_COINCIDENCE
351 coincidence->debugValidate();
352#endif
353 SkPathOpsDebug::ShowActiveSpans(contourList);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000354 return true;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000355}