Flutter Engine
The Flutter Engine
Enumerations | Functions | Variables
SkDashPath Namespace Reference

Enumerations

enum class  StrokeRecApplication { kDisallow , kAllow }
 

Functions

void CalcDashParameters (SkScalar phase, const SkScalar intervals[], int32_t count, SkScalar *initialDashLength, int32_t *initialDashIndex, SkScalar *intervalLength, SkScalar *adjustedPhase=nullptr)
 
bool FilterDashPath (SkPath *dst, const SkPath &src, SkStrokeRec *, const SkRect *, const SkPathEffect::DashInfo &info)
 
bool InternalFilter (SkPath *dst, const SkPath &src, SkStrokeRec *rec, const SkRect *cullRect, const SkScalar aIntervals[], int32_t count, SkScalar initialDashLength, int32_t initialDashIndex, SkScalar intervalLength, SkScalar startPhase, StrokeRecApplication=StrokeRecApplication::kAllow)
 
bool ValidDashPath (SkScalar phase, const SkScalar intervals[], int32_t count)
 

Variables

const SkScalar kMaxDashCount = 1000000
 

Enumeration Type Documentation

◆ StrokeRecApplication

See comments for InternalFilter

Enumerator
kDisallow 
kAllow 

Definition at line 36 of file SkDashPathPriv.h.

Function Documentation

◆ CalcDashParameters()

void SkDashPath::CalcDashParameters ( SkScalar  phase,
const SkScalar  intervals[],
int32_t  count,
SkScalar initialDashLength,
int32_t *  initialDashIndex,
SkScalar intervalLength,
SkScalar adjustedPhase = nullptr 
)

Calculates the initialDashLength, initialDashIndex, and intervalLength based on the inputed phase and intervals. If adjustedPhase is passed in, then the phase will be adjusted to be between 0 and intervalLength. The result will be stored in adjustedPhase. If adjustedPhase is nullptr then it is assumed phase is already between 0 and intervalLength

Caller should have already used ValidDashPath to exclude invalid data.

Definition at line 54 of file SkDashPath.cpp.

56 {
57 SkScalar len = 0;
58 for (int i = 0; i < count; i++) {
59 len += intervals[i];
60 }
61 *intervalLength = len;
62 // Adjust phase to be between 0 and len, "flipping" phase if negative.
63 // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80
64 if (adjustedPhase) {
65 if (phase < 0) {
66 phase = -phase;
67 if (phase > len) {
68 phase = SkScalarMod(phase, len);
69 }
70 phase = len - phase;
71
72 // Due to finite precision, it's possible that phase == len,
73 // even after the subtract (if len >>> phase), so fix that here.
74 // This fixes http://crbug.com/124652 .
75 SkASSERT(phase <= len);
76 if (phase == len) {
77 phase = 0;
78 }
79 } else if (phase >= len) {
80 phase = SkScalarMod(phase, len);
81 }
82 *adjustedPhase = phase;
83 }
84 SkASSERT(phase >= 0 && phase < len);
85
86 *initialDashLength = find_first_interval(intervals, phase,
87 initialDashIndex, count);
88
89 SkASSERT(*initialDashLength >= 0);
90 SkASSERT(*initialDashIndex >= 0 && *initialDashIndex < count);
91}
int count
Definition: FontMgrTest.cpp:50
#define SkASSERT(cond)
Definition: SkAssert.h:116
static SkScalar find_first_interval(const SkScalar intervals[], SkScalar phase, int32_t *index, int count)
Definition: SkDashPath.cpp:35
#define SkScalarMod(x, y)
Definition: SkScalar.h:41
float SkScalar
Definition: extension.cpp:12

◆ FilterDashPath()

bool SkDashPath::FilterDashPath ( SkPath dst,
const SkPath src,
SkStrokeRec rec,
const SkRect cullRect,
const SkPathEffect::DashInfo info 
)

Definition at line 462 of file SkDashPath.cpp.

