blob: f8b8a65eec17711aa4dbb484c90f79a9ec849715 [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 */
7#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"
12
Ben Wagnerf08d1d02018-06-18 15:11:00 -040013#include <utility>
14
caryclark54359292015-03-26 07:52:43 -070015static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr,
16 SkOpSpanBase** endPtr) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000017 while (chase.count()) {
caryclark54359292015-03-26 07:52:43 -070018 SkOpSpanBase* span;
caryclark@google.com07393ca2013-04-08 11:47:37 +000019 chase.pop(&span);
caryclark54359292015-03-26 07:52:43 -070020 // OPTIMIZE: prev makes this compatible with old code -- but is it necessary?
21 *startPtr = span->ptT()->prev()->span();
22 SkOpSegment* segment = (*startPtr)->segment();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000023 bool done = true;
halcanary96fcdcc2015-08-27 07:41:13 -070024 *endPtr = nullptr;
caryclarkbca19f72015-05-13 08:23:48 -070025 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
caryclark54359292015-03-26 07:52:43 -070026 *startPtr = last->start();
27 *endPtr = last->end();
caryclark@google.com07393ca2013-04-08 11:47:37 +000028 #if TRY_ROTATE
29 *chase.insert(0) = span;
30 #else
31 *chase.append() = span;
32 #endif
33 return last->segment();
34 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000035 if (done) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000036 continue;
37 }
caryclarkbca19f72015-05-13 08:23:48 -070038 int winding;
39 bool sortable;
40 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
caryclarkaa7ceb62016-06-29 10:46:08 -070041 if (!angle) {
42 return nullptr;
43 }
caryclark65f55312014-11-13 06:58:52 -080044 if (winding == SK_MinS32) {
45 continue;
46 }
caryclarkbca19f72015-05-13 08:23:48 -070047 int sumMiWinding, sumSuWinding;
48 if (sortable) {
49 segment = angle->segment();
50 sumMiWinding = segment->updateWindingReverse(angle);
caryclark79418092016-08-26 14:24:24 -070051 if (sumMiWinding == SK_MinS32) {
52 SkASSERT(segment->globalState()->debugSkipAssert());
53 return nullptr;
54 }
caryclarkbca19f72015-05-13 08:23:48 -070055 sumSuWinding = segment->updateOppWindingReverse(angle);
caryclark79418092016-08-26 14:24:24 -070056 if (sumSuWinding == SK_MinS32) {
57 SkASSERT(segment->globalState()->debugSkipAssert());
58 return nullptr;
59 }
caryclarkbca19f72015-05-13 08:23:48 -070060 if (segment->operand()) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -040061 using std::swap;
62 swap(sumMiWinding, sumSuWinding);
caryclarkbca19f72015-05-13 08:23:48 -070063 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000064 }
halcanary96fcdcc2015-08-27 07:41:13 -070065 SkOpSegment* first = nullptr;
caryclarkbca19f72015-05-13 08:23:48 -070066 const SkOpAngle* firstAngle = angle;
caryclark54359292015-03-26 07:52:43 -070067 while ((angle = angle->next()) != firstAngle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000068 segment = angle->segment();
caryclark54359292015-03-26 07:52:43 -070069 SkOpSpanBase* start = angle->start();
70 SkOpSpanBase* end = angle->end();
Kevin Lubick42846132018-01-05 10:11:11 -050071 int maxWinding = 0, sumWinding = 0, oppMaxWinding = 0, oppSumWinding = 0;
caryclarkbca19f72015-05-13 08:23:48 -070072 if (sortable) {
73 segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
74 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
75 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000076 if (!segment->done(angle)) {
caryclarkbca19f72015-05-13 08:23:48 -070077 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000078 first = segment;
caryclark54359292015-03-26 07:52:43 -070079 *startPtr = start;
80 *endPtr = end;
caryclark65f55312014-11-13 06:58:52 -080081 }
caryclarkdac1d172014-06-17 05:15:38 -070082 // OPTIMIZATION: should this also add to the chase?
caryclarkbca19f72015-05-13 08:23:48 -070083 if (sortable) {
84 (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
85 oppSumWinding, angle);
86 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000087 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000088 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000089 if (first) {
90 #if TRY_ROTATE
91 *chase.insert(0) = span;
92 #else
93 *chase.append() = span;
94 #endif
95 return first;
96 }
97 }
halcanary96fcdcc2015-08-27 07:41:13 -070098 return nullptr;
caryclark@google.com07393ca2013-04-08 11:47:37 +000099}
100
caryclark624637c2015-05-11 07:21:27 -0700101static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
caryclark55888e42016-07-18 10:01:36 -0700102 const int xorMask, const int xorOpMask, SkPathWriter* simple) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000103 bool unsortable = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000104 do {
caryclark624637c2015-05-11 07:21:27 -0700105 SkOpSpan* span = FindSortableTop(contourList);
106 if (!span) {
caryclarkdac1d172014-06-17 05:15:38 -0700107 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000108 }
caryclark624637c2015-05-11 07:21:27 -0700109 SkOpSegment* current = span->segment();
110 SkOpSpanBase* start = span->next();
111 SkOpSpanBase* end = span;
caryclark54359292015-03-26 07:52:43 -0700112 SkTDArray<SkOpSpanBase*> chase;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000113 do {
caryclark54359292015-03-26 07:52:43 -0700114 if (current->activeOp(start, end, xorMask, xorOpMask, op)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000115 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000116 if (!unsortable && current->done()) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000117 break;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000118 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000119 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -0700120 SkOpSpanBase* nextStart = start;
121 SkOpSpanBase* nextEnd = end;
caryclarkdac1d172014-06-17 05:15:38 -0700122 SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd,
caryclark@google.com07393ca2013-04-08 11:47:37 +0000123 &unsortable, op, xorMask, xorOpMask);
124 if (!next) {
125 if (!unsortable && simple->hasMove()
126 && current->verb() != SkPath::kLine_Verb
127 && !simple->isClosed()) {
caryclarkef784fb2015-10-30 12:03:06 -0700128 if (!current->addCurveTo(start, end, simple)) {
129 return false;
130 }
caryclarkdac1d172014-06-17 05:15:38 -0700131 if (!simple->isClosed()) {
caryclark55888e42016-07-18 10:01:36 -0700132 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclarkdac1d172014-06-17 05:15:38 -0700133 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000134 }
135 break;
136 }
137 #if DEBUG_FLOW
caryclark54359292015-03-26 07:52:43 -0700138 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
139 current->debugID(), start->pt().fX, start->pt().fY,
140 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000141 #endif
caryclarkef784fb2015-10-30 12:03:06 -0700142 if (!current->addCurveTo(start, end, simple)) {
143 return false;
144 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000145 current = next;
caryclark54359292015-03-26 07:52:43 -0700146 start = nextStart;
147 end = nextEnd;
148 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
149 if (current->activeWinding(start, end) && !simple->isClosed()) {
150 SkOpSpan* spanStart = start->starter(end);
151 if (!spanStart->done()) {
caryclarkef784fb2015-10-30 12:03:06 -0700152 if (!current->addCurveTo(start, end, simple)) {
153 return false;
154 }
caryclark54359292015-03-26 07:52:43 -0700155 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000156 }
157 }
caryclarkeed356d2016-09-14 07:18:20 -0700158 simple->finishContour();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000159 } else {
Cary Clarkc050a1a2018-06-25 08:45:40 -0400160 SkOpSpanBase* last;
161 if (!current->markAndChaseDone(start, end, &last)) {
162 return false;
163 }
caryclark54359292015-03-26 07:52:43 -0700164 if (last && !last->chased()) {
165 last->setChased(true);
caryclarkdac1d172014-06-17 05:15:38 -0700166 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
167 *chase.append() = last;
168#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -0700169 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
170 if (!last->final()) {
171 SkDebugf(" windSum=%d", last->upCast()->windSum());
172 }
173 SkDebugf("\n");
caryclarkdac1d172014-06-17 05:15:38 -0700174#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000175 }
176 }
caryclark54359292015-03-26 07:52:43 -0700177 current = findChaseOp(chase, &start, &end);
caryclark55888e42016-07-18 10:01:36 -0700178 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000179 if (!current) {
180 break;
181 }
182 } while (true);
183 } while (true);
caryclarkeed356d2016-09-14 07:18:20 -0700184 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000185}
186
Cary Clarkd8a39a02018-01-09 16:56:46 -0500187// diagram of why this simplifcation is possible is here:
188// https://skia.org/dev/present/pathops link at bottom of the page
189// https://drive.google.com/file/d/0BwoLUwz9PYkHLWpsaXd0UDdaN00/view?usp=sharing
caryclark54359292015-03-26 07:52:43 -0700190static const SkPathOp gOpInverse[kReverseDifference_SkPathOp + 1][2][2] = {
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000191// inside minuend outside minuend
192// inside subtrahend outside subtrahend inside subtrahend outside subtrahend
caryclark54359292015-03-26 07:52:43 -0700193{{ kDifference_SkPathOp, kIntersect_SkPathOp }, { kUnion_SkPathOp, kReverseDifference_SkPathOp }},
194{{ kIntersect_SkPathOp, kDifference_SkPathOp }, { kReverseDifference_SkPathOp, kUnion_SkPathOp }},
195{{ kUnion_SkPathOp, kReverseDifference_SkPathOp }, { kDifference_SkPathOp, kIntersect_SkPathOp }},
196{{ kXOR_SkPathOp, kXOR_SkPathOp }, { kXOR_SkPathOp, kXOR_SkPathOp }},
197{{ kReverseDifference_SkPathOp, kUnion_SkPathOp }, { kIntersect_SkPathOp, kDifference_SkPathOp }},
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000198};
199
caryclark54359292015-03-26 07:52:43 -0700200static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = {
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000201 {{ false, false }, { true, false }}, // diff
202 {{ false, false }, { false, true }}, // sect
203 {{ false, true }, { true, true }}, // union
204 {{ false, true }, { true, false }}, // xor
205 {{ false, true }, { false, false }}, // rev diff
206};
207
caryclark26ad22a2015-10-16 09:03:38 -0700208#if DEBUG_T_SECT_LOOP_COUNT
209
210#include "SkMutex.h"
211
reed086eea92016-05-04 17:12:46 -0700212SK_DECLARE_STATIC_MUTEX(debugWorstLoop);
caryclark26ad22a2015-10-16 09:03:38 -0700213
Cary Clark74b42902018-03-09 07:38:47 -0500214SkOpGlobalState debugWorstState(nullptr, nullptr SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
caryclark26ad22a2015-10-16 09:03:38 -0700215
216void ReportPathOpsDebugging() {
217 debugWorstState.debugLoopReport();
218}
219
220extern void (*gVerboseFinalize)();
221
222#endif
223
caryclark3f0753d2016-06-28 09:23:57 -0700224bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
225 SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
Cary Clark918fb1f2016-11-15 13:22:25 -0500226#if DEBUG_DUMP_VERIFY
227#ifndef SK_DEBUG
228 const char* testName = "release";
229#endif
caryclark13260682016-10-24 05:10:14 -0700230 if (SkPathOpsDebug::gDumpOp) {
231 SkPathOpsDebug::DumpOp(one, two, op, testName);
232 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000233#endif
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000234 op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
Cary Clark1bb47df2018-06-18 08:53:00 -0400235 bool inverseFill = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()];
236 SkPath::FillType fillType = inverseFill ? SkPath::kInverseEvenOdd_FillType :
237 SkPath::kEvenOdd_FillType;
238 SkRect rect1, rect2;
239 if (kIntersect_SkPathOp == op && one.isRect(&rect1) && two.isRect(&rect2)) {
240 result->reset();
241 result->setFillType(fillType);
242 if (rect1.intersect(rect2)) {
243 result->addRect(rect1);
244 }
245 return true;
246 }
247 if (one.isEmpty() || two.isEmpty()) {
248 SkPath work;
249 switch (op) {
250 case kIntersect_SkPathOp:
251 break;
252 case kUnion_SkPathOp:
253 case kXOR_SkPathOp:
254 work = one.isEmpty() ? two : one;
255 break;
256 case kDifference_SkPathOp:
257 if (!one.isEmpty()) {
258 work = one;
259 }
260 break;
261 case kReverseDifference_SkPathOp:
262 if (!two.isEmpty()) {
263 work = two;
264 }
265 break;
266 default:
267 SkASSERT(0); // unhandled case
268 }
269 if (inverseFill != work.isInverseFillType()) {
270 work.toggleInverseFillType();
271 }
272 return Simplify(work, result);
273 }
274 SkSTArenaAlloc<4096> allocator; // FIXME: add a constant expression here, tune
275 SkOpContour contour;
276 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
277 SkOpGlobalState globalState(contourList, &allocator
278 SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
279 SkOpCoincidence coincidence(&globalState);
caryclark55888e42016-07-18 10:01:36 -0700280 SkScalar scaleFactor = SkTMax(ScaleFactor(one), ScaleFactor(two));
281 SkPath scaledOne, scaledTwo;
282 const SkPath* minuend, * subtrahend;
283 if (scaleFactor > SK_Scalar1) {
284 ScalePath(one, 1.f / scaleFactor, &scaledOne);
285 minuend = &scaledOne;
286 ScalePath(two, 1.f / scaleFactor, &scaledTwo);
287 subtrahend = &scaledTwo;
288 } else {
289 minuend = &one;
290 subtrahend = &two;
291 }
caryclark54359292015-03-26 07:52:43 -0700292 if (op == kReverseDifference_SkPathOp) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400293 using std::swap;
294 swap(minuend, subtrahend);
caryclark54359292015-03-26 07:52:43 -0700295 op = kDifference_SkPathOp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000296 }
caryclark624637c2015-05-11 07:21:27 -0700297#if DEBUG_SORT
caryclark@google.com570863f2013-09-16 15:55:01 +0000298 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000299#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000300 // turn path into list of segments
caryclarkeed356d2016-09-14 07:18:20 -0700301 SkOpEdgeBuilder builder(*minuend, contourList, &globalState);
caryclarkd751ac02014-10-03 05:36:27 -0700302 if (builder.unparseable()) {
303 return false;
304 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000305 const int xorMask = builder.xorMask();
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000306 builder.addOperand(*subtrahend);
caryclark55888e42016-07-18 10:01:36 -0700307 if (!builder.finish()) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000308 return false;
309 }
caryclark03b03ca2015-04-23 09:13:37 -0700310#if DEBUG_DUMP_SEGMENTS
caryclark26ad22a2015-10-16 09:03:38 -0700311 contourList->dumpSegments("seg", op);
caryclark54359292015-03-26 07:52:43 -0700312#endif
313
caryclark@google.com07393ca2013-04-08 11:47:37 +0000314 const int xorOpMask = builder.xorMask();
caryclark624637c2015-05-11 07:21:27 -0700315 if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask,
316 xorOpMask == kEvenOdd_PathOpsMask)) {
caryclark1049f122015-04-20 08:31:59 -0700317 result->reset();
318 result->setFillType(fillType);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000319 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000320 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000321 // find all intersections between segments
caryclark624637c2015-05-11 07:21:27 -0700322 SkOpContour* current = contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000323 do {
caryclark624637c2015-05-11 07:21:27 -0700324 SkOpContour* next = current;
caryclark55888e42016-07-18 10:01:36 -0700325 while (AddIntersectTs(current, next, &coincidence)
caryclark624637c2015-05-11 07:21:27 -0700326 && (next = next->next()))
327 ;
328 } while ((current = current->next()));
caryclark54359292015-03-26 07:52:43 -0700329#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -0400330 globalState.setPhase(SkOpPhase::kWalking);
caryclark54359292015-03-26 07:52:43 -0700331#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -0400332 bool success = HandleCoincidence(contourList, &coincidence);
333#if DEBUG_COIN
334 globalState.debugAddToGlobalCoinDicts();
335#endif
336 if (!success) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000337 return false;
338 }
caryclark26ad22a2015-10-16 09:03:38 -0700339#if DEBUG_ALIGNMENT
340 contourList->dumpSegments("aligned");
341#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000342 // construct closed contours
caryclark1049f122015-04-20 08:31:59 -0700343 result->reset();
344 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000345 SkPathWriter wrapper(*result);
caryclarkeed356d2016-09-14 07:18:20 -0700346 if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) {
347 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000348 }
caryclarkeed356d2016-09-14 07:18:20 -0700349 wrapper.assemble(); // if some edges could not be resolved, assemble remaining
caryclark26ad22a2015-10-16 09:03:38 -0700350#if DEBUG_T_SECT_LOOP_COUNT
351 {
352 SkAutoMutexAcquire autoM(debugWorstLoop);
353 if (!gVerboseFinalize) {
354 gVerboseFinalize = &ReportPathOpsDebugging;
355 }
356 debugWorstState.debugDoYourWorst(&globalState);
357 }
358#endif
caryclark55888e42016-07-18 10:01:36 -0700359 if (scaleFactor > 1) {
360 ScalePath(*result, scaleFactor, result);
361 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000362 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000363}
caryclark624637c2015-05-11 07:21:27 -0700364
365bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
Cary Clark918fb1f2016-11-15 13:22:25 -0500366#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -0700367 if (SkPathOpsDebug::gVerifyOp) {
368 if (!OpDebug(one, two, op, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) {
369 SkPathOpsDebug::ReportOpFail(one, two, op);
370 return false;
371 }
372 SkPathOpsDebug::VerifyOp(one, two, op, *result);
373 return true;
caryclark26ad22a2015-10-16 09:03:38 -0700374 }
caryclark26ad22a2015-10-16 09:03:38 -0700375#endif
caryclark13260682016-10-24 05:10:14 -0700376 return OpDebug(one, two, op, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
caryclark624637c2015-05-11 07:21:27 -0700377}