OLD | NEW |
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2006 The Android Open Source Project | 3 * Copyright 2006 The Android Open Source Project |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 | 9 |
10 #include "SkDashPathEffect.h" | 10 #include "SkDashPathEffect.h" |
(...skipping 17 matching lines...) Expand all Loading... |
28 } | 28 } |
29 // If we get here, phase "appears" to be larger than our length. This | 29 // If we get here, phase "appears" to be larger than our length. This |
30 // shouldn't happen with perfect precision, but we can accumulate errors | 30 // shouldn't happen with perfect precision, but we can accumulate errors |
31 // during the initial length computation (rounding can make our sum be too | 31 // during the initial length computation (rounding can make our sum be too |
32 // big or too small. In that event, we just have to eat the error here. | 32 // big or too small. In that event, we just have to eat the error here. |
33 *index = 0; | 33 *index = 0; |
34 return intervals[0]; | 34 return intervals[0]; |
35 } | 35 } |
36 | 36 |
37 SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count, | 37 SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count, |
38 SkScalar phase, bool scaleToFit) | 38 SkScalar phase) { |
39 : fScaleToFit(scaleToFit) { | |
40 SkASSERT(intervals); | 39 SkASSERT(intervals); |
41 SkASSERT(count > 1 && SkAlign2(count) == count); | 40 SkASSERT(count > 1 && SkAlign2(count) == count); |
42 | 41 |
43 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count); | 42 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count); |
44 fCount = count; | 43 fCount = count; |
45 | 44 |
46 SkScalar len = 0; | 45 SkScalar len = 0; |
47 for (int i = 0; i < count; i++) { | 46 for (int i = 0; i < count; i++) { |
48 SkASSERT(intervals[i] >= 0); | 47 SkASSERT(intervals[i] >= 0); |
49 fIntervals[i] = intervals[i]; | 48 fIntervals[i] = intervals[i]; |
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
249 SpecialLineRec lineRec; | 248 SpecialLineRec lineRec; |
250 bool specialLine = lineRec.init(*srcPtr, dst, rec, fCount >> 1, fIntervalLen
gth); | 249 bool specialLine = lineRec.init(*srcPtr, dst, rec, fCount >> 1, fIntervalLen
gth); |
251 | 250 |
252 SkPathMeasure meas(*srcPtr, false); | 251 SkPathMeasure meas(*srcPtr, false); |
253 | 252 |
254 do { | 253 do { |
255 bool skipFirstSegment = meas.isClosed(); | 254 bool skipFirstSegment = meas.isClosed(); |
256 bool addedSegment = false; | 255 bool addedSegment = false; |
257 SkScalar length = meas.getLength(); | 256 SkScalar length = meas.getLength(); |
258 int index = fInitialDashIndex; | 257 int index = fInitialDashIndex; |
259 SkScalar scale = SK_Scalar1; | |
260 | 258 |
261 // Since the path length / dash length ratio may be arbitrarily large, w
e can exert | 259 // Since the path length / dash length ratio may be arbitrarily large, w
e can exert |
262 // significant memory pressure while attempting to build the filtered pa
th. To avoid this, | 260 // significant memory pressure while attempting to build the filtered pa
th. To avoid this, |
263 // we simply give up dashing beyond a certain threshold. | 261 // we simply give up dashing beyond a certain threshold. |
264 // | 262 // |
265 // The original bug report (http://crbug.com/165432) is based on a path
yielding more than | 263 // The original bug report (http://crbug.com/165432) is based on a path
yielding more than |
266 // 90 million dash segments and crashing the memory allocator. A limit o
f 1 million | 264 // 90 million dash segments and crashing the memory allocator. A limit o
f 1 million |
267 // segments seems reasonable: at 2 verbs per segment * 9 bytes per verb,
this caps the | 265 // segments seems reasonable: at 2 verbs per segment * 9 bytes per verb,
this caps the |
268 // maximum dash memory overhead at roughly 17MB per path. | 266 // maximum dash memory overhead at roughly 17MB per path. |
269 static const SkScalar kMaxDashCount = 1000000; | 267 static const SkScalar kMaxDashCount = 1000000; |
270 dashCount += length * (fCount >> 1) / fIntervalLength; | 268 dashCount += length * (fCount >> 1) / fIntervalLength; |
271 if (dashCount > kMaxDashCount) { | 269 if (dashCount > kMaxDashCount) { |
272 dst->reset(); | 270 dst->reset(); |
273 return false; | 271 return false; |
274 } | 272 } |
275 | 273 |
276 if (fScaleToFit) { | |
277 if (fIntervalLength >= length) { | |
278 scale = SkScalarDiv(length, fIntervalLength); | |
279 } else { | |
280 SkScalar div = SkScalarDiv(length, fIntervalLength); | |
281 int n = SkScalarFloorToInt(div); | |
282 scale = SkScalarDiv(length, n * fIntervalLength); | |
283 } | |
284 } | |
285 | |
286 // Using double precision to avoid looping indefinitely due to single pr
ecision rounding | 274 // Using double precision to avoid looping indefinitely due to single pr
ecision rounding |
287 // (for extreme path_length/dash_length ratios). See test_infinite_dash(
) unittest. | 275 // (for extreme path_length/dash_length ratios). See test_infinite_dash(
) unittest. |
288 double distance = 0; | 276 double distance = 0; |
289 double dlen = SkScalarMul(fInitialDashLength, scale); | 277 double dlen = fInitialDashLength; |
290 | 278 |
291 while (distance < length) { | 279 while (distance < length) { |
292 SkASSERT(dlen >= 0); | 280 SkASSERT(dlen >= 0); |
293 addedSegment = false; | 281 addedSegment = false; |
294 if (is_even(index) && dlen > 0 && !skipFirstSegment) { | 282 if (is_even(index) && dlen > 0 && !skipFirstSegment) { |
295 addedSegment = true; | 283 addedSegment = true; |
296 ++segCount; | 284 ++segCount; |
297 | 285 |
298 if (specialLine) { | 286 if (specialLine) { |
299 lineRec.addSegment(SkDoubleToScalar(distance), | 287 lineRec.addSegment(SkDoubleToScalar(distance), |
(...skipping 11 matching lines...) Expand all Loading... |
311 skipFirstSegment = false; | 299 skipFirstSegment = false; |
312 | 300 |
313 // wrap around our intervals array if necessary | 301 // wrap around our intervals array if necessary |
314 index += 1; | 302 index += 1; |
315 SkASSERT(index <= fCount); | 303 SkASSERT(index <= fCount); |
316 if (index == fCount) { | 304 if (index == fCount) { |
317 index = 0; | 305 index = 0; |
318 } | 306 } |
319 | 307 |
320 // fetch our next dlen | 308 // fetch our next dlen |
321 dlen = SkScalarMul(intervals[index], scale); | 309 dlen = intervals[index]; |
322 } | 310 } |
323 | 311 |
324 // extend if we ended on a segment and we need to join up with the (skip
ped) initial segment | 312 // extend if we ended on a segment and we need to join up with the (skip
ped) initial segment |
325 if (meas.isClosed() && is_even(fInitialDashIndex) && | 313 if (meas.isClosed() && is_even(fInitialDashIndex) && |
326 fInitialDashLength > 0) { | 314 fInitialDashLength > 0) { |
327 meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !add
edSegment); | 315 meas.getSegment(0, fInitialDashLength, dst, !addedSegment); |
328 ++segCount; | 316 ++segCount; |
329 } | 317 } |
330 } while (meas.nextContour()); | 318 } while (meas.nextContour()); |
331 | 319 |
332 if (segCount > 1) { | 320 if (segCount > 1) { |
333 dst->setConvexity(SkPath::kConcave_Convexity); | 321 dst->setConvexity(SkPath::kConcave_Convexity); |
334 } | 322 } |
335 | 323 |
336 return true; | 324 return true; |
337 } | 325 } |
(...skipping 17 matching lines...) Expand all Loading... |
355 // Additionally, they do not necessarily need to be integers. | 343 // Additionally, they do not necessarily need to be integers. |
356 // We cannot allow arbitrary intervals since we want the returned points | 344 // We cannot allow arbitrary intervals since we want the returned points |
357 // to be uniformly sized. | 345 // to be uniformly sized. |
358 if (fCount != 2 || | 346 if (fCount != 2 || |
359 !SkScalarNearlyEqual(fIntervals[0], fIntervals[1]) || | 347 !SkScalarNearlyEqual(fIntervals[0], fIntervals[1]) || |
360 !SkScalarIsInt(fIntervals[0]) || | 348 !SkScalarIsInt(fIntervals[0]) || |
361 !SkScalarIsInt(fIntervals[1])) { | 349 !SkScalarIsInt(fIntervals[1])) { |
362 return false; | 350 return false; |
363 } | 351 } |
364 | 352 |
365 // TODO: this next test could be eased up. The rescaling should not impact | |
366 // the equality of the ons & offs. However, we would need to remove the | |
367 // integer intervals restriction first | |
368 if (fScaleToFit) { | |
369 return false; | |
370 } | |
371 | |
372 SkPoint pts[2]; | 353 SkPoint pts[2]; |
373 | 354 |
374 if (!src.isLine(pts)) { | 355 if (!src.isLine(pts)) { |
375 return false; | 356 return false; |
376 } | 357 } |
377 | 358 |
378 // TODO: this test could be eased up to allow circles | 359 // TODO: this test could be eased up to allow circles |
379 if (SkPaint::kButt_Cap != rec.getCap()) { | 360 if (SkPaint::kButt_Cap != rec.getCap()) { |
380 return false; | 361 return false; |
381 } | 362 } |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
531 | 512 |
532 SkFlattenable::Factory SkDashPathEffect::getFactory() const { | 513 SkFlattenable::Factory SkDashPathEffect::getFactory() const { |
533 return CreateProc; | 514 return CreateProc; |
534 } | 515 } |
535 | 516 |
536 void SkDashPathEffect::flatten(SkWriteBuffer& buffer) const { | 517 void SkDashPathEffect::flatten(SkWriteBuffer& buffer) const { |
537 this->INHERITED::flatten(buffer); | 518 this->INHERITED::flatten(buffer); |
538 buffer.writeInt(fInitialDashIndex); | 519 buffer.writeInt(fInitialDashIndex); |
539 buffer.writeScalar(fInitialDashLength); | 520 buffer.writeScalar(fInitialDashLength); |
540 buffer.writeScalar(fIntervalLength); | 521 buffer.writeScalar(fIntervalLength); |
541 buffer.writeBool(fScaleToFit); | 522 // Dummy write to stay compatible with old skps. Write will be removed in fo
llow up patch. |
| 523 buffer.writeBool(false); |
542 buffer.writeScalarArray(fIntervals, fCount); | 524 buffer.writeScalarArray(fIntervals, fCount); |
543 } | 525 } |
544 | 526 |
545 SkFlattenable* SkDashPathEffect::CreateProc(SkReadBuffer& buffer) { | 527 SkFlattenable* SkDashPathEffect::CreateProc(SkReadBuffer& buffer) { |
546 return SkNEW_ARGS(SkDashPathEffect, (buffer)); | 528 return SkNEW_ARGS(SkDashPathEffect, (buffer)); |
547 } | 529 } |
548 | 530 |
549 SkDashPathEffect::SkDashPathEffect(SkReadBuffer& buffer) : INHERITED(buffer) { | 531 SkDashPathEffect::SkDashPathEffect(SkReadBuffer& buffer) : INHERITED(buffer) { |
550 fInitialDashIndex = buffer.readInt(); | 532 fInitialDashIndex = buffer.readInt(); |
551 fInitialDashLength = buffer.readScalar(); | 533 fInitialDashLength = buffer.readScalar(); |
552 fIntervalLength = buffer.readScalar(); | 534 fIntervalLength = buffer.readScalar(); |
553 fScaleToFit = buffer.readBool(); | 535 buffer.readBool(); // dummy read to stay compatible with old skps |
554 | 536 |
555 fCount = buffer.getArrayCount(); | 537 fCount = buffer.getArrayCount(); |
556 size_t allocSize = sizeof(SkScalar) * fCount; | 538 size_t allocSize = sizeof(SkScalar) * fCount; |
557 if (buffer.validateAvailable(allocSize)) { | 539 if (buffer.validateAvailable(allocSize)) { |
558 fIntervals = (SkScalar*)sk_malloc_throw(allocSize); | 540 fIntervals = (SkScalar*)sk_malloc_throw(allocSize); |
559 buffer.readScalarArray(fIntervals, fCount); | 541 buffer.readScalarArray(fIntervals, fCount); |
560 } else { | 542 } else { |
561 fIntervals = NULL; | 543 fIntervals = NULL; |
562 } | 544 } |
563 } | 545 } |
OLD | NEW |