463 {
464 if (!ValidDashPath(info.fPhase, info.fIntervals, info.fCount)) {
465 return false;
466 }
467 SkScalar initialDashLength = 0;
468 int32_t initialDashIndex = 0;
469 SkScalar intervalLength = 0;
470 CalcDashParameters(info.fPhase, info.fIntervals, info.fCount,
471 &initialDashLength, &initialDashIndex, &intervalLength);
472 return InternalFilter(dst, src, rec, cullRect, info.fIntervals, info.fCount, initialDashLength,
473 initialDashIndex, intervalLength, info.fPhase);
474}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
bool InternalFilter(SkPath *dst, const SkPath &src, SkStrokeRec *rec, const SkRect *cullRect, const SkScalar aIntervals[], int32_t count, SkScalar initialDashLength, int32_t initialDashIndex, SkScalar intervalLength, SkScalar startPhase, StrokeRecApplication=StrokeRecApplication::kAllow)
Definition: SkDashPath.cpp:309
void CalcDashParameters(SkScalar phase, const SkScalar intervals[], int32_t count, SkScalar *initialDashLength, int32_t *initialDashIndex, SkScalar *intervalLength, SkScalar *adjustedPhase=nullptr)
Definition: SkDashPath.cpp:54
bool ValidDashPath(SkScalar phase, const SkScalar intervals[], int32_t count)
Definition: SkDashPath.cpp:476
dst
Definition: cp.py:12

◆ InternalFilter()

bool SkDashPath::InternalFilter ( SkPath dst,
const SkPath src,
SkStrokeRec rec,
const SkRect cullRect,
const SkScalar  aIntervals[],
int32_t  count,
SkScalar  initialDashLength,
int32_t  initialDashIndex,
SkScalar  intervalLength,
SkScalar  startPhase,
StrokeRecApplication  strokeRecApplication = StrokeRecApplication::kAllow 
)

Caller should have already used ValidDashPath to exclude invalid data. Typically, this leaves the strokeRec unmodified. However, for some simple shapes (e.g. a line) it may directly evaluate the dash and stroke to produce a stroked output path with a fill strokeRec. Passing true for disallowStrokeRecApplication turns this behavior off.

Definition at line 309 of file SkDashPath.cpp.

