blob: 54495109f09a3c30bfdff9837a9620170c806f1f [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
caryclark54359292015-03-26 07:52:43 -070013static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr,
14 SkOpSpanBase** endPtr) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000015 while (chase.count()) {
caryclark54359292015-03-26 07:52:43 -070016 SkOpSpanBase* span;
caryclark@google.com07393ca2013-04-08 11:47:37 +000017 chase.pop(&span);
caryclark54359292015-03-26 07:52:43 -070018 // OPTIMIZE: prev makes this compatible with old code -- but is it necessary?
19 *startPtr = span->ptT()->prev()->span();
20 SkOpSegment* segment = (*startPtr)->segment();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000021 bool done = true;
halcanary96fcdcc2015-08-27 07:41:13 -070022 *endPtr = nullptr;
caryclarkbca19f72015-05-13 08:23:48 -070023 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
caryclark54359292015-03-26 07:52:43 -070024 *startPtr = last->start();
25 *endPtr = last->end();
caryclark@google.com07393ca2013-04-08 11:47:37 +000026 #if TRY_ROTATE
27 *chase.insert(0) = span;
28 #else
29 *chase.append() = span;
30 #endif
31 return last->segment();
32 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000033 if (done) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000034 continue;
35 }
caryclarkbca19f72015-05-13 08:23:48 -070036 int winding;
37 bool sortable;
38 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
caryclarkaa7ceb62016-06-29 10:46:08 -070039 if (!angle) {
40 return nullptr;
41 }
caryclark65f55312014-11-13 06:58:52 -080042 if (winding == SK_MinS32) {
43 continue;
44 }
caryclarkbca19f72015-05-13 08:23:48 -070045 int sumMiWinding, sumSuWinding;
46 if (sortable) {
47 segment = angle->segment();
48 sumMiWinding = segment->updateWindingReverse(angle);
caryclark79418092016-08-26 14:24:24 -070049 if (sumMiWinding == SK_MinS32) {
50 SkASSERT(segment->globalState()->debugSkipAssert());
51 return nullptr;
52 }
caryclarkbca19f72015-05-13 08:23:48 -070053 sumSuWinding = segment->updateOppWindingReverse(angle);
caryclark79418092016-08-26 14:24:24 -070054 if (sumSuWinding == SK_MinS32) {
55 SkASSERT(segment->globalState()->debugSkipAssert());
56 return nullptr;
57 }
caryclarkbca19f72015-05-13 08:23:48 -070058 if (segment->operand()) {
59 SkTSwap<int>(sumMiWinding, sumSuWinding);
60 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000061 }
halcanary96fcdcc2015-08-27 07:41:13 -070062 SkOpSegment* first = nullptr;
caryclarkbca19f72015-05-13 08:23:48 -070063 const SkOpAngle* firstAngle = angle;
caryclark54359292015-03-26 07:52:43 -070064 while ((angle = angle->next()) != firstAngle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000065 segment = angle->segment();
caryclark54359292015-03-26 07:52:43 -070066 SkOpSpanBase* start = angle->start();
67 SkOpSpanBase* end = angle->end();
caryclark@google.com07393ca2013-04-08 11:47:37 +000068 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
caryclarkbca19f72015-05-13 08:23:48 -070069 if (sortable) {
70 segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
71 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
72 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000073 if (!segment->done(angle)) {
caryclarkbca19f72015-05-13 08:23:48 -070074 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000075 first = segment;
caryclark54359292015-03-26 07:52:43 -070076 *startPtr = start;
77 *endPtr = end;
caryclark65f55312014-11-13 06:58:52 -080078 }
caryclarkdac1d172014-06-17 05:15:38 -070079 // OPTIMIZATION: should this also add to the chase?
caryclarkbca19f72015-05-13 08:23:48 -070080 if (sortable) {
81 (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
82 oppSumWinding, angle);
83 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000084 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000085 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000086 if (first) {
87 #if TRY_ROTATE
88 *chase.insert(0) = span;
89 #else
90 *chase.append() = span;
91 #endif
92 return first;
93 }
94 }
halcanary96fcdcc2015-08-27 07:41:13 -070095 return nullptr;
caryclark@google.com07393ca2013-04-08 11:47:37 +000096}
97
caryclark624637c2015-05-11 07:21:27 -070098static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
caryclark55888e42016-07-18 10:01:36 -070099 const int xorMask, const int xorOpMask, SkPathWriter* simple) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000100 bool unsortable = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000101 do {
caryclark624637c2015-05-11 07:21:27 -0700102 SkOpSpan* span = FindSortableTop(contourList);
103 if (!span) {
caryclarkdac1d172014-06-17 05:15:38 -0700104 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000105 }
caryclark624637c2015-05-11 07:21:27 -0700106 SkOpSegment* current = span->segment();
107 SkOpSpanBase* start = span->next();
108 SkOpSpanBase* end = span;
caryclark54359292015-03-26 07:52:43 -0700109 SkTDArray<SkOpSpanBase*> chase;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000110 do {
caryclark54359292015-03-26 07:52:43 -0700111 if (current->activeOp(start, end, xorMask, xorOpMask, op)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000112 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000113 if (!unsortable && current->done()) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000114 break;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000115 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000116 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -0700117 SkOpSpanBase* nextStart = start;
118 SkOpSpanBase* nextEnd = end;
caryclarkdac1d172014-06-17 05:15:38 -0700119 SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd,
caryclark@google.com07393ca2013-04-08 11:47:37 +0000120 &unsortable, op, xorMask, xorOpMask);
121 if (!next) {
122 if (!unsortable && simple->hasMove()
123 && current->verb() != SkPath::kLine_Verb
124 && !simple->isClosed()) {
caryclarkef784fb2015-10-30 12:03:06 -0700125 if (!current->addCurveTo(start, end, simple)) {
126 return false;
127 }
caryclarkdac1d172014-06-17 05:15:38 -0700128 if (!simple->isClosed()) {
caryclark55888e42016-07-18 10:01:36 -0700129 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclarkdac1d172014-06-17 05:15:38 -0700130 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000131 }
132 break;
133 }
134 #if DEBUG_FLOW
caryclark54359292015-03-26 07:52:43 -0700135 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
136 current->debugID(), start->pt().fX, start->pt().fY,
137 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000138 #endif
caryclarkef784fb2015-10-30 12:03:06 -0700139 if (!current->addCurveTo(start, end, simple)) {
140 return false;
141 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000142 current = next;
caryclark54359292015-03-26 07:52:43 -0700143 start = nextStart;
144 end = nextEnd;
145 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
146 if (current->activeWinding(start, end) && !simple->isClosed()) {
147 SkOpSpan* spanStart = start->starter(end);
148 if (!spanStart->done()) {
caryclarkef784fb2015-10-30 12:03:06 -0700149 if (!current->addCurveTo(start, end, simple)) {
150 return false;
151 }
caryclark54359292015-03-26 07:52:43 -0700152 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000153 }
154 }
caryclarkeed356d2016-09-14 07:18:20 -0700155 simple->finishContour();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000156 } else {
caryclark54359292015-03-26 07:52:43 -0700157 SkOpSpanBase* last = current->markAndChaseDone(start, end);
158 if (last && !last->chased()) {
159 last->setChased(true);
caryclarkdac1d172014-06-17 05:15:38 -0700160 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
161 *chase.append() = last;
162#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -0700163 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
164 if (!last->final()) {
165 SkDebugf(" windSum=%d", last->upCast()->windSum());
166 }
167 SkDebugf("\n");
caryclarkdac1d172014-06-17 05:15:38 -0700168#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000169 }
170 }
caryclark54359292015-03-26 07:52:43 -0700171 current = findChaseOp(chase, &start, &end);
caryclark55888e42016-07-18 10:01:36 -0700172 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000173 if (!current) {
174 break;
175 }
176 } while (true);
177 } while (true);
caryclarkeed356d2016-09-14 07:18:20 -0700178 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000179}
180
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000181// pretty picture:
182// https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b-7vD9JP2V-kn9Ss/edit?usp=sharing
caryclark54359292015-03-26 07:52:43 -0700183static const SkPathOp gOpInverse[kReverseDifference_SkPathOp + 1][2][2] = {
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000184// inside minuend outside minuend
185// inside subtrahend outside subtrahend inside subtrahend outside subtrahend
caryclark54359292015-03-26 07:52:43 -0700186{{ kDifference_SkPathOp, kIntersect_SkPathOp }, { kUnion_SkPathOp, kReverseDifference_SkPathOp }},
187{{ kIntersect_SkPathOp, kDifference_SkPathOp }, { kReverseDifference_SkPathOp, kUnion_SkPathOp }},
188{{ kUnion_SkPathOp, kReverseDifference_SkPathOp }, { kDifference_SkPathOp, kIntersect_SkPathOp }},
189{{ kXOR_SkPathOp, kXOR_SkPathOp }, { kXOR_SkPathOp, kXOR_SkPathOp }},
190{{ kReverseDifference_SkPathOp, kUnion_SkPathOp }, { kIntersect_SkPathOp, kDifference_SkPathOp }},
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000191};
192
caryclark54359292015-03-26 07:52:43 -0700193static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = {
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000194 {{ false, false }, { true, false }}, // diff
195 {{ false, false }, { false, true }}, // sect
196 {{ false, true }, { true, true }}, // union
197 {{ false, true }, { true, false }}, // xor
198 {{ false, true }, { false, false }}, // rev diff
199};
200
caryclark26ad22a2015-10-16 09:03:38 -0700201#if DEBUG_T_SECT_LOOP_COUNT
202
203#include "SkMutex.h"
204
reed086eea92016-05-04 17:12:46 -0700205SK_DECLARE_STATIC_MUTEX(debugWorstLoop);
caryclark26ad22a2015-10-16 09:03:38 -0700206
Cary Clarkab87d7a2016-10-04 10:01:04 -0400207SkOpGlobalState debugWorstState(nullptr, nullptr SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr)
208 SkDEBUGPARAMS(nullptr));
caryclark26ad22a2015-10-16 09:03:38 -0700209
210void ReportPathOpsDebugging() {
211 debugWorstState.debugLoopReport();
212}
213
214extern void (*gVerboseFinalize)();
215
216#endif
217
caryclark3f0753d2016-06-28 09:23:57 -0700218bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
219 SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
Florin Malita14a64302017-05-24 14:53:44 -0400220 SkSTArenaAlloc<4096> allocator; // FIXME: add a constant expression here, tune
caryclark54359292015-03-26 07:52:43 -0700221 SkOpContour contour;
caryclark624637c2015-05-11 07:21:27 -0700222 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
caryclark55888e42016-07-18 10:01:36 -0700223 SkOpGlobalState globalState(contourList, &allocator
224 SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
225 SkOpCoincidence coincidence(&globalState);
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()];
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000235 SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]
236 ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
caryclark55888e42016-07-18 10:01:36 -0700237 SkScalar scaleFactor = SkTMax(ScaleFactor(one), ScaleFactor(two));
238 SkPath scaledOne, scaledTwo;
239 const SkPath* minuend, * subtrahend;
240 if (scaleFactor > SK_Scalar1) {
241 ScalePath(one, 1.f / scaleFactor, &scaledOne);
242 minuend = &scaledOne;
243 ScalePath(two, 1.f / scaleFactor, &scaledTwo);
244 subtrahend = &scaledTwo;
245 } else {
246 minuend = &one;
247 subtrahend = &two;
248 }
caryclark54359292015-03-26 07:52:43 -0700249 if (op == kReverseDifference_SkPathOp) {
caryclark55888e42016-07-18 10:01:36 -0700250 SkTSwap(minuend, subtrahend);
caryclark54359292015-03-26 07:52:43 -0700251 op = kDifference_SkPathOp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000252 }
caryclark624637c2015-05-11 07:21:27 -0700253#if DEBUG_SORT
caryclark@google.com570863f2013-09-16 15:55:01 +0000254 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000255#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000256 // turn path into list of segments
caryclarkeed356d2016-09-14 07:18:20 -0700257 SkOpEdgeBuilder builder(*minuend, contourList, &globalState);
caryclarkd751ac02014-10-03 05:36:27 -0700258 if (builder.unparseable()) {
259 return false;
260 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000261 const int xorMask = builder.xorMask();
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000262 builder.addOperand(*subtrahend);
caryclark55888e42016-07-18 10:01:36 -0700263 if (!builder.finish()) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000264 return false;
265 }
caryclark03b03ca2015-04-23 09:13:37 -0700266#if DEBUG_DUMP_SEGMENTS
caryclark26ad22a2015-10-16 09:03:38 -0700267 contourList->dumpSegments("seg", op);
caryclark54359292015-03-26 07:52:43 -0700268#endif
269
caryclark@google.com07393ca2013-04-08 11:47:37 +0000270 const int xorOpMask = builder.xorMask();
caryclark624637c2015-05-11 07:21:27 -0700271 if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask,
272 xorOpMask == kEvenOdd_PathOpsMask)) {
caryclark1049f122015-04-20 08:31:59 -0700273 result->reset();
274 result->setFillType(fillType);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000275 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000276 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000277 // find all intersections between segments
caryclark624637c2015-05-11 07:21:27 -0700278 SkOpContour* current = contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000279 do {
caryclark624637c2015-05-11 07:21:27 -0700280 SkOpContour* next = current;
caryclark55888e42016-07-18 10:01:36 -0700281 while (AddIntersectTs(current, next, &coincidence)
caryclark624637c2015-05-11 07:21:27 -0700282 && (next = next->next()))
283 ;
284 } while ((current = current->next()));
caryclark54359292015-03-26 07:52:43 -0700285#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -0400286 globalState.setPhase(SkOpPhase::kWalking);
caryclark54359292015-03-26 07:52:43 -0700287#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -0400288 bool success = HandleCoincidence(contourList, &coincidence);
289#if DEBUG_COIN
290 globalState.debugAddToGlobalCoinDicts();
291#endif
292 if (!success) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000293 return false;
294 }
caryclark26ad22a2015-10-16 09:03:38 -0700295#if DEBUG_ALIGNMENT
296 contourList->dumpSegments("aligned");
297#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000298 // construct closed contours
caryclark1049f122015-04-20 08:31:59 -0700299 result->reset();
300 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000301 SkPathWriter wrapper(*result);
caryclarkeed356d2016-09-14 07:18:20 -0700302 if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) {
303 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000304 }
caryclarkeed356d2016-09-14 07:18:20 -0700305 wrapper.assemble(); // if some edges could not be resolved, assemble remaining
caryclark26ad22a2015-10-16 09:03:38 -0700306#if DEBUG_T_SECT_LOOP_COUNT
307 {
308 SkAutoMutexAcquire autoM(debugWorstLoop);
309 if (!gVerboseFinalize) {
310 gVerboseFinalize = &ReportPathOpsDebugging;
311 }
312 debugWorstState.debugDoYourWorst(&globalState);
313 }
314#endif
caryclark55888e42016-07-18 10:01:36 -0700315 if (scaleFactor > 1) {
316 ScalePath(*result, scaleFactor, result);
317 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000318 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000319}
caryclark624637c2015-05-11 07:21:27 -0700320
321bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
Cary Clark918fb1f2016-11-15 13:22:25 -0500322#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -0700323 if (SkPathOpsDebug::gVerifyOp) {
324 if (!OpDebug(one, two, op, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) {
325 SkPathOpsDebug::ReportOpFail(one, two, op);
326 return false;
327 }
328 SkPathOpsDebug::VerifyOp(one, two, op, *result);
329 return true;
caryclark26ad22a2015-10-16 09:03:38 -0700330 }
caryclark26ad22a2015-10-16 09:03:38 -0700331#endif
caryclark13260682016-10-24 05:10:14 -0700332 return OpDebug(one, two, op, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
caryclark624637c2015-05-11 07:21:27 -0700333}