Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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)
SkPath::Verb verb() const
bool done() const
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
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
const SkOpSpan * starter(const SkOpSpanBase *end) const
Definition SkOpSpan.h:347
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
@ kLine_Verb
Definition SkPath.h:1459
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
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)
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
306 SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
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()
bool isEmpty() const
Definition SkPath.cpp:406
bool isInverseFillType() const
Definition SkPath.h:244
void setFillType(SkPathFillType ft)
Definition SkPath.h:235
void toggleInverseFillType()
Definition SkPath.h:249
SkPath & reset()
Definition SkPath.cpp:360
bool isRect(SkRect *rect, bool *isClosed=nullptr, SkPathDirection *direction=nullptr) const
Definition SkPath.cpp:506
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.

230 {
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};