blob: 9cdf8124a8e1bcb3fbb49ef445987dd0c43fe9a9 [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 */
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00007#include "SkAddIntersections.h"
caryclark54359292015-03-26 07:52:43 -07008#include "SkOpCoincidence.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +00009#include "SkOpEdgeBuilder.h"
10#include "SkPathOpsCommon.h"
11#include "SkPathWriter.h"
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +000012#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000013
caryclark55888e42016-07-18 10:01:36 -070014SkScalar ScaleFactor(const SkPath& path) {
15 static const SkScalar twoTo10 = 1024.f;
16 SkScalar largest = 0;
17 const SkScalar* oneBounds = &path.getBounds().fLeft;
18 for (int index = 0; index < 4; ++index) {
19 largest = SkTMax(largest, SkScalarAbs(oneBounds[index]));
20 }
21 SkScalar scale = twoTo10;
22 SkScalar next;
23 while ((next = scale * twoTo10) < largest) {
24 scale = next;
25 }
26 return scale == twoTo10 ? SK_Scalar1 : scale;
27}
28
29void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled) {
30 SkMatrix matrix;
31 matrix.setScale(scale, scale);
32 *scaled = path;
33 scaled->transform(matrix);
34}
35
caryclarkbca19f72015-05-13 08:23:48 -070036const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
37 bool* sortablePtr) {
38 // find first angle, initialize winding to computed fWindSum
39 SkOpSegment* segment = start->segment();
40 const SkOpAngle* angle = segment->spanToAngle(start, end);
41 if (!angle) {
42 *windingPtr = SK_MinS32;
halcanary96fcdcc2015-08-27 07:41:13 -070043 return nullptr;
caryclarkbca19f72015-05-13 08:23:48 -070044 }
45 bool computeWinding = false;
46 const SkOpAngle* firstAngle = angle;
47 bool loop = false;
48 bool unorderable = false;
49 int winding = SK_MinS32;
50 do {
51 angle = angle->next();
caryclarkaa7ceb62016-06-29 10:46:08 -070052 if (!angle) {
53 return nullptr;
54 }
caryclarkbca19f72015-05-13 08:23:48 -070055 unorderable |= angle->unorderable();
56 if ((computeWinding = unorderable || (angle == firstAngle && loop))) {
57 break; // if we get here, there's no winding, loop is unorderable
58 }
59 loop |= angle == firstAngle;
60 segment = angle->segment();
61 winding = segment->windSum(angle);
62 } while (winding == SK_MinS32);
63 // if the angle loop contains an unorderable span, the angle order may be useless
64 // directly compute the winding in this case for each span
65 if (computeWinding) {
66 firstAngle = angle;
67 winding = SK_MinS32;
68 do {
69 SkOpSpanBase* startSpan = angle->start();
70 SkOpSpanBase* endSpan = angle->end();
71 SkOpSpan* lesser = startSpan->starter(endSpan);
72 int testWinding = lesser->windSum();
73 if (testWinding == SK_MinS32) {
74 testWinding = lesser->computeWindSum();
75 }
76 if (testWinding != SK_MinS32) {
77 segment = angle->segment();
78 winding = testWinding;
79 }
80 angle = angle->next();
81 } while (angle != firstAngle);
82 }
83 *sortablePtr = !unorderable;
84 *windingPtr = winding;
85 return angle;
86}
87
Cary Clarkab2d73b2016-12-16 17:17:25 -050088SkOpSpan* FindUndone(SkOpContourHead* contourHead) {
89 SkOpContour* contour = contourHead;
caryclark624637c2015-05-11 07:21:27 -070090 do {
Cary Clarkab2d73b2016-12-16 17:17:25 -050091 if (contour->done()) {
92 continue;
93 }
94 SkOpSpan* result = contour->undoneSpan();
caryclark@google.com07393ca2013-04-08 11:47:37 +000095 if (result) {
96 return result;
97 }
caryclark624637c2015-05-11 07:21:27 -070098 } while ((contour = contour->next()));
halcanary96fcdcc2015-08-27 07:41:13 -070099 return nullptr;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000100}
101
caryclark54359292015-03-26 07:52:43 -0700102SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
103 SkOpSpanBase** endPtr) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000104 while (chase->count()) {
caryclark54359292015-03-26 07:52:43 -0700105 SkOpSpanBase* span;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000106 chase->pop(&span);
caryclark54359292015-03-26 07:52:43 -0700107 SkOpSegment* segment = span->segment();
108 *startPtr = span->ptT()->next()->span();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000109 bool done = true;
halcanary96fcdcc2015-08-27 07:41:13 -0700110 *endPtr = nullptr;
caryclarkbca19f72015-05-13 08:23:48 -0700111 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
caryclark54359292015-03-26 07:52:43 -0700112 *startPtr = last->start();
113 *endPtr = last->end();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000114 #if TRY_ROTATE
115 *chase->insert(0) = span;
116 #else
117 *chase->append() = span;
118 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000119 return last->segment();
120 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000121 if (done) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000122 continue;
123 }
caryclark65f55312014-11-13 06:58:52 -0800124 // find first angle, initialize winding to computed wind sum
caryclarkbca19f72015-05-13 08:23:48 -0700125 int winding;
126 bool sortable;
127 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
caryclark8ccc0752016-08-17 06:14:06 -0700128 if (!angle) {
129 return nullptr;
130 }
caryclark54359292015-03-26 07:52:43 -0700131 if (winding == SK_MinS32) {
132 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000133 }
caryclarkbca19f72015-05-13 08:23:48 -0700134 int sumWinding SK_INIT_TO_AVOID_WARNING;
135 if (sortable) {
136 segment = angle->segment();
137 sumWinding = segment->updateWindingReverse(angle);
138 }
halcanary96fcdcc2015-08-27 07:41:13 -0700139 SkOpSegment* first = nullptr;
caryclarkbca19f72015-05-13 08:23:48 -0700140 const SkOpAngle* firstAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000141 while ((angle = angle->next()) != firstAngle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000142 segment = angle->segment();
caryclark54359292015-03-26 07:52:43 -0700143 SkOpSpanBase* start = angle->start();
144 SkOpSpanBase* end = angle->end();
djsollen2f124632016-04-29 13:53:05 -0700145 int maxWinding SK_INIT_TO_AVOID_WARNING;
caryclarkbca19f72015-05-13 08:23:48 -0700146 if (sortable) {
147 segment->setUpWinding(start, end, &maxWinding, &sumWinding);
148 }
caryclark54359292015-03-26 07:52:43 -0700149 if (!segment->done(angle)) {
caryclarkbca19f72015-05-13 08:23:48 -0700150 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
caryclark54359292015-03-26 07:52:43 -0700151 first = segment;
152 *startPtr = start;
153 *endPtr = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000154 }
caryclark54359292015-03-26 07:52:43 -0700155 // OPTIMIZATION: should this also add to the chase?
caryclarkbca19f72015-05-13 08:23:48 -0700156 if (sortable) {
157 (void) segment->markAngle(maxWinding, sumWinding, angle);
158 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000159 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000160 }
caryclark54359292015-03-26 07:52:43 -0700161 if (first) {
162 #if TRY_ROTATE
163 *chase->insert(0) = span;
164 #else
165 *chase->append() = span;
166 #endif
167 return first;
168 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000169 }
halcanary96fcdcc2015-08-27 07:41:13 -0700170 return nullptr;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000171}
172
caryclark624637c2015-05-11 07:21:27 -0700173bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) {
174 SkTDArray<SkOpContour* > list;
175 SkOpContour* contour = *contourList;
caryclark54359292015-03-26 07:52:43 -0700176 do {
177 if (contour->count()) {
178 contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
179 *list.append() = contour;
180 }
181 } while ((contour = contour->next()));
caryclark624637c2015-05-11 07:21:27 -0700182 int count = list.count();
183 if (!count) {
184 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000185 }
caryclark624637c2015-05-11 07:21:27 -0700186 if (count > 1) {
187 SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
188 }
189 contour = list[0];
190 SkOpContourHead* contourHead = static_cast<SkOpContourHead*>(contour);
191 contour->globalState()->setContourHead(contourHead);
192 *contourList = contourHead;
193 for (int index = 1; index < count; ++index) {
194 SkOpContour* next = list[index];
195 contour->setNext(next);
196 contour = next;
197 }
halcanary96fcdcc2015-08-27 07:41:13 -0700198 contour->setNext(nullptr);
caryclark624637c2015-05-11 07:21:27 -0700199 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000200}
201
Cary Clarkab87d7a2016-10-04 10:01:04 -0400202static void calc_angles(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS()) {
203 DEBUG_STATIC_SET_PHASE(contourList);
caryclark624637c2015-05-11 07:21:27 -0700204 SkOpContour* contour = contourList;
205 do {
caryclark55888e42016-07-18 10:01:36 -0700206 contour->calcAngles();
caryclark624637c2015-05-11 07:21:27 -0700207 } while ((contour = contour->next()));
caryclark54359292015-03-26 07:52:43 -0700208}
209
Cary Clarkab87d7a2016-10-04 10:01:04 -0400210static bool missing_coincidence(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS()) {
211 DEBUG_STATIC_SET_PHASE(contourList);
caryclark624637c2015-05-11 07:21:27 -0700212 SkOpContour* contour = contourList;
caryclark27c8eb82015-07-06 11:38:33 -0700213 bool result = false;
caryclark624637c2015-05-11 07:21:27 -0700214 do {
caryclark55888e42016-07-18 10:01:36 -0700215 result |= contour->missingCoincidence();
caryclark624637c2015-05-11 07:21:27 -0700216 } while ((contour = contour->next()));
caryclark27c8eb82015-07-06 11:38:33 -0700217 return result;
caryclark54359292015-03-26 07:52:43 -0700218}
219
Cary Clarkab87d7a2016-10-04 10:01:04 -0400220static bool move_multiples(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS()) {
221 DEBUG_STATIC_SET_PHASE(contourList);
caryclark624637c2015-05-11 07:21:27 -0700222 SkOpContour* contour = contourList;
223 do {
caryclarkd78c0882016-02-24 09:03:07 -0800224 if (!contour->moveMultiples()) {
225 return false;
226 }
caryclark624637c2015-05-11 07:21:27 -0700227 } while ((contour = contour->next()));
caryclarkd78c0882016-02-24 09:03:07 -0800228 return true;
caryclark08bc8482015-04-24 09:08:57 -0700229}
230
Cary Clark28da2832017-03-21 10:30:50 -0400231static bool move_nearby(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400232 DEBUG_STATIC_SET_PHASE(contourList);
caryclark624637c2015-05-11 07:21:27 -0700233 SkOpContour* contour = contourList;
234 do {
Cary Clark28da2832017-03-21 10:30:50 -0400235 if (!contour->moveNearby()) {
236 return false;
237 }
caryclark624637c2015-05-11 07:21:27 -0700238 } while ((contour = contour->next()));
Cary Clark28da2832017-03-21 10:30:50 -0400239 return true;
caryclark54359292015-03-26 07:52:43 -0700240}
241
caryclarkb36a3cd2016-10-18 07:59:44 -0700242static bool sort_angles(SkOpContourHead* contourList) {
caryclark624637c2015-05-11 07:21:27 -0700243 SkOpContour* contour = contourList;
244 do {
caryclarkb36a3cd2016-10-18 07:59:44 -0700245 if (!contour->sortAngles()) {
246 return false;
247 }
caryclark624637c2015-05-11 07:21:27 -0700248 } while ((contour = contour->next()));
caryclarkb36a3cd2016-10-18 07:59:44 -0700249 return true;
caryclark54359292015-03-26 07:52:43 -0700250}
251
caryclark55888e42016-07-18 10:01:36 -0700252bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence) {
caryclark624637c2015-05-11 07:21:27 -0700253 SkOpGlobalState* globalState = contourList->globalState();
caryclark55888e42016-07-18 10:01:36 -0700254 // match up points within the coincident runs
Cary Clarkab87d7a2016-10-04 10:01:04 -0400255 if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kIntersecting))) {
caryclark55888e42016-07-18 10:01:36 -0700256 return false;
257 }
caryclark55888e42016-07-18 10:01:36 -0700258 // combine t values when multiple intersections occur on some segments but not others
Cary Clarkab87d7a2016-10-04 10:01:04 -0400259 if (!move_multiples(contourList DEBUG_PHASE_PARAMS(kWalking))) {
caryclarkd78c0882016-02-24 09:03:07 -0800260 return false;
261 }
caryclark54359292015-03-26 07:52:43 -0700262 // move t values and points together to eliminate small/tiny gaps
Cary Clark28da2832017-03-21 10:30:50 -0400263 if (!move_nearby(contourList DEBUG_COIN_PARAMS())) {
264 return false;
265 }
caryclark55888e42016-07-18 10:01:36 -0700266 // add coincidence formed by pairing on curve points and endpoints
Cary Clarkab87d7a2016-10-04 10:01:04 -0400267 coincidence->correctEnds(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
268 if (!coincidence->addEndMovedSpans(DEBUG_COIN_ONLY_PARAMS())) {
caryclark55888e42016-07-18 10:01:36 -0700269 return false;
270 }
Cary Clarke47ae292016-10-05 08:51:39 -0400271 const int SAFETY_COUNT = 3;
caryclark55888e42016-07-18 10:01:36 -0700272 int safetyHatch = SAFETY_COUNT;
273 // look for coincidence present in A-B and A-C but missing in B-C
caryclark81a478c2016-09-09 09:37:57 -0700274 do {
275 bool added;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400276 if (!coincidence->addMissing(&added DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
caryclark81a478c2016-09-09 09:37:57 -0700277 return false;
278 }
279 if (!added) {
280 break;
281 }
caryclark55888e42016-07-18 10:01:36 -0700282 if (!--safetyHatch) {
caryclark30b9fdd2016-08-31 14:36:29 -0700283 SkASSERT(globalState->debugSkipAssert());
caryclark3f0753d2016-06-28 09:23:57 -0700284 return false;
285 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400286 move_nearby(contourList DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch - 1));
caryclark81a478c2016-09-09 09:37:57 -0700287 } while (true);
caryclark55888e42016-07-18 10:01:36 -0700288 // check to see if, loosely, coincident ranges may be expanded
Cary Clarkab87d7a2016-10-04 10:01:04 -0400289 if (coincidence->expand(DEBUG_COIN_ONLY_PARAMS())) {
caryclark81a478c2016-09-09 09:37:57 -0700290 bool added;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400291 if (!coincidence->addMissing(&added DEBUG_COIN_PARAMS())) {
caryclark81a478c2016-09-09 09:37:57 -0700292 return false;
293 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400294 if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
caryclark55888e42016-07-18 10:01:36 -0700295 return false;
296 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400297 if (!move_multiples(contourList DEBUG_COIN_PARAMS())) {
caryclark55888e42016-07-18 10:01:36 -0700298 return false;
299 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400300 move_nearby(contourList DEBUG_COIN_PARAMS());
caryclark26ad22a2015-10-16 09:03:38 -0700301 }
caryclark27c8eb82015-07-06 11:38:33 -0700302 // the expanded ranges may not align -- add the missing spans
Cary Clarkab87d7a2016-10-04 10:01:04 -0400303 if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
caryclark8bc90e22016-07-25 06:05:08 -0700304 return false;
305 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400306 // mark spans of coincident segments as coincident
Cary Clarke47ae292016-10-05 08:51:39 -0400307 coincidence->mark(DEBUG_COIN_ONLY_PARAMS());
caryclark55888e42016-07-18 10:01:36 -0700308 // look for coincidence lines and curves undetected by intersection
Cary Clarkab87d7a2016-10-04 10:01:04 -0400309 if (missing_coincidence(contourList DEBUG_COIN_PARAMS())) {
310 (void) coincidence->expand(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
311 if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
caryclark26ad22a2015-10-16 09:03:38 -0700312 return false;
313 }
caryclarke6522ea2016-10-17 07:54:33 -0700314 if (!coincidence->mark(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
315 return false;
316 }
caryclark55888e42016-07-18 10:01:36 -0700317 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400318 (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
caryclark27c8eb82015-07-06 11:38:33 -0700319 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400320 (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
caryclark55888e42016-07-18 10:01:36 -0700321
caryclark55888e42016-07-18 10:01:36 -0700322 SkOpCoincidence overlaps(globalState);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400323 safetyHatch = SAFETY_COUNT;
caryclark27c8eb82015-07-06 11:38:33 -0700324 do {
325 SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400326 // adjust the winding value to account for coincident edges
caryclarka35ab3e2016-10-20 08:32:18 -0700327 if (!pairs->apply(DEBUG_ITER_ONLY_PARAMS(SAFETY_COUNT - safetyHatch))) {
328 return false;
329 }
caryclark27c8eb82015-07-06 11:38:33 -0700330 // For each coincident pair that overlaps another, when the receivers (the 1st of the pair)
331 // are different, construct a new pair to resolve their mutual span
Cary Clark40f23782016-10-06 12:04:16 -0400332 if (!pairs->findOverlaps(&overlaps DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
333 return false;
334 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400335 if (!--safetyHatch) {
336 SkASSERT(globalState->debugSkipAssert());
337 return false;
338 }
caryclark27c8eb82015-07-06 11:38:33 -0700339 } while (!overlaps.isEmpty());
Cary Clarkab87d7a2016-10-04 10:01:04 -0400340 calc_angles(contourList DEBUG_COIN_PARAMS());
caryclarkb36a3cd2016-10-18 07:59:44 -0700341 if (!sort_angles(contourList)) {
342 return false;
343 }
caryclark55888e42016-07-18 10:01:36 -0700344#if DEBUG_COINCIDENCE_VERBOSE
caryclark624637c2015-05-11 07:21:27 -0700345 coincidence->debugShowCoincidence();
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000346#endif
caryclark55888e42016-07-18 10:01:36 -0700347#if DEBUG_COINCIDENCE
348 coincidence->debugValidate();
349#endif
350 SkPathOpsDebug::ShowActiveSpans(contourList);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000351 return true;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000352}