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