xref: /aosp_15_r20/external/skia/src/pathops/SkPathOpsSimplify.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2012 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathTypes.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/pathops/SkPathOps.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkPoint_impl.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTDArray.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkArenaAlloc.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkAddIntersections.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkOpCoincidence.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkOpContour.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkOpEdgeBuilder.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkOpSegment.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkOpSpan.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkPathOpsCommon.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkPathOpsTypes.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkPathWriter.h"
23*c8dee2aaSAndroid Build Coastguard Worker 
bridgeWinding(SkOpContourHead * contourList,SkPathWriter * writer)24*c8dee2aaSAndroid Build Coastguard Worker static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* writer) {
25*c8dee2aaSAndroid Build Coastguard Worker     bool unsortable = false;
26*c8dee2aaSAndroid Build Coastguard Worker     do {
27*c8dee2aaSAndroid Build Coastguard Worker         SkOpSpan* span = FindSortableTop(contourList);
28*c8dee2aaSAndroid Build Coastguard Worker         if (!span) {
29*c8dee2aaSAndroid Build Coastguard Worker             break;
30*c8dee2aaSAndroid Build Coastguard Worker         }
31*c8dee2aaSAndroid Build Coastguard Worker         SkOpSegment* current = span->segment();
32*c8dee2aaSAndroid Build Coastguard Worker         SkOpSpanBase* start = span->next();
33*c8dee2aaSAndroid Build Coastguard Worker         SkOpSpanBase* end = span;
34*c8dee2aaSAndroid Build Coastguard Worker         SkTDArray<SkOpSpanBase*> chase;
35*c8dee2aaSAndroid Build Coastguard Worker         do {
36*c8dee2aaSAndroid Build Coastguard Worker             if (current->activeWinding(start, end)) {
37*c8dee2aaSAndroid Build Coastguard Worker                 do {
38*c8dee2aaSAndroid Build Coastguard Worker                     if (!unsortable && current->done()) {
39*c8dee2aaSAndroid Build Coastguard Worker                         break;
40*c8dee2aaSAndroid Build Coastguard Worker                     }
41*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(unsortable || !current->done());
42*c8dee2aaSAndroid Build Coastguard Worker                     SkOpSpanBase* nextStart = start;
43*c8dee2aaSAndroid Build Coastguard Worker                     SkOpSpanBase* nextEnd = end;
44*c8dee2aaSAndroid Build Coastguard Worker                     SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
45*c8dee2aaSAndroid Build Coastguard Worker                             &unsortable);
46*c8dee2aaSAndroid Build Coastguard Worker                     if (!next) {
47*c8dee2aaSAndroid Build Coastguard Worker                         break;
48*c8dee2aaSAndroid Build Coastguard Worker                     }
49*c8dee2aaSAndroid Build Coastguard Worker         #if DEBUG_FLOW
50*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
51*c8dee2aaSAndroid Build Coastguard Worker                     current->debugID(), start->pt().fX, start->pt().fY,
52*c8dee2aaSAndroid Build Coastguard Worker                     end->pt().fX, end->pt().fY);
53*c8dee2aaSAndroid Build Coastguard Worker         #endif
54*c8dee2aaSAndroid Build Coastguard Worker                     if (!current->addCurveTo(start, end, writer)) {
55*c8dee2aaSAndroid Build Coastguard Worker                         return false;
56*c8dee2aaSAndroid Build Coastguard Worker                     }
57*c8dee2aaSAndroid Build Coastguard Worker                     current = next;
58*c8dee2aaSAndroid Build Coastguard Worker                     start = nextStart;
59*c8dee2aaSAndroid Build Coastguard Worker                     end = nextEnd;
60*c8dee2aaSAndroid Build Coastguard Worker                 } while (!writer->isClosed() && (!unsortable || !start->starter(end)->done()));
61*c8dee2aaSAndroid Build Coastguard Worker                 if (current->activeWinding(start, end) && !writer->isClosed()) {
62*c8dee2aaSAndroid Build Coastguard Worker                     SkOpSpan* spanStart = start->starter(end);
63*c8dee2aaSAndroid Build Coastguard Worker                     if (!spanStart->done()) {
64*c8dee2aaSAndroid Build Coastguard Worker                         if (!current->addCurveTo(start, end, writer)) {
65*c8dee2aaSAndroid Build Coastguard Worker                             return false;
66*c8dee2aaSAndroid Build Coastguard Worker                         }
67*c8dee2aaSAndroid Build Coastguard Worker                         current->markDone(spanStart);
68*c8dee2aaSAndroid Build Coastguard Worker                     }
69*c8dee2aaSAndroid Build Coastguard Worker                 }
70*c8dee2aaSAndroid Build Coastguard Worker                 writer->finishContour();
71*c8dee2aaSAndroid Build Coastguard Worker             } else {
72*c8dee2aaSAndroid Build Coastguard Worker                 SkOpSpanBase* last;
73*c8dee2aaSAndroid Build Coastguard Worker                  if (!current->markAndChaseDone(start, end, &last)) {
74*c8dee2aaSAndroid Build Coastguard Worker                     return false;
75*c8dee2aaSAndroid Build Coastguard Worker                 }
76*c8dee2aaSAndroid Build Coastguard Worker                 if (last && !last->chased()) {
77*c8dee2aaSAndroid Build Coastguard Worker                     last->setChased(true);
78*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
79*c8dee2aaSAndroid Build Coastguard Worker                     *chase.append() = last;
80*c8dee2aaSAndroid Build Coastguard Worker #if DEBUG_WINDING
81*c8dee2aaSAndroid Build Coastguard Worker                     SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
82*c8dee2aaSAndroid Build Coastguard Worker                     if (!last->final()) {
83*c8dee2aaSAndroid Build Coastguard Worker                          SkDebugf(" windSum=%d", last->upCast()->windSum());
84*c8dee2aaSAndroid Build Coastguard Worker                     }
85*c8dee2aaSAndroid Build Coastguard Worker                     SkDebugf("\n");
86*c8dee2aaSAndroid Build Coastguard Worker #endif
87*c8dee2aaSAndroid Build Coastguard Worker                 }
88*c8dee2aaSAndroid Build Coastguard Worker             }
89*c8dee2aaSAndroid Build Coastguard Worker             current = FindChase(&chase, &start, &end);
90*c8dee2aaSAndroid Build Coastguard Worker             SkPathOpsDebug::ShowActiveSpans(contourList);
91*c8dee2aaSAndroid Build Coastguard Worker             if (!current) {
92*c8dee2aaSAndroid Build Coastguard Worker                 break;
93*c8dee2aaSAndroid Build Coastguard Worker             }
94*c8dee2aaSAndroid Build Coastguard Worker         } while (true);
95*c8dee2aaSAndroid Build Coastguard Worker     } while (true);
96*c8dee2aaSAndroid Build Coastguard Worker     return true;
97*c8dee2aaSAndroid Build Coastguard Worker }
98*c8dee2aaSAndroid Build Coastguard Worker 
99*c8dee2aaSAndroid Build Coastguard Worker // returns true if all edges were processed
bridgeXor(SkOpContourHead * contourList,SkPathWriter * writer)100*c8dee2aaSAndroid Build Coastguard Worker static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* writer) {
101*c8dee2aaSAndroid Build Coastguard Worker     bool unsortable = false;
102*c8dee2aaSAndroid Build Coastguard Worker     int safetyNet = 1000000;
103*c8dee2aaSAndroid Build Coastguard Worker     do {
104*c8dee2aaSAndroid Build Coastguard Worker         SkOpSpan* span = FindUndone(contourList);
105*c8dee2aaSAndroid Build Coastguard Worker         if (!span) {
106*c8dee2aaSAndroid Build Coastguard Worker             break;
107*c8dee2aaSAndroid Build Coastguard Worker         }
108*c8dee2aaSAndroid Build Coastguard Worker         SkOpSegment* current = span->segment();
109*c8dee2aaSAndroid Build Coastguard Worker         SkOpSpanBase* start = span->next();
110*c8dee2aaSAndroid Build Coastguard Worker         SkOpSpanBase* end = span;
111*c8dee2aaSAndroid Build Coastguard Worker         do {
112*c8dee2aaSAndroid Build Coastguard Worker             if (--safetyNet < 0) {
113*c8dee2aaSAndroid Build Coastguard Worker                 return false;
114*c8dee2aaSAndroid Build Coastguard Worker             }
115*c8dee2aaSAndroid Build Coastguard Worker             if (!unsortable && current->done()) {
116*c8dee2aaSAndroid Build Coastguard Worker                 break;
117*c8dee2aaSAndroid Build Coastguard Worker             }
118*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(unsortable || !current->done());
119*c8dee2aaSAndroid Build Coastguard Worker             SkOpSpanBase* nextStart = start;
120*c8dee2aaSAndroid Build Coastguard Worker             SkOpSpanBase* nextEnd = end;
121*c8dee2aaSAndroid Build Coastguard Worker             SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd,
122*c8dee2aaSAndroid Build Coastguard Worker                     &unsortable);
123*c8dee2aaSAndroid Build Coastguard Worker             if (!next) {
124*c8dee2aaSAndroid Build Coastguard Worker                 break;
125*c8dee2aaSAndroid Build Coastguard Worker             }
126*c8dee2aaSAndroid Build Coastguard Worker         #if DEBUG_FLOW
127*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
128*c8dee2aaSAndroid Build Coastguard Worker                     current->debugID(), start->pt().fX, start->pt().fY,
129*c8dee2aaSAndroid Build Coastguard Worker                     end->pt().fX, end->pt().fY);
130*c8dee2aaSAndroid Build Coastguard Worker         #endif
131*c8dee2aaSAndroid Build Coastguard Worker             if (!current->addCurveTo(start, end, writer)) {
132*c8dee2aaSAndroid Build Coastguard Worker                 return false;
133*c8dee2aaSAndroid Build Coastguard Worker             }
134*c8dee2aaSAndroid Build Coastguard Worker             current = next;
135*c8dee2aaSAndroid Build Coastguard Worker             start = nextStart;
136*c8dee2aaSAndroid Build Coastguard Worker             end = nextEnd;
137*c8dee2aaSAndroid Build Coastguard Worker         } while (!writer->isClosed() && (!unsortable || !start->starter(end)->done()));
138*c8dee2aaSAndroid Build Coastguard Worker         if (!writer->isClosed()) {
139*c8dee2aaSAndroid Build Coastguard Worker             SkOpSpan* spanStart = start->starter(end);
140*c8dee2aaSAndroid Build Coastguard Worker             if (!spanStart->done()) {
141*c8dee2aaSAndroid Build Coastguard Worker                 return false;
142*c8dee2aaSAndroid Build Coastguard Worker             }
143*c8dee2aaSAndroid Build Coastguard Worker         }
144*c8dee2aaSAndroid Build Coastguard Worker         writer->finishContour();
145*c8dee2aaSAndroid Build Coastguard Worker         SkPathOpsDebug::ShowActiveSpans(contourList);
146*c8dee2aaSAndroid Build Coastguard Worker     } while (true);
147*c8dee2aaSAndroid Build Coastguard Worker     return true;
148*c8dee2aaSAndroid Build Coastguard Worker }
149*c8dee2aaSAndroid Build Coastguard Worker 
path_is_trivial(const SkPath & path)150*c8dee2aaSAndroid Build Coastguard Worker static bool path_is_trivial(const SkPath& path) {
151*c8dee2aaSAndroid Build Coastguard Worker     SkPath::Iter iter(path, true);
152*c8dee2aaSAndroid Build Coastguard Worker 
153*c8dee2aaSAndroid Build Coastguard Worker     SkPath::Verb verb;
154*c8dee2aaSAndroid Build Coastguard Worker     SkPoint points[4];
155*c8dee2aaSAndroid Build Coastguard Worker 
156*c8dee2aaSAndroid Build Coastguard Worker     class Trivializer {
157*c8dee2aaSAndroid Build Coastguard Worker         SkPoint prevPt{0,0};
158*c8dee2aaSAndroid Build Coastguard Worker         SkVector prevVec{0,0};
159*c8dee2aaSAndroid Build Coastguard Worker     public:
160*c8dee2aaSAndroid Build Coastguard Worker         void moveTo(const SkPoint& currPt) {
161*c8dee2aaSAndroid Build Coastguard Worker             prevPt = currPt;
162*c8dee2aaSAndroid Build Coastguard Worker             prevVec = {0, 0};
163*c8dee2aaSAndroid Build Coastguard Worker         }
164*c8dee2aaSAndroid Build Coastguard Worker         bool addTrivialContourPoint(const SkPoint& currPt) {
165*c8dee2aaSAndroid Build Coastguard Worker             if (currPt == prevPt) {
166*c8dee2aaSAndroid Build Coastguard Worker                 return true;
167*c8dee2aaSAndroid Build Coastguard Worker             }
168*c8dee2aaSAndroid Build Coastguard Worker             // There are more numericaly stable ways of determining if many points are co-linear.
169*c8dee2aaSAndroid Build Coastguard Worker             // However, this mirrors SkPath's Convexicator for consistency.
170*c8dee2aaSAndroid Build Coastguard Worker             SkVector currVec = currPt - prevPt;
171*c8dee2aaSAndroid Build Coastguard Worker             if (SkPoint::CrossProduct(prevVec, currVec) != 0) {
172*c8dee2aaSAndroid Build Coastguard Worker                 return false;
173*c8dee2aaSAndroid Build Coastguard Worker             }
174*c8dee2aaSAndroid Build Coastguard Worker             prevVec = currVec;
175*c8dee2aaSAndroid Build Coastguard Worker             prevPt = currPt;
176*c8dee2aaSAndroid Build Coastguard Worker             return true;
177*c8dee2aaSAndroid Build Coastguard Worker         }
178*c8dee2aaSAndroid Build Coastguard Worker     } triv;
179*c8dee2aaSAndroid Build Coastguard Worker 
180*c8dee2aaSAndroid Build Coastguard Worker     while ((verb = iter.next(points)) != SkPath::kDone_Verb) {
181*c8dee2aaSAndroid Build Coastguard Worker         switch (verb) {
182*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kMove_Verb:
183*c8dee2aaSAndroid Build Coastguard Worker                 triv.moveTo(points[0]);
184*c8dee2aaSAndroid Build Coastguard Worker                 break;
185*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kCubic_Verb:
186*c8dee2aaSAndroid Build Coastguard Worker                 if (!triv.addTrivialContourPoint(points[3])) { return false; }
187*c8dee2aaSAndroid Build Coastguard Worker                 [[fallthrough]];
188*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kConic_Verb:
189*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kQuad_Verb:
190*c8dee2aaSAndroid Build Coastguard Worker                 if (!triv.addTrivialContourPoint(points[2])) { return false; }
191*c8dee2aaSAndroid Build Coastguard Worker                 [[fallthrough]];
192*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kLine_Verb:
193*c8dee2aaSAndroid Build Coastguard Worker                 if (!triv.addTrivialContourPoint(points[1])) { return false; }
194*c8dee2aaSAndroid Build Coastguard Worker                 if (!triv.addTrivialContourPoint(points[0])) { return false; }
195*c8dee2aaSAndroid Build Coastguard Worker                 break;
196*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kClose_Verb:
197*c8dee2aaSAndroid Build Coastguard Worker             case SkPath::kDone_Verb:
198*c8dee2aaSAndroid Build Coastguard Worker                 break;
199*c8dee2aaSAndroid Build Coastguard Worker         }
200*c8dee2aaSAndroid Build Coastguard Worker     }
201*c8dee2aaSAndroid Build Coastguard Worker     return true;
202*c8dee2aaSAndroid Build Coastguard Worker }
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker // FIXME : add this as a member of SkPath
SimplifyDebug(const SkPath & path,SkPath * result SkDEBUGPARAMS (bool skipAssert)SkDEBUGPARAMS (const char * testName))205*c8dee2aaSAndroid Build Coastguard Worker bool SimplifyDebug(const SkPath& path, SkPath* result
206*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
207*c8dee2aaSAndroid Build Coastguard Worker     // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
208*c8dee2aaSAndroid Build Coastguard Worker     SkPathFillType fillType = path.isInverseFillType() ? SkPathFillType::kInverseEvenOdd
209*c8dee2aaSAndroid Build Coastguard Worker             : SkPathFillType::kEvenOdd;
210*c8dee2aaSAndroid Build Coastguard Worker 
211*c8dee2aaSAndroid Build Coastguard Worker     if (path.isConvex()) {
212*c8dee2aaSAndroid Build Coastguard Worker         // If the path is trivially convex, simplify to empty.
213*c8dee2aaSAndroid Build Coastguard Worker         if (path_is_trivial(path)) {
214*c8dee2aaSAndroid Build Coastguard Worker             result->reset();
215*c8dee2aaSAndroid Build Coastguard Worker         } else if (result != &path) {
216*c8dee2aaSAndroid Build Coastguard Worker             *result = path;
217*c8dee2aaSAndroid Build Coastguard Worker         }
218*c8dee2aaSAndroid Build Coastguard Worker         result->setFillType(fillType);
219*c8dee2aaSAndroid Build Coastguard Worker         return true;
220*c8dee2aaSAndroid Build Coastguard Worker     }
221*c8dee2aaSAndroid Build Coastguard Worker     // turn path into list of segments
222*c8dee2aaSAndroid Build Coastguard Worker     SkSTArenaAlloc<4096> allocator;  // FIXME: constant-ize, tune
223*c8dee2aaSAndroid Build Coastguard Worker     SkOpContour contour;
224*c8dee2aaSAndroid Build Coastguard Worker     SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
225*c8dee2aaSAndroid Build Coastguard Worker     SkOpGlobalState globalState(contourList, &allocator
226*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
227*c8dee2aaSAndroid Build Coastguard Worker     SkOpCoincidence coincidence(&globalState);
228*c8dee2aaSAndroid Build Coastguard Worker #if DEBUG_DUMP_VERIFY
229*c8dee2aaSAndroid Build Coastguard Worker #ifndef SK_DEBUG
230*c8dee2aaSAndroid Build Coastguard Worker     const char* testName = "release";
231*c8dee2aaSAndroid Build Coastguard Worker #endif
232*c8dee2aaSAndroid Build Coastguard Worker     if (SkPathOpsDebug::gDumpOp) {
233*c8dee2aaSAndroid Build Coastguard Worker         DumpSimplify(path, testName);
234*c8dee2aaSAndroid Build Coastguard Worker     }
235*c8dee2aaSAndroid Build Coastguard Worker #endif
236*c8dee2aaSAndroid Build Coastguard Worker #if DEBUG_SORT
237*c8dee2aaSAndroid Build Coastguard Worker     SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
238*c8dee2aaSAndroid Build Coastguard Worker #endif
239*c8dee2aaSAndroid Build Coastguard Worker     SkOpEdgeBuilder builder(path, contourList, &globalState);
240*c8dee2aaSAndroid Build Coastguard Worker     if (!builder.finish()) {
241*c8dee2aaSAndroid Build Coastguard Worker         return false;
242*c8dee2aaSAndroid Build Coastguard Worker     }
243*c8dee2aaSAndroid Build Coastguard Worker #if DEBUG_DUMP_SEGMENTS
244*c8dee2aaSAndroid Build Coastguard Worker     contour.dumpSegments();
245*c8dee2aaSAndroid Build Coastguard Worker #endif
246*c8dee2aaSAndroid Build Coastguard Worker     if (!SortContourList(&contourList, false, false)) {
247*c8dee2aaSAndroid Build Coastguard Worker         result->reset();
248*c8dee2aaSAndroid Build Coastguard Worker         result->setFillType(fillType);
249*c8dee2aaSAndroid Build Coastguard Worker         return true;
250*c8dee2aaSAndroid Build Coastguard Worker     }
251*c8dee2aaSAndroid Build Coastguard Worker     // find all intersections between segments
252*c8dee2aaSAndroid Build Coastguard Worker     SkOpContour* current = contourList;
253*c8dee2aaSAndroid Build Coastguard Worker     do {
254*c8dee2aaSAndroid Build Coastguard Worker         SkOpContour* next = current;
255*c8dee2aaSAndroid Build Coastguard Worker         while (AddIntersectTs(current, next, &coincidence)
256*c8dee2aaSAndroid Build Coastguard Worker                 && (next = next->next()));
257*c8dee2aaSAndroid Build Coastguard Worker     } while ((current = current->next()));
258*c8dee2aaSAndroid Build Coastguard Worker #if DEBUG_VALIDATE
259*c8dee2aaSAndroid Build Coastguard Worker     globalState.setPhase(SkOpPhase::kWalking);
260*c8dee2aaSAndroid Build Coastguard Worker #endif
261*c8dee2aaSAndroid Build Coastguard Worker     bool success = HandleCoincidence(contourList, &coincidence);
262*c8dee2aaSAndroid Build Coastguard Worker #if DEBUG_COIN
263*c8dee2aaSAndroid Build Coastguard Worker     globalState.debugAddToGlobalCoinDicts();
264*c8dee2aaSAndroid Build Coastguard Worker #endif
265*c8dee2aaSAndroid Build Coastguard Worker     if (!success) {
266*c8dee2aaSAndroid Build Coastguard Worker         return false;
267*c8dee2aaSAndroid Build Coastguard Worker     }
268*c8dee2aaSAndroid Build Coastguard Worker #if DEBUG_DUMP_ALIGNMENT
269*c8dee2aaSAndroid Build Coastguard Worker     contour.dumpSegments("aligned");
270*c8dee2aaSAndroid Build Coastguard Worker #endif
271*c8dee2aaSAndroid Build Coastguard Worker     // construct closed contours
272*c8dee2aaSAndroid Build Coastguard Worker     result->reset();
273*c8dee2aaSAndroid Build Coastguard Worker     result->setFillType(fillType);
274*c8dee2aaSAndroid Build Coastguard Worker     SkPathWriter wrapper(*result);
275*c8dee2aaSAndroid Build Coastguard Worker     if (builder.xorMask() == kWinding_PathOpsMask ? !bridgeWinding(contourList, &wrapper)
276*c8dee2aaSAndroid Build Coastguard Worker             : !bridgeXor(contourList, &wrapper)) {
277*c8dee2aaSAndroid Build Coastguard Worker         return false;
278*c8dee2aaSAndroid Build Coastguard Worker     }
279*c8dee2aaSAndroid Build Coastguard Worker     wrapper.assemble();  // if some edges could not be resolved, assemble remaining
280*c8dee2aaSAndroid Build Coastguard Worker     return true;
281*c8dee2aaSAndroid Build Coastguard Worker }
282*c8dee2aaSAndroid Build Coastguard Worker 
Simplify(const SkPath & path,SkPath * result)283*c8dee2aaSAndroid Build Coastguard Worker bool Simplify(const SkPath& path, SkPath* result) {
284*c8dee2aaSAndroid Build Coastguard Worker #if DEBUG_DUMP_VERIFY
285*c8dee2aaSAndroid Build Coastguard Worker     if (SkPathOpsDebug::gVerifyOp) {
286*c8dee2aaSAndroid Build Coastguard Worker         if (!SimplifyDebug(path, result  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) {
287*c8dee2aaSAndroid Build Coastguard Worker             ReportSimplifyFail(path);
288*c8dee2aaSAndroid Build Coastguard Worker             return false;
289*c8dee2aaSAndroid Build Coastguard Worker         }
290*c8dee2aaSAndroid Build Coastguard Worker         VerifySimplify(path, *result);
291*c8dee2aaSAndroid Build Coastguard Worker         return true;
292*c8dee2aaSAndroid Build Coastguard Worker     }
293*c8dee2aaSAndroid Build Coastguard Worker #endif
294*c8dee2aaSAndroid Build Coastguard Worker     return SimplifyDebug(path, result  SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
295*c8dee2aaSAndroid Build Coastguard Worker }
296