Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
84static SkScalar rect_side(const SkRect& r, SkOpRayDir dir) {
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}
int count
#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
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static bool between(SkScalar a, SkScalar b, SkScalar c)
static void sk_bzero(void *buffer, size_t size)
Definition SkMalloc.h:105
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 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)
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
auto make(Ctor &&ctor) -> decltype(ctor(nullptr))
SkOpSpan * findSortableTop(SkOpContour *)
void rayCheck(const SkOpRayHit &base, SkOpRayDir dir, SkOpRayHit **hits, SkArenaAlloc *)
void setCcw(int ccw)
SkOpSegment fHead
SkPathOpsBounds fBounds
SkOpPhase phase() const
SkDVector dSlopeAtT(double mid) const
void dumpPtsInner(const char *prefix="seg") const
SkPath::Verb verb() const
bool operand() const
SkOpSpan * findSortableTop(SkOpContour *)
bool done() const
SkPoint ptAtT(double mid) const
void rayCheck(const SkOpRayHit &base, SkOpRayDir dir, SkOpRayHit **hits, SkArenaAlloc *)
bool markAndChaseWinding(SkOpSpanBase *start, SkOpSpanBase *end, int winding, SkOpSpanBase **lastPtr)
int debugID() const
static bool UseInnerWinding(int outerWinding, int innerWinding)
bool oppXor() const
bool isXor() const
SkOpSpan * windingSpanAtT(double tHit)
SkOpSegment * next() const
SkOpContour * contour() const
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:1462
@ kLine_Verb
Definition SkPath.h:1459
int size() const
Definition SkTArray.h:416
float SkScalar
Definition extension.cpp:12
static bool b
struct MyStruct a[10]
GAsyncResult * result
static bool ApproximatelyEqual(const SkPoint &a, const SkPoint &b)
static bool RoughlyEqual(const SkPoint &a, const SkPoint &b)
SkOpRayHit * fNext
SkOpRayDir makeTestBase(SkOpSpan *span, double t)
float fX
x-axis value
float fY
y-axis value
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14