Flutter Engine
The Flutter Engine
Functions | Variables
SkPathOpsOp.cpp File Reference
#include "include/core/SkPath.h"
#include "include/core/SkPathTypes.h"
#include "include/core/SkRect.h"
#include "include/core/SkTypes.h"
#include "include/pathops/SkPathOps.h"
#include "include/private/base/SkMath.h"
#include "include/private/base/SkTDArray.h"
#include "src/base/SkArenaAlloc.h"
#include "src/pathops/SkAddIntersections.h"
#include "src/pathops/SkOpAngle.h"
#include "src/pathops/SkOpCoincidence.h"
#include "src/pathops/SkOpContour.h"
#include "src/pathops/SkOpEdgeBuilder.h"
#include "src/pathops/SkOpSegment.h"
#include "src/pathops/SkOpSpan.h"
#include "src/pathops/SkPathOpsCommon.h"
#include "src/pathops/SkPathOpsTypes.h"
#include "src/pathops/SkPathWriter.h"
#include <utility>

Go to the source code of this file.

Functions

static bool findChaseOp (SkTDArray< SkOpSpanBase * > &chase, SkOpSpanBase **startPtr, SkOpSpanBase **endPtr, SkOpSegment **result)
 
static bool bridgeOp (SkOpContourHead *contourList, const SkPathOp op, const int xorMask, const int xorOpMask, SkPathWriter *writer)
 
bool OpDebug (const SkPath &one, const SkPath &two, SkPathOp op, SkPath *result SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char *testName))
 
bool Op (const SkPath &one, const SkPath &two, SkPathOp op, SkPath *result)
 

Variables

static const SkPathOp gOpInverse [kReverseDifference_SkPathOp+1][2][2]
 
static const bool gOutInverse [kReverseDifference_SkPathOp+1][2][2]
 

Function Documentation

◆ bridgeOp()

static bool bridgeOp ( SkOpContourHead contourList,
const SkPathOp  op,
const int  xorMask,
const int  xorOpMask,
SkPathWriter writer 
)
static

Definition at line 122 of file SkPathOpsOp.cpp.

123 {
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}
static float next(float f)
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
SkOpSpan * FindSortableTop(SkOpContourHead *)
static bool findChaseOp(SkTDArray< SkOpSpanBase * > &chase, SkOpSpanBase **startPtr, SkOpSpanBase **endPtr, SkOpSegment **result)
Definition: SkPathOpsOp.cpp:28
SkPath::Verb verb() const
Definition: SkOpSegment.h:419
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)
bool markAndChaseDone(SkOpSpanBase *start, SkOpSpanBase *end, SkOpSpanBase **found)
int debugID() const
Definition: SkOpSegment.h:154
bool addCurveTo(const SkOpSpanBase *start, const SkOpSpanBase *end, SkPathWriter *path) const
bool activeOp(SkOpSpanBase *start, SkOpSpanBase *end, int xorMiMask, int xorSuMask, SkPathOp op)
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
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
@ kLine_Verb
Definition: SkPath.h:1467
T * append()
Definition: SkTDArray.h:191
glong glong end

◆ findChaseOp()

static bool findChaseOp ( SkTDArray< SkOpSpanBase * > &  chase,
SkOpSpanBase **  startPtr,
SkOpSpanBase **  endPtr,
SkOpSegment **  result 
)
static

Definition at line 28 of file SkPathOpsOp.cpp.

29 {
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}
static void done(const char *config, const char *src, const char *srcOptions, const char *name)
Definition: DM.cpp:263
static constexpr int32_t SK_MinS32
Definition: SkMath.h:22
const SkOpAngle * AngleWinding(SkOpSpanBase *start, SkOpSpanBase *end, int *windingPtr, bool *sortablePtr)
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
const SkOpSpanBase * span() const
Definition: SkOpSpan.h:154
SkOpPtT * prev()
Definition: SkOpSpan.cpp:134
bool operand() const
int updateWindingReverse(const SkOpAngle *angle)
void setUpWindings(SkOpSpanBase *start, SkOpSpanBase *end, int *sumMiWinding, int *maxWinding, int *sumWinding)
SkOpGlobalState * globalState() const
SkOpAngle * activeAngle(SkOpSpanBase *start, SkOpSpanBase **startPtr, SkOpSpanBase **endPtr, bool *done)
Definition: SkOpSegment.cpp:54
int updateOppWindingReverse(const SkOpAngle *angle) const
bool markAngle(int maxWinding, int sumWinding, const SkOpAngle *angle, SkOpSpanBase **result)
const SkOpPtT * ptT() const
Definition: SkOpSpan.h:310
const T & back() const
Definition: SkTDArray.h:162
bool empty() const
Definition: SkTDArray.h:135
T * insert(int index)
Definition: SkTDArray.h:203
void pop_back()
Definition: SkTDArray.h:223
GAsyncResult * result

◆ Op()

bool Op ( const SkPath one,
const SkPath two,
SkPathOp  op,
SkPath result 
)

Set this path to the result of applying the Op to this path and the specified path: this = (this op operand). The resulting path will be constructed from non-overlapping contours. The curve order is reduced where possible so that cubics may be turned into quadratics, and quadratics maybe turned into lines.

Returns true if operation was able to produce a result; otherwise, result is unmodified.

Parameters
oneThe first operand (for difference, the minuend)
twoThe second operand (for difference, the subtrahend)
opThe operator to apply.
resultThe product of the operands. The result may be one of the inputs.
Returns
True if the operation succeeded.

Definition at line 383 of file SkPathOpsOp.cpp.

383 {
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}
#define SkDEBUGPARAMS(...)
bool OpDebug(const SkPath &one, const SkPath &two, SkPathOp op, SkPath *result SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char *testName))

◆ 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}
bool AddIntersectTs(SkOpContour *test, SkOpContour *next, SkOpCoincidence *coincidence)
bool SortContourList(SkOpContourHead **contourList, bool evenOdd, bool oppEvenOdd)
bool HandleCoincidence(SkOpContourHead *contourList, SkOpCoincidence *coincidence)
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 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

Variable Documentation

◆ gOpInverse

const SkPathOp gOpInverse[kReverseDifference_SkPathOp+1][2][2]
static

◆ gOutInverse

const bool gOutInverse[kReverseDifference_SkPathOp+1][2][2]
static
Initial value:
= {
{{ false, false }, { true, false }},
{{ false, false }, { false, true }},
{{ false, true }, { true, true }},
{{ false, true }, { true, false }},
{{ false, true }, { false, false }},
}

Definition at line 230 of file SkPathOpsOp.cpp.