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