313 {
314 // we must always have an even number of intervals
316
317 // we do nothing if the src wants to be filled
318 SkStrokeRec::Style style = rec->getStyle();
320 return false;
321 }
322
323 const SkScalar* intervals = aIntervals;
324 SkScalar dashCount = 0;
325 int segCount = 0;
326
327 SkPath cullPathStorage;
328 const SkPath* srcPtr = &src;
329 if (cull_path(src, *rec, cullRect, intervalLength, &cullPathStorage)) {
330 // if rect is closed, starts in a dash, and ends in a dash, add the initial join
331 // potentially a better fix is described here: bug.skia.org/7445
332 if (src.isRect(nullptr) && src.isLastContourClosed() && is_even(initialDashIndex)) {
333 SkScalar pathLength = SkPathMeasure(src, false, rec->getResScale()).getLength();
334#if defined(SK_LEGACY_RECT_DASHING_BUG)
335 SkScalar endPhase = SkScalarMod(pathLength + initialDashLength, intervalLength);
336#else
337 SkScalar endPhase = SkScalarMod(pathLength + startPhase, intervalLength);
338#endif
339 int index = 0;
340 while (endPhase > intervals[index]) {
341 endPhase -= intervals[index++];
342 SkASSERT(index <= count);
343 if (index == count) {
344 // We have run out of intervals. endPhase "should" never get to this point,
345 // but it could if the subtracts underflowed. Hence we will pin it as if it
346 // perfectly ran through the intervals.
347 // See crbug.com/875494 (and skbug.com/8274)
348 endPhase = 0;
349 break;
350 }
351 }
352 // if dash ends inside "on", or ends at beginning of "off"
353 if (is_even(index) == (endPhase > 0)) {
354 SkPoint midPoint = src.getPoint(0);
355 // get vector at end of rect
356 int last = src.countPoints() - 1;
357 while (midPoint == src.getPoint(last)) {
358 --last;
359 SkASSERT(last >= 0);
360 }
361 // get vector at start of rect
362 int next = 1;
363 while (midPoint == src.getPoint(next)) {
364 ++next;
365 SkASSERT(next < last);
366 }
367 SkVector v = midPoint - src.getPoint(last);
368 const SkScalar kTinyOffset = SK_ScalarNearlyZero;
369 // scale vector to make start of tiny right angle
370 v *= kTinyOffset;
371 cullPathStorage.moveTo(midPoint - v);
372 cullPathStorage.lineTo(midPoint);
373 v = midPoint - src.getPoint(next);
374 // scale vector to make end of tiny right angle
375 v *= kTinyOffset;
376 cullPathStorage.lineTo(midPoint - v);
377 }
378 }
379 srcPtr = &cullPathStorage;
380 }
381
382 SpecialLineRec lineRec;
383 bool specialLine = (StrokeRecApplication::kAllow == strokeRecApplication) &&
384 lineRec.init(*srcPtr, dst, rec, count >> 1, intervalLength);
385
386 SkPathMeasure meas(*srcPtr, false, rec->getResScale());
387
388 do {
389 bool skipFirstSegment = meas.isClosed();
390 bool addedSegment = false;
391 SkScalar length = meas.getLength();
392 int index = initialDashIndex;
393
394 // Since the path length / dash length ratio may be arbitrarily large, we can exert
395 // significant memory pressure while attempting to build the filtered path. To avoid this,
396 // we simply give up dashing beyond a certain threshold.
397 //
398 // The original bug report (http://crbug.com/165432) is based on a path yielding more than
399 // 90 million dash segments and crashing the memory allocator. A limit of 1 million
400 // segments seems reasonable: at 2 verbs per segment * 9 bytes per verb, this caps the
401 // maximum dash memory overhead at roughly 17MB per path.
402 dashCount += length * (count >> 1) / intervalLength;
403 if (dashCount > kMaxDashCount) {
404 dst->reset();
405 return false;
406 }
407
408 // Using double precision to avoid looping indefinitely due to single precision rounding
409 // (for extreme path_length/dash_length ratios). See test_infinite_dash() unittest.
410 double distance = 0;
411 double dlen = initialDashLength;
412
413 while (distance < length) {
414 SkASSERT(dlen >= 0);
415 addedSegment = false;
416 if (is_even(index) && !skipFirstSegment) {
417 addedSegment = true;
418 ++segCount;
419
420 if (specialLine) {
423 dst);
424 } else {
425 meas.getSegment(SkDoubleToScalar(distance),
427 dst, true);
428 }
429 }
430 distance += dlen;
431
432 // clear this so we only respect it the first time around
433 skipFirstSegment = false;
434
435 // wrap around our intervals array if necessary
436 index += 1;
437 SkASSERT(index <= count);
438 if (index == count) {
439 index = 0;
440 }
441
442 // fetch our next dlen
443 dlen = intervals[index];
444 }
445
446 // extend if we ended on a segment and we need to join up with the (skipped) initial segment
447 if (meas.isClosed() && is_even(initialDashIndex) &&
448 initialDashLength >= 0) {
449 meas.getSegment(0, initialDashLength, dst, !addedSegment);
450 ++segCount;
451 }
452 } while (meas.nextContour());
453
454 // TODO: do we still need this?
455 if (segCount > 1) {
457 }
458
459 return true;
460}
static float next(float f)
static int is_even(int x)
Definition: SkDashPath.cpp:31
static bool cull_path(const SkPath &srcPath, const SkStrokeRec &rec, const SkRect *cullRect, SkScalar intervalLength, SkPath *dstPath)
Definition: SkDashPath.cpp:173
#define SkDoubleToScalar(x)
Definition: SkScalar.h:64
#define SK_ScalarNearlyZero
Definition: SkScalar.h:99
SkScalar getLength()
static void SetConvexity(const SkPath &path, SkPathConvexity c)
Definition: SkPathPriv.h:413
Definition: SkPath.h:59
SkPath & moveTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:688
SkPath & lineTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:728
Style getStyle() const
Definition: SkStrokeRec.cpp:71
@ kStrokeAndFill_Style
Definition: SkStrokeRec.h:36
SkScalar getResScale() const
Definition: SkStrokeRec.h:71
bool init(const SkPath &src, SkPath *dst, SkStrokeRec *rec, int intervalCount, SkScalar intervalLength)
Definition: SkDashPath.cpp:237
void addSegment(SkScalar d0, SkScalar d1, SkPath *path) const
Definition: SkDashPath.cpp:280
size_t length
const SkScalar kMaxDashCount

◆ ValidDashPath()

bool SkDashPath::ValidDashPath ( SkScalar  phase,
const SkScalar  intervals[],
int32_t  count 
)

Definition at line 476 of file SkDashPath.cpp.

476 {
477 if (count < 2 || !SkIsAlign2(count)) {
478 return false;
479 }
480 SkScalar length = 0;
481 for (int i = 0; i < count; i++) {
482 if (intervals[i] < 0) {
483 return false;
484 }
485 length += intervals[i];
486 }
487 // watch out for values that might make us go out of bounds
488 return length > 0 && SkIsFinite(phase, length);
489}
static constexpr bool SkIsAlign2(T x)
Definition: SkAlign.h:19
static bool SkIsFinite(T x, Pack... values)

Variable Documentation

◆ kMaxDashCount

const SkScalar SkDashPath::kMaxDashCount = 1000000

Definition at line 32 of file SkDashPathPriv.h.