blob: 8d525fa5f7c68f813e613ef435d6b05acd737b7f [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 bool bridgeWinding(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simple,
14 SkChunkAlloc* allocator) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000015 bool firstContour = true;
16 bool unsortable = false;
17 bool topUnsortable = false;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000018 bool firstPass = true;
19 SkPoint lastTopLeft;
caryclark@google.com07393ca2013-04-08 11:47:37 +000020 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
21 do {
caryclark54359292015-03-26 07:52:43 -070022 SkOpSpanBase* start;
23 SkOpSpanBase* end;
caryclark@google.com07393ca2013-04-08 11:47:37 +000024 bool topDone;
caryclarkdac1d172014-06-17 05:15:38 -070025 bool onlyVertical = false;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000026 lastTopLeft = topLeft;
caryclark54359292015-03-26 07:52:43 -070027 SkOpSegment* current = FindSortableTop(contourList, firstPass, SkOpAngle::kUnaryWinding,
28 &firstContour, &start, &end, &topLeft, &topUnsortable, &topDone, &onlyVertical,
29 allocator);
caryclark@google.com07393ca2013-04-08 11:47:37 +000030 if (!current) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000031 if ((!topUnsortable || firstPass) && !topDone) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000032 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
caryclark54359292015-03-26 07:52:43 -070033 if (lastTopLeft.fX == SK_ScalarMin && lastTopLeft.fY == SK_ScalarMin) {
34 if (firstPass) {
35 firstPass = false;
36 } else {
37 break;
38 }
39 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000040 topLeft.fX = topLeft.fY = SK_ScalarMin;
41 continue;
42 }
43 break;
caryclarkdac1d172014-06-17 05:15:38 -070044 } else if (onlyVertical) {
45 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +000046 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000047 firstPass = !topUnsortable || lastTopLeft != topLeft;
caryclark54359292015-03-26 07:52:43 -070048 SkTDArray<SkOpSpanBase*> chase;
caryclark@google.com07393ca2013-04-08 11:47:37 +000049 do {
caryclark54359292015-03-26 07:52:43 -070050 if (current->activeWinding(start, end)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000051 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +000052 if (!unsortable && current->done()) {
caryclarkdac1d172014-06-17 05:15:38 -070053 break;
caryclark@google.coma5e55922013-05-07 18:51:31 +000054 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000055 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -070056 SkOpSpanBase* nextStart = start;
57 SkOpSpanBase* nextEnd = end;
caryclarkdac1d172014-06-17 05:15:38 -070058 SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
caryclark@google.com07393ca2013-04-08 11:47:37 +000059 &unsortable);
60 if (!next) {
61 if (!unsortable && simple->hasMove()
62 && current->verb() != SkPath::kLine_Verb
63 && !simple->isClosed()) {
caryclark54359292015-03-26 07:52:43 -070064 current->addCurveTo(start, end, simple, true);
65 #if DEBUG_ACTIVE_SPANS
66 if (!simple->isClosed()) {
67 DebugShowActiveSpans(contourList);
68 }
69 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +000070 }
71 break;
72 }
73 #if DEBUG_FLOW
74 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -070075 current->debugID(), start->pt().fX, start->pt().fY,
76 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +000077 #endif
caryclark54359292015-03-26 07:52:43 -070078 current->addCurveTo(start, end, simple, true);
caryclark@google.com07393ca2013-04-08 11:47:37 +000079 current = next;
caryclark54359292015-03-26 07:52:43 -070080 start = nextStart;
81 end = nextEnd;
82 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
83 if (current->activeWinding(start, end) && !simple->isClosed()) {
84 SkOpSpan* spanStart = start->starter(end);
85 if (!spanStart->done()) {
86 current->addCurveTo(start, end, simple, true);
87 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +000088 }
89 }
90 simple->close();
91 } else {
caryclark54359292015-03-26 07:52:43 -070092 SkOpSpanBase* last = current->markAndChaseDone(start, end);
93 if (last && !last->chased()) {
94 last->setChased(true);
caryclarkdac1d172014-06-17 05:15:38 -070095 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000096 // assert that last isn't already in array
caryclarkdac1d172014-06-17 05:15:38 -070097 *chase.append() = last;
98#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -070099 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
100 if (!last->final()) {
101 SkDebugf(" windSum=%d", last->upCast()->windSum());
102 }
103 SkDebugf("\n");
caryclarkdac1d172014-06-17 05:15:38 -0700104#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000105 }
106 }
caryclark54359292015-03-26 07:52:43 -0700107 current = FindChase(&chase, &start, &end);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000108 #if DEBUG_ACTIVE_SPANS
109 DebugShowActiveSpans(contourList);
110 #endif
111 if (!current) {
112 break;
113 }
114 } while (true);
115 } while (true);
116 return simple->someAssemblyRequired();
117}
118
119// returns true if all edges were processed
caryclark54359292015-03-26 07:52:43 -0700120static bool bridgeXor(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simple,
121 SkChunkAlloc* allocator) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000122 SkOpSegment* current;
caryclark54359292015-03-26 07:52:43 -0700123 SkOpSpanBase* start;
124 SkOpSpanBase* end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000125 bool unsortable = false;
126 bool closable = true;
127 while ((current = FindUndone(contourList, &start, &end))) {
128 do {
129 #if DEBUG_ACTIVE_SPANS
130 if (!unsortable && current->done()) {
131 DebugShowActiveSpans(contourList);
132 }
133 #endif
134 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -0700135 SkOpSpanBase* nextStart = start;
136 SkOpSpanBase* nextEnd = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000137 SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
138 if (!next) {
139 if (!unsortable && simple->hasMove()
140 && current->verb() != SkPath::kLine_Verb
141 && !simple->isClosed()) {
142 current->addCurveTo(start, end, simple, true);
caryclark54359292015-03-26 07:52:43 -0700143 #if DEBUG_ACTIVE_SPANS
144 if (!simple->isClosed()) {
145 DebugShowActiveSpans(contourList);
146 }
147 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000148 }
149 break;
150 }
151 #if DEBUG_FLOW
152 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -0700153 current->debugID(), start->pt().fX, start->pt().fY,
154 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000155 #endif
156 current->addCurveTo(start, end, simple, true);
157 current = next;
158 start = nextStart;
159 end = nextEnd;
caryclark54359292015-03-26 07:52:43 -0700160 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000161 if (!simple->isClosed()) {
162 SkASSERT(unsortable);
caryclark54359292015-03-26 07:52:43 -0700163 SkOpSpan* spanStart = start->starter(end);
164 if (!spanStart->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000165 current->addCurveTo(start, end, simple, true);
caryclark54359292015-03-26 07:52:43 -0700166 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000167 }
168 closable = false;
169 }
170 simple->close();
171 #if DEBUG_ACTIVE_SPANS
172 DebugShowActiveSpans(contourList);
173 #endif
174 }
175 return closable;
176}
177
178// FIXME : add this as a member of SkPath
caryclark@google.com66560ca2013-04-26 19:51:16 +0000179bool Simplify(const SkPath& path, SkPath* result) {
caryclark54359292015-03-26 07:52:43 -0700180 SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
reed0dc4dd62015-03-24 13:55:33 -0700181 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
182 SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
183 : SkPath::kEvenOdd_FillType;
caryclark54359292015-03-26 07:52:43 -0700184 if (path.isConvex()) {
185 if (result != &path) {
186 *result = path;
187 }
188 result->setFillType(fillType);
189 return true;
190 }
reed0dc4dd62015-03-24 13:55:33 -0700191 // turn path into list of segments
caryclark54359292015-03-26 07:52:43 -0700192 SkOpCoincidence coincidence;
193 SkOpContour contour;
caryclark1049f122015-04-20 08:31:59 -0700194 SkOpGlobalState globalState(&coincidence SkDEBUGPARAMS(&contour));
caryclark54359292015-03-26 07:52:43 -0700195#if DEBUG_SORT || DEBUG_SWAP_TOP
196 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
197#endif
198 SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
199 if (!builder.finish(&allocator)) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000200 return false;
201 }
caryclark54359292015-03-26 07:52:43 -0700202#if !FORCE_RELEASE
203 contour.dumpSegments((SkPathOp) -1);
204#endif
caryclark54359292015-03-26 07:52:43 -0700205 SkTDArray<SkOpContour* > contourList;
206 MakeContourList(&contour, contourList, false, false);
207 SkOpContour** currentPtr = contourList.begin();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000208 if (!currentPtr) {
caryclark1049f122015-04-20 08:31:59 -0700209 result->reset();
210 result->setFillType(fillType);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000211 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000212 }
caryclark54359292015-03-26 07:52:43 -0700213 if ((*currentPtr)->count() == 0) {
214 SkASSERT((*currentPtr)->next() == NULL);
caryclark1049f122015-04-20 08:31:59 -0700215 result->reset();
216 result->setFillType(fillType);
caryclark54359292015-03-26 07:52:43 -0700217 return true;
218 }
219 SkOpContour** listEnd2 = contourList.end();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000220 // find all intersections between segments
221 do {
222 SkOpContour** nextPtr = currentPtr;
223 SkOpContour* current = *currentPtr++;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000224 SkOpContour* next;
225 do {
226 next = *nextPtr++;
caryclark54359292015-03-26 07:52:43 -0700227 } while (AddIntersectTs(current, next, &coincidence, &allocator) && nextPtr != listEnd2);
228 } while (currentPtr != listEnd2);
229#if DEBUG_VALIDATE
230 globalState.setPhase(SkOpGlobalState::kWalking);
231#endif
232 if (!HandleCoincidence(&contourList, &coincidence, &allocator, &globalState)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000233 return false;
234 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000235 // construct closed contours
caryclark1049f122015-04-20 08:31:59 -0700236 result->reset();
237 result->setFillType(fillType);
caryclark54359292015-03-26 07:52:43 -0700238 SkPathWriter wrapper(*result);
239 if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &wrapper, &allocator)
240 : !bridgeXor(contourList, &wrapper, &allocator))
caryclark@google.com07393ca2013-04-08 11:47:37 +0000241 { // if some edges could not be resolved, assemble remaining fragments
242 SkPath temp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000243 temp.setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000244 SkPathWriter assembled(temp);
caryclark54359292015-03-26 07:52:43 -0700245 Assemble(wrapper, &assembled);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000246 *result = *assembled.nativePath();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000247 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000248 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000249 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000250}