Flutter Engine
The Flutter Engine
Functions
SkPathOpsCommon.h File Reference
#include "include/pathops/SkPathOps.h"
#include "src/pathops/SkPathOpsTypes.h"

Go to the source code of this file.

Functions

const SkOpAngleAngleWinding (SkOpSpanBase *start, SkOpSpanBase *end, int *windingPtr, bool *sortable)
 
SkOpSegmentFindChase (SkTDArray< SkOpSpanBase * > *chase, SkOpSpanBase **startPtr, SkOpSpanBase **endPtr)
 
SkOpSpanFindSortableTop (SkOpContourHead *)
 
SkOpSpanFindUndone (SkOpContourHead *)
 
bool FixWinding (SkPath *path)
 
bool SortContourList (SkOpContourHead **, bool evenOdd, bool oppEvenOdd)
 
bool HandleCoincidence (SkOpContourHead *, SkOpCoincidence *)
 
bool OpDebug (const SkPath &one, const SkPath &two, SkPathOp op, SkPath *result SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char *testName))
 

Function Documentation

◆ AngleWinding()

const SkOpAngle * AngleWinding ( SkOpSpanBase start,
SkOpSpanBase end,
int windingPtr,
bool *  sortable 
)

Definition at line 21 of file SkPathOpsCommon.cpp.

22 {
23 // find first angle, initialize winding to computed fWindSum
24 SkOpSegment* segment = start->segment();
25 const SkOpAngle* angle = segment->spanToAngle(start, end);
26 if (!angle) {
27 *windingPtr = SK_MinS32;
28 return nullptr;
29 }
30 bool computeWinding = false;
31 const SkOpAngle* firstAngle = angle;
32 bool loop = false;
33 bool unorderable = false;
34 int winding = SK_MinS32;
35 do {
36 angle = angle->next();
37 if (!angle) {
38 return nullptr;
39 }
40 unorderable |= angle->unorderable();
41 if ((computeWinding = unorderable || (angle == firstAngle && loop))) {
42 break; // if we get here, there's no winding, loop is unorderable
43 }
44 loop |= angle == firstAngle;
45 segment = angle->segment();
46 winding = segment->windSum(angle);
47 } while (winding == SK_MinS32);
48 // if the angle loop contains an unorderable span, the angle order may be useless
49 // directly compute the winding in this case for each span
50 if (computeWinding) {
51 firstAngle = angle;
52 winding = SK_MinS32;
53 do {
54 SkOpSpanBase* startSpan = angle->start();
55 SkOpSpanBase* endSpan = angle->end();
56 SkOpSpan* lesser = startSpan->starter(endSpan);
57 int testWinding = lesser->windSum();
58 if (testWinding == SK_MinS32) {
59 testWinding = lesser->computeWindSum();
60 }
61 if (testWinding != SK_MinS32) {
62 segment = angle->segment();
63 winding = testWinding;
64 }
65 angle = angle->next();
66 } while (angle != firstAngle);
67 }
68 *sortablePtr = !unorderable;
69 *windingPtr = winding;
70 return angle;
71}
static constexpr int32_t SK_MinS32
Definition: SkMath.h:22
bool unorderable() const
Definition: SkOpAngle.h:104
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
int windSum(const SkOpAngle *angle) const
SkOpAngle * spanToAngle(SkOpSpanBase *start, SkOpSpanBase *end)
Definition: SkOpSegment.h:391
const SkOpSpan * starter(const SkOpSpanBase *end) const
Definition: SkOpSpan.h:347
int windSum() const
Definition: SkOpSpan.h:555
int computeWindSum()
Definition: SkOpSpan.cpp:378
glong glong end

◆ FindChase()

SkOpSegment * FindChase ( SkTDArray< SkOpSpanBase * > *  chase,
SkOpSpanBase **  startPtr,
SkOpSpanBase **  endPtr 
)

Definition at line 87 of file SkPathOpsCommon.cpp.

