blob: dcd75f166641b90f6c3b2f2fce9088d685cf9d57 [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
caryclark55888e42016-07-18 10:01:36 -070013static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple, bool* closable) {
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()) {
caryclarkdac1d172014-06-17 05:15:38 -070028 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) {
36 if (!unsortable && simple->hasMove()
37 && current->verb() != SkPath::kLine_Verb
38 && !simple->isClosed()) {
caryclarkef784fb2015-10-30 12:03:06 -070039 if (!current->addCurveTo(start, end, simple)) {
40 return false;
41 }
caryclark54359292015-03-26 07:52:43 -070042 if (!simple->isClosed()) {
caryclark55888e42016-07-18 10:01:36 -070043 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark54359292015-03-26 07:52:43 -070044 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000045 }
46 break;
47 }
48 #if DEBUG_FLOW
49 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -070050 current->debugID(), start->pt().fX, start->pt().fY,
51 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +000052 #endif
caryclarkef784fb2015-10-30 12:03:06 -070053 if (!current->addCurveTo(start, end, simple)) {
54 return false;
55 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000056 current = next;
caryclark54359292015-03-26 07:52:43 -070057 start = nextStart;
58 end = nextEnd;
59 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
60 if (current->activeWinding(start, end) && !simple->isClosed()) {
61 SkOpSpan* spanStart = start->starter(end);
62 if (!spanStart->done()) {
caryclarkef784fb2015-10-30 12:03:06 -070063 if (!current->addCurveTo(start, end, simple)) {
64 return false;
65 }
caryclark54359292015-03-26 07:52:43 -070066 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +000067 }
68 }
69 simple->close();
70 } else {
caryclark54359292015-03-26 07:52:43 -070071 SkOpSpanBase* last = current->markAndChaseDone(start, end);
72 if (last && !last->chased()) {
73 last->setChased(true);
caryclarkdac1d172014-06-17 05:15:38 -070074 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
caryclarkdac1d172014-06-17 05:15:38 -070075 *chase.append() = last;
76#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -070077 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
78 if (!last->final()) {
79 SkDebugf(" windSum=%d", last->upCast()->windSum());
80 }
81 SkDebugf("\n");
caryclarkdac1d172014-06-17 05:15:38 -070082#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +000083 }
84 }
caryclark54359292015-03-26 07:52:43 -070085 current = FindChase(&chase, &start, &end);
caryclark55888e42016-07-18 10:01:36 -070086 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +000087 if (!current) {
88 break;
89 }
90 } while (true);
91 } while (true);
caryclarkef784fb2015-10-30 12:03:06 -070092 *closable = !simple->someAssemblyRequired();
93 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +000094}
95
96// returns true if all edges were processed
caryclark55888e42016-07-18 10:01:36 -070097static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple, bool* closable) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000098 SkOpSegment* current;
caryclark54359292015-03-26 07:52:43 -070099 SkOpSpanBase* start;
100 SkOpSpanBase* end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000101 bool unsortable = false;
caryclarkef784fb2015-10-30 12:03:06 -0700102 *closable = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000103 while ((current = FindUndone(contourList, &start, &end))) {
104 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000105 if (!unsortable && current->done()) {
caryclark55888e42016-07-18 10:01:36 -0700106 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000107 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000108 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -0700109 SkOpSpanBase* nextStart = start;
110 SkOpSpanBase* nextEnd = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000111 SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
112 if (!next) {
113 if (!unsortable && simple->hasMove()
114 && current->verb() != SkPath::kLine_Verb
115 && !simple->isClosed()) {
caryclarkef784fb2015-10-30 12:03:06 -0700116 if (!current->addCurveTo(start, end, simple)) {
117 return false;
118 }
caryclark54359292015-03-26 07:52:43 -0700119 if (!simple->isClosed()) {
caryclark55888e42016-07-18 10:01:36 -0700120 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark54359292015-03-26 07:52:43 -0700121 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000122 }
123 break;
124 }
125 #if DEBUG_FLOW
126 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -0700127 current->debugID(), start->pt().fX, start->pt().fY,
128 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000129 #endif
caryclarkef784fb2015-10-30 12:03:06 -0700130 if (!current->addCurveTo(start, end, simple)) {
131 return false;
132 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000133 current = next;
134 start = nextStart;
135 end = nextEnd;
caryclark54359292015-03-26 07:52:43 -0700136 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000137 if (!simple->isClosed()) {
138 SkASSERT(unsortable);
caryclark54359292015-03-26 07:52:43 -0700139 SkOpSpan* spanStart = start->starter(end);
140 if (!spanStart->done()) {
caryclarkef784fb2015-10-30 12:03:06 -0700141 if (!current->addCurveTo(start, end, simple)) {
142 return false;
143 }
caryclark54359292015-03-26 07:52:43 -0700144 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000145 }
caryclarkef784fb2015-10-30 12:03:06 -0700146 *closable = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000147 }
148 simple->close();
caryclark55888e42016-07-18 10:01:36 -0700149 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000150 }
caryclarkef784fb2015-10-30 12:03:06 -0700151 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000152}
153
154// FIXME : add this as a member of SkPath
caryclark55888e42016-07-18 10:01:36 -0700155bool SimplifyDebug(const SkPath& path, SkPath* result
156 SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
reed0dc4dd62015-03-24 13:55:33 -0700157 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
158 SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
159 : SkPath::kEvenOdd_FillType;
caryclark54359292015-03-26 07:52:43 -0700160 if (path.isConvex()) {
161 if (result != &path) {
162 *result = path;
163 }
164 result->setFillType(fillType);
165 return true;
166 }
reed0dc4dd62015-03-24 13:55:33 -0700167 // turn path into list of segments
caryclark55888e42016-07-18 10:01:36 -0700168 SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
caryclark54359292015-03-26 07:52:43 -0700169 SkOpContour contour;
caryclark624637c2015-05-11 07:21:27 -0700170 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
caryclark55888e42016-07-18 10:01:36 -0700171 SkOpGlobalState globalState(contourList, &allocator
172 SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
173 SkOpCoincidence coincidence(&globalState);
174 SkScalar scaleFactor = ScaleFactor(path);
175 SkPath scaledPath;
176 const SkPath* workingPath;
177 if (scaleFactor > SK_Scalar1) {
178 ScalePath(path, 1.f / scaleFactor, &scaledPath);
179 workingPath = &scaledPath;
180 } else {
181 workingPath = &path;
182 }
caryclark624637c2015-05-11 07:21:27 -0700183#if DEBUG_SORT
caryclark54359292015-03-26 07:52:43 -0700184 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
185#endif
caryclark55888e42016-07-18 10:01:36 -0700186 SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
187 if (!builder.finish()) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000188 return false;
189 }
caryclark03b03ca2015-04-23 09:13:37 -0700190#if DEBUG_DUMP_SEGMENTS
caryclark26ad22a2015-10-16 09:03:38 -0700191 contour.dumpSegments();
caryclark54359292015-03-26 07:52:43 -0700192#endif
caryclark624637c2015-05-11 07:21:27 -0700193 if (!SortContourList(&contourList, false, false)) {
caryclark1049f122015-04-20 08:31:59 -0700194 result->reset();
195 result->setFillType(fillType);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000196 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000197 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000198 // find all intersections between segments
caryclark624637c2015-05-11 07:21:27 -0700199 SkOpContour* current = contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000200 do {
caryclark624637c2015-05-11 07:21:27 -0700201 SkOpContour* next = current;
caryclark55888e42016-07-18 10:01:36 -0700202 while (AddIntersectTs(current, next, &coincidence)
caryclark624637c2015-05-11 07:21:27 -0700203 && (next = next->next()));
204 } while ((current = current->next()));
caryclark54359292015-03-26 07:52:43 -0700205#if DEBUG_VALIDATE
206 globalState.setPhase(SkOpGlobalState::kWalking);
207#endif
caryclark55888e42016-07-18 10:01:36 -0700208 if (!HandleCoincidence(contourList, &coincidence)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000209 return false;
210 }
caryclark26ad22a2015-10-16 09:03:38 -0700211#if DEBUG_DUMP_ALIGNMENT
212 contour.dumpSegments("aligned");
213#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000214 // construct closed contours
caryclark1049f122015-04-20 08:31:59 -0700215 result->reset();
216 result->setFillType(fillType);
caryclark54359292015-03-26 07:52:43 -0700217 SkPathWriter wrapper(*result);
robertphillips73add932016-04-07 09:01:20 -0700218 bool closable SK_INIT_TO_AVOID_WARNING;
caryclarkef784fb2015-10-30 12:03:06 -0700219 if (builder.xorMask() == kWinding_PathOpsMask
caryclark55888e42016-07-18 10:01:36 -0700220 ? !bridgeWinding(contourList, &wrapper, &closable)
221 : !bridgeXor(contourList, &wrapper, &closable)) {
caryclarkef784fb2015-10-30 12:03:06 -0700222 return false;
223 }
224 if (!closable)
caryclark@google.com07393ca2013-04-08 11:47:37 +0000225 { // if some edges could not be resolved, assemble remaining fragments
226 SkPath temp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000227 temp.setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000228 SkPathWriter assembled(temp);
caryclark54359292015-03-26 07:52:43 -0700229 Assemble(wrapper, &assembled);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000230 *result = *assembled.nativePath();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000231 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000232 }
caryclark55888e42016-07-18 10:01:36 -0700233 if (scaleFactor > 1) {
234 ScalePath(*result, scaleFactor, result);
235 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000236 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000237}
caryclark5b5ddd72015-05-18 05:12:56 -0700238
caryclark55888e42016-07-18 10:01:36 -0700239bool Simplify(const SkPath& path, SkPath* result) {
240 return SimplifyDebug(path, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
241}