blob: 2252a1bd037d8468fc9cb855caa99ec67af58bbe [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkDashPathEffect.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000011#include "SkFlattenableBuffers.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkPathMeasure.h"
13
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000014static inline int is_even(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000015 return (~x) << 31;
16}
17
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000018static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase,
reed@google.comd9ee3482012-08-06 14:58:35 +000019 int32_t* index, int count) {
20 for (int i = 0; i < count; ++i) {
21 if (phase > intervals[i]) {
22 phase -= intervals[i];
23 } else {
24 *index = i;
25 return intervals[i] - phase;
26 }
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000027 }
reed@google.comd9ee3482012-08-06 14:58:35 +000028 // If we get here, phase "appears" to be larger than our length. This
29 // shouldn't happen with perfect precision, but we can accumulate errors
30 // during the initial length computation (rounding can make our sum be too
31 // big or too small. In that event, we just have to eat the error here.
32 *index = 0;
33 return intervals[0];
reed@android.com8a1c16f2008-12-17 15:59:43 +000034}
35
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000036SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count,
37 SkScalar phase, bool scaleToFit)
38 : fScaleToFit(scaleToFit) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000039 SkASSERT(intervals);
40 SkASSERT(count > 1 && SkAlign2(count) == count);
41
42 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count);
43 fCount = count;
44
45 SkScalar len = 0;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000046 for (int i = 0; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000047 SkASSERT(intervals[i] >= 0);
48 fIntervals[i] = intervals[i];
49 len += intervals[i];
50 }
51 fIntervalLength = len;
52
epoger@google.com20bf4ca2012-04-27 13:34:52 +000053 // watch out for values that might make us go out of bounds
54 if ((len > 0) && SkScalarIsFinite(phase) && SkScalarIsFinite(len)) {
55
56 // Adjust phase to be between 0 and len, "flipping" phase if negative.
57 // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000058 if (phase < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000059 phase = -phase;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000060 if (phase > len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000061 phase = SkScalarMod(phase, len);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000062 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000063 phase = len - phase;
epoger@google.com20bf4ca2012-04-27 13:34:52 +000064
65 // Due to finite precision, it's possible that phase == len,
66 // even after the subtract (if len >>> phase), so fix that here.
67 // This fixes http://crbug.com/124652 .
reed@google.com1df888b2012-04-24 22:47:21 +000068 SkASSERT(phase <= len);
69 if (phase == len) {
70 phase = 0;
71 }
epoger@google.com20bf4ca2012-04-27 13:34:52 +000072 } else if (phase >= len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000073 phase = SkScalarMod(phase, len);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000074 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000075 SkASSERT(phase >= 0 && phase < len);
epoger@google.com20bf4ca2012-04-27 13:34:52 +000076
reed@google.comd9ee3482012-08-06 14:58:35 +000077 fInitialDashLength = FindFirstInterval(intervals, phase,
78 &fInitialDashIndex, count);
reed@android.com8a1c16f2008-12-17 15:59:43 +000079
80 SkASSERT(fInitialDashLength >= 0);
81 SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000082 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 fInitialDashLength = -1; // signal bad dash intervals
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000084 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000085}
86
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000087SkDashPathEffect::~SkDashPathEffect() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 sk_free(fIntervals);
89}
90
reed@google.com3ec68f02012-05-29 20:48:50 +000091class SpecialLineRec {
92public:
93 bool init(const SkPath& src, SkPath* dst, SkStrokeRec* rec,
94 SkScalar pathLength,
95 int intervalCount, SkScalar intervalLength) {
96 if (rec->isHairlineStyle() || !src.isLine(fPts)) {
97 return false;
98 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000099
reed@google.com3ec68f02012-05-29 20:48:50 +0000100 // can relax this in the future, if we handle square and round caps
101 if (SkPaint::kButt_Cap != rec->getCap()) {
102 return false;
103 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000104
reed@google.com3ec68f02012-05-29 20:48:50 +0000105 fTangent = fPts[1] - fPts[0];
106 if (fTangent.isZero()) {
107 return false;
108 }
109
110 fPathLength = pathLength;
111 fTangent.scale(SkScalarInvert(pathLength));
112 fTangent.rotateCCW(&fNormal);
113 fNormal.scale(SkScalarHalf(rec->getWidth()));
114
115 // now estimate how many quads will be added to the path
116 // resulting segments = pathLen * intervalCount / intervalLen
117 // resulting points = 4 * segments
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000118
reed@google.com3ec68f02012-05-29 20:48:50 +0000119 SkScalar ptCount = SkScalarMulDiv(pathLength,
120 SkIntToScalar(intervalCount),
121 intervalLength);
122 int n = SkScalarCeilToInt(ptCount) << 2;
123 dst->incReserve(n);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000124
reed@google.com3ec68f02012-05-29 20:48:50 +0000125 // we will take care of the stroking
126 rec->setFillStyle();
127 return true;
128 }
129
130 void addSegment(SkScalar d0, SkScalar d1, SkPath* path) const {
131 SkASSERT(d0 < fPathLength);
132 // clamp the segment to our length
133 if (d1 > fPathLength) {
134 d1 = fPathLength;
135 }
136
137 SkScalar x0 = fPts[0].fX + SkScalarMul(fTangent.fX, d0);
138 SkScalar x1 = fPts[0].fX + SkScalarMul(fTangent.fX, d1);
139 SkScalar y0 = fPts[0].fY + SkScalarMul(fTangent.fY, d0);
140 SkScalar y1 = fPts[0].fY + SkScalarMul(fTangent.fY, d1);
141
142 SkPoint pts[4];
143 pts[0].set(x0 + fNormal.fX, y0 + fNormal.fY); // moveTo
144 pts[1].set(x1 + fNormal.fX, y1 + fNormal.fY); // lineTo
145 pts[2].set(x1 - fNormal.fX, y1 - fNormal.fY); // lineTo
146 pts[3].set(x0 - fNormal.fX, y0 - fNormal.fY); // lineTo
147
148 path->addPoly(pts, SK_ARRAY_COUNT(pts), false);
149 }
150
151private:
152 SkPoint fPts[2];
153 SkVector fTangent;
154 SkVector fNormal;
155 SkScalar fPathLength;
156};
157
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000158bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
reed@google.comfd4be262012-05-25 01:04:12 +0000159 SkStrokeRec* rec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 // we do nothing if the src wants to be filled, or if our dashlength is 0
reed@google.comfd4be262012-05-25 01:04:12 +0000161 if (rec->isFillStyle() || fInitialDashLength < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162 return false;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000163 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164
165 SkPathMeasure meas(src, false);
166 const SkScalar* intervals = fIntervals;
fmalita@google.com6b18d242012-12-17 16:27:34 +0000167 SkScalar dashCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168
reed@google.com3ec68f02012-05-29 20:48:50 +0000169 SpecialLineRec lineRec;
170 const bool specialLine = lineRec.init(src, dst, rec, meas.getLength(),
171 fCount >> 1, fIntervalLength);
172
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 do {
174 bool skipFirstSegment = meas.isClosed();
175 bool addedSegment = false;
176 SkScalar length = meas.getLength();
177 int index = fInitialDashIndex;
178 SkScalar scale = SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000179
fmalita@google.com6b18d242012-12-17 16:27:34 +0000180 // Since the path length / dash length ratio may be arbitrarily large, we can exert
181 // significant memory pressure while attempting to build the filtered path. To avoid this,
182 // we simply give up dashing beyond a certain threshold.
183 //
184 // The original bug report (http://crbug.com/165432) is based on a path yielding more than
185 // 90 million dash segments and crashing the memory allocator. A limit of 1 million
186 // segments seems reasonable: at 2 verbs per segment * 9 bytes per verb, this caps the
187 // maximum dash memory overhead at roughly 17MB per path.
188 static const SkScalar kMaxDashCount = 1000000;
189 dashCount += length * (fCount >> 1) / fIntervalLength;
190 if (dashCount > kMaxDashCount) {
191 dst->reset();
192 return false;
193 }
194
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000195 if (fScaleToFit) {
196 if (fIntervalLength >= length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 scale = SkScalarDiv(length, fIntervalLength);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000198 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 SkScalar div = SkScalarDiv(length, fIntervalLength);
200 int n = SkScalarFloor(div);
201 scale = SkScalarDiv(length, n * fIntervalLength);
202 }
203 }
204
fmalita@google.combfa04012012-12-12 22:13:58 +0000205 // Using double precision to avoid looping indefinitely due to single precision rounding
206 // (for extreme path_length/dash_length ratios). See test_infinite_dash() unittest.
207 double distance = 0;
208 double dlen = SkScalarMul(fInitialDashLength, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000210 while (distance < length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 SkASSERT(dlen >= 0);
212 addedSegment = false;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000213 if (is_even(index) && dlen > 0 && !skipFirstSegment) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 addedSegment = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000215
reed@google.com3ec68f02012-05-29 20:48:50 +0000216 if (specialLine) {
skia.committer@gmail.coma7aedfe2012-12-15 02:03:10 +0000217 lineRec.addSegment(SkDoubleToScalar(distance),
218 SkDoubleToScalar(distance + dlen),
robertphillips@google.com441a0052012-12-14 13:55:06 +0000219 dst);
reed@google.com3ec68f02012-05-29 20:48:50 +0000220 } else {
skia.committer@gmail.coma7aedfe2012-12-15 02:03:10 +0000221 meas.getSegment(SkDoubleToScalar(distance),
222 SkDoubleToScalar(distance + dlen),
robertphillips@google.com441a0052012-12-14 13:55:06 +0000223 dst, true);
reed@google.com3ec68f02012-05-29 20:48:50 +0000224 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 }
226 distance += dlen;
227
228 // clear this so we only respect it the first time around
229 skipFirstSegment = false;
230
231 // wrap around our intervals array if necessary
232 index += 1;
233 SkASSERT(index <= fCount);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000234 if (index == fCount) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 index = 0;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000236 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237
238 // fetch our next dlen
239 dlen = SkScalarMul(intervals[index], scale);
240 }
241
242 // extend if we ended on a segment and we need to join up with the (skipped) initial segment
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000243 if (meas.isClosed() && is_even(fInitialDashIndex) &&
244 fInitialDashLength > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000246 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 } while (meas.nextContour());
reed@google.com3ec68f02012-05-29 20:48:50 +0000248
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 return true;
250}
251
robertphillips@google.com629ab542012-11-28 17:18:11 +0000252// Currently asPoints is more restrictive then it needs to be. In the future
253// we need to:
254// allow kRound_Cap capping (could allow rotations in the matrix with this)
robertphillips@google.com6d875572012-12-17 18:56:29 +0000255// allow paths to be returned
robertphillips@google.com629ab542012-11-28 17:18:11 +0000256bool SkDashPathEffect::asPoints(PointData* results,
257 const SkPath& src,
258 const SkStrokeRec& rec,
259 const SkMatrix& matrix) const {
robertphillips@google.com6d875572012-12-17 18:56:29 +0000260 // width < 0 -> fill && width == 0 -> hairline so requiring width > 0 rules both out
261 if (fInitialDashLength < 0 || 0 >= rec.getWidth()) {
robertphillips@google.com629ab542012-11-28 17:18:11 +0000262 return false;
263 }
264
robertphillips@google.com6d875572012-12-17 18:56:29 +0000265 // TODO: this next test could be eased up. We could allow any number of
266 // intervals as long as all the ons match and all the offs match.
267 // Additionally, they do not necessarily need to be integers.
268 // We cannot allow arbitrary intervals since we want the returned points
269 // to be uniformly sized.
270 if (fCount != 2 ||
271 !SkScalarNearlyEqual(fIntervals[0], fIntervals[1]) ||
272 !SkScalarIsInt(fIntervals[0]) ||
273 !SkScalarIsInt(fIntervals[1])) {
robertphillips@google.com629ab542012-11-28 17:18:11 +0000274 return false;
275 }
276
robertphillips@google.com6d875572012-12-17 18:56:29 +0000277 // TODO: this next test could be eased up. The rescaling should not impact
278 // the equality of the ons & offs. However, we would need to remove the
279 // integer intervals restriction first
280 if (fScaleToFit) {
robertphillips@google.com629ab542012-11-28 17:18:11 +0000281 return false;
282 }
283
284 SkPoint pts[2];
285
robertphillips@google.com6d875572012-12-17 18:56:29 +0000286 if (!src.isLine(pts)) {
robertphillips@google.com629ab542012-11-28 17:18:11 +0000287 return false;
288 }
289
robertphillips@google.com6d875572012-12-17 18:56:29 +0000290 // TODO: this test could be eased up to allow circles
robertphillips@google.com629ab542012-11-28 17:18:11 +0000291 if (SkPaint::kButt_Cap != rec.getCap()) {
292 return false;
293 }
294
robertphillips@google.com6d875572012-12-17 18:56:29 +0000295 // TODO: this test could be eased up for circles. Rotations could be allowed.
robertphillips@google.com629ab542012-11-28 17:18:11 +0000296 if (!matrix.rectStaysRect()) {
297 return false;
298 }
299
robertphillips@google.com6d875572012-12-17 18:56:29 +0000300 SkScalar length = SkPoint::Distance(pts[1], pts[0]);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000301
robertphillips@google.com6d875572012-12-17 18:56:29 +0000302 SkVector tangent = pts[1] - pts[0];
303 if (tangent.isZero()) {
304 return false;
305 }
306
307 tangent.scale(SkScalarInvert(length));
308
309 // TODO: make this test for horizontal & vertical lines more robust
310 bool isXAxis = true;
311 if (SK_Scalar1 == tangent.fX || -SK_Scalar1 == tangent.fX) {
312 results->fSize.set(SkScalarHalf(fIntervals[0]), SkScalarHalf(rec.getWidth()));
313 } else if (SK_Scalar1 == tangent.fY || -SK_Scalar1 == tangent.fY) {
314 results->fSize.set(SkScalarHalf(rec.getWidth()), SkScalarHalf(fIntervals[0]));
315 isXAxis = false;
316 } else if (SkPaint::kRound_Cap != rec.getCap()) {
317 // Angled lines don't have axis-aligned boxes.
robertphillips@google.com629ab542012-11-28 17:18:11 +0000318 return false;
319 }
320
321 if (NULL != results) {
robertphillips@google.com6d875572012-12-17 18:56:29 +0000322 results->fFlags = 0;
robertphillips@google.com629ab542012-11-28 17:18:11 +0000323
robertphillips@google.com6d875572012-12-17 18:56:29 +0000324 if (SkPaint::kRound_Cap == rec.getCap()) {
325 results->fFlags |= PointData::kCircles_PointFlag;
robertphillips@google.com629ab542012-11-28 17:18:11 +0000326 }
327
robertphillips@google.com6d875572012-12-17 18:56:29 +0000328 results->fNumPoints = 0;
329 SkScalar len2 = length;
330 bool partialFirst = false;
331 if (fInitialDashLength > 0 || 0 == fInitialDashIndex) {
332 SkASSERT(len2 >= fInitialDashLength);
333 if (0 == fInitialDashIndex) {
334 if (fInitialDashLength > 0) {
335 partialFirst = true;
336 if (fInitialDashLength >= fIntervals[0]) {
337 ++results->fNumPoints; // partial first dash
338 }
339 len2 -= fInitialDashLength;
340 }
341 len2 -= fIntervals[1]; // also skip first space
342 if (len2 < 0) {
343 len2 = 0;
344 }
345 } else {
346 len2 -= fInitialDashLength; // skip initial partial empty
347 }
348 }
349 int numMidPoints = SkScalarFloorToInt(SkScalarDiv(len2, fIntervalLength));
350 results->fNumPoints += numMidPoints;
351 len2 -= numMidPoints * fIntervalLength;
352 bool partialLast = false;
353 if (len2 > 0) {
354 if (len2 < fIntervals[0]) {
355 partialLast = true;
356 } else {
357 ++numMidPoints;
358 ++results->fNumPoints;
359 }
360 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000361
robertphillips@google.com935ad022012-12-05 19:07:21 +0000362 results->fPoints = new SkPoint[results->fNumPoints];
robertphillips@google.com629ab542012-11-28 17:18:11 +0000363
robertphillips@google.com6d875572012-12-17 18:56:29 +0000364 SkScalar distance = 0;
365 int curPt = 0;
robertphillips@google.com629ab542012-11-28 17:18:11 +0000366
robertphillips@google.com6d875572012-12-17 18:56:29 +0000367 if (fInitialDashLength > 0 || 0 == fInitialDashIndex) {
368 SkASSERT(fInitialDashLength <= length);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000369
robertphillips@google.com6d875572012-12-17 18:56:29 +0000370 if (0 == fInitialDashIndex) {
371 if (fInitialDashLength > 0) {
372 // partial first block
373 SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles
374 SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, SkScalarHalf(fInitialDashLength));
375 SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, SkScalarHalf(fInitialDashLength));
376 SkScalar halfWidth, halfHeight;
377 if (isXAxis) {
378 halfWidth = SkScalarHalf(fInitialDashLength);
379 halfHeight = SkScalarHalf(rec.getWidth());
380 } else {
381 halfWidth = SkScalarHalf(rec.getWidth());
382 halfHeight = SkScalarHalf(fInitialDashLength);
383 }
384 if (fInitialDashLength < fIntervals[0]) {
385 // This one will not be like the others
386 results->fFirst.addRect(x - halfWidth, y - halfHeight,
387 x + halfWidth, y + halfHeight);
388 } else {
389 SkASSERT(curPt < results->fNumPoints);
390 results->fPoints[curPt].set(x, y);
391 ++curPt;
392 }
393
394 distance += fInitialDashLength;
395 }
396
397 distance += fIntervals[1]; // skip over the next blank block too
398 } else {
399 distance += fInitialDashLength;
robertphillips@google.com629ab542012-11-28 17:18:11 +0000400 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000401 }
robertphillips@google.com935ad022012-12-05 19:07:21 +0000402
robertphillips@google.com6d875572012-12-17 18:56:29 +0000403 if (0 != numMidPoints) {
404 distance += SkScalarHalf(fIntervals[0]);
405
406 for (int i = 0; i < numMidPoints; ++i) {
407 SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance);
408 SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance);
409
410 SkASSERT(curPt < results->fNumPoints);
411 results->fPoints[curPt].set(x, y);
412 ++curPt;
413
414 distance += fIntervalLength;
415 }
416
417 distance -= SkScalarHalf(fIntervals[0]);
418 }
419
420 if (partialLast) {
421 // partial final block
422 SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles
423 SkScalar temp = length - distance;
424 SkASSERT(temp < fIntervals[0]);
425 SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance + SkScalarHalf(temp));
426 SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance + SkScalarHalf(temp));
427 SkScalar halfWidth, halfHeight;
428 if (isXAxis) {
429 halfWidth = SkScalarHalf(temp);
430 halfHeight = SkScalarHalf(rec.getWidth());
431 } else {
432 halfWidth = SkScalarHalf(rec.getWidth());
433 halfHeight = SkScalarHalf(temp);
434 }
435 results->fLast.addRect(x - halfWidth, y - halfHeight,
436 x + halfWidth, y + halfHeight);
437 }
438
439 SkASSERT(curPt == results->fNumPoints);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000440 }
441
442 return true;
443}
444
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000445SkFlattenable::Factory SkDashPathEffect::getFactory() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 return fInitialDashLength < 0 ? NULL : CreateProc;
447}
448
djsollen@google.com54924242012-03-29 15:18:04 +0000449void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 SkASSERT(fInitialDashLength >= 0);
451
djsollen@google.com54924242012-03-29 15:18:04 +0000452 this->INHERITED::flatten(buffer);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000453 buffer.writeInt(fInitialDashIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 buffer.writeScalar(fInitialDashLength);
455 buffer.writeScalar(fIntervalLength);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000456 buffer.writeBool(fScaleToFit);
457 buffer.writeScalarArray(fIntervals, fCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458}
459
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000460SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461 return SkNEW_ARGS(SkDashPathEffect, (buffer));
462}
463
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000464SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
465 fInitialDashIndex = buffer.readInt();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 fInitialDashLength = buffer.readScalar();
467 fIntervalLength = buffer.readScalar();
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000468 fScaleToFit = buffer.readBool();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000469
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000470 fCount = buffer.getArrayCount();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000472 buffer.readScalarArray(fIntervals);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473}