88 {
89 while (!chase->empty()) {
90 SkOpSpanBase* span = chase->back();
91 chase->pop_back();
92 SkOpSegment* segment = span->segment();
93 *startPtr = span->ptT()->next()->span();
94 bool done = true;
95 *endPtr = nullptr;
96 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
97 *startPtr = last->start();
98 *endPtr = last->end();
99 #if TRY_ROTATE
100 *chase->insert(0) = span;
101 #else
102 *chase->append() = span;
103 #endif
104 return last->segment();
105 }
106 if (done) {
107 continue;
108 }
109 // find first angle, initialize winding to computed wind sum
110 int winding;
111 bool sortable;
112 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
113 if (!angle) {
114 return nullptr;
115 }
116 if (winding == SK_MinS32) {
117 continue;
118 }
119 int sumWinding SK_INIT_TO_AVOID_WARNING;
120 if (sortable) {
121 segment = angle->segment();
122 sumWinding = segment->updateWindingReverse(angle);
123 }
124 SkOpSegment* first = nullptr;
125 const SkOpAngle* firstAngle = angle;
126 while ((angle = angle->next()) != firstAngle) {
127 segment = angle->segment();
128 SkOpSpanBase* start = angle->start();
129 SkOpSpanBase* end = angle->end();
130 int maxWinding SK_INIT_TO_AVOID_WARNING;
131 if (sortable) {
132 segment->setUpWinding(start, end, &maxWinding, &sumWinding);
133 }
134 if (!segment->done(angle)) {
135 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
136 first = segment;
137 *startPtr = start;
138 *endPtr = end;
139 }
140 // OPTIMIZATION: should this also add to the chase?
141 if (sortable) {
142 // TODO: add error handling
143 SkAssertResult(segment->markAngle(maxWinding, sumWinding, angle, nullptr));
144 }
145 }
146 }
147 if (first) {
148 #if TRY_ROTATE
149 *chase->insert(0) = span;
150 #else
151 *chase->append() = span;
152 #endif
153 return first;
154 }
155 }
156 return nullptr;
157}
static void done(const char *config, const char *src, const char *srcOptions, const char *name)
Definition: DM.cpp:263
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
#define SK_INIT_TO_AVOID_WARNING
Definition: SkMacros.h:58
const SkOpAngle * AngleWinding(SkOpSpanBase *start, SkOpSpanBase *end, int *windingPtr, bool *sortablePtr)
const SkOpSpanBase * span() const
Definition: SkOpSpan.h:154
const SkOpPtT * next() const
Definition: SkOpSpan.h:93
bool done() const
Definition: SkOpSegment.h:200
int updateWindingReverse(const SkOpAngle *angle)
void setUpWinding(SkOpSpanBase *start, SkOpSpanBase *end, int *maxWinding, int *sumWinding)
Definition: SkOpSegment.h:369
SkOpAngle * activeAngle(SkOpSpanBase *start, SkOpSpanBase **startPtr, SkOpSpanBase **endPtr, bool *done)
Definition: SkOpSegment.cpp:54
bool markAngle(int maxWinding, int sumWinding, const SkOpAngle *angle, SkOpSpanBase **result)
SkOpSegment * segment() const
Definition: SkOpSpan.h:318
const SkOpPtT * ptT() const
Definition: SkOpSpan.h:310
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

◆ FindSortableTop()

SkOpSpan * FindSortableTop ( SkOpContourHead contourHead)

Definition at line 429 of file SkPathOpsWinding.cpp.

429 {
430 for (int index = 0; index < SkOpGlobalState::kMaxWindingTries; ++index) {
431 SkOpContour* contour = contourHead;
432 do {
433 if (contour->done()) {
434 continue;
435 }
436 SkOpSpan* result = contour->findSortableTop(contourHead);
437 if (result) {
438 return result;
439 }
440 } while ((contour = contour->next()));
441 }
442 return nullptr;
443}
GAsyncResult * result

◆ FindUndone()

SkOpSpan * FindUndone ( SkOpContourHead contourHead)

Definition at line 73 of file SkPathOpsCommon.cpp.

73 {
74 SkOpContour* contour = contourHead;
75 do {
76 if (contour->done()) {
77 continue;
78 }
79 SkOpSpan* result = contour->undoneSpan();
80 if (result) {
81 return result;
82 }
83 } while ((contour = contour->next()));
84 return nullptr;
85}

