Flutter Engine
The Flutter Engine
SkPathOpsOp.cpp
Go to the documentation of this file.
1/*
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 */
25
26#include <utility>
27
28static bool findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr,
29 SkOpSpanBase** endPtr, SkOpSegment** result) {
30 while (!chase.empty()) {
31 SkOpSpanBase* span = chase.back();
32 chase.pop_back();
33 // OPTIMIZE: prev makes this compatible with old code -- but is it necessary?
34 *startPtr = span->ptT()->prev()->span();
35 SkOpSegment* segment = (*startPtr)->segment();
36 bool done = true;
37 *endPtr = nullptr;
38 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
39 *startPtr = last->start();
40 *endPtr = last->end();
41 #if TRY_ROTATE
42 *chase.insert(0) = span;
43 #else
44 *chase.append() = span;
45 #endif
46 *result = last->segment();
47 return true;
48 }
49 if (done) {
50 continue;
51 }
52 int winding;
53 bool sortable;
54 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
55 if (!angle) {
56 *result = nullptr;
57 return true;
58 }
59 if (winding == SK_MinS32) {
60 continue;
61 }
62 int sumMiWinding, sumSuWinding;
63 if (sortable) {
64 segment = angle->segment();
65 sumMiWinding = segment->updateWindingReverse(angle);
66 if (sumMiWinding == SK_MinS32) {
67 SkASSERT(segment->globalState()->debugSkipAssert());
68 *result = nullptr;
69 return true;
70 }
71 sumSuWinding = segment->updateOppWindingReverse(angle);
72 if (sumSuWinding == SK_MinS32) {
73 SkASSERT(segment->globalState()->debugSkipAssert());
74 *result = nullptr;
75 return true;
76 }
77 if (segment->operand()) {
78 using std::swap;
79 swap(sumMiWinding, sumSuWinding);
80 }
81 }
82 SkOpSegment* first = nullptr;
83 const SkOpAngle* firstAngle = angle;
84 while ((angle = angle->next()) != firstAngle) {
85 segment = angle->segment();
86 SkOpSpanBase* start = angle->start();
87 SkOpSpanBase* end = angle->end();
88 int maxWinding = 0, sumWinding = 0, oppMaxWinding = 0, oppSumWinding = 0;
89 if (sortable) {
90 segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
91 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
92 }
93 if (!segment->done(angle)) {
94 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
95 first = segment;
96 *startPtr = start;
97 *endPtr = end;
98 }
99 // OPTIMIZATION: should this also add to the chase?
100 if (sortable) {
101 if (!segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
102 oppSumWinding, angle, nullptr)) {
103 return false;
104 }
105 }
106 }
107 }
108 if (first) {
109 #if TRY_ROTATE
110 *chase.insert(0) = span;
111 #else
112 *chase.append() = span;
113 #endif
114 *result = first;
115 return true;
116 }
117 }
118 *result = nullptr;
119 return true;
120}
121
122static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
123 const int xorMask, const int xorOpMask, SkPathWriter* writer) {
124 bool unsortable = false;
125 bool lastSimple = false;
126 bool simple = false;
127 do {
128 SkOpSpan* span = FindSortableTop(contourList);
129 if (!span) {
130 break;
131 }
132 SkOpSegment* current = span->segment();
133 SkOpSpanBase* start = span->next();
134 SkOpSpanBase* end = span;
136 do {
137 if (current->activeOp(start, end, xorMask, xorOpMask, op)) {
138 do {
139 if (!unsortable && current->done()) {
140 break;
141 }
142 SkASSERT(unsortable || !current->done());
143 SkOpSpanBase* nextStart = start;
144 SkOpSpanBase* nextEnd = end;
145 lastSimple = simple;
146 SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd,
147 &unsortable, &simple, op, xorMask, xorOpMask);
148 if (!next) {
149 if (!unsortable && writer->hasMove()
150 && current->verb() != SkPath::kLine_Verb
151 && !writer->isClosed()) {
152 if (!current->addCurveTo(start, end, writer)) {
153 return false;
154 }
155 if (!writer->isClosed()) {
157 }
158 } else if (lastSimple) {
159 if (!current->addCurveTo(start, end, writer)) {
160 return false;
161 }
162 }
163 break;
164 }
165 #if DEBUG_FLOW
166 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
167 current->debugID(), start->pt().fX, start->pt().fY,
168 end->pt().fX, end->pt().fY);
169 #endif
170 if (!current->addCurveTo(start, end, writer)) {
171 return false;
172 }
173 current = next;
174 start = nextStart;
175 end = nextEnd;
176 } while (!writer->isClosed() && (!unsortable || !start->starter(end)->done()));
177 if (current->activeWinding(start, end) && !writer->isClosed()) {
178 SkOpSpan* spanStart = start->starter(end);
179 if (!spanStart->done()) {
180 if (!current->addCurveTo(start, end, writer)) {
181 return false;
182 }
183 current->markDone(spanStart);
184 }
185 }
186 writer->finishContour();
187 } else {
188 SkOpSpanBase* last;
189 if (!current->markAndChaseDone(start, end, &last)) {
190 return false;
191 }
192 if (last && !last->chased()) {
193 last->setChased(true);
195 *chase.append() = last;
196#if DEBUG_WINDING
197 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
198 if (!last->final()) {
199 SkDebugf(" windSum=%d", last->upCast()->windSum());
200 }
201 SkDebugf("\n");
202#endif
203 }
204 }
205 if (!findChaseOp(chase, &start, &end, &current)) {
206 return false;
207 }
209 if (!current) {
210 break;
211 }
212 } while (true);
213 } while (true);
214 return true;
215}
216
217// diagram of why this simplifcation is possible is here:
218// https://skia.org/dev/present/pathops link at bottom of the page
219// https://drive.google.com/file/d/0BwoLUwz9PYkHLWpsaXd0UDdaN00/view?usp=sharing
221// inside minuend outside minuend
222// inside subtrahend outside subtrahend inside subtrahend outside subtrahend
228};
229
230static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = {
231 {{ false, false }, { true, false }}, // diff
232 {{ false, false }, { false, true }}, // sect
233 {{ false, true }, { true, true }}, // union
234 {{ false, true }, { true, false }}, // xor
235 {{ false, true }, { false, false }}, // rev diff
236};
237
238#if DEBUG_T_SECT_LOOP_COUNT
239
241
242SkOpGlobalState debugWorstState(nullptr, nullptr SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
243
244void ReportPathOpsDebugging() {
245 debugWorstState.debugLoopReport();
246}
247
248extern void (*gVerboseFinalize)();
249
250#endif
251
252bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
253 SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
254#if DEBUG_DUMP_VERIFY
255#ifndef SK_DEBUG
256 const char* testName = "release";
257#endif
258 if (SkPathOpsDebug::gDumpOp) {
259 DumpOp(one, two, op, testName);
260 }
261#endif
262 op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
263 bool inverseFill = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()];
264 SkPathFillType fillType = inverseFill ? SkPathFillType::kInverseEvenOdd :
266 SkRect rect1, rect2;
267 if (kIntersect_SkPathOp == op && one.isRect(&rect1) && two.isRect(&rect2)) {
268 result->reset();
269 result->setFillType(fillType);
270 if (rect1.intersect(rect2)) {
271 result->addRect(rect1);
272 }
273 return true;
274 }
275 if (one.isEmpty() || two.isEmpty()) {
276 SkPath work;
277 switch (op) {
279 break;
280 case kUnion_SkPathOp:
281 case kXOR_SkPathOp:
282 work = one.isEmpty() ? two : one;
283 break;
285 if (!one.isEmpty()) {
286 work = one;
287 }
288 break;
290 if (!two.isEmpty()) {
291 work = two;
292 }
293 break;
294 default:
295 SkASSERT(0); // unhandled case
296 }
297 if (inverseFill != work.isInverseFillType()) {
299 }
300 return Simplify(work, result);
301 }
302 SkSTArenaAlloc<4096> allocator; // FIXME: add a constant expression here, tune
304 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
305 SkOpGlobalState globalState(contourList, &allocator
307 SkOpCoincidence coincidence(&globalState);
308 const SkPath* minuend = &one;
309 const SkPath* subtrahend = &two;
310 if (op == kReverseDifference_SkPathOp) {
311 using std::swap;
312 swap(minuend, subtrahend);
314 }
315#if DEBUG_SORT
316 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
317#endif
318 // turn path into list of segments
319 SkOpEdgeBuilder builder(*minuend, contourList, &globalState);
320 if (builder.unparseable()) {
321 return false;
322 }
323 const int xorMask = builder.xorMask();
324 builder.addOperand(*subtrahend);
325 if (!builder.finish()) {
326 return false;
327 }
328#if DEBUG_DUMP_SEGMENTS
329 contourList->dumpSegments("seg", op);
330#endif
331
332 const int xorOpMask = builder.xorMask();
333 if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask,
334 xorOpMask == kEvenOdd_PathOpsMask)) {
335 result->reset();
336 result->setFillType(fillType);
337 return true;
338 }
339 // find all intersections between segments
340 SkOpContour* current = contourList;
341 do {
342 SkOpContour* next = current;
343 while (AddIntersectTs(current, next, &coincidence)
344 && (next = next->next()))
345 ;
346 } while ((current = current->next()));
347#if DEBUG_VALIDATE
348 globalState.setPhase(SkOpPhase::kWalking);
349#endif
350 bool success = HandleCoincidence(contourList, &coincidence);
351#if DEBUG_COIN
352 globalState.debugAddToGlobalCoinDicts();
353#endif
354 if (!success) {
355 return false;
356 }
357#if DEBUG_ALIGNMENT
358 contourList->dumpSegments("aligned");
359#endif
360 // construct closed contours
361 SkPath original = *result;
362 result->reset();
363 result->setFillType(fillType);
364 SkPathWriter wrapper(*result);
365 if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) {
366 *result = original;
367 return false;
368 }
369 wrapper.assemble(); // if some edges could not be resolved, assemble remaining
370#if DEBUG_T_SECT_LOOP_COUNT
371 static SkMutex& debugWorstLoop = *(new SkMutex);
372 {
373 SkAutoMutexExclusive autoM(debugWorstLoop);
374 if (!gVerboseFinalize) {
375 gVerboseFinalize = &ReportPathOpsDebugging;
376 }
377 debugWorstState.debugDoYourWorst(&globalState);
378 }
379#endif
380 return true;
381}
382
383bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
384#if DEBUG_DUMP_VERIFY
385 if (SkPathOpsDebug::gVerifyOp) {
386 if (!OpDebug(one, two, op, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) {
387 ReportOpFail(one, two, op);
388 return false;
389 }
390 VerifyOp(one, two, op, *result);
391 return true;
392 }
393#endif
394 return OpDebug(one, two, op, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
395}
static void done(const char *config, const char *src, const char *srcOptions, const char *name)
Definition: DM.cpp:263
static float next(float f)
bool AddIntersectTs(SkOpContour *test, SkOpContour *next, SkOpCoincidence *coincidence)
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static constexpr int32_t SK_MinS32
Definition: SkMath.h:22
bool SortContourList(SkOpContourHead **contourList, bool evenOdd, bool oppEvenOdd)
const SkOpAngle * AngleWinding(SkOpSpanBase *start, SkOpSpanBase *end, int *windingPtr, bool *sortablePtr)
bool HandleCoincidence(SkOpContourHead *contourList, SkOpCoincidence *coincidence)
SkOpSpan * FindSortableTop(SkOpContourHead *)
#define SkDEBUGPARAMS(...)
bool OpDebug(const SkPath &one, const SkPath &two, SkPathOp op, SkPath *result SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char *testName))
static const bool gOutInverse[kReverseDifference_SkPathOp+1][2][2]
bool Op(const SkPath &one, const SkPath &two, SkPathOp op, SkPath *result)
static bool findChaseOp(SkTDArray< SkOpSpanBase * > &chase, SkOpSpanBase **startPtr, SkOpSpanBase **endPtr, SkOpSegment **result)
Definition: SkPathOpsOp.cpp:28
static bool bridgeOp(SkOpContourHead *contourList, const SkPathOp op, const int xorMask, const int xorOpMask, SkPathWriter *writer)
static const SkPathOp gOpInverse[kReverseDifference_SkPathOp+1][2][2]
@ kEvenOdd_PathOpsMask
SkPathOp
Definition: SkPathOps.h:22
@ kReverseDifference_SkPathOp
subtract the first path from the op path
Definition: SkPathOps.h:27
@ kDifference_SkPathOp
subtract the op path from the first path
Definition: SkPathOps.h:23
@ kIntersect_SkPathOp
intersect the two paths
Definition: SkPathOps.h:24
@ kUnion_SkPathOp
union (inclusive-or) the two paths
Definition: SkPathOps.h:25
@ kXOR_SkPathOp
exclusive-or the two paths
Definition: SkPathOps.h:26
bool SK_API Simplify(const SkPath &path, SkPath *result)
SkPathFillType
Definition: SkPathTypes.h:11
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition: SkRefCnt.h:341
SkOpSegment * segment() const
Definition: SkOpAngle.cpp:969
SkOpSpanBase * end() const
Definition: SkOpAngle.h:73
SkOpAngle * next() const
Definition: SkOpAngle.h:82
SkOpSpanBase * start() const
Definition: SkOpAngle.h:94
void dumpSegments(const char *prefix="seg", SkPathOp op=(SkPathOp) -1) const
SkOpContour * next()
Definition: SkOpContour.h:266
void setPhase(SkOpPhase phase)
const SkOpSpanBase * span() const
Definition: SkOpSpan.h:154
SkOpPtT * prev()
Definition: SkOpSpan.cpp:134
SkPath::Verb verb() const
Definition: SkOpSegment.h:419
bool operand() const
bool done() const
Definition: SkOpSegment.h:200
SkOpSegment * findNextOp(SkTDArray< SkOpSpanBase * > *chase, SkOpSpanBase **nextStart, SkOpSpanBase **nextEnd, bool *unsortable, bool *simple, SkPathOp op, int xorMiMask, int xorSuMask)
bool activeWinding(SkOpSpanBase *start, SkOpSpanBase *end)
int updateWindingReverse(const SkOpAngle *angle)
bool markAndChaseDone(SkOpSpanBase *start, SkOpSpanBase *end, SkOpSpanBase **found)
void setUpWindings(SkOpSpanBase *start, SkOpSpanBase *end, int *sumMiWinding, int *maxWinding, int *sumWinding)
int debugID() const
Definition: SkOpSegment.h:154
SkOpGlobalState * globalState() const
bool addCurveTo(const SkOpSpanBase *start, const SkOpSpanBase *end, SkPathWriter *path) const
SkOpAngle * activeAngle(SkOpSpanBase *start, SkOpSpanBase **startPtr, SkOpSpanBase **endPtr, bool *done)
Definition: SkOpSegment.cpp:54
bool activeOp(SkOpSpanBase *start, SkOpSpanBase *end, int xorMiMask, int xorSuMask, SkPathOp op)
int updateOppWindingReverse(const SkOpAngle *angle) const
bool markAngle(int maxWinding, int sumWinding, const SkOpAngle *angle, SkOpSpanBase **result)
void markDone(SkOpSpan *)
void setChased(bool chased)
Definition: SkOpSpan.h:326
bool final() const
Definition: SkOpSpan.h:271
SkOpSpan * upCast()
Definition: SkOpSpan.h:383
bool chased() const
Definition: SkOpSpan.h:192
SkOpSegment * segment() const
Definition: SkOpSpan.h:318
const SkOpPtT * ptT() const
Definition: SkOpSpan.h:310
int windSum() const
Definition: SkOpSpan.h:555
SkOpSpanBase * next() const
Definition: SkOpSpan.h:495
bool done() const
Definition: SkOpSpan.h:459
static void ShowActiveSpans(SkOpContourHead *contourList)
static bool ChaseContains(const SkTDArray< SkOpSpanBase * > &, const SkOpSpanBase *)
void finishContour()
bool isClosed() const
bool hasMove() const
Definition: SkPathWriter.h:31
Definition: SkPath.h:59
bool isEmpty() const
Definition: SkPath.cpp:416
bool isInverseFillType() const
Definition: SkPath.h:244
void toggleInverseFillType()
Definition: SkPath.h:249
@ kLine_Verb
Definition: SkPath.h:1467
bool isRect(SkRect *rect, bool *isClosed=nullptr, SkPathDirection *direction=nullptr) const
Definition: SkPath.cpp:516
const T & back() const
Definition: SkTDArray.h:162
bool empty() const
Definition: SkTDArray.h:135
T * append()
Definition: SkTDArray.h:191
T * insert(int index)
Definition: SkTDArray.h:203
void pop_back()
Definition: SkTDArray.h:223
glong glong end
GAsyncResult * result
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114