1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/effects/SkDashPathEffect.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,563 @@ 1.4 + 1.5 +/* 1.6 + * Copyright 2006 The Android Open Source Project 1.7 + * 1.8 + * Use of this source code is governed by a BSD-style license that can be 1.9 + * found in the LICENSE file. 1.10 + */ 1.11 + 1.12 + 1.13 +#include "SkDashPathEffect.h" 1.14 +#include "SkReadBuffer.h" 1.15 +#include "SkWriteBuffer.h" 1.16 +#include "SkPathMeasure.h" 1.17 + 1.18 +static inline int is_even(int x) { 1.19 + return (~x) << 31; 1.20 +} 1.21 + 1.22 +static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase, 1.23 + int32_t* index, int count) { 1.24 + for (int i = 0; i < count; ++i) { 1.25 + if (phase > intervals[i]) { 1.26 + phase -= intervals[i]; 1.27 + } else { 1.28 + *index = i; 1.29 + return intervals[i] - phase; 1.30 + } 1.31 + } 1.32 + // If we get here, phase "appears" to be larger than our length. This 1.33 + // shouldn't happen with perfect precision, but we can accumulate errors 1.34 + // during the initial length computation (rounding can make our sum be too 1.35 + // big or too small. In that event, we just have to eat the error here. 1.36 + *index = 0; 1.37 + return intervals[0]; 1.38 +} 1.39 + 1.40 +SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count, 1.41 + SkScalar phase, bool scaleToFit) 1.42 + : fScaleToFit(scaleToFit) { 1.43 + SkASSERT(intervals); 1.44 + SkASSERT(count > 1 && SkAlign2(count) == count); 1.45 + 1.46 + fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count); 1.47 + fCount = count; 1.48 + 1.49 + SkScalar len = 0; 1.50 + for (int i = 0; i < count; i++) { 1.51 + SkASSERT(intervals[i] >= 0); 1.52 + fIntervals[i] = intervals[i]; 1.53 + len += intervals[i]; 1.54 + } 1.55 + fIntervalLength = len; 1.56 + 1.57 + // watch out for values that might make us go out of bounds 1.58 + if ((len > 0) && SkScalarIsFinite(phase) && SkScalarIsFinite(len)) { 1.59 + 1.60 + // Adjust phase to be between 0 and len, "flipping" phase if negative. 1.61 + // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80 1.62 + if (phase < 0) { 1.63 + phase = -phase; 1.64 + if (phase > len) { 1.65 + phase = SkScalarMod(phase, len); 1.66 + } 1.67 + phase = len - phase; 1.68 + 1.69 + // Due to finite precision, it's possible that phase == len, 1.70 + // even after the subtract (if len >>> phase), so fix that here. 1.71 + // This fixes http://crbug.com/124652 . 1.72 + SkASSERT(phase <= len); 1.73 + if (phase == len) { 1.74 + phase = 0; 1.75 + } 1.76 + } else if (phase >= len) { 1.77 + phase = SkScalarMod(phase, len); 1.78 + } 1.79 + SkASSERT(phase >= 0 && phase < len); 1.80 + 1.81 + fInitialDashLength = FindFirstInterval(intervals, phase, 1.82 + &fInitialDashIndex, count); 1.83 + 1.84 + SkASSERT(fInitialDashLength >= 0); 1.85 + SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount); 1.86 + } else { 1.87 + fInitialDashLength = -1; // signal bad dash intervals 1.88 + } 1.89 +} 1.90 + 1.91 +SkDashPathEffect::~SkDashPathEffect() { 1.92 + sk_free(fIntervals); 1.93 +} 1.94 + 1.95 +static void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) { 1.96 + SkScalar radius = SkScalarHalf(rec.getWidth()); 1.97 + if (0 == radius) { 1.98 + radius = SK_Scalar1; // hairlines 1.99 + } 1.100 + if (SkPaint::kMiter_Join == rec.getJoin()) { 1.101 + radius = SkScalarMul(radius, rec.getMiter()); 1.102 + } 1.103 + rect->outset(radius, radius); 1.104 +} 1.105 + 1.106 +// Only handles lines for now. If returns true, dstPath is the new (smaller) 1.107 +// path. If returns false, then dstPath parameter is ignored. 1.108 +static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec, 1.109 + const SkRect* cullRect, SkScalar intervalLength, 1.110 + SkPath* dstPath) { 1.111 + if (NULL == cullRect) { 1.112 + return false; 1.113 + } 1.114 + 1.115 + SkPoint pts[2]; 1.116 + if (!srcPath.isLine(pts)) { 1.117 + return false; 1.118 + } 1.119 + 1.120 + SkRect bounds = *cullRect; 1.121 + outset_for_stroke(&bounds, rec); 1.122 + 1.123 + SkScalar dx = pts[1].x() - pts[0].x(); 1.124 + SkScalar dy = pts[1].y() - pts[0].y(); 1.125 + 1.126 + // just do horizontal lines for now (lazy) 1.127 + if (dy) { 1.128 + return false; 1.129 + } 1.130 + 1.131 + SkScalar minX = pts[0].fX; 1.132 + SkScalar maxX = pts[1].fX; 1.133 + 1.134 + if (maxX < bounds.fLeft || minX > bounds.fRight) { 1.135 + return false; 1.136 + } 1.137 + 1.138 + if (dx < 0) { 1.139 + SkTSwap(minX, maxX); 1.140 + } 1.141 + 1.142 + // Now we actually perform the chop, removing the excess to the left and 1.143 + // right of the bounds (keeping our new line "in phase" with the dash, 1.144 + // hence the (mod intervalLength). 1.145 + 1.146 + if (minX < bounds.fLeft) { 1.147 + minX = bounds.fLeft - SkScalarMod(bounds.fLeft - minX, 1.148 + intervalLength); 1.149 + } 1.150 + if (maxX > bounds.fRight) { 1.151 + maxX = bounds.fRight + SkScalarMod(maxX - bounds.fRight, 1.152 + intervalLength); 1.153 + } 1.154 + 1.155 + SkASSERT(maxX >= minX); 1.156 + if (dx < 0) { 1.157 + SkTSwap(minX, maxX); 1.158 + } 1.159 + pts[0].fX = minX; 1.160 + pts[1].fX = maxX; 1.161 + 1.162 + dstPath->moveTo(pts[0]); 1.163 + dstPath->lineTo(pts[1]); 1.164 + return true; 1.165 +} 1.166 + 1.167 +class SpecialLineRec { 1.168 +public: 1.169 + bool init(const SkPath& src, SkPath* dst, SkStrokeRec* rec, 1.170 + int intervalCount, SkScalar intervalLength) { 1.171 + if (rec->isHairlineStyle() || !src.isLine(fPts)) { 1.172 + return false; 1.173 + } 1.174 + 1.175 + // can relax this in the future, if we handle square and round caps 1.176 + if (SkPaint::kButt_Cap != rec->getCap()) { 1.177 + return false; 1.178 + } 1.179 + 1.180 + SkScalar pathLength = SkPoint::Distance(fPts[0], fPts[1]); 1.181 + 1.182 + fTangent = fPts[1] - fPts[0]; 1.183 + if (fTangent.isZero()) { 1.184 + return false; 1.185 + } 1.186 + 1.187 + fPathLength = pathLength; 1.188 + fTangent.scale(SkScalarInvert(pathLength)); 1.189 + fTangent.rotateCCW(&fNormal); 1.190 + fNormal.scale(SkScalarHalf(rec->getWidth())); 1.191 + 1.192 + // now estimate how many quads will be added to the path 1.193 + // resulting segments = pathLen * intervalCount / intervalLen 1.194 + // resulting points = 4 * segments 1.195 + 1.196 + SkScalar ptCount = SkScalarMulDiv(pathLength, 1.197 + SkIntToScalar(intervalCount), 1.198 + intervalLength); 1.199 + int n = SkScalarCeilToInt(ptCount) << 2; 1.200 + dst->incReserve(n); 1.201 + 1.202 + // we will take care of the stroking 1.203 + rec->setFillStyle(); 1.204 + return true; 1.205 + } 1.206 + 1.207 + void addSegment(SkScalar d0, SkScalar d1, SkPath* path) const { 1.208 + SkASSERT(d0 < fPathLength); 1.209 + // clamp the segment to our length 1.210 + if (d1 > fPathLength) { 1.211 + d1 = fPathLength; 1.212 + } 1.213 + 1.214 + SkScalar x0 = fPts[0].fX + SkScalarMul(fTangent.fX, d0); 1.215 + SkScalar x1 = fPts[0].fX + SkScalarMul(fTangent.fX, d1); 1.216 + SkScalar y0 = fPts[0].fY + SkScalarMul(fTangent.fY, d0); 1.217 + SkScalar y1 = fPts[0].fY + SkScalarMul(fTangent.fY, d1); 1.218 + 1.219 + SkPoint pts[4]; 1.220 + pts[0].set(x0 + fNormal.fX, y0 + fNormal.fY); // moveTo 1.221 + pts[1].set(x1 + fNormal.fX, y1 + fNormal.fY); // lineTo 1.222 + pts[2].set(x1 - fNormal.fX, y1 - fNormal.fY); // lineTo 1.223 + pts[3].set(x0 - fNormal.fX, y0 - fNormal.fY); // lineTo 1.224 + 1.225 + path->addPoly(pts, SK_ARRAY_COUNT(pts), false); 1.226 + } 1.227 + 1.228 +private: 1.229 + SkPoint fPts[2]; 1.230 + SkVector fTangent; 1.231 + SkVector fNormal; 1.232 + SkScalar fPathLength; 1.233 +}; 1.234 + 1.235 +bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src, 1.236 + SkStrokeRec* rec, const SkRect* cullRect) const { 1.237 + // we do nothing if the src wants to be filled, or if our dashlength is 0 1.238 + if (rec->isFillStyle() || fInitialDashLength < 0) { 1.239 + return false; 1.240 + } 1.241 + 1.242 + const SkScalar* intervals = fIntervals; 1.243 + SkScalar dashCount = 0; 1.244 + int segCount = 0; 1.245 + 1.246 + SkPath cullPathStorage; 1.247 + const SkPath* srcPtr = &src; 1.248 + if (cull_path(src, *rec, cullRect, fIntervalLength, &cullPathStorage)) { 1.249 + srcPtr = &cullPathStorage; 1.250 + } 1.251 + 1.252 + SpecialLineRec lineRec; 1.253 + bool specialLine = lineRec.init(*srcPtr, dst, rec, fCount >> 1, fIntervalLength); 1.254 + 1.255 + SkPathMeasure meas(*srcPtr, false); 1.256 + 1.257 + do { 1.258 + bool skipFirstSegment = meas.isClosed(); 1.259 + bool addedSegment = false; 1.260 + SkScalar length = meas.getLength(); 1.261 + int index = fInitialDashIndex; 1.262 + SkScalar scale = SK_Scalar1; 1.263 + 1.264 + // Since the path length / dash length ratio may be arbitrarily large, we can exert 1.265 + // significant memory pressure while attempting to build the filtered path. To avoid this, 1.266 + // we simply give up dashing beyond a certain threshold. 1.267 + // 1.268 + // The original bug report (http://crbug.com/165432) is based on a path yielding more than 1.269 + // 90 million dash segments and crashing the memory allocator. A limit of 1 million 1.270 + // segments seems reasonable: at 2 verbs per segment * 9 bytes per verb, this caps the 1.271 + // maximum dash memory overhead at roughly 17MB per path. 1.272 + static const SkScalar kMaxDashCount = 1000000; 1.273 + dashCount += length * (fCount >> 1) / fIntervalLength; 1.274 + if (dashCount > kMaxDashCount) { 1.275 + dst->reset(); 1.276 + return false; 1.277 + } 1.278 + 1.279 + if (fScaleToFit) { 1.280 + if (fIntervalLength >= length) { 1.281 + scale = SkScalarDiv(length, fIntervalLength); 1.282 + } else { 1.283 + SkScalar div = SkScalarDiv(length, fIntervalLength); 1.284 + int n = SkScalarFloorToInt(div); 1.285 + scale = SkScalarDiv(length, n * fIntervalLength); 1.286 + } 1.287 + } 1.288 + 1.289 + // Using double precision to avoid looping indefinitely due to single precision rounding 1.290 + // (for extreme path_length/dash_length ratios). See test_infinite_dash() unittest. 1.291 + double distance = 0; 1.292 + double dlen = SkScalarMul(fInitialDashLength, scale); 1.293 + 1.294 + while (distance < length) { 1.295 + SkASSERT(dlen >= 0); 1.296 + addedSegment = false; 1.297 + if (is_even(index) && dlen > 0 && !skipFirstSegment) { 1.298 + addedSegment = true; 1.299 + ++segCount; 1.300 + 1.301 + if (specialLine) { 1.302 + lineRec.addSegment(SkDoubleToScalar(distance), 1.303 + SkDoubleToScalar(distance + dlen), 1.304 + dst); 1.305 + } else { 1.306 + meas.getSegment(SkDoubleToScalar(distance), 1.307 + SkDoubleToScalar(distance + dlen), 1.308 + dst, true); 1.309 + } 1.310 + } 1.311 + distance += dlen; 1.312 + 1.313 + // clear this so we only respect it the first time around 1.314 + skipFirstSegment = false; 1.315 + 1.316 + // wrap around our intervals array if necessary 1.317 + index += 1; 1.318 + SkASSERT(index <= fCount); 1.319 + if (index == fCount) { 1.320 + index = 0; 1.321 + } 1.322 + 1.323 + // fetch our next dlen 1.324 + dlen = SkScalarMul(intervals[index], scale); 1.325 + } 1.326 + 1.327 + // extend if we ended on a segment and we need to join up with the (skipped) initial segment 1.328 + if (meas.isClosed() && is_even(fInitialDashIndex) && 1.329 + fInitialDashLength > 0) { 1.330 + meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment); 1.331 + ++segCount; 1.332 + } 1.333 + } while (meas.nextContour()); 1.334 + 1.335 + if (segCount > 1) { 1.336 + dst->setConvexity(SkPath::kConcave_Convexity); 1.337 + } 1.338 + 1.339 + return true; 1.340 +} 1.341 + 1.342 +// Currently asPoints is more restrictive then it needs to be. In the future 1.343 +// we need to: 1.344 +// allow kRound_Cap capping (could allow rotations in the matrix with this) 1.345 +// allow paths to be returned 1.346 +bool SkDashPathEffect::asPoints(PointData* results, 1.347 + const SkPath& src, 1.348 + const SkStrokeRec& rec, 1.349 + const SkMatrix& matrix, 1.350 + const SkRect* cullRect) const { 1.351 + // width < 0 -> fill && width == 0 -> hairline so requiring width > 0 rules both out 1.352 + if (fInitialDashLength < 0 || 0 >= rec.getWidth()) { 1.353 + return false; 1.354 + } 1.355 + 1.356 + // TODO: this next test could be eased up. We could allow any number of 1.357 + // intervals as long as all the ons match and all the offs match. 1.358 + // Additionally, they do not necessarily need to be integers. 1.359 + // We cannot allow arbitrary intervals since we want the returned points 1.360 + // to be uniformly sized. 1.361 + if (fCount != 2 || 1.362 + !SkScalarNearlyEqual(fIntervals[0], fIntervals[1]) || 1.363 + !SkScalarIsInt(fIntervals[0]) || 1.364 + !SkScalarIsInt(fIntervals[1])) { 1.365 + return false; 1.366 + } 1.367 + 1.368 + // TODO: this next test could be eased up. The rescaling should not impact 1.369 + // the equality of the ons & offs. However, we would need to remove the 1.370 + // integer intervals restriction first 1.371 + if (fScaleToFit) { 1.372 + return false; 1.373 + } 1.374 + 1.375 + SkPoint pts[2]; 1.376 + 1.377 + if (!src.isLine(pts)) { 1.378 + return false; 1.379 + } 1.380 + 1.381 + // TODO: this test could be eased up to allow circles 1.382 + if (SkPaint::kButt_Cap != rec.getCap()) { 1.383 + return false; 1.384 + } 1.385 + 1.386 + // TODO: this test could be eased up for circles. Rotations could be allowed. 1.387 + if (!matrix.rectStaysRect()) { 1.388 + return false; 1.389 + } 1.390 + 1.391 + SkScalar length = SkPoint::Distance(pts[1], pts[0]); 1.392 + 1.393 + SkVector tangent = pts[1] - pts[0]; 1.394 + if (tangent.isZero()) { 1.395 + return false; 1.396 + } 1.397 + 1.398 + tangent.scale(SkScalarInvert(length)); 1.399 + 1.400 + // TODO: make this test for horizontal & vertical lines more robust 1.401 + bool isXAxis = true; 1.402 + if (SK_Scalar1 == tangent.fX || -SK_Scalar1 == tangent.fX) { 1.403 + results->fSize.set(SkScalarHalf(fIntervals[0]), SkScalarHalf(rec.getWidth())); 1.404 + } else if (SK_Scalar1 == tangent.fY || -SK_Scalar1 == tangent.fY) { 1.405 + results->fSize.set(SkScalarHalf(rec.getWidth()), SkScalarHalf(fIntervals[0])); 1.406 + isXAxis = false; 1.407 + } else if (SkPaint::kRound_Cap != rec.getCap()) { 1.408 + // Angled lines don't have axis-aligned boxes. 1.409 + return false; 1.410 + } 1.411 + 1.412 + if (NULL != results) { 1.413 + results->fFlags = 0; 1.414 + SkScalar clampedInitialDashLength = SkMinScalar(length, fInitialDashLength); 1.415 + 1.416 + if (SkPaint::kRound_Cap == rec.getCap()) { 1.417 + results->fFlags |= PointData::kCircles_PointFlag; 1.418 + } 1.419 + 1.420 + results->fNumPoints = 0; 1.421 + SkScalar len2 = length; 1.422 + if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) { 1.423 + SkASSERT(len2 >= clampedInitialDashLength); 1.424 + if (0 == fInitialDashIndex) { 1.425 + if (clampedInitialDashLength > 0) { 1.426 + if (clampedInitialDashLength >= fIntervals[0]) { 1.427 + ++results->fNumPoints; // partial first dash 1.428 + } 1.429 + len2 -= clampedInitialDashLength; 1.430 + } 1.431 + len2 -= fIntervals[1]; // also skip first space 1.432 + if (len2 < 0) { 1.433 + len2 = 0; 1.434 + } 1.435 + } else { 1.436 + len2 -= clampedInitialDashLength; // skip initial partial empty 1.437 + } 1.438 + } 1.439 + int numMidPoints = SkScalarFloorToInt(SkScalarDiv(len2, fIntervalLength)); 1.440 + results->fNumPoints += numMidPoints; 1.441 + len2 -= numMidPoints * fIntervalLength; 1.442 + bool partialLast = false; 1.443 + if (len2 > 0) { 1.444 + if (len2 < fIntervals[0]) { 1.445 + partialLast = true; 1.446 + } else { 1.447 + ++numMidPoints; 1.448 + ++results->fNumPoints; 1.449 + } 1.450 + } 1.451 + 1.452 + results->fPoints = new SkPoint[results->fNumPoints]; 1.453 + 1.454 + SkScalar distance = 0; 1.455 + int curPt = 0; 1.456 + 1.457 + if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) { 1.458 + SkASSERT(clampedInitialDashLength <= length); 1.459 + 1.460 + if (0 == fInitialDashIndex) { 1.461 + if (clampedInitialDashLength > 0) { 1.462 + // partial first block 1.463 + SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles 1.464 + SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, SkScalarHalf(clampedInitialDashLength)); 1.465 + SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, SkScalarHalf(clampedInitialDashLength)); 1.466 + SkScalar halfWidth, halfHeight; 1.467 + if (isXAxis) { 1.468 + halfWidth = SkScalarHalf(clampedInitialDashLength); 1.469 + halfHeight = SkScalarHalf(rec.getWidth()); 1.470 + } else { 1.471 + halfWidth = SkScalarHalf(rec.getWidth()); 1.472 + halfHeight = SkScalarHalf(clampedInitialDashLength); 1.473 + } 1.474 + if (clampedInitialDashLength < fIntervals[0]) { 1.475 + // This one will not be like the others 1.476 + results->fFirst.addRect(x - halfWidth, y - halfHeight, 1.477 + x + halfWidth, y + halfHeight); 1.478 + } else { 1.479 + SkASSERT(curPt < results->fNumPoints); 1.480 + results->fPoints[curPt].set(x, y); 1.481 + ++curPt; 1.482 + } 1.483 + 1.484 + distance += clampedInitialDashLength; 1.485 + } 1.486 + 1.487 + distance += fIntervals[1]; // skip over the next blank block too 1.488 + } else { 1.489 + distance += clampedInitialDashLength; 1.490 + } 1.491 + } 1.492 + 1.493 + if (0 != numMidPoints) { 1.494 + distance += SkScalarHalf(fIntervals[0]); 1.495 + 1.496 + for (int i = 0; i < numMidPoints; ++i) { 1.497 + SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance); 1.498 + SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance); 1.499 + 1.500 + SkASSERT(curPt < results->fNumPoints); 1.501 + results->fPoints[curPt].set(x, y); 1.502 + ++curPt; 1.503 + 1.504 + distance += fIntervalLength; 1.505 + } 1.506 + 1.507 + distance -= SkScalarHalf(fIntervals[0]); 1.508 + } 1.509 + 1.510 + if (partialLast) { 1.511 + // partial final block 1.512 + SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles 1.513 + SkScalar temp = length - distance; 1.514 + SkASSERT(temp < fIntervals[0]); 1.515 + SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance + SkScalarHalf(temp)); 1.516 + SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance + SkScalarHalf(temp)); 1.517 + SkScalar halfWidth, halfHeight; 1.518 + if (isXAxis) { 1.519 + halfWidth = SkScalarHalf(temp); 1.520 + halfHeight = SkScalarHalf(rec.getWidth()); 1.521 + } else { 1.522 + halfWidth = SkScalarHalf(rec.getWidth()); 1.523 + halfHeight = SkScalarHalf(temp); 1.524 + } 1.525 + results->fLast.addRect(x - halfWidth, y - halfHeight, 1.526 + x + halfWidth, y + halfHeight); 1.527 + } 1.528 + 1.529 + SkASSERT(curPt == results->fNumPoints); 1.530 + } 1.531 + 1.532 + return true; 1.533 +} 1.534 + 1.535 +SkFlattenable::Factory SkDashPathEffect::getFactory() const { 1.536 + return CreateProc; 1.537 +} 1.538 + 1.539 +void SkDashPathEffect::flatten(SkWriteBuffer& buffer) const { 1.540 + this->INHERITED::flatten(buffer); 1.541 + buffer.writeInt(fInitialDashIndex); 1.542 + buffer.writeScalar(fInitialDashLength); 1.543 + buffer.writeScalar(fIntervalLength); 1.544 + buffer.writeBool(fScaleToFit); 1.545 + buffer.writeScalarArray(fIntervals, fCount); 1.546 +} 1.547 + 1.548 +SkFlattenable* SkDashPathEffect::CreateProc(SkReadBuffer& buffer) { 1.549 + return SkNEW_ARGS(SkDashPathEffect, (buffer)); 1.550 +} 1.551 + 1.552 +SkDashPathEffect::SkDashPathEffect(SkReadBuffer& buffer) : INHERITED(buffer) { 1.553 + fInitialDashIndex = buffer.readInt(); 1.554 + fInitialDashLength = buffer.readScalar(); 1.555 + fIntervalLength = buffer.readScalar(); 1.556 + fScaleToFit = buffer.readBool(); 1.557 + 1.558 + fCount = buffer.getArrayCount(); 1.559 + size_t allocSize = sizeof(SkScalar) * fCount; 1.560 + if (buffer.validateAvailable(allocSize)) { 1.561 + fIntervals = (SkScalar*)sk_malloc_throw(allocSize); 1.562 + buffer.readScalarArray(fIntervals, fCount); 1.563 + } else { 1.564 + fIntervals = NULL; 1.565 + } 1.566 +}