◆ FixWinding()

bool FixWinding ( SkPath path)

◆ HandleCoincidence()

bool HandleCoincidence ( SkOpContourHead contourList,
SkOpCoincidence coincidence 
)

Definition at line 238 of file SkPathOpsCommon.cpp.

238 {
239 SkOpGlobalState* globalState = contourList->globalState();
240 // match up points within the coincident runs
241 if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kIntersecting))) {
242 return false;
243 }
244 // combine t values when multiple intersections occur on some segments but not others
245 if (!move_multiples(contourList DEBUG_PHASE_PARAMS(kWalking))) {
246 return false;
247 }
248 // move t values and points together to eliminate small/tiny gaps
249 if (!move_nearby(contourList DEBUG_COIN_PARAMS())) {
250 return false;
251 }
252 // add coincidence formed by pairing on curve points and endpoints
253 coincidence->correctEnds(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
254 if (!coincidence->addEndMovedSpans(DEBUG_COIN_ONLY_PARAMS())) {
255 return false;
256 }
257 const int SAFETY_COUNT = 3;
258 int safetyHatch = SAFETY_COUNT;
259 // look for coincidence present in A-B and A-C but missing in B-C
260 do {
261 bool added;
262 if (!coincidence->addMissing(&added DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
263 return false;
264 }
265 if (!added) {
266 break;
267 }
268 if (!--safetyHatch) {
269 SkASSERT(globalState->debugSkipAssert());
270 return false;
271 }
272 move_nearby(contourList DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch - 1));
273 } while (true);
274 // check to see if, loosely, coincident ranges may be expanded
275 if (coincidence->expand(DEBUG_COIN_ONLY_PARAMS())) {
276 bool added;
277 if (!coincidence->addMissing(&added DEBUG_COIN_PARAMS())) {
278 return false;
279 }
280 if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
281 return false;
282 }
283 if (!move_multiples(contourList DEBUG_COIN_PARAMS())) {
284 return false;
285 }
286 move_nearby(contourList DEBUG_COIN_PARAMS());
287 }
288 // the expanded ranges may not align -- add the missing spans
289 if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
290 return false;
291 }
292 // mark spans of coincident segments as coincident
293 coincidence->mark(DEBUG_COIN_ONLY_PARAMS());
294 // look for coincidence lines and curves undetected by intersection
295 if (missing_coincidence(contourList DEBUG_COIN_PARAMS())) {
296 (void) coincidence->expand(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
297 if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
298 return false;
299 }
300 if (!coincidence->mark(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
301 return false;
302 }
303 } else {
304 (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
305 }
306 (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
307
308 SkOpCoincidence overlaps(globalState);
309 safetyHatch = SAFETY_COUNT;
310 do {
311 SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
312 // adjust the winding value to account for coincident edges
313 if (!pairs->apply(DEBUG_ITER_ONLY_PARAMS(SAFETY_COUNT - safetyHatch))) {
314 return false;
315 }
316 // For each coincident pair that overlaps another, when the receivers (the 1st of the pair)
317 // are different, construct a new pair to resolve their mutual span
318 if (!pairs->findOverlaps(&overlaps DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
319 return false;
320 }
321 if (!--safetyHatch) {
322 SkASSERT(globalState->debugSkipAssert());
323 return false;
324 }
325 } while (!overlaps.isEmpty());
326 calc_angles(contourList DEBUG_COIN_PARAMS());
327 if (!sort_angles(contourList)) {
328 return false;
329 }
330#if DEBUG_COINCIDENCE_VERBOSE
331 coincidence->debugShowCoincidence();
332#endif
333#if DEBUG_COINCIDENCE
334 coincidence->debugValidate();
335#endif
337 return true;
338}
#define SkASSERT(cond)
Definition: SkAssert.h:116
static void calc_angles(SkOpContourHead *contourList DEBUG_COIN_DECLARE_PARAMS())
static bool move_nearby(SkOpContourHead *contourList DEBUG_COIN_DECLARE_PARAMS())
static bool sort_angles(SkOpContourHead *contourList)
static bool missing_coincidence(SkOpContourHead *contourList DEBUG_COIN_DECLARE_PARAMS())
static bool move_multiples(SkOpContourHead *contourList DEBUG_COIN_DECLARE_PARAMS())
#define DEBUG_PHASE_ONLY_PARAMS(phase)
#define DEBUG_PHASE_PARAMS(phase)
#define DEBUG_ITER_PARAMS(iteration)
#define DEBUG_ITER_ONLY_PARAMS(iteration)
#define DEBUG_COIN_ONLY_PARAMS()
#define DEBUG_COIN_PARAMS()
bool addExpanded(DEBUG_COIN_DECLARE_ONLY_PARAMS())
bool addEndMovedSpans(DEBUG_COIN_DECLARE_ONLY_PARAMS())
bool apply(DEBUG_COIN_DECLARE_ONLY_PARAMS())
void debugShowCoincidence() const
bool addMissing(bool *added DEBUG_COIN_DECLARE_PARAMS())
void correctEnds(DEBUG_COIN_DECLARE_ONLY_PARAMS())
bool isEmpty() const
bool findOverlaps(SkOpCoincidence *DEBUG_COIN_DECLARE_PARAMS()) const
bool expand(DEBUG_COIN_DECLARE_ONLY_PARAMS())
bool mark(DEBUG_COIN_DECLARE_ONLY_PARAMS())
void debugValidate() const
SkOpGlobalState * globalState() const
Definition: SkOpContour.h:139
static void ShowActiveSpans(SkOpContourHead *contourList)

◆ OpDebug()

bool OpDebug ( const SkPath one,
const SkPath two,
SkPathOp  op,
SkPath *result   SkDEBUGPARAMSbool skipAssert) SkDEBUGPARAMS(const char *testName 
)

Definition at line 252 of file SkPathOpsOp.cpp.

253 {
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}
static float next(float f)
bool AddIntersectTs(SkOpContour *test, SkOpContour *next, SkOpCoincidence *coincidence)
bool SortContourList(SkOpContourHead **contourList, bool evenOdd, bool oppEvenOdd)
bool HandleCoincidence(SkOpContourHead *contourList, SkOpCoincidence *coincidence)
#define SkDEBUGPARAMS(...)
static const bool gOutInverse[kReverseDifference_SkPathOp+1][2][2]
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
@ 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
void dumpSegments(const char *prefix="seg", SkPathOp op=(SkPathOp) -1) const
SkOpContour * next()
Definition: SkOpContour.h:266
Definition: SkPath.h:59
bool isEmpty() const
Definition: SkPath.cpp:416
bool isInverseFillType() const
Definition: SkPath.h:244
void toggleInverseFillType()
Definition: SkPath.h:249
bool isRect(SkRect *rect, bool *isClosed=nullptr, SkPathDirection *direction=nullptr) const
Definition: SkPath.cpp:516
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114

◆ SortContourList()

bool SortContourList ( SkOpContourHead **  contourList,
bool  evenOdd,
bool  oppEvenOdd 
)

Definition at line 159 of file SkPathOpsCommon.cpp.

159 {
161 SkOpContour* contour = *contourList;
162 do {
163 if (contour->count()) {
164 contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
165 *list.append() = contour;
166 }
167 } while ((contour = contour->next()));
168 int count = list.size();
169 if (!count) {
170 return false;
171 }
172 if (count > 1) {
173 SkTQSort<SkOpContour>(list.begin(), list.end());
174 }
175 contour = list[0];
176 SkOpContourHead* contourHead = static_cast<SkOpContourHead*>(contour);
177 contour->globalState()->setContourHead(contourHead);
178 *contourList = contourHead;
179 for (int index = 1; index < count; ++index) {
180 SkOpContour* next = list[index];
181 contour->setNext(next);
182 contour = next;
183 }
184 contour->setNext(nullptr);
185 return true;
186}
int count
Definition: FontMgrTest.cpp:50
T * end()
Definition: SkTDArray.h:152
int size() const
Definition: SkTDArray.h:138
T * begin()
Definition: SkTDArray.h:150