xref: /aosp_15_r20/external/skia/src/effects/SkDashPathEffect.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2006 The Android Open Source Project
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkDashPathEffect.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFlattenable.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathEffect.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStrokeRec.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAlign.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkFloatingPoint.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMalloc.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPathEffectBase.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkReadBuffer.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkWriteBuffer.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/effects/SkDashImpl.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkDashPathPriv.h"
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
30*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
31*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
32*c8dee2aaSAndroid Build Coastguard Worker 
33*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
34*c8dee2aaSAndroid Build Coastguard Worker 
SkDashImpl(const SkScalar intervals[],int count,SkScalar phase)35*c8dee2aaSAndroid Build Coastguard Worker SkDashImpl::SkDashImpl(const SkScalar intervals[], int count, SkScalar phase)
36*c8dee2aaSAndroid Build Coastguard Worker         : fPhase(0)
37*c8dee2aaSAndroid Build Coastguard Worker         , fInitialDashLength(-1)
38*c8dee2aaSAndroid Build Coastguard Worker         , fInitialDashIndex(0)
39*c8dee2aaSAndroid Build Coastguard Worker         , fIntervalLength(0) {
40*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(intervals);
41*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(count > 1 && SkIsAlign2(count));
42*c8dee2aaSAndroid Build Coastguard Worker 
43*c8dee2aaSAndroid Build Coastguard Worker     fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count);
44*c8dee2aaSAndroid Build Coastguard Worker     fCount = count;
45*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < count; i++) {
46*c8dee2aaSAndroid Build Coastguard Worker         fIntervals[i] = intervals[i];
47*c8dee2aaSAndroid Build Coastguard Worker     }
48*c8dee2aaSAndroid Build Coastguard Worker 
49*c8dee2aaSAndroid Build Coastguard Worker     // set the internal data members
50*c8dee2aaSAndroid Build Coastguard Worker     SkDashPath::CalcDashParameters(phase, fIntervals, fCount,
51*c8dee2aaSAndroid Build Coastguard Worker             &fInitialDashLength, &fInitialDashIndex, &fIntervalLength, &fPhase);
52*c8dee2aaSAndroid Build Coastguard Worker }
53*c8dee2aaSAndroid Build Coastguard Worker 
~SkDashImpl()54*c8dee2aaSAndroid Build Coastguard Worker SkDashImpl::~SkDashImpl() {
55*c8dee2aaSAndroid Build Coastguard Worker     sk_free(fIntervals);
56*c8dee2aaSAndroid Build Coastguard Worker }
57*c8dee2aaSAndroid Build Coastguard Worker 
onFilterPath(SkPath * dst,const SkPath & src,SkStrokeRec * rec,const SkRect * cullRect,const SkMatrix &) const58*c8dee2aaSAndroid Build Coastguard Worker bool SkDashImpl::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
59*c8dee2aaSAndroid Build Coastguard Worker                               const SkRect* cullRect, const SkMatrix&) const {
60*c8dee2aaSAndroid Build Coastguard Worker     return SkDashPath::InternalFilter(dst, src, rec, cullRect, fIntervals, fCount,
61*c8dee2aaSAndroid Build Coastguard Worker                                       fInitialDashLength, fInitialDashIndex, fIntervalLength,
62*c8dee2aaSAndroid Build Coastguard Worker                                       fPhase);
63*c8dee2aaSAndroid Build Coastguard Worker }
64*c8dee2aaSAndroid Build Coastguard Worker 
outset_for_stroke(SkRect * rect,const SkStrokeRec & rec)65*c8dee2aaSAndroid Build Coastguard Worker static void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) {
66*c8dee2aaSAndroid Build Coastguard Worker     SkScalar radius = SkScalarHalf(rec.getWidth());
67*c8dee2aaSAndroid Build Coastguard Worker     if (0 == radius) {
68*c8dee2aaSAndroid Build Coastguard Worker         radius = SK_Scalar1;    // hairlines
69*c8dee2aaSAndroid Build Coastguard Worker     }
70*c8dee2aaSAndroid Build Coastguard Worker     if (SkPaint::kMiter_Join == rec.getJoin()) {
71*c8dee2aaSAndroid Build Coastguard Worker         radius *= rec.getMiter();
72*c8dee2aaSAndroid Build Coastguard Worker     }
73*c8dee2aaSAndroid Build Coastguard Worker     rect->outset(radius, radius);
74*c8dee2aaSAndroid Build Coastguard Worker }
75*c8dee2aaSAndroid Build Coastguard Worker 
76*c8dee2aaSAndroid Build Coastguard Worker // Attempt to trim the line to minimally cover the cull rect (currently
77*c8dee2aaSAndroid Build Coastguard Worker // only works for horizontal and vertical lines).
78*c8dee2aaSAndroid Build Coastguard Worker // Return true if processing should continue; false otherwise.
cull_line(SkPoint * pts,const SkStrokeRec & rec,const SkMatrix & ctm,const SkRect * cullRect,const SkScalar intervalLength)79*c8dee2aaSAndroid Build Coastguard Worker static bool cull_line(SkPoint* pts, const SkStrokeRec& rec,
80*c8dee2aaSAndroid Build Coastguard Worker                       const SkMatrix& ctm, const SkRect* cullRect,
81*c8dee2aaSAndroid Build Coastguard Worker                       const SkScalar intervalLength) {
82*c8dee2aaSAndroid Build Coastguard Worker     if (nullptr == cullRect) {
83*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(false); // Shouldn't ever occur in practice
84*c8dee2aaSAndroid Build Coastguard Worker         return false;
85*c8dee2aaSAndroid Build Coastguard Worker     }
86*c8dee2aaSAndroid Build Coastguard Worker 
87*c8dee2aaSAndroid Build Coastguard Worker     SkScalar dx = pts[1].x() - pts[0].x();
88*c8dee2aaSAndroid Build Coastguard Worker     SkScalar dy = pts[1].y() - pts[0].y();
89*c8dee2aaSAndroid Build Coastguard Worker 
90*c8dee2aaSAndroid Build Coastguard Worker     if ((dx && dy) || (!dx && !dy)) {
91*c8dee2aaSAndroid Build Coastguard Worker         return false;
92*c8dee2aaSAndroid Build Coastguard Worker     }
93*c8dee2aaSAndroid Build Coastguard Worker 
94*c8dee2aaSAndroid Build Coastguard Worker     SkRect bounds = *cullRect;
95*c8dee2aaSAndroid Build Coastguard Worker     outset_for_stroke(&bounds, rec);
96*c8dee2aaSAndroid Build Coastguard Worker 
97*c8dee2aaSAndroid Build Coastguard Worker     // cullRect is in device space while pts are in the local coordinate system
98*c8dee2aaSAndroid Build Coastguard Worker     // defined by the ctm. We want our answer in the local coordinate system.
99*c8dee2aaSAndroid Build Coastguard Worker 
100*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(ctm.rectStaysRect());
101*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix inv;
102*c8dee2aaSAndroid Build Coastguard Worker     if (!ctm.invert(&inv)) {
103*c8dee2aaSAndroid Build Coastguard Worker         return false;
104*c8dee2aaSAndroid Build Coastguard Worker     }
105*c8dee2aaSAndroid Build Coastguard Worker 
106*c8dee2aaSAndroid Build Coastguard Worker     inv.mapRect(&bounds);
107*c8dee2aaSAndroid Build Coastguard Worker 
108*c8dee2aaSAndroid Build Coastguard Worker     if (dx) {
109*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(dx && !dy);
110*c8dee2aaSAndroid Build Coastguard Worker         SkScalar minX = pts[0].fX;
111*c8dee2aaSAndroid Build Coastguard Worker         SkScalar maxX = pts[1].fX;
112*c8dee2aaSAndroid Build Coastguard Worker 
113*c8dee2aaSAndroid Build Coastguard Worker         if (dx < 0) {
114*c8dee2aaSAndroid Build Coastguard Worker             using std::swap;
115*c8dee2aaSAndroid Build Coastguard Worker             swap(minX, maxX);
116*c8dee2aaSAndroid Build Coastguard Worker         }
117*c8dee2aaSAndroid Build Coastguard Worker 
118*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(minX < maxX);
119*c8dee2aaSAndroid Build Coastguard Worker         if (maxX <= bounds.fLeft || minX >= bounds.fRight) {
120*c8dee2aaSAndroid Build Coastguard Worker             return false;
121*c8dee2aaSAndroid Build Coastguard Worker         }
122*c8dee2aaSAndroid Build Coastguard Worker 
123*c8dee2aaSAndroid Build Coastguard Worker         // Now we actually perform the chop, removing the excess to the left and
124*c8dee2aaSAndroid Build Coastguard Worker         // right of the bounds (keeping our new line "in phase" with the dash,
125*c8dee2aaSAndroid Build Coastguard Worker         // hence the (mod intervalLength).
126*c8dee2aaSAndroid Build Coastguard Worker 
127*c8dee2aaSAndroid Build Coastguard Worker         if (minX < bounds.fLeft) {
128*c8dee2aaSAndroid Build Coastguard Worker             minX = bounds.fLeft - SkScalarMod(bounds.fLeft - minX, intervalLength);
129*c8dee2aaSAndroid Build Coastguard Worker         }
130*c8dee2aaSAndroid Build Coastguard Worker         if (maxX > bounds.fRight) {
131*c8dee2aaSAndroid Build Coastguard Worker             maxX = bounds.fRight + SkScalarMod(maxX - bounds.fRight, intervalLength);
132*c8dee2aaSAndroid Build Coastguard Worker         }
133*c8dee2aaSAndroid Build Coastguard Worker 
134*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(maxX > minX);
135*c8dee2aaSAndroid Build Coastguard Worker         if (dx < 0) {
136*c8dee2aaSAndroid Build Coastguard Worker             using std::swap;
137*c8dee2aaSAndroid Build Coastguard Worker             swap(minX, maxX);
138*c8dee2aaSAndroid Build Coastguard Worker         }
139*c8dee2aaSAndroid Build Coastguard Worker         pts[0].fX = minX;
140*c8dee2aaSAndroid Build Coastguard Worker         pts[1].fX = maxX;
141*c8dee2aaSAndroid Build Coastguard Worker     } else {
142*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(dy && !dx);
143*c8dee2aaSAndroid Build Coastguard Worker         SkScalar minY = pts[0].fY;
144*c8dee2aaSAndroid Build Coastguard Worker         SkScalar maxY = pts[1].fY;
145*c8dee2aaSAndroid Build Coastguard Worker 
146*c8dee2aaSAndroid Build Coastguard Worker         if (dy < 0) {
147*c8dee2aaSAndroid Build Coastguard Worker             using std::swap;
148*c8dee2aaSAndroid Build Coastguard Worker             swap(minY, maxY);
149*c8dee2aaSAndroid Build Coastguard Worker         }
150*c8dee2aaSAndroid Build Coastguard Worker 
151*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(minY < maxY);
152*c8dee2aaSAndroid Build Coastguard Worker         if (maxY <= bounds.fTop || minY >= bounds.fBottom) {
153*c8dee2aaSAndroid Build Coastguard Worker             return false;
154*c8dee2aaSAndroid Build Coastguard Worker         }
155*c8dee2aaSAndroid Build Coastguard Worker 
156*c8dee2aaSAndroid Build Coastguard Worker         // Now we actually perform the chop, removing the excess to the top and
157*c8dee2aaSAndroid Build Coastguard Worker         // bottom of the bounds (keeping our new line "in phase" with the dash,
158*c8dee2aaSAndroid Build Coastguard Worker         // hence the (mod intervalLength).
159*c8dee2aaSAndroid Build Coastguard Worker 
160*c8dee2aaSAndroid Build Coastguard Worker         if (minY < bounds.fTop) {
161*c8dee2aaSAndroid Build Coastguard Worker             minY = bounds.fTop - SkScalarMod(bounds.fTop - minY, intervalLength);
162*c8dee2aaSAndroid Build Coastguard Worker         }
163*c8dee2aaSAndroid Build Coastguard Worker         if (maxY > bounds.fBottom) {
164*c8dee2aaSAndroid Build Coastguard Worker             maxY = bounds.fBottom + SkScalarMod(maxY - bounds.fBottom, intervalLength);
165*c8dee2aaSAndroid Build Coastguard Worker         }
166*c8dee2aaSAndroid Build Coastguard Worker 
167*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(maxY > minY);
168*c8dee2aaSAndroid Build Coastguard Worker         if (dy < 0) {
169*c8dee2aaSAndroid Build Coastguard Worker             using std::swap;
170*c8dee2aaSAndroid Build Coastguard Worker             swap(minY, maxY);
171*c8dee2aaSAndroid Build Coastguard Worker         }
172*c8dee2aaSAndroid Build Coastguard Worker         pts[0].fY = minY;
173*c8dee2aaSAndroid Build Coastguard Worker         pts[1].fY = maxY;
174*c8dee2aaSAndroid Build Coastguard Worker     }
175*c8dee2aaSAndroid Build Coastguard Worker 
176*c8dee2aaSAndroid Build Coastguard Worker     return true;
177*c8dee2aaSAndroid Build Coastguard Worker }
178*c8dee2aaSAndroid Build Coastguard Worker 
179*c8dee2aaSAndroid Build Coastguard Worker // Currently asPoints is more restrictive then it needs to be. In the future
180*c8dee2aaSAndroid Build Coastguard Worker // we need to:
181*c8dee2aaSAndroid Build Coastguard Worker //      allow kRound_Cap capping (could allow rotations in the matrix with this)
182*c8dee2aaSAndroid Build Coastguard Worker //      allow paths to be returned
onAsPoints(PointData * results,const SkPath & src,const SkStrokeRec & rec,const SkMatrix & matrix,const SkRect * cullRect) const183*c8dee2aaSAndroid Build Coastguard Worker bool SkDashImpl::onAsPoints(PointData* results, const SkPath& src, const SkStrokeRec& rec,
184*c8dee2aaSAndroid Build Coastguard Worker                             const SkMatrix& matrix, const SkRect* cullRect) const {
185*c8dee2aaSAndroid Build Coastguard Worker     // width < 0 -> fill && width == 0 -> hairline so requiring width > 0 rules both out
186*c8dee2aaSAndroid Build Coastguard Worker     if (0 >= rec.getWidth()) {
187*c8dee2aaSAndroid Build Coastguard Worker         return false;
188*c8dee2aaSAndroid Build Coastguard Worker     }
189*c8dee2aaSAndroid Build Coastguard Worker 
190*c8dee2aaSAndroid Build Coastguard Worker     // TODO: this next test could be eased up. We could allow any number of
191*c8dee2aaSAndroid Build Coastguard Worker     // intervals as long as all the ons match and all the offs match.
192*c8dee2aaSAndroid Build Coastguard Worker     // Additionally, they do not necessarily need to be integers.
193*c8dee2aaSAndroid Build Coastguard Worker     // We cannot allow arbitrary intervals since we want the returned points
194*c8dee2aaSAndroid Build Coastguard Worker     // to be uniformly sized.
195*c8dee2aaSAndroid Build Coastguard Worker     if (fCount != 2 ||
196*c8dee2aaSAndroid Build Coastguard Worker         !SkScalarNearlyEqual(fIntervals[0], fIntervals[1]) ||
197*c8dee2aaSAndroid Build Coastguard Worker         !SkScalarIsInt(fIntervals[0]) ||
198*c8dee2aaSAndroid Build Coastguard Worker         !SkScalarIsInt(fIntervals[1])) {
199*c8dee2aaSAndroid Build Coastguard Worker         return false;
200*c8dee2aaSAndroid Build Coastguard Worker     }
201*c8dee2aaSAndroid Build Coastguard Worker 
202*c8dee2aaSAndroid Build Coastguard Worker     SkPoint pts[2];
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker     if (!src.isLine(pts)) {
205*c8dee2aaSAndroid Build Coastguard Worker         return false;
206*c8dee2aaSAndroid Build Coastguard Worker     }
207*c8dee2aaSAndroid Build Coastguard Worker 
208*c8dee2aaSAndroid Build Coastguard Worker     // TODO: this test could be eased up to allow circles
209*c8dee2aaSAndroid Build Coastguard Worker     if (SkPaint::kButt_Cap != rec.getCap()) {
210*c8dee2aaSAndroid Build Coastguard Worker         return false;
211*c8dee2aaSAndroid Build Coastguard Worker     }
212*c8dee2aaSAndroid Build Coastguard Worker 
213*c8dee2aaSAndroid Build Coastguard Worker     // TODO: this test could be eased up for circles. Rotations could be allowed.
214*c8dee2aaSAndroid Build Coastguard Worker     if (!matrix.rectStaysRect()) {
215*c8dee2aaSAndroid Build Coastguard Worker         return false;
216*c8dee2aaSAndroid Build Coastguard Worker     }
217*c8dee2aaSAndroid Build Coastguard Worker 
218*c8dee2aaSAndroid Build Coastguard Worker     // See if the line can be limited to something plausible.
219*c8dee2aaSAndroid Build Coastguard Worker     if (!cull_line(pts, rec, matrix, cullRect, fIntervalLength)) {
220*c8dee2aaSAndroid Build Coastguard Worker         return false;
221*c8dee2aaSAndroid Build Coastguard Worker     }
222*c8dee2aaSAndroid Build Coastguard Worker 
223*c8dee2aaSAndroid Build Coastguard Worker     SkScalar length = SkPoint::Distance(pts[1], pts[0]);
224*c8dee2aaSAndroid Build Coastguard Worker 
225*c8dee2aaSAndroid Build Coastguard Worker     SkVector tangent = pts[1] - pts[0];
226*c8dee2aaSAndroid Build Coastguard Worker     if (tangent.isZero()) {
227*c8dee2aaSAndroid Build Coastguard Worker         return false;
228*c8dee2aaSAndroid Build Coastguard Worker     }
229*c8dee2aaSAndroid Build Coastguard Worker 
230*c8dee2aaSAndroid Build Coastguard Worker     tangent.scale(SkScalarInvert(length));
231*c8dee2aaSAndroid Build Coastguard Worker 
232*c8dee2aaSAndroid Build Coastguard Worker     // TODO: make this test for horizontal & vertical lines more robust
233*c8dee2aaSAndroid Build Coastguard Worker     bool isXAxis = true;
234*c8dee2aaSAndroid Build Coastguard Worker     if (SkScalarNearlyEqual(SK_Scalar1, tangent.fX) ||
235*c8dee2aaSAndroid Build Coastguard Worker         SkScalarNearlyEqual(-SK_Scalar1, tangent.fX)) {
236*c8dee2aaSAndroid Build Coastguard Worker         results->fSize.set(SkScalarHalf(fIntervals[0]), SkScalarHalf(rec.getWidth()));
237*c8dee2aaSAndroid Build Coastguard Worker     } else if (SkScalarNearlyEqual(SK_Scalar1, tangent.fY) ||
238*c8dee2aaSAndroid Build Coastguard Worker                SkScalarNearlyEqual(-SK_Scalar1, tangent.fY)) {
239*c8dee2aaSAndroid Build Coastguard Worker         results->fSize.set(SkScalarHalf(rec.getWidth()), SkScalarHalf(fIntervals[0]));
240*c8dee2aaSAndroid Build Coastguard Worker         isXAxis = false;
241*c8dee2aaSAndroid Build Coastguard Worker     } else if (SkPaint::kRound_Cap != rec.getCap()) {
242*c8dee2aaSAndroid Build Coastguard Worker         // Angled lines don't have axis-aligned boxes.
243*c8dee2aaSAndroid Build Coastguard Worker         return false;
244*c8dee2aaSAndroid Build Coastguard Worker     }
245*c8dee2aaSAndroid Build Coastguard Worker 
246*c8dee2aaSAndroid Build Coastguard Worker     if (results) {
247*c8dee2aaSAndroid Build Coastguard Worker         results->fFlags = 0;
248*c8dee2aaSAndroid Build Coastguard Worker         SkScalar clampedInitialDashLength = std::min(length, fInitialDashLength);
249*c8dee2aaSAndroid Build Coastguard Worker 
250*c8dee2aaSAndroid Build Coastguard Worker         if (SkPaint::kRound_Cap == rec.getCap()) {
251*c8dee2aaSAndroid Build Coastguard Worker             results->fFlags |= PointData::kCircles_PointFlag;
252*c8dee2aaSAndroid Build Coastguard Worker         }
253*c8dee2aaSAndroid Build Coastguard Worker 
254*c8dee2aaSAndroid Build Coastguard Worker         results->fNumPoints = 0;
255*c8dee2aaSAndroid Build Coastguard Worker         SkScalar len2 = length;
256*c8dee2aaSAndroid Build Coastguard Worker         if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) {
257*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(len2 >= clampedInitialDashLength);
258*c8dee2aaSAndroid Build Coastguard Worker             if (0 == fInitialDashIndex) {
259*c8dee2aaSAndroid Build Coastguard Worker                 if (clampedInitialDashLength > 0) {
260*c8dee2aaSAndroid Build Coastguard Worker                     if (clampedInitialDashLength >= fIntervals[0]) {
261*c8dee2aaSAndroid Build Coastguard Worker                         ++results->fNumPoints;  // partial first dash
262*c8dee2aaSAndroid Build Coastguard Worker                     }
263*c8dee2aaSAndroid Build Coastguard Worker                     len2 -= clampedInitialDashLength;
264*c8dee2aaSAndroid Build Coastguard Worker                 }
265*c8dee2aaSAndroid Build Coastguard Worker                 len2 -= fIntervals[1];  // also skip first space
266*c8dee2aaSAndroid Build Coastguard Worker                 if (len2 < 0) {
267*c8dee2aaSAndroid Build Coastguard Worker                     len2 = 0;
268*c8dee2aaSAndroid Build Coastguard Worker                 }
269*c8dee2aaSAndroid Build Coastguard Worker             } else {
270*c8dee2aaSAndroid Build Coastguard Worker                 len2 -= clampedInitialDashLength; // skip initial partial empty
271*c8dee2aaSAndroid Build Coastguard Worker             }
272*c8dee2aaSAndroid Build Coastguard Worker         }
273*c8dee2aaSAndroid Build Coastguard Worker         // Too many midpoints can cause results->fNumPoints to overflow or
274*c8dee2aaSAndroid Build Coastguard Worker         // otherwise cause the results->fPoints allocation below to OOM.
275*c8dee2aaSAndroid Build Coastguard Worker         // Cap it to a sane value.
276*c8dee2aaSAndroid Build Coastguard Worker         SkScalar numIntervals = len2 / fIntervalLength;
277*c8dee2aaSAndroid Build Coastguard Worker         if (!SkIsFinite(numIntervals) || numIntervals > SkDashPath::kMaxDashCount) {
278*c8dee2aaSAndroid Build Coastguard Worker             return false;
279*c8dee2aaSAndroid Build Coastguard Worker         }
280*c8dee2aaSAndroid Build Coastguard Worker         int numMidPoints = SkScalarFloorToInt(numIntervals);
281*c8dee2aaSAndroid Build Coastguard Worker         results->fNumPoints += numMidPoints;
282*c8dee2aaSAndroid Build Coastguard Worker         len2 -= numMidPoints * fIntervalLength;
283*c8dee2aaSAndroid Build Coastguard Worker         bool partialLast = false;
284*c8dee2aaSAndroid Build Coastguard Worker         if (len2 > 0) {
285*c8dee2aaSAndroid Build Coastguard Worker             if (len2 < fIntervals[0]) {
286*c8dee2aaSAndroid Build Coastguard Worker                 partialLast = true;
287*c8dee2aaSAndroid Build Coastguard Worker             } else {
288*c8dee2aaSAndroid Build Coastguard Worker                 ++numMidPoints;
289*c8dee2aaSAndroid Build Coastguard Worker                 ++results->fNumPoints;
290*c8dee2aaSAndroid Build Coastguard Worker             }
291*c8dee2aaSAndroid Build Coastguard Worker         }
292*c8dee2aaSAndroid Build Coastguard Worker 
293*c8dee2aaSAndroid Build Coastguard Worker         results->fPoints = new SkPoint[results->fNumPoints];
294*c8dee2aaSAndroid Build Coastguard Worker 
295*c8dee2aaSAndroid Build Coastguard Worker         SkScalar    distance = 0;
296*c8dee2aaSAndroid Build Coastguard Worker         int         curPt = 0;
297*c8dee2aaSAndroid Build Coastguard Worker 
298*c8dee2aaSAndroid Build Coastguard Worker         if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) {
299*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(clampedInitialDashLength <= length);
300*c8dee2aaSAndroid Build Coastguard Worker 
301*c8dee2aaSAndroid Build Coastguard Worker             if (0 == fInitialDashIndex) {
302*c8dee2aaSAndroid Build Coastguard Worker                 if (clampedInitialDashLength > 0) {
303*c8dee2aaSAndroid Build Coastguard Worker                     // partial first block
304*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles
305*c8dee2aaSAndroid Build Coastguard Worker                     SkScalar x = pts[0].fX + tangent.fX * SkScalarHalf(clampedInitialDashLength);
306*c8dee2aaSAndroid Build Coastguard Worker                     SkScalar y = pts[0].fY + tangent.fY * SkScalarHalf(clampedInitialDashLength);
307*c8dee2aaSAndroid Build Coastguard Worker                     SkScalar halfWidth, halfHeight;
308*c8dee2aaSAndroid Build Coastguard Worker                     if (isXAxis) {
309*c8dee2aaSAndroid Build Coastguard Worker                         halfWidth = SkScalarHalf(clampedInitialDashLength);
310*c8dee2aaSAndroid Build Coastguard Worker                         halfHeight = SkScalarHalf(rec.getWidth());
311*c8dee2aaSAndroid Build Coastguard Worker                     } else {
312*c8dee2aaSAndroid Build Coastguard Worker                         halfWidth = SkScalarHalf(rec.getWidth());
313*c8dee2aaSAndroid Build Coastguard Worker                         halfHeight = SkScalarHalf(clampedInitialDashLength);
314*c8dee2aaSAndroid Build Coastguard Worker                     }
315*c8dee2aaSAndroid Build Coastguard Worker                     if (clampedInitialDashLength < fIntervals[0]) {
316*c8dee2aaSAndroid Build Coastguard Worker                         // This one will not be like the others
317*c8dee2aaSAndroid Build Coastguard Worker                         results->fFirst.addRect(x - halfWidth, y - halfHeight,
318*c8dee2aaSAndroid Build Coastguard Worker                                                 x + halfWidth, y + halfHeight);
319*c8dee2aaSAndroid Build Coastguard Worker                     } else {
320*c8dee2aaSAndroid Build Coastguard Worker                         SkASSERT(curPt < results->fNumPoints);
321*c8dee2aaSAndroid Build Coastguard Worker                         results->fPoints[curPt].set(x, y);
322*c8dee2aaSAndroid Build Coastguard Worker                         ++curPt;
323*c8dee2aaSAndroid Build Coastguard Worker                     }
324*c8dee2aaSAndroid Build Coastguard Worker 
325*c8dee2aaSAndroid Build Coastguard Worker                     distance += clampedInitialDashLength;
326*c8dee2aaSAndroid Build Coastguard Worker                 }
327*c8dee2aaSAndroid Build Coastguard Worker 
328*c8dee2aaSAndroid Build Coastguard Worker                 distance += fIntervals[1];  // skip over the next blank block too
329*c8dee2aaSAndroid Build Coastguard Worker             } else {
330*c8dee2aaSAndroid Build Coastguard Worker                 distance += clampedInitialDashLength;
331*c8dee2aaSAndroid Build Coastguard Worker             }
332*c8dee2aaSAndroid Build Coastguard Worker         }
333*c8dee2aaSAndroid Build Coastguard Worker 
334*c8dee2aaSAndroid Build Coastguard Worker         if (0 != numMidPoints) {
335*c8dee2aaSAndroid Build Coastguard Worker             distance += SkScalarHalf(fIntervals[0]);
336*c8dee2aaSAndroid Build Coastguard Worker 
337*c8dee2aaSAndroid Build Coastguard Worker             for (int i = 0; i < numMidPoints; ++i) {
338*c8dee2aaSAndroid Build Coastguard Worker                 SkScalar x = pts[0].fX + tangent.fX * distance;
339*c8dee2aaSAndroid Build Coastguard Worker                 SkScalar y = pts[0].fY + tangent.fY * distance;
340*c8dee2aaSAndroid Build Coastguard Worker 
341*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(curPt < results->fNumPoints);
342*c8dee2aaSAndroid Build Coastguard Worker                 results->fPoints[curPt].set(x, y);
343*c8dee2aaSAndroid Build Coastguard Worker                 ++curPt;
344*c8dee2aaSAndroid Build Coastguard Worker 
345*c8dee2aaSAndroid Build Coastguard Worker                 distance += fIntervalLength;
346*c8dee2aaSAndroid Build Coastguard Worker             }
347*c8dee2aaSAndroid Build Coastguard Worker 
348*c8dee2aaSAndroid Build Coastguard Worker             distance -= SkScalarHalf(fIntervals[0]);
349*c8dee2aaSAndroid Build Coastguard Worker         }
350*c8dee2aaSAndroid Build Coastguard Worker 
351*c8dee2aaSAndroid Build Coastguard Worker         if (partialLast) {
352*c8dee2aaSAndroid Build Coastguard Worker             // partial final block
353*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles
354*c8dee2aaSAndroid Build Coastguard Worker             SkScalar temp = length - distance;
355*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(temp < fIntervals[0]);
356*c8dee2aaSAndroid Build Coastguard Worker             SkScalar x = pts[0].fX + tangent.fX * (distance + SkScalarHalf(temp));
357*c8dee2aaSAndroid Build Coastguard Worker             SkScalar y = pts[0].fY + tangent.fY * (distance + SkScalarHalf(temp));
358*c8dee2aaSAndroid Build Coastguard Worker             SkScalar halfWidth, halfHeight;
359*c8dee2aaSAndroid Build Coastguard Worker             if (isXAxis) {
360*c8dee2aaSAndroid Build Coastguard Worker                 halfWidth = SkScalarHalf(temp);
361*c8dee2aaSAndroid Build Coastguard Worker                 halfHeight = SkScalarHalf(rec.getWidth());
362*c8dee2aaSAndroid Build Coastguard Worker             } else {
363*c8dee2aaSAndroid Build Coastguard Worker                 halfWidth = SkScalarHalf(rec.getWidth());
364*c8dee2aaSAndroid Build Coastguard Worker                 halfHeight = SkScalarHalf(temp);
365*c8dee2aaSAndroid Build Coastguard Worker             }
366*c8dee2aaSAndroid Build Coastguard Worker             results->fLast.addRect(x - halfWidth, y - halfHeight,
367*c8dee2aaSAndroid Build Coastguard Worker                                    x + halfWidth, y + halfHeight);
368*c8dee2aaSAndroid Build Coastguard Worker         }
369*c8dee2aaSAndroid Build Coastguard Worker 
370*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(curPt == results->fNumPoints);
371*c8dee2aaSAndroid Build Coastguard Worker     }
372*c8dee2aaSAndroid Build Coastguard Worker 
373*c8dee2aaSAndroid Build Coastguard Worker     return true;
374*c8dee2aaSAndroid Build Coastguard Worker }
375*c8dee2aaSAndroid Build Coastguard Worker 
asADash(DashInfo * info) const376*c8dee2aaSAndroid Build Coastguard Worker SkPathEffectBase::DashType SkDashImpl::asADash(DashInfo* info) const {
377*c8dee2aaSAndroid Build Coastguard Worker     if (info) {
378*c8dee2aaSAndroid Build Coastguard Worker         if (info->fCount >= fCount && info->fIntervals) {
379*c8dee2aaSAndroid Build Coastguard Worker             memcpy(info->fIntervals, fIntervals, fCount * sizeof(SkScalar));
380*c8dee2aaSAndroid Build Coastguard Worker         }
381*c8dee2aaSAndroid Build Coastguard Worker         info->fCount = fCount;
382*c8dee2aaSAndroid Build Coastguard Worker         info->fPhase = fPhase;
383*c8dee2aaSAndroid Build Coastguard Worker     }
384*c8dee2aaSAndroid Build Coastguard Worker     return DashType::kDash;
385*c8dee2aaSAndroid Build Coastguard Worker }
386*c8dee2aaSAndroid Build Coastguard Worker 
flatten(SkWriteBuffer & buffer) const387*c8dee2aaSAndroid Build Coastguard Worker void SkDashImpl::flatten(SkWriteBuffer& buffer) const {
388*c8dee2aaSAndroid Build Coastguard Worker     buffer.writeScalar(fPhase);
389*c8dee2aaSAndroid Build Coastguard Worker     buffer.writeScalarArray(fIntervals, fCount);
390*c8dee2aaSAndroid Build Coastguard Worker }
391*c8dee2aaSAndroid Build Coastguard Worker 
CreateProc(SkReadBuffer & buffer)392*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkFlattenable> SkDashImpl::CreateProc(SkReadBuffer& buffer) {
393*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar phase = buffer.readScalar();
394*c8dee2aaSAndroid Build Coastguard Worker     uint32_t count = buffer.getArrayCount();
395*c8dee2aaSAndroid Build Coastguard Worker 
396*c8dee2aaSAndroid Build Coastguard Worker     // Don't allocate gigantic buffers if there's not data for them.
397*c8dee2aaSAndroid Build Coastguard Worker     if (!buffer.validateCanReadN<SkScalar>(count)) {
398*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
399*c8dee2aaSAndroid Build Coastguard Worker     }
400*c8dee2aaSAndroid Build Coastguard Worker 
401*c8dee2aaSAndroid Build Coastguard Worker     AutoSTArray<32, SkScalar> intervals(count);
402*c8dee2aaSAndroid Build Coastguard Worker     if (buffer.readScalarArray(intervals.get(), count)) {
403*c8dee2aaSAndroid Build Coastguard Worker         return SkDashPathEffect::Make(intervals.get(), SkToInt(count), phase);
404*c8dee2aaSAndroid Build Coastguard Worker     }
405*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
406*c8dee2aaSAndroid Build Coastguard Worker }
407*c8dee2aaSAndroid Build Coastguard Worker 
408*c8dee2aaSAndroid Build Coastguard Worker //////////////////////////////////////////////////////////////////////////////////////////////////
409*c8dee2aaSAndroid Build Coastguard Worker 
Make(const SkScalar intervals[],int count,SkScalar phase)410*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPathEffect> SkDashPathEffect::Make(const SkScalar intervals[], int count, SkScalar phase) {
411*c8dee2aaSAndroid Build Coastguard Worker     if (!SkDashPath::ValidDashPath(phase, intervals, count)) {
412*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
413*c8dee2aaSAndroid Build Coastguard Worker     }
414*c8dee2aaSAndroid Build Coastguard Worker     return sk_sp<SkPathEffect>(new SkDashImpl(intervals, count, phase));
415*c8dee2aaSAndroid Build Coastguard Worker }
416