blob: 86b2c068d8712f0b3950b177643166488d7b44bf [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
Cary Clark1857ddb2018-07-11 11:01:43 -040013static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* writer) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000014 bool unsortable = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +000015 do {
caryclark624637c2015-05-11 07:21:27 -070016 SkOpSpan* span = FindSortableTop(contourList);
17 if (!span) {
caryclarkdac1d172014-06-17 05:15:38 -070018 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +000019 }
caryclark624637c2015-05-11 07:21:27 -070020 SkOpSegment* current = span->segment();
21 SkOpSpanBase* start = span->next();
22 SkOpSpanBase* end = span;
caryclark54359292015-03-26 07:52:43 -070023 SkTDArray<SkOpSpanBase*> chase;
caryclark@google.com07393ca2013-04-08 11:47:37 +000024 do {
caryclark54359292015-03-26 07:52:43 -070025 if (current->activeWinding(start, end)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000026 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +000027 if (!unsortable && current->done()) {
Cary Clark1857ddb2018-07-11 11:01:43 -040028 break;
caryclark@google.coma5e55922013-05-07 18:51:31 +000029 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000030 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -070031 SkOpSpanBase* nextStart = start;
32 SkOpSpanBase* nextEnd = end;
caryclarkdac1d172014-06-17 05:15:38 -070033 SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
caryclark@google.com07393ca2013-04-08 11:47:37 +000034 &unsortable);
35 if (!next) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000036 break;
37 }
38 #if DEBUG_FLOW
39 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -070040 current->debugID(), start->pt().fX, start->pt().fY,
41 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +000042 #endif
Cary Clark1857ddb2018-07-11 11:01:43 -040043 if (!current->addCurveTo(start, end, writer)) {
caryclarkef784fb2015-10-30 12:03:06 -070044 return false;
45 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000046 current = next;
caryclark54359292015-03-26 07:52:43 -070047 start = nextStart;
48 end = nextEnd;
Cary Clark1857ddb2018-07-11 11:01:43 -040049 } while (!writer->isClosed() && (!unsortable || !start->starter(end)->done()));
50 if (current->activeWinding(start, end) && !writer->isClosed()) {
caryclark54359292015-03-26 07:52:43 -070051 SkOpSpan* spanStart = start->starter(end);
52 if (!spanStart->done()) {
Cary Clark521f1ed2018-10-15 17:14:42 -040053 SkAssertResult(current->addCurveTo(start, end, writer));
caryclark54359292015-03-26 07:52:43 -070054 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +000055 }
56 }
Cary Clark1857ddb2018-07-11 11:01:43 -040057 writer->finishContour();
caryclark@google.com07393ca2013-04-08 11:47:37 +000058 } else {
Cary Clarkc050a1a2018-06-25 08:45:40 -040059 SkOpSpanBase* last;
Cary Clark521f1ed2018-10-15 17:14:42 -040060 SkAssertResult(current->markAndChaseDone(start, end, &last));
caryclark54359292015-03-26 07:52:43 -070061 if (last && !last->chased()) {
62 last->setChased(true);
caryclarkdac1d172014-06-17 05:15:38 -070063 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
caryclarkdac1d172014-06-17 05:15:38 -070064 *chase.append() = last;
65#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -070066 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
67 if (!last->final()) {
68 SkDebugf(" windSum=%d", last->upCast()->windSum());
69 }
70 SkDebugf("\n");
caryclarkdac1d172014-06-17 05:15:38 -070071#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +000072 }
73 }
caryclark54359292015-03-26 07:52:43 -070074 current = FindChase(&chase, &start, &end);
caryclark55888e42016-07-18 10:01:36 -070075 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +000076 if (!current) {
77 break;
78 }
79 } while (true);
80 } while (true);
caryclarkef784fb2015-10-30 12:03:06 -070081 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +000082}
83
84// returns true if all edges were processed
Cary Clark1857ddb2018-07-11 11:01:43 -040085static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* writer) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000086 bool unsortable = false;
Cary Clarkc91fe3a2018-06-29 09:49:02 -040087 int safetyNet = 1000000;
Cary Clarkab2d73b2016-12-16 17:17:25 -050088 do {
89 SkOpSpan* span = FindUndone(contourList);
90 if (!span) {
91 break;
92 }
93 SkOpSegment* current = span->segment();
94 SkOpSpanBase* start = span->next();
95 SkOpSpanBase* end = span;
caryclark@google.com07393ca2013-04-08 11:47:37 +000096 do {
Cary Clarkc91fe3a2018-06-29 09:49:02 -040097 if (--safetyNet < 0) {
98 return false;
99 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000100 if (!unsortable && current->done()) {
Cary Clarkab2d73b2016-12-16 17:17:25 -0500101 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000102 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000103 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -0700104 SkOpSpanBase* nextStart = start;
105 SkOpSpanBase* nextEnd = end;
Cary Clarkab2d73b2016-12-16 17:17:25 -0500106 SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd,
107 &unsortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000108 if (!next) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000109 break;
110 }
111 #if DEBUG_FLOW
112 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -0700113 current->debugID(), start->pt().fX, start->pt().fY,
114 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000115 #endif
Cary Clark1857ddb2018-07-11 11:01:43 -0400116 if (!current->addCurveTo(start, end, writer)) {
caryclarkef784fb2015-10-30 12:03:06 -0700117 return false;
118 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000119 current = next;
120 start = nextStart;
121 end = nextEnd;
Cary Clark1857ddb2018-07-11 11:01:43 -0400122 } while (!writer->isClosed() && (!unsortable || !start->starter(end)->done()));
Cary Clark521f1ed2018-10-15 17:14:42 -0400123#ifdef SK_DEBUG
Cary Clark1857ddb2018-07-11 11:01:43 -0400124 if (!writer->isClosed()) {
caryclark54359292015-03-26 07:52:43 -0700125 SkOpSpan* spanStart = start->starter(end);
Cary Clark521f1ed2018-10-15 17:14:42 -0400126 SkASSERT(spanStart->done());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000127 }
Cary Clark521f1ed2018-10-15 17:14:42 -0400128#endif
Cary Clark1857ddb2018-07-11 11:01:43 -0400129 writer->finishContour();
caryclark55888e42016-07-18 10:01:36 -0700130 SkPathOpsDebug::ShowActiveSpans(contourList);
Cary Clarkab2d73b2016-12-16 17:17:25 -0500131 } while (true);
caryclarkef784fb2015-10-30 12:03:06 -0700132 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000133}
134
135// FIXME : add this as a member of SkPath
caryclark55888e42016-07-18 10:01:36 -0700136bool SimplifyDebug(const SkPath& path, SkPath* result
137 SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
reed0dc4dd62015-03-24 13:55:33 -0700138 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
139 SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
140 : SkPath::kEvenOdd_FillType;
caryclark54359292015-03-26 07:52:43 -0700141 if (path.isConvex()) {
142 if (result != &path) {
143 *result = path;
144 }
145 result->setFillType(fillType);
146 return true;
147 }
reed0dc4dd62015-03-24 13:55:33 -0700148 // turn path into list of segments
Florin Malita14a64302017-05-24 14:53:44 -0400149 SkSTArenaAlloc<4096> allocator; // FIXME: constant-ize, tune
caryclark54359292015-03-26 07:52:43 -0700150 SkOpContour contour;
caryclark624637c2015-05-11 07:21:27 -0700151 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
caryclark55888e42016-07-18 10:01:36 -0700152 SkOpGlobalState globalState(contourList, &allocator
153 SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
154 SkOpCoincidence coincidence(&globalState);
Cary Clark918fb1f2016-11-15 13:22:25 -0500155#if DEBUG_DUMP_VERIFY
156#ifndef SK_DEBUG
157 const char* testName = "release";
158#endif
caryclark13260682016-10-24 05:10:14 -0700159 if (SkPathOpsDebug::gDumpOp) {
160 SkPathOpsDebug::DumpSimplify(path, testName);
161 }
162#endif
caryclark624637c2015-05-11 07:21:27 -0700163#if DEBUG_SORT
caryclark54359292015-03-26 07:52:43 -0700164 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
165#endif
Cary Clark5de52332018-08-30 12:58:23 -0400166 SkOpEdgeBuilder builder(path, contourList, &globalState);
caryclark55888e42016-07-18 10:01:36 -0700167 if (!builder.finish()) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000168 return false;
169 }
caryclark03b03ca2015-04-23 09:13:37 -0700170#if DEBUG_DUMP_SEGMENTS
caryclark26ad22a2015-10-16 09:03:38 -0700171 contour.dumpSegments();
caryclark54359292015-03-26 07:52:43 -0700172#endif
caryclark624637c2015-05-11 07:21:27 -0700173 if (!SortContourList(&contourList, false, false)) {
caryclark1049f122015-04-20 08:31:59 -0700174 result->reset();
175 result->setFillType(fillType);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000176 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000177 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000178 // find all intersections between segments
caryclark624637c2015-05-11 07:21:27 -0700179 SkOpContour* current = contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000180 do {
caryclark624637c2015-05-11 07:21:27 -0700181 SkOpContour* next = current;
caryclark55888e42016-07-18 10:01:36 -0700182 while (AddIntersectTs(current, next, &coincidence)
caryclark624637c2015-05-11 07:21:27 -0700183 && (next = next->next()));
184 } while ((current = current->next()));
caryclark54359292015-03-26 07:52:43 -0700185#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -0400186 globalState.setPhase(SkOpPhase::kWalking);
caryclark54359292015-03-26 07:52:43 -0700187#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -0400188 bool success = HandleCoincidence(contourList, &coincidence);
189#if DEBUG_COIN
190 globalState.debugAddToGlobalCoinDicts();
191#endif
192 if (!success) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000193 return false;
194 }
caryclark26ad22a2015-10-16 09:03:38 -0700195#if DEBUG_DUMP_ALIGNMENT
196 contour.dumpSegments("aligned");
197#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000198 // construct closed contours
caryclark1049f122015-04-20 08:31:59 -0700199 result->reset();
200 result->setFillType(fillType);
caryclark54359292015-03-26 07:52:43 -0700201 SkPathWriter wrapper(*result);
caryclarkeed356d2016-09-14 07:18:20 -0700202 if (builder.xorMask() == kWinding_PathOpsMask ? !bridgeWinding(contourList, &wrapper)
203 : !bridgeXor(contourList, &wrapper)) {
caryclarkef784fb2015-10-30 12:03:06 -0700204 return false;
205 }
caryclarkeed356d2016-09-14 07:18:20 -0700206 wrapper.assemble(); // if some edges could not be resolved, assemble remaining
caryclark@google.com66560ca2013-04-26 19:51:16 +0000207 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000208}
caryclark5b5ddd72015-05-18 05:12:56 -0700209
caryclark55888e42016-07-18 10:01:36 -0700210bool Simplify(const SkPath& path, SkPath* result) {
Cary Clark918fb1f2016-11-15 13:22:25 -0500211#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -0700212 if (SkPathOpsDebug::gVerifyOp) {
213 if (!SimplifyDebug(path, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) {
214 SkPathOpsDebug::ReportSimplifyFail(path);
215 return false;
216 }
217 SkPathOpsDebug::VerifySimplify(path, *result);
218 return true;
219 }
220#endif
caryclark55888e42016-07-18 10:01:36 -0700221 return SimplifyDebug(path, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
222}