Flutter Engine
The Flutter Engine
SkPathOpsWinding.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2015 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 */
7
8// given a prospective edge, compute its initial winding by projecting a ray
9// if the ray hits another edge
10 // if the edge doesn't have a winding yet, hop up to that edge and start over
11 // concern : check for hops forming a loop
12 // if the edge is unsortable, or
13 // the intersection is nearly at the ends, or
14 // the tangent at the intersection is nearly coincident to the ray,
15 // choose a different ray and try again
16 // concern : if it is unable to succeed after N tries, try another edge? direction?
17// if no edge is hit, compute the winding directly
18
19// given the top span, project the most perpendicular ray and look for intersections
20 // let's try up and then down. What the hey
21
22// bestXY is initialized by caller with basePt
23
24#include "include/core/SkPath.h"
26#include "include/core/SkRect.h"
34#include "src/base/SkTSort.h"
42
43#include <cmath>
44#include <utility>
45
46using namespace skia_private;
47
48enum class SkOpRayDir {
49 kLeft,
50 kTop,
51 kRight,
52 kBottom,
53};
54
55#if DEBUG_WINDING
56const char* gDebugRayDirName[] = {
57 "kLeft",
58 "kTop",
59 "kRight",
60 "kBottom"
61};
62#endif
63
64static int xy_index(SkOpRayDir dir) {
65 return static_cast<int>(dir) & 1;
66}
67
68static SkScalar pt_xy(const SkPoint& pt, SkOpRayDir dir) {
69 return (&pt.fX)[xy_index(dir)];
70}
71
72static SkScalar pt_yx(const SkPoint& pt, SkOpRayDir dir) {
73 return (&pt.fX)[!xy_index(dir)];
74}
75
76static double pt_dxdy(const SkDVector& v, SkOpRayDir dir) {
77 return (&v.fX)[xy_index(dir)];
78}
79
80static double pt_dydx(const SkDVector& v, SkOpRayDir dir) {
81 return (&v.fX)[!xy_index(dir)];
82}
83
85 return (&r.fLeft)[static_cast<int>(dir)];
86}
87
88static bool sideways_overlap(const SkRect& rect, const SkPoint& pt, SkOpRayDir dir) {
89 int i = !xy_index(dir);
90 return approximately_between((&rect.fLeft)[i], (&pt.fX)[i], (&rect.fRight)[i]);
91}
92
93static bool less_than(SkOpRayDir dir) {
94 return static_cast<bool>((static_cast<int>(dir) & 2) == 0);
95}
96
97static bool ccw_dxdy(const SkDVector& v, SkOpRayDir dir) {
98 bool vPartPos = pt_dydx(v, dir) > 0;
99 bool leftBottom = ((static_cast<int>(dir) + 1) & 2) != 0;
100 return vPartPos == leftBottom;
101}
102
105 fNext = nullptr;
106 fSpan = span;
107 fT = span->t() * (1 - t) + span->next()->t() * t;
108 SkOpSegment* segment = span->segment();
109 fSlope = segment->dSlopeAtT(fT);
110 fPt = segment->ptAtT(fT);
111 fValid = true;
112 return fabs(fSlope.fX) < fabs(fSlope.fY) ? SkOpRayDir::kLeft : SkOpRayDir::kTop;
113 }
114
118 double fT;
120 bool fValid;
121};
122
124 SkArenaAlloc* allocator) {
125 // if the bounds extreme is outside the best, we're done
126 SkScalar baseXY = pt_xy(base.fPt, dir);
127 SkScalar boundsXY = rect_side(fBounds, dir);
128 bool checkLessThan = less_than(dir);
129 if (!approximately_equal(baseXY, boundsXY) && (baseXY < boundsXY) == checkLessThan) {
130 return;
131 }
132 SkOpSegment* testSegment = &fHead;
133 do {
134 testSegment->rayCheck(base, dir, hits, allocator);
135 } while ((testSegment = testSegment->next()));
136}
137
139 SkArenaAlloc* allocator) {
140 if (!sideways_overlap(fBounds, base.fPt, dir)) {
141 return;
142 }
143 SkScalar baseXY = pt_xy(base.fPt, dir);
144 SkScalar boundsXY = rect_side(fBounds, dir);
145 bool checkLessThan = less_than(dir);
146 if (!approximately_equal(baseXY, boundsXY) && (baseXY < boundsXY) == checkLessThan) {
147 return;
148 }
149 double tVals[3];
150 SkScalar baseYX = pt_yx(base.fPt, dir);
151 int roots = (*CurveIntercept[fVerb * 2 + xy_index(dir)])(fPts, fWeight, baseYX, tVals);
152 for (int index = 0; index < roots; ++index) {
153 double t = tVals[index];
154 if (base.fSpan->segment() == this && approximately_equal(base.fT, t)) {
155 continue;
156 }
157 SkDVector slope;
158 SkPoint pt;
159 SkDEBUGCODE(sk_bzero(&slope, sizeof(slope)));
160 bool valid = false;
161 if (approximately_zero(t)) {
162 pt = fPts[0];
163 } else if (approximately_equal(t, 1)) {
164 pt = fPts[SkPathOpsVerbToPoints(fVerb)];
165 } else {
166 SkASSERT(between(0, t, 1));
167 pt = this->ptAtT(t);
168 if (SkDPoint::ApproximatelyEqual(pt, base.fPt)) {
169 if (base.fSpan->segment() == this) {
170 continue;
171 }
172 } else {
173 SkScalar ptXY = pt_xy(pt, dir);
174 if (!approximately_equal(baseXY, ptXY) && (baseXY < ptXY) == checkLessThan) {
175 continue;
176 }
177 slope = this->dSlopeAtT(t);
178 if (fVerb == SkPath::kCubic_Verb && base.fSpan->segment() == this
179 && roughly_equal(base.fT, t)
180 && SkDPoint::RoughlyEqual(pt, base.fPt)) {
181 #if DEBUG_WINDING
182 SkDebugf("%s (rarely expect this)\n", __FUNCTION__);
183 #endif
184 continue;
185 }
186 if (fabs(pt_dydx(slope, dir) * 10000) > fabs(pt_dxdy(slope, dir))) {
187 valid = true;
188 }
189 }
190 }
191 SkOpSpan* span = this->windingSpanAtT(t);
192 if (!span) {
193 valid = false;
194 } else if (!span->windValue() && !span->oppValue()) {
195 continue;
196 }
197 SkOpRayHit* newHit = allocator->make<SkOpRayHit>();
198 newHit->fNext = *hits;
199 newHit->fPt = pt;
200 newHit->fSlope = slope;
201 newHit->fSpan = span;
202 newHit->fT = t;
203 newHit->fValid = valid;
204 *hits = newHit;
205 }
206}
207
209 SkOpSpan* span = &fHead;
211 do {
212 next = span->next();
213 if (approximately_equal(tHit, next->t())) {
214 return nullptr;
215 }
216 if (tHit < next->t()) {
217 return span;
218 }
219 } while (!next->final() && (span = next->upCast()));
220 return nullptr;
221}
222
223static bool hit_compare_x(const SkOpRayHit* a, const SkOpRayHit* b) {
224 return a->fPt.fX < b->fPt.fX;
225}
226
227static bool reverse_hit_compare_x(const SkOpRayHit* a, const SkOpRayHit* b) {
228 return b->fPt.fX < a->fPt.fX;
229}
230
231static bool hit_compare_y(const SkOpRayHit* a, const SkOpRayHit* b) {
232 return a->fPt.fY < b->fPt.fY;
233}
234
235static bool reverse_hit_compare_y(const SkOpRayHit* a, const SkOpRayHit* b) {
236 return b->fPt.fY < a->fPt.fY;
237}
238
239static double get_t_guess(int tTry, int* dirOffset) {
240 double t = 0.5;
241 *dirOffset = tTry & 1;
242 int tBase = tTry >> 1;
243 int tBits = 0;
244 while (tTry >>= 1) {
245 t /= 2;
246 ++tBits;
247 }
248 if (tBits) {
249 int tIndex = (tBase - 1) & ((1 << tBits) - 1);
250 t += t * 2 * tIndex;
251 }
252 return t;
253}
254
256 SkSTArenaAlloc<1024> allocator;
257 int dirOffset;
258 double t = get_t_guess(fTopTTry++, &dirOffset);
259 SkOpRayHit hitBase;
260 SkOpRayDir dir = hitBase.makeTestBase(this, t);
261 if (hitBase.fSlope.fX == 0 && hitBase.fSlope.fY == 0) {
262 return false;
263 }
264 SkOpRayHit* hitHead = &hitBase;
265 dir = static_cast<SkOpRayDir>(static_cast<int>(dir) + dirOffset);
266 if (hitBase.fSpan && hitBase.fSpan->segment()->verb() > SkPath::kLine_Verb
267 && !pt_dydx(hitBase.fSlope, dir)) {
268 return false;
269 }
270 SkOpContour* contour = contourHead;
271 do {
272 if (!contour->count()) {
273 continue;
274 }
275 contour->rayCheck(hitBase, dir, &hitHead, &allocator);
276 } while ((contour = contour->next()));
277 // sort hits
279 SkOpRayHit* hit = hitHead;
280 while (hit) {
281 sorted.push_back(hit);
282 hit = hit->fNext;
283 }
284 int count = sorted.size();
285 SkTQSort(sorted.begin(), sorted.end(),
288 // verify windings
289#if DEBUG_WINDING
290 SkDebugf("%s dir=%s seg=%d t=%1.9g pt=(%1.9g,%1.9g)\n", __FUNCTION__,
291 gDebugRayDirName[static_cast<int>(dir)], hitBase.fSpan->segment()->debugID(),
292 hitBase.fT, hitBase.fPt.fX, hitBase.fPt.fY);
293 for (int index = 0; index < count; ++index) {
294 hit = sorted[index];
295 SkOpSpan* span = hit->fSpan;
296 SkOpSegment* hitSegment = span ? span->segment() : nullptr;
297 bool operand = span ? hitSegment->operand() : false;
298 bool ccw = ccw_dxdy(hit->fSlope, dir);
299 SkDebugf("%s [%d] valid=%d operand=%d span=%d ccw=%d ", __FUNCTION__, index,
300 hit->fValid, operand, span ? span->debugID() : -1, ccw);
301 if (span) {
302 hitSegment->dumpPtsInner();
303 }
304 SkDebugf(" t=%1.9g pt=(%1.9g,%1.9g) slope=(%1.9g,%1.9g)\n", hit->fT,
305 hit->fPt.fX, hit->fPt.fY, hit->fSlope.fX, hit->fSlope.fY);
306 }
307#endif
308 const SkPoint* last = nullptr;
309 int wind = 0;
310 int oppWind = 0;
311 for (int index = 0; index < count; ++index) {
312 hit = sorted[index];
313 if (!hit->fValid) {
314 return false;
315 }
316 bool ccw = ccw_dxdy(hit->fSlope, dir);
317// SkASSERT(!approximately_zero(hit->fT) || !hit->fValid);
318 SkOpSpan* span = hit->fSpan;
319 if (!span) {
320 return false;
321 }
322 SkOpSegment* hitSegment = span->segment();
323 if (span->windValue() == 0 && span->oppValue() == 0) {
324 continue;
325 }
326 if (last && SkDPoint::ApproximatelyEqual(*last, hit->fPt)) {
327 return false;
328 }
329 if (index < count - 1) {
330 const SkPoint& next = sorted[index + 1]->fPt;
332 return false;
333 }
334 }
335 bool operand = hitSegment->operand();
336 if (operand) {
337 using std::swap;
338 swap(wind, oppWind);
339 }
340 int lastWind = wind;
341 int lastOpp = oppWind;
342 int windValue = ccw ? -span->windValue() : span->windValue();
343 int oppValue = ccw ? -span->oppValue() : span->oppValue();
344 wind += windValue;
345 oppWind += oppValue;
346 bool sumSet = false;
347 int spanSum = span->windSum();
348 int windSum = SkOpSegment::UseInnerWinding(lastWind, wind) ? wind : lastWind;
349 if (spanSum == SK_MinS32) {
350 span->setWindSum(windSum);
351 sumSet = true;
352 } else {
353 // the need for this condition suggests that UseInnerWinding is flawed
354 // happened when last = 1 wind = -1
355#if 0
356 SkASSERT((hitSegment->isXor() ? (windSum & 1) == (spanSum & 1) : windSum == spanSum)
357 || (abs(wind) == abs(lastWind)
358 && (windSum ^ wind ^ lastWind) == spanSum));
359#endif
360 }
361 int oSpanSum = span->oppSum();
362 int oppSum = SkOpSegment::UseInnerWinding(lastOpp, oppWind) ? oppWind : lastOpp;
363 if (oSpanSum == SK_MinS32) {
364 span->setOppSum(oppSum);
365 } else {
366#if 0
367 SkASSERT(hitSegment->oppXor() ? (oppSum & 1) == (oSpanSum & 1) : oppSum == oSpanSum
368 || (abs(oppWind) == abs(lastOpp)
369 && (oppSum ^ oppWind ^ lastOpp) == oSpanSum));
370#endif
371 }
372 if (sumSet) {
373 if (this->globalState()->phase() == SkOpPhase::kFixWinding) {
374 hitSegment->contour()->setCcw(ccw);
375 } else {
376 (void) hitSegment->markAndChaseWinding(span, span->next(), windSum, oppSum, nullptr);
377 (void) hitSegment->markAndChaseWinding(span->next(), span, windSum, oppSum, nullptr);
378 }
379 }
380 if (operand) {
381 using std::swap;
382 swap(wind, oppWind);
383 }
384 last = &hit->fPt;
385 this->globalState()->bumpNested();
386 }
387 return true;
388}
389
391 SkOpSpan* span = &fHead;
393 do {
394 next = span->next();
395 if (span->done()) {
396 continue;
397 }
398 if (span->windSum() != SK_MinS32) {
399 return span;
400 }
401 if (span->sortableTop(contourHead)) {
402 return span;
403 }
404 } while (!next->final() && (span = next->upCast()));
405 return nullptr;
406}
407
409 bool allDone = true;
410 if (fCount) {
411 SkOpSegment* testSegment = &fHead;
412 do {
413 if (testSegment->done()) {
414 continue;
415 }
416 allDone = false;
417 SkOpSpan* result = testSegment->findSortableTop(contourHead);
418 if (result) {
419 return result;
420 }
421 } while ((testSegment = testSegment->next()));
422 }
423 if (allDone) {
424 fDone = true;
425 }
426 return nullptr;
427}
428
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}
sk_bzero(glyphs, sizeof(glyphs))
int count
Definition: FontMgrTest.cpp:50
#define SkASSERT(cond)
Definition: SkAssert.h:116
static bool approximately_zero(double x)
Definition: SkCubics.cpp:153
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static constexpr int32_t SK_MinS32
Definition: SkMath.h:22
static int(*const CurveIntercept[])(const SkPoint[], SkScalar, SkScalar, double *)
bool approximately_equal(double x, double y)
bool between(double a, double b, double c)
bool roughly_equal(double x, double y)
bool approximately_between(double a, double b, double c)
int SkPathOpsVerbToPoints(SkPath::Verb verb)
static double get_t_guess(int tTry, int *dirOffset)
static bool hit_compare_x(const SkOpRayHit *a, const SkOpRayHit *b)
static bool ccw_dxdy(const SkDVector &v, SkOpRayDir dir)
static int xy_index(SkOpRayDir dir)
SkOpSpan * FindSortableTop(SkOpContourHead *contourHead)
static bool hit_compare_y(const SkOpRayHit *a, const SkOpRayHit *b)
static bool reverse_hit_compare_y(const SkOpRayHit *a, const SkOpRayHit *b)
static bool less_than(SkOpRayDir dir)
SkOpRayDir
static SkScalar pt_xy(const SkPoint &pt, SkOpRayDir dir)
static double pt_dydx(const SkDVector &v, SkOpRayDir dir)
static bool sideways_overlap(const SkRect &rect, const SkPoint &pt, SkOpRayDir dir)
static bool reverse_hit_compare_x(const SkOpRayHit *a, const SkOpRayHit *b)
static SkScalar pt_yx(const SkPoint &pt, SkOpRayDir dir)
static double pt_dxdy(const SkDVector &v, SkOpRayDir dir)
static SkScalar rect_side(const SkRect &r, SkOpRayDir dir)
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition: SkRefCnt.h:341
void SkTQSort(T *begin, T *end, const C &lessThan)
Definition: SkTSort.h:194
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
auto make(Ctor &&ctor) -> decltype(ctor(nullptr))
Definition: SkArenaAlloc.h:120
SkOpSpan * findSortableTop(SkOpContour *)
void rayCheck(const SkOpRayHit &base, SkOpRayDir dir, SkOpRayHit **hits, SkArenaAlloc *)
void setCcw(int ccw)
Definition: SkOpContour.h:322
SkOpSegment fHead
Definition: SkOpContour.h:384
SkPathOpsBounds fBounds
Definition: SkOpContour.h:387
SkDVector dSlopeAtT(double mid) const
Definition: SkOpSegment.h:213
void dumpPtsInner(const char *prefix="seg") const
SkPath::Verb verb() const
Definition: SkOpSegment.h:419
bool operand() const
SkOpSpan * findSortableTop(SkOpContour *)
bool done() const
Definition: SkOpSegment.h:200
SkPoint ptAtT(double mid) const
Definition: SkOpSegment.h:323
void rayCheck(const SkOpRayHit &base, SkOpRayDir dir, SkOpRayHit **hits, SkArenaAlloc *)
bool markAndChaseWinding(SkOpSpanBase *start, SkOpSpanBase *end, int winding, SkOpSpanBase **lastPtr)
int debugID() const
Definition: SkOpSegment.h:154
static bool UseInnerWinding(int outerWinding, int innerWinding)
bool oppXor() const
bool isXor() const
SkOpSpan * windingSpanAtT(double tHit)
SkOpSegment * next() const
Definition: SkOpSegment.h:304
SkOpContour * contour() const
Definition: SkOpSegment.h:130
SkOpGlobalState * globalState() const
Definition: SkOpSpan.cpp:239
int debugID() const
Definition: SkOpSpan.h:224
double t() const
Definition: SkOpSpan.h:375
SkOpSegment * segment() const
Definition: SkOpSpan.h:318
bool sortableTop(SkOpContour *)
int windSum() const
Definition: SkOpSpan.h:555
SkOpSpanBase * next() const
Definition: SkOpSpan.h:495
int oppValue() const
Definition: SkOpSpan.h:505
void setWindSum(int windSum)
Definition: SkOpSpan.cpp:482
int oppSum() const
Definition: SkOpSpan.h:500
bool done() const
Definition: SkOpSpan.h:459
void setOppSum(int oppSum)
Definition: SkOpSpan.cpp:472
int windValue() const
Definition: SkOpSpan.h:560
@ kCubic_Verb
Definition: SkPath.h:1470
@ kLine_Verb
Definition: SkPath.h:1467
int size() const
Definition: SkTArray.h:421
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct a[10]
GAsyncResult * result
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition: switches.h:145
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition: SkVx.h:707
static bool ApproximatelyEqual(const SkPoint &a, const SkPoint &b)
static bool RoughlyEqual(const SkPoint &a, const SkPoint &b)
SkOpRayHit * fNext
SkOpSpan * fSpan
SkDVector fSlope
SkOpRayDir makeTestBase(SkOpSpan *span, double t)
float fX
x-axis value
Definition: SkPoint_impl.h:164
float fY
y-axis value
Definition: SkPoint_impl.h:165
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14