1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2014 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/core/SkPathMeasure.h"
9cb93a386Sopenharmony_ci#include "include/core/SkStrokeRec.h"
10cb93a386Sopenharmony_ci#include "src/core/SkPathPriv.h"
11cb93a386Sopenharmony_ci#include "src/core/SkPointPriv.h"
12cb93a386Sopenharmony_ci#include "src/utils/SkDashPathPriv.h"
13cb93a386Sopenharmony_ci
14cb93a386Sopenharmony_ci#include <utility>
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_cistatic inline int is_even(int x) {
17cb93a386Sopenharmony_ci    return !(x & 1);
18cb93a386Sopenharmony_ci}
19cb93a386Sopenharmony_ci
20cb93a386Sopenharmony_cistatic SkScalar find_first_interval(const SkScalar intervals[], SkScalar phase,
21cb93a386Sopenharmony_ci                                    int32_t* index, int count) {
22cb93a386Sopenharmony_ci    for (int i = 0; i < count; ++i) {
23cb93a386Sopenharmony_ci        SkScalar gap = intervals[i];
24cb93a386Sopenharmony_ci        if (phase > gap || (phase == gap && gap)) {
25cb93a386Sopenharmony_ci            phase -= gap;
26cb93a386Sopenharmony_ci        } else {
27cb93a386Sopenharmony_ci            *index = i;
28cb93a386Sopenharmony_ci            return gap - phase;
29cb93a386Sopenharmony_ci        }
30cb93a386Sopenharmony_ci    }
31cb93a386Sopenharmony_ci    // If we get here, phase "appears" to be larger than our length. This
32cb93a386Sopenharmony_ci    // shouldn't happen with perfect precision, but we can accumulate errors
33cb93a386Sopenharmony_ci    // during the initial length computation (rounding can make our sum be too
34cb93a386Sopenharmony_ci    // big or too small. In that event, we just have to eat the error here.
35cb93a386Sopenharmony_ci    *index = 0;
36cb93a386Sopenharmony_ci    return intervals[0];
37cb93a386Sopenharmony_ci}
38cb93a386Sopenharmony_ci
39cb93a386Sopenharmony_civoid SkDashPath::CalcDashParameters(SkScalar phase, const SkScalar intervals[], int32_t count,
40cb93a386Sopenharmony_ci                                    SkScalar* initialDashLength, int32_t* initialDashIndex,
41cb93a386Sopenharmony_ci                                    SkScalar* intervalLength, SkScalar* adjustedPhase) {
42cb93a386Sopenharmony_ci    SkScalar len = 0;
43cb93a386Sopenharmony_ci    for (int i = 0; i < count; i++) {
44cb93a386Sopenharmony_ci        len += intervals[i];
45cb93a386Sopenharmony_ci    }
46cb93a386Sopenharmony_ci    *intervalLength = len;
47cb93a386Sopenharmony_ci    // Adjust phase to be between 0 and len, "flipping" phase if negative.
48cb93a386Sopenharmony_ci    // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80
49cb93a386Sopenharmony_ci    if (adjustedPhase) {
50cb93a386Sopenharmony_ci        if (phase < 0) {
51cb93a386Sopenharmony_ci            phase = -phase;
52cb93a386Sopenharmony_ci            if (phase > len) {
53cb93a386Sopenharmony_ci                phase = SkScalarMod(phase, len);
54cb93a386Sopenharmony_ci            }
55cb93a386Sopenharmony_ci            phase = len - phase;
56cb93a386Sopenharmony_ci
57cb93a386Sopenharmony_ci            // Due to finite precision, it's possible that phase == len,
58cb93a386Sopenharmony_ci            // even after the subtract (if len >>> phase), so fix that here.
59cb93a386Sopenharmony_ci            // This fixes http://crbug.com/124652 .
60cb93a386Sopenharmony_ci            SkASSERT(phase <= len);
61cb93a386Sopenharmony_ci            if (phase == len) {
62cb93a386Sopenharmony_ci                phase = 0;
63cb93a386Sopenharmony_ci            }
64cb93a386Sopenharmony_ci        } else if (phase >= len) {
65cb93a386Sopenharmony_ci            phase = SkScalarMod(phase, len);
66cb93a386Sopenharmony_ci        }
67cb93a386Sopenharmony_ci        *adjustedPhase = phase;
68cb93a386Sopenharmony_ci    }
69cb93a386Sopenharmony_ci    SkASSERT(phase >= 0 && phase < len);
70cb93a386Sopenharmony_ci
71cb93a386Sopenharmony_ci    *initialDashLength = find_first_interval(intervals, phase,
72cb93a386Sopenharmony_ci                                            initialDashIndex, count);
73cb93a386Sopenharmony_ci
74cb93a386Sopenharmony_ci    SkASSERT(*initialDashLength >= 0);
75cb93a386Sopenharmony_ci    SkASSERT(*initialDashIndex >= 0 && *initialDashIndex < count);
76cb93a386Sopenharmony_ci}
77cb93a386Sopenharmony_ci
78cb93a386Sopenharmony_cistatic void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) {
79cb93a386Sopenharmony_ci    SkScalar radius = SkScalarHalf(rec.getWidth());
80cb93a386Sopenharmony_ci    if (0 == radius) {
81cb93a386Sopenharmony_ci        radius = SK_Scalar1;    // hairlines
82cb93a386Sopenharmony_ci    }
83cb93a386Sopenharmony_ci    if (SkPaint::kMiter_Join == rec.getJoin()) {
84cb93a386Sopenharmony_ci        radius *= rec.getMiter();
85cb93a386Sopenharmony_ci    }
86cb93a386Sopenharmony_ci    rect->outset(radius, radius);
87cb93a386Sopenharmony_ci}
88cb93a386Sopenharmony_ci
89cb93a386Sopenharmony_ci// If line is zero-length, bump out the end by a tiny amount
90cb93a386Sopenharmony_ci// to draw endcaps. The bump factor is sized so that
91cb93a386Sopenharmony_ci// SkPoint::Distance() computes a non-zero length.
92cb93a386Sopenharmony_ci// Offsets SK_ScalarNearlyZero or smaller create empty paths when Iter measures length.
93cb93a386Sopenharmony_ci// Large values are scaled by SK_ScalarNearlyZero so significant bits change.
94cb93a386Sopenharmony_cistatic void adjust_zero_length_line(SkPoint pts[2]) {
95cb93a386Sopenharmony_ci    SkASSERT(pts[0] == pts[1]);
96cb93a386Sopenharmony_ci    pts[1].fX += std::max(1.001f, pts[1].fX) * SK_ScalarNearlyZero;
97cb93a386Sopenharmony_ci}
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_cistatic bool clip_line(SkPoint pts[2], const SkRect& bounds, SkScalar intervalLength,
100cb93a386Sopenharmony_ci                      SkScalar priorPhase) {
101cb93a386Sopenharmony_ci    SkVector dxy = pts[1] - pts[0];
102cb93a386Sopenharmony_ci
103cb93a386Sopenharmony_ci    // only horizontal or vertical lines
104cb93a386Sopenharmony_ci    if (dxy.fX && dxy.fY) {
105cb93a386Sopenharmony_ci        return false;
106cb93a386Sopenharmony_ci    }
107cb93a386Sopenharmony_ci    int xyOffset = SkToBool(dxy.fY);  // 0 to adjust horizontal, 1 to adjust vertical
108cb93a386Sopenharmony_ci
109cb93a386Sopenharmony_ci    SkScalar minXY = (&pts[0].fX)[xyOffset];
110cb93a386Sopenharmony_ci    SkScalar maxXY = (&pts[1].fX)[xyOffset];
111cb93a386Sopenharmony_ci    bool swapped = maxXY < minXY;
112cb93a386Sopenharmony_ci    if (swapped) {
113cb93a386Sopenharmony_ci        using std::swap;
114cb93a386Sopenharmony_ci        swap(minXY, maxXY);
115cb93a386Sopenharmony_ci    }
116cb93a386Sopenharmony_ci
117cb93a386Sopenharmony_ci    SkASSERT(minXY <= maxXY);
118cb93a386Sopenharmony_ci    SkScalar leftTop = (&bounds.fLeft)[xyOffset];
119cb93a386Sopenharmony_ci    SkScalar rightBottom = (&bounds.fRight)[xyOffset];
120cb93a386Sopenharmony_ci    if (maxXY < leftTop || minXY > rightBottom) {
121cb93a386Sopenharmony_ci        return false;
122cb93a386Sopenharmony_ci    }
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci    // Now we actually perform the chop, removing the excess to the left/top and
125cb93a386Sopenharmony_ci    // right/bottom of the bounds (keeping our new line "in phase" with the dash,
126cb93a386Sopenharmony_ci    // hence the (mod intervalLength).
127cb93a386Sopenharmony_ci
128cb93a386Sopenharmony_ci    if (minXY < leftTop) {
129cb93a386Sopenharmony_ci        minXY = leftTop - SkScalarMod(leftTop - minXY, intervalLength);
130cb93a386Sopenharmony_ci        if (!swapped) {
131cb93a386Sopenharmony_ci            minXY -= priorPhase;  // for rectangles, adjust by prior phase
132cb93a386Sopenharmony_ci        }
133cb93a386Sopenharmony_ci    }
134cb93a386Sopenharmony_ci    if (maxXY > rightBottom) {
135cb93a386Sopenharmony_ci        maxXY = rightBottom + SkScalarMod(maxXY - rightBottom, intervalLength);
136cb93a386Sopenharmony_ci        if (swapped) {
137cb93a386Sopenharmony_ci            maxXY += priorPhase;  // for rectangles, adjust by prior phase
138cb93a386Sopenharmony_ci        }
139cb93a386Sopenharmony_ci    }
140cb93a386Sopenharmony_ci
141cb93a386Sopenharmony_ci    SkASSERT(maxXY >= minXY);
142cb93a386Sopenharmony_ci    if (swapped) {
143cb93a386Sopenharmony_ci        using std::swap;
144cb93a386Sopenharmony_ci        swap(minXY, maxXY);
145cb93a386Sopenharmony_ci    }
146cb93a386Sopenharmony_ci    (&pts[0].fX)[xyOffset] = minXY;
147cb93a386Sopenharmony_ci    (&pts[1].fX)[xyOffset] = maxXY;
148cb93a386Sopenharmony_ci
149cb93a386Sopenharmony_ci    if (minXY == maxXY) {
150cb93a386Sopenharmony_ci        adjust_zero_length_line(pts);
151cb93a386Sopenharmony_ci    }
152cb93a386Sopenharmony_ci    return true;
153cb93a386Sopenharmony_ci}
154cb93a386Sopenharmony_ci
155cb93a386Sopenharmony_ci// Handles only lines and rects.
156cb93a386Sopenharmony_ci// If cull_path() returns true, dstPath is the new smaller path,
157cb93a386Sopenharmony_ci// otherwise dstPath may have been changed but you should ignore it.
158cb93a386Sopenharmony_cistatic bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec,
159cb93a386Sopenharmony_ci                      const SkRect* cullRect, SkScalar intervalLength, SkPath* dstPath) {
160cb93a386Sopenharmony_ci    if (!cullRect) {
161cb93a386Sopenharmony_ci        SkPoint pts[2];
162cb93a386Sopenharmony_ci        if (srcPath.isLine(pts) && pts[0] == pts[1]) {
163cb93a386Sopenharmony_ci            adjust_zero_length_line(pts);
164cb93a386Sopenharmony_ci            dstPath->moveTo(pts[0]);
165cb93a386Sopenharmony_ci            dstPath->lineTo(pts[1]);
166cb93a386Sopenharmony_ci            return true;
167cb93a386Sopenharmony_ci        }
168cb93a386Sopenharmony_ci        return false;
169cb93a386Sopenharmony_ci    }
170cb93a386Sopenharmony_ci
171cb93a386Sopenharmony_ci    SkRect bounds;
172cb93a386Sopenharmony_ci    bounds = *cullRect;
173cb93a386Sopenharmony_ci    outset_for_stroke(&bounds, rec);
174cb93a386Sopenharmony_ci
175cb93a386Sopenharmony_ci    {
176cb93a386Sopenharmony_ci        SkPoint pts[2];
177cb93a386Sopenharmony_ci        if (srcPath.isLine(pts)) {
178cb93a386Sopenharmony_ci            if (clip_line(pts, bounds, intervalLength, 0)) {
179cb93a386Sopenharmony_ci                dstPath->moveTo(pts[0]);
180cb93a386Sopenharmony_ci                dstPath->lineTo(pts[1]);
181cb93a386Sopenharmony_ci                return true;
182cb93a386Sopenharmony_ci            }
183cb93a386Sopenharmony_ci            return false;
184cb93a386Sopenharmony_ci        }
185cb93a386Sopenharmony_ci    }
186cb93a386Sopenharmony_ci
187cb93a386Sopenharmony_ci    if (srcPath.isRect(nullptr)) {
188cb93a386Sopenharmony_ci        // We'll break the rect into four lines, culling each separately.
189cb93a386Sopenharmony_ci        SkPath::Iter iter(srcPath, false);
190cb93a386Sopenharmony_ci
191cb93a386Sopenharmony_ci        SkPoint pts[4];  // Rects are all moveTo and lineTo, so we'll only use pts[0] and pts[1].
192cb93a386Sopenharmony_ci        SkAssertResult(SkPath::kMove_Verb == iter.next(pts));
193cb93a386Sopenharmony_ci
194cb93a386Sopenharmony_ci        double accum = 0;  // Sum of unculled edge lengths to keep the phase correct.
195cb93a386Sopenharmony_ci                           // Intentionally a double to minimize the risk of overflow and drift.
196cb93a386Sopenharmony_ci        while (iter.next(pts) == SkPath::kLine_Verb) {
197cb93a386Sopenharmony_ci            // Notice this vector v and accum work with the original unclipped length.
198cb93a386Sopenharmony_ci            SkVector v = pts[1] - pts[0];
199cb93a386Sopenharmony_ci
200cb93a386Sopenharmony_ci            if (clip_line(pts, bounds, intervalLength, std::fmod(accum, intervalLength))) {
201cb93a386Sopenharmony_ci                // pts[0] may have just been changed by clip_line().
202cb93a386Sopenharmony_ci                // If that's not where we ended the previous lineTo(), we need to moveTo() there.
203cb93a386Sopenharmony_ci                SkPoint last;
204cb93a386Sopenharmony_ci                if (!dstPath->getLastPt(&last) || last != pts[0]) {
205cb93a386Sopenharmony_ci                    dstPath->moveTo(pts[0]);
206cb93a386Sopenharmony_ci                }
207cb93a386Sopenharmony_ci                dstPath->lineTo(pts[1]);
208cb93a386Sopenharmony_ci            }
209cb93a386Sopenharmony_ci
210cb93a386Sopenharmony_ci            // We either just traveled v.fX horizontally or v.fY vertically.
211cb93a386Sopenharmony_ci            SkASSERT(v.fX == 0 || v.fY == 0);
212cb93a386Sopenharmony_ci            accum += SkScalarAbs(v.fX + v.fY);
213cb93a386Sopenharmony_ci        }
214cb93a386Sopenharmony_ci        return !dstPath->isEmpty();
215cb93a386Sopenharmony_ci    }
216cb93a386Sopenharmony_ci
217cb93a386Sopenharmony_ci    return false;
218cb93a386Sopenharmony_ci}
219cb93a386Sopenharmony_ci
220cb93a386Sopenharmony_ciclass SpecialLineRec {
221cb93a386Sopenharmony_cipublic:
222cb93a386Sopenharmony_ci    bool init(const SkPath& src, SkPath* dst, SkStrokeRec* rec,
223cb93a386Sopenharmony_ci              int intervalCount, SkScalar intervalLength) {
224cb93a386Sopenharmony_ci        if (rec->isHairlineStyle() || !src.isLine(fPts)) {
225cb93a386Sopenharmony_ci            return false;
226cb93a386Sopenharmony_ci        }
227cb93a386Sopenharmony_ci
228cb93a386Sopenharmony_ci        // can relax this in the future, if we handle square and round caps
229cb93a386Sopenharmony_ci        if (SkPaint::kButt_Cap != rec->getCap()) {
230cb93a386Sopenharmony_ci            return false;
231cb93a386Sopenharmony_ci        }
232cb93a386Sopenharmony_ci
233cb93a386Sopenharmony_ci        SkScalar pathLength = SkPoint::Distance(fPts[0], fPts[1]);
234cb93a386Sopenharmony_ci
235cb93a386Sopenharmony_ci        fTangent = fPts[1] - fPts[0];
236cb93a386Sopenharmony_ci        if (fTangent.isZero()) {
237cb93a386Sopenharmony_ci            return false;
238cb93a386Sopenharmony_ci        }
239cb93a386Sopenharmony_ci
240cb93a386Sopenharmony_ci        fPathLength = pathLength;
241cb93a386Sopenharmony_ci        fTangent.scale(SkScalarInvert(pathLength));
242cb93a386Sopenharmony_ci        SkPointPriv::RotateCCW(fTangent, &fNormal);
243cb93a386Sopenharmony_ci        fNormal.scale(SkScalarHalf(rec->getWidth()));
244cb93a386Sopenharmony_ci
245cb93a386Sopenharmony_ci        // now estimate how many quads will be added to the path
246cb93a386Sopenharmony_ci        //     resulting segments = pathLen * intervalCount / intervalLen
247cb93a386Sopenharmony_ci        //     resulting points = 4 * segments
248cb93a386Sopenharmony_ci
249cb93a386Sopenharmony_ci        SkScalar ptCount = pathLength * intervalCount / (float)intervalLength;
250cb93a386Sopenharmony_ci        ptCount = std::min(ptCount, SkDashPath::kMaxDashCount);
251cb93a386Sopenharmony_ci        if (SkScalarIsNaN(ptCount)) {
252cb93a386Sopenharmony_ci            return false;
253cb93a386Sopenharmony_ci        }
254cb93a386Sopenharmony_ci        int n = SkScalarCeilToInt(ptCount) << 2;
255cb93a386Sopenharmony_ci        dst->incReserve(n);
256cb93a386Sopenharmony_ci
257cb93a386Sopenharmony_ci        // we will take care of the stroking
258cb93a386Sopenharmony_ci        rec->setFillStyle();
259cb93a386Sopenharmony_ci        return true;
260cb93a386Sopenharmony_ci    }
261cb93a386Sopenharmony_ci
262cb93a386Sopenharmony_ci    void addSegment(SkScalar d0, SkScalar d1, SkPath* path) const {
263cb93a386Sopenharmony_ci        SkASSERT(d0 <= fPathLength);
264cb93a386Sopenharmony_ci        // clamp the segment to our length
265cb93a386Sopenharmony_ci        if (d1 > fPathLength) {
266cb93a386Sopenharmony_ci            d1 = fPathLength;
267cb93a386Sopenharmony_ci        }
268cb93a386Sopenharmony_ci
269cb93a386Sopenharmony_ci        SkScalar x0 = fPts[0].fX + fTangent.fX * d0;
270cb93a386Sopenharmony_ci        SkScalar x1 = fPts[0].fX + fTangent.fX * d1;
271cb93a386Sopenharmony_ci        SkScalar y0 = fPts[0].fY + fTangent.fY * d0;
272cb93a386Sopenharmony_ci        SkScalar y1 = fPts[0].fY + fTangent.fY * d1;
273cb93a386Sopenharmony_ci
274cb93a386Sopenharmony_ci        SkPoint pts[4];
275cb93a386Sopenharmony_ci        pts[0].set(x0 + fNormal.fX, y0 + fNormal.fY);   // moveTo
276cb93a386Sopenharmony_ci        pts[1].set(x1 + fNormal.fX, y1 + fNormal.fY);   // lineTo
277cb93a386Sopenharmony_ci        pts[2].set(x1 - fNormal.fX, y1 - fNormal.fY);   // lineTo
278cb93a386Sopenharmony_ci        pts[3].set(x0 - fNormal.fX, y0 - fNormal.fY);   // lineTo
279cb93a386Sopenharmony_ci
280cb93a386Sopenharmony_ci        path->addPoly(pts, SK_ARRAY_COUNT(pts), false);
281cb93a386Sopenharmony_ci    }
282cb93a386Sopenharmony_ci
283cb93a386Sopenharmony_ciprivate:
284cb93a386Sopenharmony_ci    SkPoint fPts[2];
285cb93a386Sopenharmony_ci    SkVector fTangent;
286cb93a386Sopenharmony_ci    SkVector fNormal;
287cb93a386Sopenharmony_ci    SkScalar fPathLength;
288cb93a386Sopenharmony_ci};
289cb93a386Sopenharmony_ci
290cb93a386Sopenharmony_ci
291cb93a386Sopenharmony_cibool SkDashPath::InternalFilter(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
292cb93a386Sopenharmony_ci                                const SkRect* cullRect, const SkScalar aIntervals[],
293cb93a386Sopenharmony_ci                                int32_t count, SkScalar initialDashLength, int32_t initialDashIndex,
294cb93a386Sopenharmony_ci                                SkScalar intervalLength,
295cb93a386Sopenharmony_ci                                StrokeRecApplication strokeRecApplication) {
296cb93a386Sopenharmony_ci    // we must always have an even number of intervals
297cb93a386Sopenharmony_ci    SkASSERT(is_even(count));
298cb93a386Sopenharmony_ci
299cb93a386Sopenharmony_ci    // we do nothing if the src wants to be filled
300cb93a386Sopenharmony_ci    SkStrokeRec::Style style = rec->getStyle();
301cb93a386Sopenharmony_ci    if (SkStrokeRec::kFill_Style == style || SkStrokeRec::kStrokeAndFill_Style == style) {
302cb93a386Sopenharmony_ci        return false;
303cb93a386Sopenharmony_ci    }
304cb93a386Sopenharmony_ci
305cb93a386Sopenharmony_ci    const SkScalar* intervals = aIntervals;
306cb93a386Sopenharmony_ci    SkScalar        dashCount = 0;
307cb93a386Sopenharmony_ci    int             segCount = 0;
308cb93a386Sopenharmony_ci
309cb93a386Sopenharmony_ci    SkPath cullPathStorage;
310cb93a386Sopenharmony_ci    const SkPath* srcPtr = &src;
311cb93a386Sopenharmony_ci    if (cull_path(src, *rec, cullRect, intervalLength, &cullPathStorage)) {
312cb93a386Sopenharmony_ci        // if rect is closed, starts in a dash, and ends in a dash, add the initial join
313cb93a386Sopenharmony_ci        // potentially a better fix is described here: bug.skia.org/7445
314cb93a386Sopenharmony_ci        if (src.isRect(nullptr) && src.isLastContourClosed() && is_even(initialDashIndex)) {
315cb93a386Sopenharmony_ci            SkScalar pathLength = SkPathMeasure(src, false, rec->getResScale()).getLength();
316cb93a386Sopenharmony_ci            SkScalar endPhase = SkScalarMod(pathLength + initialDashLength, intervalLength);
317cb93a386Sopenharmony_ci            int index = 0;
318cb93a386Sopenharmony_ci            while (endPhase > intervals[index]) {
319cb93a386Sopenharmony_ci                endPhase -= intervals[index++];
320cb93a386Sopenharmony_ci                SkASSERT(index <= count);
321cb93a386Sopenharmony_ci                if (index == count) {
322cb93a386Sopenharmony_ci                    // We have run out of intervals. endPhase "should" never get to this point,
323cb93a386Sopenharmony_ci                    // but it could if the subtracts underflowed. Hence we will pin it as if it
324cb93a386Sopenharmony_ci                    // perfectly ran through the intervals.
325cb93a386Sopenharmony_ci                    // See crbug.com/875494 (and skbug.com/8274)
326cb93a386Sopenharmony_ci                    endPhase = 0;
327cb93a386Sopenharmony_ci                    break;
328cb93a386Sopenharmony_ci                }
329cb93a386Sopenharmony_ci            }
330cb93a386Sopenharmony_ci            // if dash ends inside "on", or ends at beginning of "off"
331cb93a386Sopenharmony_ci            if (is_even(index) == (endPhase > 0)) {
332cb93a386Sopenharmony_ci                SkPoint midPoint = src.getPoint(0);
333cb93a386Sopenharmony_ci                // get vector at end of rect
334cb93a386Sopenharmony_ci                int last = src.countPoints() - 1;
335cb93a386Sopenharmony_ci                while (midPoint == src.getPoint(last)) {
336cb93a386Sopenharmony_ci                    --last;
337cb93a386Sopenharmony_ci                    SkASSERT(last >= 0);
338cb93a386Sopenharmony_ci                }
339cb93a386Sopenharmony_ci                // get vector at start of rect
340cb93a386Sopenharmony_ci                int next = 1;
341cb93a386Sopenharmony_ci                while (midPoint == src.getPoint(next)) {
342cb93a386Sopenharmony_ci                    ++next;
343cb93a386Sopenharmony_ci                    SkASSERT(next < last);
344cb93a386Sopenharmony_ci                }
345cb93a386Sopenharmony_ci                SkVector v = midPoint - src.getPoint(last);
346cb93a386Sopenharmony_ci                const SkScalar kTinyOffset = SK_ScalarNearlyZero;
347cb93a386Sopenharmony_ci                // scale vector to make start of tiny right angle
348cb93a386Sopenharmony_ci                v *= kTinyOffset;
349cb93a386Sopenharmony_ci                cullPathStorage.moveTo(midPoint - v);
350cb93a386Sopenharmony_ci                cullPathStorage.lineTo(midPoint);
351cb93a386Sopenharmony_ci                v = midPoint - src.getPoint(next);
352cb93a386Sopenharmony_ci                // scale vector to make end of tiny right angle
353cb93a386Sopenharmony_ci                v *= kTinyOffset;
354cb93a386Sopenharmony_ci                cullPathStorage.lineTo(midPoint - v);
355cb93a386Sopenharmony_ci            }
356cb93a386Sopenharmony_ci        }
357cb93a386Sopenharmony_ci        srcPtr = &cullPathStorage;
358cb93a386Sopenharmony_ci    }
359cb93a386Sopenharmony_ci
360cb93a386Sopenharmony_ci    SpecialLineRec lineRec;
361cb93a386Sopenharmony_ci    bool specialLine = (StrokeRecApplication::kAllow == strokeRecApplication) &&
362cb93a386Sopenharmony_ci                       lineRec.init(*srcPtr, dst, rec, count >> 1, intervalLength);
363cb93a386Sopenharmony_ci
364cb93a386Sopenharmony_ci    SkPathMeasure   meas(*srcPtr, false, rec->getResScale());
365cb93a386Sopenharmony_ci
366cb93a386Sopenharmony_ci    do {
367cb93a386Sopenharmony_ci        bool        skipFirstSegment = meas.isClosed();
368cb93a386Sopenharmony_ci        bool        addedSegment = false;
369cb93a386Sopenharmony_ci        SkScalar    length = meas.getLength();
370cb93a386Sopenharmony_ci        int         index = initialDashIndex;
371cb93a386Sopenharmony_ci
372cb93a386Sopenharmony_ci        // Since the path length / dash length ratio may be arbitrarily large, we can exert
373cb93a386Sopenharmony_ci        // significant memory pressure while attempting to build the filtered path. To avoid this,
374cb93a386Sopenharmony_ci        // we simply give up dashing beyond a certain threshold.
375cb93a386Sopenharmony_ci        //
376cb93a386Sopenharmony_ci        // The original bug report (http://crbug.com/165432) is based on a path yielding more than
377cb93a386Sopenharmony_ci        // 90 million dash segments and crashing the memory allocator. A limit of 1 million
378cb93a386Sopenharmony_ci        // segments seems reasonable: at 2 verbs per segment * 9 bytes per verb, this caps the
379cb93a386Sopenharmony_ci        // maximum dash memory overhead at roughly 17MB per path.
380cb93a386Sopenharmony_ci        dashCount += length * (count >> 1) / intervalLength;
381cb93a386Sopenharmony_ci        if (dashCount > kMaxDashCount) {
382cb93a386Sopenharmony_ci            dst->reset();
383cb93a386Sopenharmony_ci            return false;
384cb93a386Sopenharmony_ci        }
385cb93a386Sopenharmony_ci
386cb93a386Sopenharmony_ci        // Using double precision to avoid looping indefinitely due to single precision rounding
387cb93a386Sopenharmony_ci        // (for extreme path_length/dash_length ratios). See test_infinite_dash() unittest.
388cb93a386Sopenharmony_ci        double  distance = 0;
389cb93a386Sopenharmony_ci        double  dlen = initialDashLength;
390cb93a386Sopenharmony_ci
391cb93a386Sopenharmony_ci        while (distance < length) {
392cb93a386Sopenharmony_ci            SkASSERT(dlen >= 0);
393cb93a386Sopenharmony_ci            addedSegment = false;
394cb93a386Sopenharmony_ci            if (is_even(index) && !skipFirstSegment) {
395cb93a386Sopenharmony_ci                addedSegment = true;
396cb93a386Sopenharmony_ci                ++segCount;
397cb93a386Sopenharmony_ci
398cb93a386Sopenharmony_ci                if (specialLine) {
399cb93a386Sopenharmony_ci                    lineRec.addSegment(SkDoubleToScalar(distance),
400cb93a386Sopenharmony_ci                                       SkDoubleToScalar(distance + dlen),
401cb93a386Sopenharmony_ci                                       dst);
402cb93a386Sopenharmony_ci                } else {
403cb93a386Sopenharmony_ci                    meas.getSegment(SkDoubleToScalar(distance),
404cb93a386Sopenharmony_ci                                    SkDoubleToScalar(distance + dlen),
405cb93a386Sopenharmony_ci                                    dst, true);
406cb93a386Sopenharmony_ci                }
407cb93a386Sopenharmony_ci            }
408cb93a386Sopenharmony_ci            distance += dlen;
409cb93a386Sopenharmony_ci
410cb93a386Sopenharmony_ci            // clear this so we only respect it the first time around
411cb93a386Sopenharmony_ci            skipFirstSegment = false;
412cb93a386Sopenharmony_ci
413cb93a386Sopenharmony_ci            // wrap around our intervals array if necessary
414cb93a386Sopenharmony_ci            index += 1;
415cb93a386Sopenharmony_ci            SkASSERT(index <= count);
416cb93a386Sopenharmony_ci            if (index == count) {
417cb93a386Sopenharmony_ci                index = 0;
418cb93a386Sopenharmony_ci            }
419cb93a386Sopenharmony_ci
420cb93a386Sopenharmony_ci            // fetch our next dlen
421cb93a386Sopenharmony_ci            dlen = intervals[index];
422cb93a386Sopenharmony_ci        }
423cb93a386Sopenharmony_ci
424cb93a386Sopenharmony_ci        // extend if we ended on a segment and we need to join up with the (skipped) initial segment
425cb93a386Sopenharmony_ci        if (meas.isClosed() && is_even(initialDashIndex) &&
426cb93a386Sopenharmony_ci            initialDashLength >= 0) {
427cb93a386Sopenharmony_ci            meas.getSegment(0, initialDashLength, dst, !addedSegment);
428cb93a386Sopenharmony_ci            ++segCount;
429cb93a386Sopenharmony_ci        }
430cb93a386Sopenharmony_ci    } while (meas.nextContour());
431cb93a386Sopenharmony_ci
432cb93a386Sopenharmony_ci    // TODO: do we still need this?
433cb93a386Sopenharmony_ci    if (segCount > 1) {
434cb93a386Sopenharmony_ci        SkPathPriv::SetConvexity(*dst, SkPathConvexity::kConcave);
435cb93a386Sopenharmony_ci    }
436cb93a386Sopenharmony_ci
437cb93a386Sopenharmony_ci    return true;
438cb93a386Sopenharmony_ci}
439cb93a386Sopenharmony_ci
440cb93a386Sopenharmony_cibool SkDashPath::FilterDashPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
441cb93a386Sopenharmony_ci                                const SkRect* cullRect, const SkPathEffect::DashInfo& info) {
442cb93a386Sopenharmony_ci    if (!ValidDashPath(info.fPhase, info.fIntervals, info.fCount)) {
443cb93a386Sopenharmony_ci        return false;
444cb93a386Sopenharmony_ci    }
445cb93a386Sopenharmony_ci    SkScalar initialDashLength = 0;
446cb93a386Sopenharmony_ci    int32_t initialDashIndex = 0;
447cb93a386Sopenharmony_ci    SkScalar intervalLength = 0;
448cb93a386Sopenharmony_ci    CalcDashParameters(info.fPhase, info.fIntervals, info.fCount,
449cb93a386Sopenharmony_ci                       &initialDashLength, &initialDashIndex, &intervalLength);
450cb93a386Sopenharmony_ci    return InternalFilter(dst, src, rec, cullRect, info.fIntervals, info.fCount, initialDashLength,
451cb93a386Sopenharmony_ci                          initialDashIndex, intervalLength);
452cb93a386Sopenharmony_ci}
453cb93a386Sopenharmony_ci
454cb93a386Sopenharmony_cibool SkDashPath::ValidDashPath(SkScalar phase, const SkScalar intervals[], int32_t count) {
455cb93a386Sopenharmony_ci    if (count < 2 || !SkIsAlign2(count)) {
456cb93a386Sopenharmony_ci        return false;
457cb93a386Sopenharmony_ci    }
458cb93a386Sopenharmony_ci    SkScalar length = 0;
459cb93a386Sopenharmony_ci    for (int i = 0; i < count; i++) {
460cb93a386Sopenharmony_ci        if (intervals[i] < 0) {
461cb93a386Sopenharmony_ci            return false;
462cb93a386Sopenharmony_ci        }
463cb93a386Sopenharmony_ci        length += intervals[i];
464cb93a386Sopenharmony_ci    }
465cb93a386Sopenharmony_ci    // watch out for values that might make us go out of bounds
466cb93a386Sopenharmony_ci    return length > 0 && SkScalarIsFinite(phase) && SkScalarIsFinite(length);
467cb93a386Sopenharmony_ci}
468